Files
template-fitness/src/pages/Pricing.tsx
T

132 lines
7.0 KiB
TypeScript

import { Link } from "react-router-dom";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
import { CheckCircle, Plus, ArrowRight } from "lucide-react";
import { useSaasMutation } from "@/hooks/use-tygarun";
import { toast } from "sonner";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
const tiers = [
{
name: "Basic",
price: "$29",
desc: "Perfect for getting started",
features: ["Gym floor & free weights", "Locker room access", "2 group classes per week", "Fitness assessment", "Mobile app access"],
popular: false,
},
{
name: "Pro",
price: "$59",
desc: "For dedicated fitness enthusiasts",
features: ["Everything in Basic", "Unlimited group classes", "Sauna & pool access", "1 personal training session/mo", "Custom nutrition plan", "2 guest passes/mo"],
popular: true,
},
{
name: "Elite",
price: "$99",
desc: "The ultimate training experience",
features: ["Everything in Pro", "Unlimited PT sessions", "Recovery suite & cryo", "Priority class booking", "20% merch discount", "24/7 facility access", "VIP lounge"],
popular: false,
},
];
const addons = [
{ name: "Meal Prep Box", price: "+$15/week" },
{ name: "Towel Service", price: "+$10/mo" },
{ name: "Parking Pass", price: "+$20/mo" },
];
const faqs = [
{ q: "Is there a sign-up fee?", a: "Nope! We believe in transparent pricing. The monthly rate is all you pay." },
{ q: "Can I freeze my membership?", a: "Yes, you can freeze for up to 3 months per year at no extra charge." },
{ q: "What is the cancellation policy?", a: "Cancel anytime with 30 days notice. No penalties, no hassle." },
{ q: "Do you offer student or military discounts?", a: "Absolutely! Show a valid ID and get 20% off any plan." },
];
export default function Pricing() {
const { mutate: subscribe, loading: subscribing } = useSaasMutation("billing", "/checkout");
return (
<div className="min-h-screen bg-slate-950 text-white">
<Header />
{/* ── Hero ── */}
<section className="pt-32 pb-16 bg-gradient-to-b from-slate-900 to-slate-950">
<div className="mx-auto max-w-7xl px-6 text-center">
<Badge className="mb-4 bg-orange-500/10 text-orange-400 border-orange-500/30 uppercase text-xs tracking-wider">Pricing</Badge>
<h1 className="text-4xl md:text-5xl font-black uppercase text-white animate-fade-up">INVEST IN YOURSELF</h1>
<p className="mt-4 text-slate-400 max-w-xl mx-auto">No long-term contracts. Cancel anytime. Start with a free 7-day trial on any plan.</p>
</div>
</section>
{/* ── Tiers ── */}
<section className="py-16">
<div className="mx-auto max-w-6xl px-6">
<div className="grid md:grid-cols-3 gap-8 stagger">
{tiers.map((tier, i) => (
<Card key={i} className={`relative bg-slate-900/80 backdrop-blur border transition-all duration-300 hover:-translate-y-1 ${tier.popular ? "border-orange-500/50 shadow-2xl shadow-orange-500/10 scale-105" : "border-slate-800 hover:border-slate-700"}`}>
{tier.popular && <div className="absolute -top-3 left-1/2 -translate-x-1/2"><Badge className="bg-gradient-to-r from-orange-500 to-red-500 text-white border-0 uppercase text-xs tracking-wider px-4">Most Popular</Badge></div>}
<CardContent className="p-8">
<h3 className="text-lg font-bold uppercase text-slate-300">{tier.name}</h3>
<p className="text-sm text-slate-500 mt-1">{tier.desc}</p>
<div className="mt-6 mb-8">
<span className="text-5xl font-black text-white">{tier.price}</span>
<span className="text-slate-500">/mo</span>
</div>
<div className="space-y-3 mb-8">
{tier.features.map((f, j) => (
<div key={j} className="flex items-center gap-3 text-sm text-slate-300">
<CheckCircle className="h-4 w-4 text-orange-400 shrink-0" />
<span>{f}</span>
</div>
))}
</div>
<Button disabled={subscribing} className={`w-full font-bold uppercase tracking-wide rounded-full active:scale-[0.97] transition-all ${tier.popular ? "bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white shadow-lg shadow-orange-500/25" : "bg-slate-800 hover:bg-slate-700 text-white"}`} onClick={async () => { const amount = Math.round((parseFloat(String(tier.price).replace(/[^0-9.]/g, "")) || 0) * 100); try { const res = await subscribe({ plan: tier.name, amount }); if (res?.url && String(res.url).indexOf("#") !== 0) { window.location.href = res.url; return; } toast.success(`${tier.name} membership selected`); } catch { toast.error("Checkout unavailable. Please try again."); } }}>
Get {tier.name}
</Button>
</CardContent>
</Card>
))}
</div>
</div>
</section>
{/* ── Add-ons ── */}
<section className="py-16 bg-slate-900/50">
<div className="mx-auto max-w-4xl px-6">
<h2 className="text-2xl font-black uppercase text-white text-center mb-10 animate-fade-up">Add-ons</h2>
<div className="grid sm:grid-cols-3 gap-6">
{addons.map((a, i) => (
<Card key={i} className="bg-slate-900/80 backdrop-blur border border-slate-800 hover:border-orange-500/30 hover:-translate-y-1 hover:shadow-lg transition-all duration-300">
<CardContent className="p-6 text-center">
<Plus className="h-8 w-8 text-orange-400 mx-auto mb-3" />
<h3 className="font-bold text-white">{a.name}</h3>
<p className="text-orange-400 font-bold mt-2">{a.price}</p>
</CardContent>
</Card>
))}
</div>
</div>
</section>
{/* ── FAQ ── */}
<section className="py-16">
<div className="mx-auto max-w-3xl px-6">
<h2 className="text-2xl font-black uppercase text-white text-center mb-10 animate-fade-up">Frequently Asked Questions</h2>
<Accordion type="single" collapsible className="space-y-3">
{faqs.map((faq, i) => (
<AccordionItem key={i} value={`faq-${i}`} className="bg-slate-900/80 backdrop-blur border border-slate-800 rounded-xl px-6">
<AccordionTrigger className="text-white font-semibold text-left hover:no-underline hover:text-orange-400 transition-colors">{faq.q}</AccordionTrigger>
<AccordionContent className="text-slate-400">{faq.a}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
</section>
<Footer />
</div>
);
}