mirror of
http://88.130.71.182:3000/BlitTech/contexta_fe.git
synced 2026-06-13 08:21:22 +00:00
227 lines
8.6 KiB
TypeScript
227 lines
8.6 KiB
TypeScript
import React, { useState } from 'react'
|
||
import { useNavigate } from 'react-router-dom'
|
||
import { useMutation } from '@tanstack/react-query'
|
||
import { billingAPI } from '@/services/api'
|
||
import { useAuthStore } from '@/store/authStore'
|
||
import { Button, Card } from '@/components/ui'
|
||
import { Check, Zap, Building2, Star } from 'lucide-react'
|
||
|
||
const PLANS = [
|
||
{
|
||
id: 'free',
|
||
name: 'Free',
|
||
price: 0,
|
||
description: 'Perfect for testing and development',
|
||
icon: '🆓',
|
||
color: 'gray',
|
||
features: [
|
||
{ text: 'Unlimited chatbot creation', included: true },
|
||
{ text: 'Upload PDF, DOCX, CSV, XLSX', included: true },
|
||
{ text: 'Unlimited preview testing', included: true },
|
||
{ text: 'Shareable preview links', included: true },
|
||
{ text: 'Publish to marketplace', included: false },
|
||
{ text: 'Premium AI models', included: false },
|
||
{ text: 'Code export', included: false },
|
||
{ text: 'Analytics dashboard', included: false },
|
||
],
|
||
},
|
||
{
|
||
id: 'starter',
|
||
name: 'Starter',
|
||
price: 39,
|
||
description: 'For small businesses launching their first chatbot',
|
||
icon: '🚀',
|
||
color: 'blue',
|
||
badge: 'Popular',
|
||
features: [
|
||
{ text: 'Everything in Free', included: true },
|
||
{ text: 'Publish 1 chatbot to marketplace', included: true },
|
||
{ text: 'Fireworks AI models (Llama, Mixtral)', included: true },
|
||
{ text: '5,000 conversations/month', included: true },
|
||
{ text: 'Analytics dashboard', included: true },
|
||
{ text: 'Custom branding', included: true },
|
||
{ text: 'Email support', included: true },
|
||
{ text: 'Premium AI models (GPT-4, Claude)', included: false },
|
||
{ text: 'Code export', included: false },
|
||
],
|
||
},
|
||
{
|
||
id: 'pro',
|
||
name: 'Pro',
|
||
price: 119,
|
||
description: 'For growing businesses with multiple products',
|
||
icon: '⚡',
|
||
color: 'purple',
|
||
highlighted: true,
|
||
features: [
|
||
{ text: 'Everything in Starter', included: true },
|
||
{ text: 'Build & publish 3 chatbots', included: true },
|
||
{ text: 'GPT-4o, Claude 3.5, Gemini 1.5', included: true },
|
||
{ text: '20,000 conversations/month', included: true },
|
||
{ text: 'Code export (FastAPI + React widget)', included: true },
|
||
{ text: 'Advanced analytics', included: true },
|
||
{ text: 'Remove "Powered by" badge', included: true },
|
||
{ text: 'Priority support', included: true },
|
||
{ text: 'Custom domain', included: true },
|
||
],
|
||
},
|
||
{
|
||
id: 'enterprise',
|
||
name: 'Enterprise',
|
||
price: null,
|
||
description: 'For large organizations with custom needs',
|
||
icon: '🏢',
|
||
color: 'orange',
|
||
features: [
|
||
{ text: 'Everything in Pro', included: true },
|
||
{ text: 'Unlimited chatbots', included: true },
|
||
{ text: 'Unlimited conversations', included: true },
|
||
{ text: 'Custom model fine-tuning', included: true },
|
||
{ text: 'White-label platform', included: true },
|
||
{ text: 'SSO (SAML)', included: true },
|
||
{ text: 'SLA guarantees', included: true },
|
||
{ text: 'Dedicated account manager', included: true },
|
||
{ text: '24/7 phone support', included: true },
|
||
],
|
||
},
|
||
]
|
||
|
||
export const PricingPage: React.FC = () => {
|
||
const { user } = useAuthStore()
|
||
const navigate = useNavigate()
|
||
const [loading, setLoading] = useState<string | null>(null)
|
||
|
||
const handleSubscribe = async (planId: string) => {
|
||
if (!user) { navigate('/login'); return }
|
||
if (planId === 'enterprise') {
|
||
window.open('mailto:enterprise@contexta.ai?subject=Enterprise Inquiry', '_blank')
|
||
return
|
||
}
|
||
if (planId === 'free') {
|
||
navigate('/dashboard')
|
||
return
|
||
}
|
||
|
||
setLoading(planId)
|
||
try {
|
||
const { checkout_url } = await billingAPI.createCheckout(
|
||
planId,
|
||
`${window.location.origin}/settings/billing?success=true`,
|
||
`${window.location.origin}/pricing`
|
||
)
|
||
window.location.href = checkout_url
|
||
} catch (err: any) {
|
||
alert(err.response?.data?.detail || 'Failed to create checkout session')
|
||
} finally {
|
||
setLoading(null)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="p-6 max-w-6xl mx-auto">
|
||
<div className="text-center mb-12">
|
||
<h1 className="text-3xl font-bold text-gray-900 mb-3">Simple, transparent pricing</h1>
|
||
<p className="text-gray-500 max-w-xl mx-auto">
|
||
Start free and build as many chatbots as you want. Upgrade when you're ready to publish and go live.
|
||
</p>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
|
||
{PLANS.map((plan) => (
|
||
<div
|
||
key={plan.id}
|
||
className={`relative rounded-2xl border p-6 flex flex-col ${
|
||
plan.highlighted
|
||
? 'border-primary-400 bg-gradient-to-b from-primary-50 to-white shadow-lg shadow-primary-100'
|
||
: 'border-gray-200 bg-white'
|
||
}`}
|
||
>
|
||
{plan.badge && (
|
||
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
||
<span className="bg-primary-600 text-white text-xs font-semibold px-3 py-1 rounded-full">
|
||
{plan.badge}
|
||
</span>
|
||
</div>
|
||
)}
|
||
|
||
<div className="mb-6">
|
||
<div className="text-3xl mb-2">{plan.icon}</div>
|
||
<h2 className="text-xl font-bold text-gray-900">{plan.name}</h2>
|
||
<p className="text-sm text-gray-500 mt-1 min-h-[40px]">{plan.description}</p>
|
||
<div className="mt-4">
|
||
{plan.price !== null ? (
|
||
<div className="flex items-baseline gap-1">
|
||
<span className="text-3xl font-bold text-gray-900">${plan.price}</span>
|
||
<span className="text-gray-500 text-sm">/month</span>
|
||
</div>
|
||
) : (
|
||
<span className="text-2xl font-bold text-gray-900">Custom</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<ul className="space-y-2.5 flex-1 mb-6">
|
||
{plan.features.map(({ text, included }) => (
|
||
<li key={text} className="flex items-start gap-2.5">
|
||
<div className={`w-4 h-4 rounded-full flex-shrink-0 mt-0.5 flex items-center justify-center ${
|
||
included ? 'bg-green-100 text-green-600' : 'bg-gray-100 text-gray-400'
|
||
}`}>
|
||
{included ? <Check className="w-2.5 h-2.5" /> : <span className="text-xs">–</span>}
|
||
</div>
|
||
<span className={`text-sm ${included ? 'text-gray-700' : 'text-gray-400'}`}>{text}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
|
||
<Button
|
||
variant={plan.highlighted ? 'primary' : 'outline'}
|
||
className="w-full"
|
||
loading={loading === plan.id}
|
||
onClick={() => handleSubscribe(plan.id)}
|
||
disabled={user?.plan === plan.id}
|
||
>
|
||
{user?.plan === plan.id
|
||
? 'Current Plan'
|
||
: plan.price === null
|
||
? 'Contact Sales'
|
||
: plan.price === 0
|
||
? 'Get Started Free'
|
||
: `Subscribe – $${plan.price}/mo`}
|
||
</Button>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* FAQ */}
|
||
<div className="mt-16 max-w-2xl mx-auto">
|
||
<h2 className="text-xl font-bold text-gray-900 mb-6 text-center">Frequently Asked Questions</h2>
|
||
<div className="space-y-4">
|
||
{[
|
||
{
|
||
q: 'What is preview mode?',
|
||
a: 'Preview mode lets you build and test your chatbot for free with unlimited conversations. Only you (and people you share the link with) can access it until you publish.'
|
||
},
|
||
{
|
||
q: 'Can I cancel anytime?',
|
||
a: 'Yes, you can cancel anytime. Your chatbots will remain in preview mode but will be removed from the marketplace.'
|
||
},
|
||
{
|
||
q: 'What is code export?',
|
||
a: 'Pro plan users can export their chatbot as a complete, production-ready package including a FastAPI backend and React TypeScript widget — giving you full control to self-host.'
|
||
},
|
||
{
|
||
q: 'Do I need my own API keys?',
|
||
a: 'No! Your API keys are handled by Contexta. However, if you export the code, you\'ll need your own API keys for self-hosted deployment.'
|
||
},
|
||
].map(({ q, a }) => (
|
||
<div key={q} className="bg-white border border-gray-200 rounded-xl p-4">
|
||
<h3 className="font-semibold text-gray-900 text-sm mb-1">{q}</h3>
|
||
<p className="text-sm text-gray-500">{a}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|