Add missing frontend platform components and update production deployment

This commit adds all previously untracked files and modifications needed for production deployment:
- New marketing components (BenefitsSection, CodeBlock, PluginShowcase, PricingTable)
- Platform admin components (EditPlatformEntityModal, PlatformListRow, PlatformListing, PlatformTable)
- Updated deployment configuration and scripts
- Various frontend API and component improvements

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-11-30 19:49:06 -05:00
parent 0d1a3045fb
commit 2b321aef57
34 changed files with 1930 additions and 1291 deletions

View File

@@ -1,122 +1,56 @@
import React, { useState } from 'react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import PricingCard from '../../components/marketing/PricingCard';
import PricingTable from '../../components/marketing/PricingTable';
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>
<div className="bg-gray-50 dark:bg-gray-900 min-h-screen">
{/* Header */}
<div className="pt-24 pb-12 text-center px-4 sm:px-6 lg:px-8">
<h1 className="text-4xl font-bold text-gray-900 dark:text-white mb-4">
Simple, Transparent Pricing
</h1>
<p className="text-xl text-gray-600 dark:text-gray-400 max-w-2xl mx-auto">
Start for free, upgrade as you grow. No hidden fees.
</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>
{/* Pricing Table */}
<div className="pb-20">
<PricingTable />
</div>
{/* 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>
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 pb-20">
<h2 className="text-3xl font-bold text-center text-gray-900 dark:text-white mb-12">
Frequently Asked Questions
</h2>
<FAQAccordion items={[
{
question: "Do I need to know Python to use SmoothSchedule?",
answer: "Not at all! You can use our pre-built plugins from the marketplace for common tasks like email reminders and reports. Python is only needed if you want to write custom scripts."
},
{
question: "What happens if I exceed my plan's limits?",
answer: "We'll notify you when you're close to your limit. If you exceed it, we'll give you a grace period to upgrade. We won't cut off your service immediately."
},
{
question: "Can I use my own domain name?",
answer: "Yes! On the Pro and Business plans, you can connect your own custom domain (e.g., booking.yourcompany.com) for a fully branded experience."
},
{
question: "Is my data safe?",
answer: "Absolutely. We use dedicated secure vaults (Postgres schemas) to physically isolate your data from other customers. It's the gold standard for multi-tenant security."
}
]} />
</div>
<FAQAccordion items={faqItems} />
</div>
</section>
{/* CTA Section */}
<CTASection variant="minimal" />
{/* CTA */}
<CTASection />
</div>
);
};