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:
@@ -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',
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user