Initial commit: SmoothSchedule multi-tenant scheduling platform
This commit includes: - Django backend with multi-tenancy (django-tenants) - React + TypeScript frontend with Vite - Platform administration API with role-based access control - Authentication system with token-based auth - Quick login dev tools for testing different user roles - CORS and CSRF configuration for local development - Docker development environment setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
124
frontend/src/pages/marketing/PricingPage.tsx
Normal file
124
frontend/src/pages/marketing/PricingPage.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PricingCard from '../../components/marketing/PricingCard';
|
||||
import FAQAccordion from '../../components/marketing/FAQAccordion';
|
||||
import CTASection from '../../components/marketing/CTASection';
|
||||
|
||||
const PricingPage: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [billingPeriod, setBillingPeriod] = useState<'monthly' | 'annual'>('monthly');
|
||||
|
||||
const faqItems = [
|
||||
{
|
||||
question: t('marketing.faq.questions.freePlan.question'),
|
||||
answer: t('marketing.faq.questions.freePlan.answer'),
|
||||
},
|
||||
{
|
||||
question: t('marketing.faq.questions.cancel.question'),
|
||||
answer: t('marketing.faq.questions.cancel.answer'),
|
||||
},
|
||||
{
|
||||
question: t('marketing.faq.questions.payment.question'),
|
||||
answer: t('marketing.faq.questions.payment.answer'),
|
||||
},
|
||||
{
|
||||
question: t('marketing.faq.questions.migrate.question'),
|
||||
answer: t('marketing.faq.questions.migrate.answer'),
|
||||
},
|
||||
{
|
||||
question: t('marketing.faq.questions.support.question'),
|
||||
answer: t('marketing.faq.questions.support.answer'),
|
||||
},
|
||||
{
|
||||
question: t('marketing.faq.questions.customDomain.question'),
|
||||
answer: t('marketing.faq.questions.customDomain.answer'),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Header Section */}
|
||||
<section className="py-20 lg:py-28 bg-gradient-to-br from-white via-brand-50/30 to-white dark:from-gray-900 dark:via-gray-900 dark:to-gray-900">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-12">
|
||||
<h1 className="text-4xl sm:text-5xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
{t('marketing.pricing.title')}
|
||||
</h1>
|
||||
<p className="text-lg text-gray-600 dark:text-gray-400 max-w-2xl mx-auto">
|
||||
{t('marketing.pricing.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Billing Toggle */}
|
||||
<div className="flex flex-wrap items-center justify-center gap-3 mb-12">
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className={`text-sm font-medium whitespace-nowrap transition-colors ${
|
||||
billingPeriod === 'monthly'
|
||||
? 'text-gray-900 dark:text-white'
|
||||
: 'text-gray-500 dark:text-gray-400'
|
||||
}`}
|
||||
>
|
||||
{t('marketing.pricing.monthly')}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setBillingPeriod(billingPeriod === 'monthly' ? 'annual' : 'monthly')}
|
||||
className="relative flex-shrink-0 w-12 h-6 bg-brand-600 rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900"
|
||||
aria-label="Toggle billing period"
|
||||
>
|
||||
<span
|
||||
className={`absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full shadow transition-transform duration-200 ${
|
||||
billingPeriod === 'annual' ? 'translate-x-6' : 'translate-x-0'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
<span
|
||||
className={`text-sm font-medium whitespace-nowrap transition-colors ${
|
||||
billingPeriod === 'annual'
|
||||
? 'text-gray-900 dark:text-white'
|
||||
: 'text-gray-500 dark:text-gray-400'
|
||||
}`}
|
||||
>
|
||||
{t('marketing.pricing.annual')}
|
||||
</span>
|
||||
</div>
|
||||
{billingPeriod === 'annual' && (
|
||||
<span className="px-2 py-1 text-xs font-semibold text-brand-700 bg-brand-100 dark:bg-brand-900/30 dark:text-brand-300 rounded-full whitespace-nowrap">
|
||||
{t('marketing.pricing.annualSave')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Pricing Cards */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8">
|
||||
<PricingCard tier="free" billingPeriod={billingPeriod} />
|
||||
<PricingCard tier="professional" billingPeriod={billingPeriod} highlighted />
|
||||
<PricingCard tier="business" billingPeriod={billingPeriod} />
|
||||
<PricingCard tier="enterprise" billingPeriod={billingPeriod} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* FAQ Section */}
|
||||
<section className="py-20 lg:py-28 bg-gray-50 dark:bg-gray-800/50">
|
||||
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
{t('marketing.faq.title')}
|
||||
</h2>
|
||||
<p className="text-lg text-gray-600 dark:text-gray-400">
|
||||
{t('marketing.faq.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<FAQAccordion items={faqItems} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<CTASection variant="minimal" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PricingPage;
|
||||
Reference in New Issue
Block a user