ScanMenu / src /app /pricing /page.tsx
HamzaAri's picture
πŸš€ Deploy ScanMenu - Production-ready SaaS web app for digital restaurant menus & QR ordering
e1ef9fc verified
'use client';
import Link from 'next/link';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import {
Sparkles,
ArrowRight,
ArrowLeft,
Check,
Zap,
Crown,
Building2,
HelpCircle,
} from 'lucide-react';
import { useState } from 'react';
import { cn } from '@/lib/utils';
const plans = [
{
name: 'Free',
price: 0,
yearlyPrice: 0,
description: 'Try ScanMenu for free with basic features.',
icon: Sparkles,
features: [
'1 restaurant',
'Up to 15 menu items',
'2 QR codes',
'Basic ordering',
'50 orders/month',
'Community support',
],
cta: 'Get Started',
popular: false,
},
{
name: 'Starter',
price: 29,
yearlyPrice: 24,
description: 'Perfect for small restaurants just getting started.',
icon: Zap,
features: [
'1 restaurant',
'Up to 50 menu items',
'10 QR codes',
'Dine-in + Takeaway',
'Basic analytics',
'Email support',
'500 orders/month',
'Custom branding',
],
cta: 'Start Trial',
popular: false,
},
{
name: 'Pro',
price: 79,
yearlyPrice: 66,
description: 'For growing restaurants with advanced needs.',
icon: Crown,
features: [
'Unlimited restaurants',
'Unlimited menu items',
'Unlimited QR codes',
'Dine-in + Takeaway + Delivery',
'Advanced analytics',
'Priority support',
'Unlimited orders',
'Custom branding',
'API access',
'Team members (up to 10)',
'Real-time order tracking',
'Product options & extras',
],
cta: 'Start Trial',
popular: true,
},
{
name: 'Enterprise',
price: 199,
yearlyPrice: 166,
description: 'For multi-location chains and franchises.',
icon: Building2,
features: [
'Everything in Pro',
'Multi-location dashboard',
'Unlimited team members',
'Dedicated account manager',
'Custom integrations',
'99.9% SLA guarantee',
'White-label solution',
'Onboarding training',
'Advanced security',
'Custom API limits',
'Phone support',
'Invoice billing',
],
cta: 'Contact Sales',
popular: false,
},
];
const faq = [
{ q: 'Can I try ScanMenu for free?', a: 'Yes! Our Free plan lets you try ScanMenu with basic features at no cost. We also offer a 14-day free trial on all paid plans β€” no credit card required.' },
{ q: 'How do QR codes work?', a: 'Each QR code is linked to a specific table or order type (takeaway/delivery). Customers scan with their phone camera and instantly see your menu β€” no app download needed.' },
{ q: 'Can I switch plans later?', a: 'Absolutely! You can upgrade or downgrade your plan at any time. Changes take effect at the start of your next billing cycle, and we prorate any differences.' },
{ q: 'Do customers need to install an app?', a: 'No! That\'s the beauty of ScanMenu. Customers simply scan a QR code and your menu opens in their phone\'s browser. Zero friction, zero downloads.' },
{ q: 'What payment methods do you accept?', a: 'We accept all major credit cards (Visa, Mastercard, Amex, Discover) through Stripe. Enterprise customers can also pay via invoice.' },
{ q: 'Is my data secure?', a: 'Yes. We use Supabase (built on PostgreSQL) with row-level security, SSL encryption, and regular backups. Your data is safe and compliant.' },
];
export default function PricingPage() {
const [annual, setAnnual] = useState(false);
const [openFaq, setOpenFaq] = useState<number | null>(0);
return (
<div className="min-h-screen bg-white dark:bg-zinc-950">
{/* Nav */}
<nav className="border-b border-zinc-200/50 bg-white/80 backdrop-blur-xl dark:border-zinc-800/50 dark:bg-zinc-950/80">
<div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-4 sm:px-6">
<Link href="/" className="flex items-center gap-2.5">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-emerald-500 to-cyan-500 shadow-sm">
<Sparkles className="h-4 w-4 text-white" />
</div>
<span className="text-lg font-bold tracking-tight">Scan<span className="text-emerald-600">Menu</span></span>
</Link>
<div className="flex items-center gap-3">
<Link href="/login"><Button variant="ghost" size="sm">Sign In</Button></Link>
<Link href="/register"><Button variant="primary" size="sm">Get Started</Button></Link>
</div>
</div>
</nav>
{/* Header */}
<section className="pt-16 pb-8 text-center">
<div className="mx-auto max-w-3xl px-4">
<Badge variant="outline" className="mb-4">Pricing</Badge>
<h1 className="text-4xl font-bold tracking-tight text-zinc-900 sm:text-5xl dark:text-white">
Simple, transparent pricing
</h1>
<p className="mt-4 text-lg text-zinc-600 dark:text-zinc-400">
Choose the plan that fits your restaurant. Upgrade, downgrade, or cancel anytime.
</p>
</div>
</section>
{/* Toggle */}
<div className="flex items-center justify-center gap-3 pb-12">
<span className={cn('text-sm font-medium transition-colors', !annual ? 'text-zinc-900' : 'text-zinc-500')}>Monthly</span>
<button
onClick={() => setAnnual(!annual)}
className={cn('relative h-6 w-11 rounded-full transition-colors', annual ? 'bg-emerald-500' : 'bg-zinc-300')}
>
<span className={cn('absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform', annual && 'translate-x-5')} />
</button>
<span className={cn('text-sm font-medium transition-colors', annual ? 'text-zinc-900' : 'text-zinc-500')}>
Annual <Badge variant="success" className="ml-1 text-[10px]">Save 17%</Badge>
</span>
</div>
{/* Plans */}
<section className="pb-20">
<div className="mx-auto max-w-6xl px-4 sm:px-6">
<div className="grid gap-6 lg:grid-cols-4">
{plans.map((plan) => (
<div
key={plan.name}
className={cn(
'relative flex flex-col rounded-2xl border bg-white p-6 transition-all hover:shadow-lg dark:bg-zinc-900',
plan.popular
? 'border-emerald-500 shadow-lg ring-1 ring-emerald-500/20'
: 'border-zinc-200 dark:border-zinc-800'
)}
>
{plan.popular && (
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
<Badge className="bg-emerald-500 text-white border-0 px-3 py-1">Most Popular</Badge>
</div>
)}
<div className="mb-6">
<div className="flex items-center gap-2 mb-3">
<div className={cn(
'flex h-9 w-9 items-center justify-center rounded-xl',
plan.popular ? 'bg-emerald-100 text-emerald-600' : 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800'
)}>
<plan.icon className="h-4 w-4" />
</div>
<h3 className="text-lg font-bold">{plan.name}</h3>
</div>
<p className="text-sm text-zinc-500 mb-4">{plan.description}</p>
<div className="flex items-baseline gap-1">
<span className="text-4xl font-bold text-zinc-900 dark:text-white">
${annual ? plan.yearlyPrice : plan.price}
</span>
{plan.price > 0 && <span className="text-zinc-500">/month</span>}
</div>
{annual && plan.price > 0 && (
<p className="mt-1 text-xs text-emerald-600">
${plan.yearlyPrice * 12}/year (save ${(plan.price - plan.yearlyPrice) * 12})
</p>
)}
</div>
<Link href="/register" className="block mb-6">
<Button
variant={plan.popular ? 'primary' : 'outline'}
className="w-full"
>
{plan.cta} {plan.price > 0 && <ArrowRight className="h-4 w-4" />}
</Button>
</Link>
<div className="flex-1 space-y-3">
{plan.features.map((feature) => (
<div key={feature} className="flex items-start gap-2 text-sm">
<Check className={cn('h-4 w-4 shrink-0 mt-0.5', plan.popular ? 'text-emerald-500' : 'text-zinc-400')} />
<span className="text-zinc-600 dark:text-zinc-400">{feature}</span>
</div>
))}
</div>
</div>
))}
</div>
</div>
</section>
{/* FAQ */}
<section className="border-t border-zinc-200 bg-zinc-50 py-20 dark:border-zinc-800 dark:bg-zinc-900/50">
<div className="mx-auto max-w-3xl px-4 sm:px-6">
<div className="text-center mb-12">
<Badge variant="outline" className="mb-4">FAQ</Badge>
<h2 className="text-3xl font-bold tracking-tight text-zinc-900 dark:text-white">Frequently asked questions</h2>
</div>
<div className="space-y-3">
{faq.map((item, i) => (
<div key={i} className="rounded-2xl border border-zinc-200 bg-white overflow-hidden dark:border-zinc-800 dark:bg-zinc-900">
<button
onClick={() => setOpenFaq(openFaq === i ? null : i)}
className="flex w-full items-center justify-between p-5 text-left"
>
<span className="text-sm font-semibold text-zinc-900 dark:text-zinc-100">{item.q}</span>
<HelpCircle className={cn('h-4 w-4 shrink-0 text-zinc-400 transition-transform', openFaq === i && 'rotate-180')} />
</button>
{openFaq === i && (
<div className="px-5 pb-5 pt-0">
<p className="text-sm leading-relaxed text-zinc-500">{item.a}</p>
</div>
)}
</div>
))}
</div>
</div>
</section>
{/* Footer CTA */}
<section className="py-16 text-center">
<div className="mx-auto max-w-2xl px-4">
<h2 className="text-2xl font-bold text-zinc-900 dark:text-white">Still have questions?</h2>
<p className="mt-2 text-zinc-500">We&apos;re here to help. Contact our team for a personalized demo.</p>
<div className="mt-6 flex justify-center gap-3">
<Link href="/register"><Button variant="primary">Start Free Trial</Button></Link>
<Button variant="outline">Contact Sales</Button>
</div>
</div>
</section>
</div>
);
}