GrantForge Bot
Deploy to Hugging Face
afd56bc
import React, { useState } from 'react';
import { createPortal } from 'react-dom';
import { Check, X, Loader2 } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { apiClient } from '../../api/client';
import toast from 'react-hot-toast';
interface Props {
onClose: () => void;
}
const PricingModal: React.FC<Props> = ({ onClose }) => {
const [loadingPlan, setLoadingPlan] = useState<string | null>(null);
const handleUpgrade = async (plan: string) => {
try {
setLoadingPlan(plan);
// Mocking subscription upgrade instead of real Stripe call for now
setTimeout(() => {
setLoadingPlan(null);
toast.success(`Zmieniono plan subskrypcji na: ${plan.toUpperCase()}`);
onClose();
}, 1500);
} catch (err) {
console.error(err);
setLoadingPlan(null);
toast.error("B艂膮d zmiany planu.");
}
};
return createPortal(
<AnimatePresence>
<motion.div
className="pricing-backdrop"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.85)', backdropFilter: 'blur(5px)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 9999 }}
>
<motion.div
className="glass-card pricing-modal-card"
style={{ width: '1100px', maxWidth: '95%', maxHeight: '95vh', overflowY: 'auto', background: '#0B0E14', padding: '2.5rem' }}
initial="hidden" animate="show" exit="hidden"
variants={{
hidden: { opacity: 0, scale: 0.95, y: 20 },
show: { opacity: 1, scale: 1, y: 0, transition: { staggerChildren: 0.15, ease: "easeOut", duration: 0.4 } }
}}
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '2rem' }}>
<h2>Rozszerz limity AI do maksimum</h2>
<X size={24} style={{ cursor: 'pointer', color: 'var(--text-muted)' }} onClick={onClose} />
</div>
<div className="pricing-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '2rem' }}>
{/* Free */}
<motion.div variants={{hidden: {opacity: 0, y: 30}, show: {opacity: 1, y: 0}}} className="glass-card" style={{ border: '1px solid var(--border-subtle)', opacity: 0.8, display: 'flex', flexDirection: 'column' }}>
<div style={{ color: 'var(--text-muted)', fontSize: '0.85rem', textTransform: 'uppercase', letterSpacing: '1px', fontWeight: 'bold' }}>Plan Podstawowy</div>
<div style={{ fontSize: '2.5rem', fontWeight: 800, margin: '1rem 0', color: 'var(--text-primary)' }}>0 PLN<span style={{ fontSize: '1rem', color: 'var(--text-muted)'}}>/mc</span></div>
<p style={{ fontSize: '0.9rem', color: 'var(--text-muted)', lineHeight: '1.4' }}>Dobre na start, by sprawdzi膰 mo偶liwo艣ci Kreatora AI.</p>
<ul style={{ listStyle: 'none', padding: 0, margin: '1.5rem 0 2rem 0', display: 'flex', flexDirection: 'column', gap: '1rem', flex: 1 }}>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem' }}><Check size={18} color="var(--accent-green)" style={{flexShrink:0}}/> Ograniczone iteracje: 15 / 24h</li>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem' }}><Check size={18} color="var(--accent-green)" style={{flexShrink:0}}/> Do 100K token贸w AI / mc</li>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem', color: 'var(--text-muted)' }}><X size={18} style={{flexShrink:0}}/> Brak dedykowanych baz RAG</li>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem', color: 'var(--text-muted)' }}><X size={18} style={{flexShrink:0}}/> Tylko podgl膮d wniosk贸w</li>
</ul>
<button className="btn btn-secondary" style={{ width: '100%', cursor: 'not-allowed' }} disabled>Plan Aktualny</button>
</motion.div>
{/* Pro */}
<motion.div variants={{hidden: {opacity: 0, y: 30}, show: {opacity: 1, y: 0}}} className="glass-card" style={{ border: '2px solid var(--accent-green)', position: 'relative', transform: 'scale(1.05)', zIndex: 1, boxShadow: '0 0 30px rgba(16, 185, 129, 0.15)', display: 'flex', flexDirection: 'column' }}>
<div style={{ position: 'absolute', top: '-14px', background: 'linear-gradient(90deg, #10B981, #059669)', padding: '4px 16px', borderRadius: '12px', fontSize: '0.75rem', fontWeight: "bold", left: '50%', transform: 'translateX(-50%)', whiteSpace: 'nowrap', boxShadow: '0 5px 15px rgba(16,185,129,0.3)' }}>NAJLEPSZY WYB脫R</div>
<div style={{ color: 'var(--accent-green)', fontSize: '0.85rem', textTransform: 'uppercase', letterSpacing: '1px', fontWeight: 'bold' }}>Pro</div>
<div style={{ fontSize: '3rem', fontWeight: 800, margin: '1rem 0', color: '#fff' }}>99 PLN<span style={{ fontSize: '1.2rem', color: 'var(--text-muted)'}}>/mc</span></div>
<p style={{ fontSize: '0.9rem', color: 'rgba(255,255,255,0.8)', lineHeight: '1.4' }}>Idealny dla firm doradczych optymalizuj膮cych czas pracy.</p>
<ul style={{ listStyle: 'none', padding: 0, margin: '1.5rem 0 2rem 0', display: 'flex', flexDirection: 'column', gap: '1rem', flex: 1 }}>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem', fontWeight: 600 }}><Check size={18} color="var(--accent-green)" style={{flexShrink:0}}/> Bezpieczne, 50 iteracji / 24h</li>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem', fontWeight: 600 }}><Check size={18} color="var(--accent-green)" style={{flexShrink:0}}/> Mocne 500K token贸w AI / mc</li>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem', fontWeight: 600 }}><Check size={18} color="var(--accent-green)" style={{flexShrink:0}}/> Eksporty gotowych um贸w DOCX / PDF</li>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem' }}><Check size={18} color="var(--accent-green)" style={{flexShrink:0}}/> Priorytetowe wsparcie Critic'a</li>
</ul>
<motion.button
whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }}
className="btn btn-primary"
style={{ width: '100%', background: 'linear-gradient(90deg, #10B981, #059669)', fontSize: '1.1rem', fontWeight: 800, padding: '1rem', boxShadow: '0 5px 15px rgba(16,185,129,0.3)', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.5rem' }}
onClick={() => handleUpgrade('pro')}
disabled={loadingPlan !== null}
>
{loadingPlan === 'pro' && <Loader2 className="spinner" size={20} style={{ animation: 'spin 1s linear infinite' }} />}
Przejd藕 na Pro
</motion.button>
</motion.div>
{/* Business */}
<motion.div variants={{hidden: {opacity: 0, y: 30}, show: {opacity: 1, y: 0}}} className="glass-card" style={{ border: '1px solid var(--accent-blue)', display: 'flex', flexDirection: 'column' }}>
<div style={{ color: 'var(--accent-blue)', fontSize: '0.85rem', textTransform: 'uppercase', letterSpacing: '1px', fontWeight: 'bold' }}>Business Enterprise</div>
<div style={{ fontSize: '2.5rem', fontWeight: 800, margin: '1rem 0', color: 'var(--text-primary)' }}>299 PLN<span style={{ fontSize: '1rem', color: 'var(--text-muted)'}}>/mc</span></div>
<p style={{ fontSize: '0.9rem', color: 'var(--text-muted)', lineHeight: '1.4' }}>Rozwi膮zanie bezkompromisowe dla dzia艂贸w R&D.</p>
<ul style={{ listStyle: 'none', padding: 0, margin: '1.5rem 0 2rem 0', display: 'flex', flexDirection: 'column', gap: '1rem', flex: 1 }}>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem' }}><Check size={18} color="var(--accent-blue)" style={{flexShrink:0}}/> Nielimitowane Iteracje</li>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem' }}><Check size={18} color="var(--accent-blue)" style={{flexShrink:0}}/> Do 2M+ Token贸w AI / 30 dni</li>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem' }}><Check size={18} color="var(--accent-blue)" style={{flexShrink:0}}/> Customowe RAG i wektoryzacja bazy</li>
<li style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-start', fontSize: '0.9rem' }}><Check size={18} color="var(--accent-blue)" style={{flexShrink:0}}/> Personalizowany Expert-AI</li>
</ul>
<motion.button
whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }}
className="btn btn-secondary"
style={{ width: '100%', color: 'var(--accent-blue)', borderColor: 'rgba(59, 130, 246, 0.4)', background: 'rgba(59, 130, 246, 0.05)', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.5rem' }}
onClick={() => handleUpgrade('business')}
disabled={loadingPlan !== null}
>
{loadingPlan === 'business' && <Loader2 className="spinner" size={20} style={{ animation: 'spin 1s linear infinite' }} />}
Kontakt z Sales
</motion.button>
</motion.div>
</div>
</motion.div>
</motion.div>
</AnimatePresence>,
document.body
);
};
export default PricingModal;