feat(i18n): Comprehensive internationalization of frontend components and pages

Translate all hardcoded English strings to use i18n translation keys:

Components:
- TransactionDetailModal: payment details, refunds, technical info
- ConnectOnboarding/ConnectOnboardingEmbed: Stripe Connect setup
- StripeApiKeysForm: API key management
- DomainPurchase: domain registration flow
- Sidebar: navigation labels
- Schedule/Sidebar, PendingSidebar: scheduler UI
- MasqueradeBanner: masquerade status
- Dashboard widgets: metrics, capacity, customers, tickets
- Marketing: PricingTable, PluginShowcase, BenefitsSection
- ConfirmationModal, ServiceList: common UI

Pages:
- Staff: invitation flow, role management
- Customers: form placeholders
- Payments: transactions, payouts, billing
- BookingSettings: URL and redirect configuration
- TrialExpired: upgrade prompts and features
- PlatformSettings, PlatformBusinesses: admin UI
- HelpApiDocs: API documentation

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-03 21:40:54 -05:00
parent 902582f4ba
commit c7f241b30a
34 changed files with 1313 additions and 592 deletions

View File

@@ -1,33 +1,36 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Rocket, Shield, Zap, Headphones } from 'lucide-react';
const BenefitsSection: React.FC = () => {
const { t } = useTranslation();
const benefits = [
{
icon: Rocket,
title: 'Rapid Deployment',
description: 'Launch your branded booking portal in minutes with our pre-configured industry templates.',
title: t('marketing.benefits.rapidDeployment.title'),
description: t('marketing.benefits.rapidDeployment.description'),
color: 'text-blue-600 dark:text-blue-400',
bgColor: 'bg-blue-100 dark:bg-blue-900/30',
},
{
icon: Shield,
title: 'Enterprise Security',
description: 'Sleep soundly knowing your data is physically isolated in its own dedicated secure vault.',
title: t('marketing.benefits.enterpriseSecurity.title'),
description: t('marketing.benefits.enterpriseSecurity.description'),
color: 'text-green-600 dark:text-green-400',
bgColor: 'bg-green-100 dark:bg-green-900/30',
},
{
icon: Zap,
title: 'High Performance',
description: 'Built on a modern, edge-cached architecture to ensure instant loading times globally.',
title: t('marketing.benefits.highPerformance.title'),
description: t('marketing.benefits.highPerformance.description'),
color: 'text-purple-600 dark:text-purple-400',
bgColor: 'bg-purple-100 dark:bg-purple-900/30',
},
{
icon: Headphones,
title: 'Expert Support',
description: 'Our team of scheduling experts is available to help you optimize your automation workflows.',
title: t('marketing.benefits.expertSupport.title'),
description: t('marketing.benefits.expertSupport.description'),
color: 'text-orange-600 dark:text-orange-400',
bgColor: 'bg-orange-100 dark:bg-orange-900/30',
},

View File

@@ -1,9 +1,11 @@
import React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Mail, Calendar, Bell, ArrowRight, Zap, CheckCircle2, Code, LayoutGrid } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import CodeBlock from './CodeBlock';
const PluginShowcase: React.FC = () => {
const { t } = useTranslation();
const [activeTab, setActiveTab] = useState(0);
const [viewMode, setViewMode] = useState<'marketplace' | 'code'>('marketplace');
@@ -11,69 +13,29 @@ const PluginShowcase: React.FC = () => {
{
id: 'winback',
icon: Mail,
title: 'Client Win-Back',
description: 'Automatically re-engage customers who haven\'t visited in 60 days.',
stats: ['+15% Retention', '$4k/mo Revenue'],
title: t('marketing.plugins.examples.winback.title'),
description: t('marketing.plugins.examples.winback.description'),
stats: [t('marketing.plugins.examples.winback.stats.retention'), t('marketing.plugins.examples.winback.stats.revenue')],
marketplaceImage: 'bg-gradient-to-br from-pink-500 to-rose-500',
code: `# Win back lost customers
days_inactive = 60
discount = "20%"
# Find inactive customers
inactive = api.get_customers(
last_visit_lt=days_ago(days_inactive)
)
# Send personalized offer
for customer in inactive:
api.send_email(
to=customer.email,
subject="We miss you!",
body=f"Come back for {discount} off!"
)`,
code: t('marketing.plugins.examples.winback.code'),
},
{
id: 'noshow',
icon: Bell,
title: 'No-Show Prevention',
description: 'Send SMS reminders 2 hours before appointments to reduce no-shows.',
stats: ['-40% No-Shows', 'Better Utilization'],
title: t('marketing.plugins.examples.noshow.title'),
description: t('marketing.plugins.examples.noshow.description'),
stats: [t('marketing.plugins.examples.noshow.stats.reduction'), t('marketing.plugins.examples.noshow.stats.utilization')],
marketplaceImage: 'bg-gradient-to-br from-blue-500 to-cyan-500',
code: `# Prevent no-shows
hours_before = 2
# Find upcoming appointments
upcoming = api.get_appointments(
start_time__within=hours(hours_before)
)
# Send SMS reminder
for appt in upcoming:
api.send_sms(
to=appt.customer.phone,
body=f"Reminder: Appointment in 2h at {appt.time}"
)`,
code: t('marketing.plugins.examples.noshow.code'),
},
{
id: 'report',
icon: Calendar,
title: 'Daily Reports',
description: 'Get a summary of tomorrow\'s schedule sent to your inbox every evening.',
stats: ['Save 30min/day', 'Full Visibility'],
title: t('marketing.plugins.examples.report.title'),
description: t('marketing.plugins.examples.report.description'),
stats: [t('marketing.plugins.examples.report.stats.timeSaved'), t('marketing.plugins.examples.report.stats.visibility')],
marketplaceImage: 'bg-gradient-to-br from-purple-500 to-indigo-500',
code: `# Daily Manager Report
tomorrow = date.today() + timedelta(days=1)
# Get schedule stats
stats = api.get_schedule_stats(date=tomorrow)
revenue = api.forecast_revenue(date=tomorrow)
# Email manager
api.send_email(
to="manager@business.com",
subject=f"Schedule for {tomorrow}",
body=f"Bookings: {stats.count}, Est. Rev: \${revenue}"
)`,
code: t('marketing.plugins.examples.report.code'),
},
];
@@ -88,16 +50,15 @@ api.send_email(
<div>
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-brand-100 dark:bg-brand-900/30 text-brand-600 dark:text-brand-400 text-sm font-medium mb-6">
<Zap className="w-4 h-4" />
<span>Limitless Automation</span>
<span>{t('marketing.plugins.badge')}</span>
</div>
<h2 className="text-4xl font-bold text-gray-900 dark:text-white mb-6">
Choose from our Marketplace, or build your own.
{t('marketing.plugins.headline')}
</h2>
<p className="text-lg text-gray-600 dark:text-gray-400 mb-10">
Browse hundreds of pre-built plugins to automate your workflows instantly.
Need something custom? Developers can write Python scripts to extend the platform endlessly.
{t('marketing.plugins.subheadline')}
</p>
<div className="space-y-4">
@@ -147,7 +108,7 @@ api.send_email(
}`}
>
<LayoutGrid className="w-4 h-4" />
Marketplace
{t('marketing.plugins.viewToggle.marketplace')}
</button>
<button
onClick={() => setViewMode('code')}
@@ -157,7 +118,7 @@ api.send_email(
}`}
>
<Code className="w-4 h-4" />
Developer
{t('marketing.plugins.viewToggle.developer')}
</button>
</div>
@@ -190,10 +151,10 @@ api.send_email(
<div className="flex justify-between items-start mb-4">
<div>
<h3 className="text-xl font-bold text-gray-900 dark:text-white">{examples[activeTab].title}</h3>
<div className="text-sm text-gray-500">by SmoothSchedule Team</div>
<div className="text-sm text-gray-500">{t('marketing.plugins.marketplaceCard.author')}</div>
</div>
<button className="px-4 py-2 bg-brand-600 text-white rounded-lg font-medium text-sm hover:bg-brand-700 transition-colors">
Install Plugin
{t('marketing.plugins.marketplaceCard.installButton')}
</button>
</div>
<p className="text-gray-600 dark:text-gray-300 mb-6">
@@ -205,7 +166,7 @@ api.send_email(
<div key={i} className="w-6 h-6 rounded-full bg-gray-300 border-2 border-white dark:border-gray-800" />
))}
</div>
<span>Used by 1,200+ businesses</span>
<span>{t('marketing.plugins.marketplaceCard.usedBy')}</span>
</div>
</div>
</div>
@@ -220,7 +181,7 @@ api.send_email(
{/* CTA */}
<div className="mt-6 text-right">
<a href="/features" className="inline-flex items-center gap-2 text-brand-600 dark:text-brand-400 font-medium hover:underline">
Explore the Marketplace <ArrowRight className="w-4 h-4" />
{t('marketing.plugins.cta')} <ArrowRight className="w-4 h-4" />
</a>
</div>
</motion.div>

View File

@@ -1,69 +1,72 @@
import React from 'react';
import { Check, X } from 'lucide-react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
const PricingTable: React.FC = () => {
const { t } = useTranslation();
const tiers = [
{
name: 'Starter',
name: t('marketing.pricing.tiers.starter.name'),
price: '$0',
period: '/month',
description: 'Perfect for solo practitioners and small studios.',
period: t('marketing.pricing.perMonth'),
description: t('marketing.pricing.tiers.starter.description'),
features: [
'1 User',
'Unlimited Appointments',
'1 Active Automation',
'Basic Reporting',
'Email Support',
t('marketing.pricing.tiers.starter.features.0'),
t('marketing.pricing.tiers.starter.features.1'),
t('marketing.pricing.tiers.starter.features.2'),
t('marketing.pricing.tiers.starter.features.3'),
t('marketing.pricing.tiers.starter.features.4'),
],
notIncluded: [
'Custom Domain',
'Python Scripting',
'White-Labeling',
'Priority Support',
t('marketing.pricing.tiers.starter.notIncluded.0'),
t('marketing.pricing.tiers.starter.notIncluded.1'),
t('marketing.pricing.tiers.starter.notIncluded.2'),
t('marketing.pricing.tiers.starter.notIncluded.3'),
],
cta: 'Start Free',
cta: t('marketing.pricing.tiers.starter.cta'),
ctaLink: '/signup',
popular: false,
},
{
name: 'Pro',
name: t('marketing.pricing.tiers.pro.name'),
price: '$29',
period: '/month',
description: 'For growing businesses that need automation.',
period: t('marketing.pricing.perMonth'),
description: t('marketing.pricing.tiers.pro.description'),
features: [
'5 Users',
'Unlimited Appointments',
'5 Active Automations',
'Advanced Reporting',
'Priority Email Support',
'SMS Reminders',
t('marketing.pricing.tiers.pro.features.0'),
t('marketing.pricing.tiers.pro.features.1'),
t('marketing.pricing.tiers.pro.features.2'),
t('marketing.pricing.tiers.pro.features.3'),
t('marketing.pricing.tiers.pro.features.4'),
t('marketing.pricing.tiers.pro.features.5'),
],
notIncluded: [
'Custom Domain',
'Python Scripting',
'White-Labeling',
t('marketing.pricing.tiers.pro.notIncluded.0'),
t('marketing.pricing.tiers.pro.notIncluded.1'),
t('marketing.pricing.tiers.pro.notIncluded.2'),
],
cta: 'Start Trial',
cta: t('marketing.pricing.tiers.pro.cta'),
ctaLink: '/signup?plan=pro',
popular: true,
},
{
name: 'Business',
name: t('marketing.pricing.tiers.business.name'),
price: '$99',
period: '/month',
description: 'Full power of the platform for serious operations.',
period: t('marketing.pricing.perMonth'),
description: t('marketing.pricing.tiers.business.description'),
features: [
'Unlimited Users',
'Unlimited Appointments',
'Unlimited Automations',
'Custom Python Scripts',
'Custom Domain (White-Label)',
'Dedicated Support',
'API Access',
t('marketing.pricing.tiers.business.features.0'),
t('marketing.pricing.tiers.business.features.1'),
t('marketing.pricing.tiers.business.features.2'),
t('marketing.pricing.tiers.business.features.3'),
t('marketing.pricing.tiers.business.features.4'),
t('marketing.pricing.tiers.business.features.5'),
t('marketing.pricing.tiers.business.features.6'),
],
notIncluded: [],
cta: 'Contact Sales',
cta: t('marketing.pricing.contactSales'),
ctaLink: '/contact',
popular: false,
},
@@ -81,7 +84,7 @@ const PricingTable: React.FC = () => {
>
{tier.popular && (
<div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 px-4 py-1 bg-brand-500 text-white text-sm font-medium rounded-full">
Most Popular
{t('marketing.pricing.mostPopular')}
</div>
)}