821 lines
40 KiB
TypeScript
821 lines
40 KiB
TypeScript
import { useState } from 'react';
|
||
import { Check, X, Star, Shield, Clock, Smartphone, Download, QrCode, Heart, Users, Award, Headphones } from 'lucide-react';
|
||
import { Button } from '../components/ui/button';
|
||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/ui/card';
|
||
import { RadioGroup, RadioGroupItem } from '../components/ui/radio-group';
|
||
import { Badge } from '../components/ui/badge';
|
||
import { EnhancedTestimonials } from '../components/EnhancedTestimonials';
|
||
import { ReviewsSection } from '../components/ReviewsSection';
|
||
import { Layout } from '../Layout';
|
||
import { LoginModal } from '../components/LoginModal';
|
||
import { ImageWithFallback } from '../components/figma/ImageWithFallback';
|
||
import { useAuth } from '../context/AuthContext';
|
||
import { useNavigate } from 'react-router-dom';
|
||
import { useGetSelectedCityDetailsQuery } from '../Redux/services/cities.service';
|
||
import LoadingSpinner from '../components/LoadingSpinner';
|
||
|
||
interface PassesPageProps {
|
||
onCheckoutClick?: () => void;
|
||
onSignInClick: () => void;
|
||
onSignOutClick?: () => void;
|
||
}
|
||
|
||
interface PassType {
|
||
id: string;
|
||
name: string;
|
||
title: string;
|
||
description: string;
|
||
price: string;
|
||
originalPrice?: string;
|
||
period: string;
|
||
popular?: boolean;
|
||
features: string[];
|
||
tableFeatures: {
|
||
freeDelivery: boolean | string;
|
||
attractionsIncluded: string;
|
||
validityPeriod: string;
|
||
audioGuide: boolean;
|
||
skipTheLine: boolean | string;
|
||
mobileApp: boolean;
|
||
cancellationPolicy: string;
|
||
customerSupport: string;
|
||
additionalPerks: string;
|
||
};
|
||
}
|
||
|
||
const passTypes: PassType[] = [
|
||
{
|
||
id: 'selective',
|
||
name: 'Flexi Pass',
|
||
title: 'Flexi Card',
|
||
description: 'Perfect for travelers who want to explore selected attractions at their own pace with essential features.',
|
||
price: '$59.99',
|
||
originalPrice: '$89.99',
|
||
period: 'per person',
|
||
features: [
|
||
'Access to selected attractions',
|
||
'Limited number of attractions per pass',
|
||
'Flexible validity period',
|
||
'Priority entry where available',
|
||
'Mobile ticket delivery'
|
||
],
|
||
tableFeatures: {
|
||
freeDelivery: true,
|
||
attractionsIncluded: '3, 5, or 7 attractions',
|
||
validityPeriod: '30 days',
|
||
audioGuide: false,
|
||
skipTheLine: 'Selected venues',
|
||
mobileApp: true,
|
||
cancellationPolicy: '24 hours free cancellation',
|
||
customerSupport: 'Email support',
|
||
additionalPerks: 'Basic travel tips'
|
||
}
|
||
},
|
||
{
|
||
id: 'unlimited',
|
||
name: 'Unlimited Pass',
|
||
title: 'UNLIMITED CARD',
|
||
description: 'The ultimate experience for adventure seekers who want unlimited access to all attractions with premium features.',
|
||
price: '$89.99',
|
||
originalPrice: '$149.99',
|
||
period: 'per person',
|
||
popular: true,
|
||
features: [
|
||
'Unlimited access to all attractions',
|
||
'Time-limited validity (7 days)',
|
||
'Skip-the-line access',
|
||
'Expert guide inclusion',
|
||
'Mobile app access',
|
||
'Premium customer support'
|
||
],
|
||
tableFeatures: {
|
||
freeDelivery: true,
|
||
attractionsIncluded: 'All attractions (50+)',
|
||
validityPeriod: '2, 3, 5, or 7 days',
|
||
audioGuide: true,
|
||
skipTheLine: 'All venues',
|
||
mobileApp: true,
|
||
cancellationPolicy: '24 hours free cancellation',
|
||
customerSupport: '24/7 Phone & Email',
|
||
additionalPerks: 'Exclusive discounts & priority booking'
|
||
}
|
||
}
|
||
];
|
||
|
||
const featureComparison = [
|
||
{ key: 'freeDelivery', label: 'Free Delivery' },
|
||
{ key: 'attractionsIncluded', label: 'Attractions Included' },
|
||
{ key: 'validityPeriod', label: 'Validity Period' },
|
||
{ key: 'audioGuide', label: 'Audio Guide' },
|
||
{ key: 'skipTheLine', label: 'Skip-the-Line Access' },
|
||
{ key: 'mobileApp', label: 'Mobile App' },
|
||
{ key: 'cancellationPolicy', label: 'Cancellation Policy' },
|
||
{ key: 'customerSupport', label: 'Customer Support' },
|
||
{ key: 'additionalPerks', label: 'Additional Perks' }
|
||
];
|
||
|
||
const trustFeatures = [
|
||
{
|
||
icon: Star,
|
||
title: 'Unique Benefits',
|
||
description: 'Exclusive access to premium attractions and experiences',
|
||
stat: '50+ Partner Venues',
|
||
color: 'from-yellow-400 to-orange-500'
|
||
},
|
||
{
|
||
icon: Users,
|
||
title: 'Proven Performance',
|
||
description: 'Trusted by thousands of satisfied travelers worldwide',
|
||
stat: '4.8/5 Rating',
|
||
color: 'from-blue-400 to-cyan-500'
|
||
},
|
||
{
|
||
icon: Shield,
|
||
title: 'Quality Assurance',
|
||
description: 'Safety-first approach with verified partners and secure booking',
|
||
stat: '100% Secure',
|
||
color: 'from-green-400 to-emerald-500'
|
||
},
|
||
{
|
||
icon: Headphones,
|
||
title: 'Award-winning Support',
|
||
description: 'Round-the-clock assistance for seamless travel experience',
|
||
stat: '24/7 Available',
|
||
color: 'from-purple-400 to-pink-500'
|
||
}
|
||
];
|
||
|
||
export function PassesPage({
|
||
onCheckoutClick,
|
||
onSignInClick,
|
||
onSignOutClick,
|
||
}: PassesPageProps) {
|
||
const [selectedPass, setSelectedPass] = useState<string>(passTypes[1].id);
|
||
const [isLoginOpen, setIsLoginOpen] = useState(false);
|
||
const { user } = useAuth(); // from AuthContext
|
||
|
||
const navigate = useNavigate()
|
||
const cityId = localStorage.getItem("cityId")
|
||
const cityName = localStorage.getItem("cityName")
|
||
|
||
const { data: cityDetails, isLoading: loadingCityDetails } = useGetSelectedCityDetailsQuery(cityId)
|
||
const cards = cityDetails?.city?.cards ?? []
|
||
|
||
if (loadingCityDetails) {
|
||
return (<LoadingSpinner />)
|
||
}
|
||
|
||
const handleCheckoutClick = (cardTypeName:string) => {
|
||
console.log('Proceeding to checkout for user:', user);
|
||
// Add your checkout logic here
|
||
navigate('/checkout', { state: { selectedCard: cardTypeName } });
|
||
};
|
||
|
||
const handleSignInClick = () => {
|
||
setIsLoginOpen(true);
|
||
};
|
||
|
||
const renderFeatureValue = (value: boolean | string) => {
|
||
if (typeof value === 'boolean') {
|
||
return value ? (
|
||
<Check className="w-5 h-5 text-green-500 mx-auto" />
|
||
) : (
|
||
<X className="w-5 h-5 text-red-400 mx-auto" />
|
||
);
|
||
}
|
||
return <span className="text-gray-700">{value}</span>;
|
||
};
|
||
|
||
return (
|
||
<Layout
|
||
activeCity={sessionStorage.getItem("lastKnownCity") || "shared"}
|
||
onSignInClick={onSignInClick}
|
||
onSignOutClick={onSignOutClick}
|
||
user={user} // ✅ Pass the updated user data
|
||
>
|
||
<div className="container mx-auto px-4 pt-52 pb-12 relative z-10">
|
||
{/* Page Header */}
|
||
<div className="text-center mb-16">
|
||
<div className="mb-6">
|
||
<h1 className="font-merchant font-light text-4xl md:text-5xl lg:text-6xl mb-4">
|
||
Buy <span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pr-1.5">Cards</span>
|
||
</h1>
|
||
<p className="font-poppins text-xl leading-relaxed font-normal text-gray-600 max-w-3xl mx-auto">
|
||
Skip the lines, save money, and explore more with our flexible city cards designed for modern travelers
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Pass Comparison Section */}
|
||
<div className="mb-20">
|
||
<RadioGroup
|
||
className="grid md:grid-cols-2 gap-8 max-w-6xl mx-auto"
|
||
>
|
||
{/* Flexi Pass Card */}
|
||
<div className="relative h-full">
|
||
<Card
|
||
className={`relative h-full flex flex-col transition-all duration-300 cursor-pointer ${selectedPass === passTypes[0].id
|
||
? "ring-2 ring-red-500 shadow-lg" // 🔴 red border when selected
|
||
: "border-gray-200 shadow-md hover:shadow-lg hover:border-primary/30"
|
||
}`}
|
||
onClick={() => setSelectedPass(passTypes[0].id)}
|
||
>
|
||
<div className="absolute top-5 right-5 z-10">
|
||
{/* <RadioGroupItem value={passTypes[0].id} id={passTypes[0].id} className="w-5 h-5" /> */}
|
||
</div>
|
||
|
||
<CardHeader className="text-center pb-4 pt-8 flex-shrink-0">
|
||
<CardTitle className="font-merchant text-2xl leading-tight mb-3 text-gray-900">
|
||
{cards[0]?.title}
|
||
</CardTitle>
|
||
<CardDescription className="font-poppins text-sm text-gray-600 leading-relaxed font-normal min-h-[48px] flex items-center justify-center px-4">
|
||
{cards[0]?.description}
|
||
</CardDescription>
|
||
</CardHeader>
|
||
|
||
{/* Pricing */}
|
||
<div className="px-6 pb-6 flex-shrink-0">
|
||
<div className="flex items-baseline justify-center gap-2 mb-2">
|
||
<span className="text-5xl font-bold text-gray-900 font-poppins">
|
||
${cards[0]?.adultPrice}
|
||
</span>
|
||
<span className="text-gray-500 font-poppins text-base">
|
||
/ {passTypes[0].period}
|
||
</span>
|
||
</div>
|
||
<div className="h-5 flex items-center justify-center">
|
||
{cards[0]?.adultPrice && (
|
||
<div className="text-sm text-gray-500 font-poppins">
|
||
{/* Strikethrough price = originalPrice + $5 */}
|
||
<span className="line-through mr-2">
|
||
${parseFloat(cards[0]?.adultPrice) + 5}
|
||
</span>
|
||
<span className="text-green-600 font-medium">
|
||
Save{" "}
|
||
{Math.round(
|
||
((5) / (parseFloat(cards[0]?.adultPrice) + 5)) * 100
|
||
)}
|
||
%
|
||
</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<CardContent className="pt-0 pb-6 px-6 flex-grow flex flex-col">
|
||
<div className="flex-grow mb-6">
|
||
<div className="space-y-3">
|
||
{passTypes[0].features.map((feature, index) => (
|
||
<div key={index} className="flex items-start gap-3">
|
||
<Check className="w-4 h-4 text-green-500 mt-1 flex-shrink-0" />
|
||
<span className="text-sm text-gray-700 font-poppins leading-relaxed font-normal">{feature}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex-shrink-0 space-y-3">
|
||
<Button
|
||
className={`w-full h-12 rounded-lg font-semibold transition-all cursor-pointer duration-300 font-poppins ${selectedPass === passTypes[0].id
|
||
? "bg-primary hover:bg-primary/90 text-white hover:shadow-lg"
|
||
: "bg-gray-400 hover:bg-gray-400 text-white hover:shadow-md"
|
||
}`}
|
||
onClick={() => user ? handleCheckoutClick(cards[0]?.cardType?.cardTypeName) : handleSignInClick}
|
||
disabled={selectedPass !== passTypes[0].id}
|
||
|
||
>
|
||
{user ? 'PURCHASE NOW' : 'LOGIN TO BUY PASS'}
|
||
</Button>
|
||
<p className="text-xs text-gray-500 text-center font-poppins font-normal leading-tight">
|
||
✓ Free cancellation up to 24 hours • Instant delivery
|
||
</p>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
|
||
{/* Unlimited Pass Card */}
|
||
<div className="relative h-full">
|
||
<Card
|
||
className={`relative h-full flex flex-col transition-all duration-300 cursor-pointer ${selectedPass === passTypes[1]?.id
|
||
? "ring-2 ring-red-500 shadow-lg" // 🔴 red border when selected
|
||
: "border-gray-200 shadow-md hover:shadow-lg hover:border-primary/30"
|
||
}`}
|
||
onClick={() => setSelectedPass(passTypes[1]?.id)}
|
||
|
||
>
|
||
{passTypes[1]?.popular && (
|
||
<div className="absolute -top-3 left-1/2 transform -translate-x-1/2 z-10">
|
||
<Badge className="bg-gradient-to-r from-yellow-400 to-orange-500 text-black px-6 py-1.5 font-semibold shadow-lg font-poppins">
|
||
Most Popular
|
||
</Badge>
|
||
</div>
|
||
)}
|
||
|
||
<div className="absolute top-5 right-5 z-10">
|
||
{/* <RadioGroupItem value={passTypes[1].id} id={passTypes[1].id} className="w-5 h-5" /> */}
|
||
</div>
|
||
|
||
<CardHeader className="text-center pb-4 pt-8 flex-shrink-0">
|
||
<CardTitle className="font-merchant text-2xl leading-tight mb-3 text-gray-900">
|
||
{cards[1]?.title}
|
||
</CardTitle>
|
||
<CardDescription className="font-poppins text-sm text-gray-600 leading-relaxed font-normal min-h-[48px] flex items-center justify-center px-4">
|
||
{cards[1]?.description}
|
||
</CardDescription>
|
||
</CardHeader>
|
||
|
||
{/* Pricing */}
|
||
<div className="px-6 pb-6 flex-shrink-0">
|
||
<div className="flex items-baseline justify-center gap-2 mb-2">
|
||
<span className="text-5xl font-bold text-gray-900 font-poppins">${cards[1]?.adultPrice}</span>
|
||
<span className="text-gray-500 font-poppins text-base">/ {passTypes[1]?.period}</span>
|
||
</div>
|
||
<div className="h-5 flex items-center justify-center">
|
||
{cards[1]?.adultPrice && (
|
||
<div className="text-sm text-gray-500 font-poppins">
|
||
{/* Strikethrough price = originalPrice + $5 */}
|
||
<span className="line-through mr-2">
|
||
${parseFloat(cards[1]?.adultPrice) + 5}
|
||
</span>
|
||
<span className="text-green-600 font-medium">
|
||
Save{" "}
|
||
{Math.round(
|
||
((5) / (parseFloat(cards[1]?.adultPrice) + 5)) * 100
|
||
)}
|
||
%
|
||
</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<CardContent className="pt-0 pb-6 px-6 flex-grow flex flex-col">
|
||
<div className="flex-grow mb-6">
|
||
<div className="space-y-3">
|
||
{passTypes[1]?.features.map((feature, index) => (
|
||
<div key={index} className="flex items-start gap-3">
|
||
<Check className="w-4 h-4 text-green-500 mt-1 flex-shrink-0" />
|
||
<span className="text-sm text-gray-700 font-poppins leading-relaxed font-normal">{feature}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex-shrink-0 space-y-3">
|
||
<Button
|
||
className={`w-full h-12 rounded-lg font-semibold transition-all cursor-pointer duration-300 font-poppins ${selectedPass === passTypes[1].id
|
||
? "bg-primary hover:bg-primary/90 text-white hover:shadow-lg"
|
||
: "bg-gray-400 hover:bg-gray-400 text-white hover:shadow-md"
|
||
}`}
|
||
disabled={selectedPass !== passTypes[1].id}
|
||
|
||
onClick={() => user ? handleCheckoutClick(cards[1]?.cardType?.cardTypeName) : handleSignInClick}
|
||
|
||
>
|
||
{user ? 'PURCHASE NOW' : 'LOGIN TO BUY PASS'}
|
||
</Button>
|
||
<p className="text-xs text-gray-500 text-center font-poppins font-normal leading-tight">
|
||
✓ Free cancellation up to 24 hours • Instant delivery
|
||
</p>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</RadioGroup>
|
||
</div>
|
||
|
||
|
||
{/* Good to Know Section */}
|
||
<div className="mb-24">
|
||
<div className="text-center mb-16">
|
||
<h2 className="font-merchant text-4xl md:text-5xl text-gray-900 mb-6">
|
||
<span className="font-light">Good to</span>{' '}
|
||
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pr-2">Know</span>
|
||
</h2>
|
||
<p className="font-poppins text-lg text-gray-600 font-light max-w-2xl mx-auto leading-relaxed">
|
||
Simple tips to help you get the most out of your CityCard experience
|
||
</p>
|
||
</div>
|
||
|
||
<div className="max-w-7xl mx-auto">
|
||
<div className="grid lg:grid-cols-12 gap-8 items-start">
|
||
|
||
{/* Left Column: The Rules */}
|
||
<div className="lg:col-span-7 space-y-6">
|
||
|
||
{/* Rule 1: Calendar Days */}
|
||
<div className="bg-white rounded-[2rem] p-8 border border-gray-100 shadow-[0_8px_30px_rgb(0,0,0,0.04)] hover:shadow-[0_8px_30px_rgb(0,0,0,0.08)] transition-all duration-500 relative overflow-hidden">
|
||
<div className="absolute top-0 right-0 w-64 h-64 bg-blue-50/50 rounded-full blur-3xl -mr-32 -mt-32 pointer-events-none"></div>
|
||
|
||
<div className="flex flex-col sm:flex-row items-start gap-6 relative z-10">
|
||
<div className="w-14 h-14 bg-blue-100/50 rounded-2xl flex items-center justify-center shrink-0 text-blue-600">
|
||
<Clock className="w-7 h-7" strokeWidth={1.5} />
|
||
</div>
|
||
<div className="flex-1 w-full">
|
||
<h3 className="font-merchant text-xl text-gray-900 mb-3">Calendar Days Policy</h3>
|
||
<p className="font-poppins text-gray-600 leading-relaxed mb-6">
|
||
Unlimited passes work on a <span className="font-medium text-gray-900">consecutive calendar day basis</span>, not 24-hour periods. Your pass expires at 11:59 PM on your final day.
|
||
</p>
|
||
|
||
{/* Visual Timeline Example */}
|
||
<div className="bg-blue-50/30 rounded-xl p-5 border border-blue-100/50 flex flex-col sm:flex-row gap-4 sm:items-center justify-between w-full">
|
||
<div className="flex-1">
|
||
<div className="text-xs font-bold text-blue-800 uppercase tracking-wider mb-1">Start</div>
|
||
<div className="font-poppins font-medium text-gray-900 text-sm sm:text-base">Monday 4:30 PM</div>
|
||
<div className="text-xs text-gray-500 mt-1">First scan</div>
|
||
</div>
|
||
<div className="hidden sm:flex flex-col items-center px-4 shrink-0">
|
||
<div className="text-[10px] font-medium text-blue-400 mb-1">3 Days</div>
|
||
<div className="w-24 h-0.5 bg-blue-200 relative">
|
||
<div className="absolute inset-0 bg-blue-400 w-1/2 animate-pulse"></div>
|
||
</div>
|
||
</div>
|
||
<div className="flex-1 text-left sm:text-right">
|
||
<div className="text-xs font-bold text-blue-800 uppercase tracking-wider mb-1">End</div>
|
||
<div className="font-poppins font-medium text-gray-900 text-sm sm:text-base">Wednesday 11:59 PM</div>
|
||
<div className="text-xs text-gray-500 mt-1">Expiration</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Rule 2: Fair Use */}
|
||
<div className="bg-white rounded-[2rem] p-8 border border-gray-100 shadow-[0_8px_30px_rgb(0,0,0,0.04)] hover:shadow-[0_8px_30px_rgb(0,0,0,0.08)] transition-all duration-500 relative overflow-hidden">
|
||
<div className="absolute top-0 right-0 w-64 h-64 bg-orange-50/50 rounded-full blur-3xl -mr-32 -mt-32 pointer-events-none"></div>
|
||
|
||
<div className="flex flex-col sm:flex-row items-start gap-6 relative z-10">
|
||
<div className="w-14 h-14 bg-orange-100/50 rounded-2xl flex items-center justify-center shrink-0 text-orange-600">
|
||
<Shield className="w-7 h-7" strokeWidth={1.5} />
|
||
</div>
|
||
<div className="flex-1">
|
||
<h3 className="font-merchant text-xl text-gray-900 mb-3">60-Minute Adventure Gap</h3>
|
||
<p className="font-poppins text-gray-600 leading-relaxed">
|
||
To keep the journey smooth for everyone, there's a simple <span className="font-medium text-gray-900">60-minute wait</span> between scanning your pass at attractions.
|
||
</p>
|
||
<div className="mt-4 flex flex-wrap items-center gap-3 text-sm text-orange-800 bg-orange-50/50 py-2 px-4 rounded-full w-fit">
|
||
<span className="w-2 h-2 bg-orange-400 rounded-full animate-pulse shrink-0"></span>
|
||
Perfect for grabbing a coffee or traveling to your next stop!
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Right Column: The "Card Types" Summary */}
|
||
<div className="lg:col-span-5 h-full min-h-[400px]">
|
||
<div className="bg-gradient-to-br from-[#F95F62] to-[#ff8f92] rounded-[2rem] p-8 border border-white/20 shadow-[0_8px_30px_rgb(249,95,98,0.3)] h-full relative overflow-hidden flex flex-col justify-center text-white">
|
||
{/* Abstract Shapes */}
|
||
<div className="absolute top-0 right-0 w-64 h-64 bg-white/10 rounded-full blur-3xl -mr-10 -mt-10 pointer-events-none"></div>
|
||
<div className="absolute bottom-0 left-0 w-64 h-64 bg-black/5 rounded-full blur-3xl -ml-10 -mb-10 pointer-events-none"></div>
|
||
|
||
<div className="relative z-10">
|
||
<h3 className="font-merchant text-3xl mb-2 text-white">Your Pass, Your Way</h3>
|
||
<p className="text-white/90 font-poppins mb-8 font-light leading-relaxed">Choose the flexibility that matches your travel style.</p>
|
||
|
||
<div className="space-y-4">
|
||
{/* Flex Option - White Card */}
|
||
<div className="bg-white rounded-2xl p-5 shadow-lg border border-white/50 hover:scale-[1.02] transition-transform duration-300 cursor-default group relative overflow-hidden">
|
||
<div className="absolute left-0 top-0 bottom-0 w-1.5 bg-[#F95FAF]"></div>
|
||
<div className="flex justify-between items-center mb-2 pl-3">
|
||
<span className="font-poppins font-semibold text-lg text-gray-900 group-hover:text-[#F95FAF] transition-colors">Flexi Card</span>
|
||
<span className="text-[10px] uppercase tracking-wider bg-[#F95FAF]/10 px-2 py-1 rounded-md text-[#F95FAF] font-bold">Casual</span>
|
||
</div>
|
||
<p className="text-sm text-gray-600 font-light mb-4 pl-3">Best for visiting specific attractions at your own pace.</p>
|
||
<div className="flex gap-2 pl-3">
|
||
{[3, 5, 7].map(n => (
|
||
<div key={n} className="text-xs border border-[#F95FAF]/30 text-[#F95FAF] rounded-lg px-3 py-1.5 font-medium bg-[#F95FAF]/5">{n}</div>
|
||
))}
|
||
<span className="text-xs flex items-center text-gray-400 ml-1">Attractions</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Unlimited Option - White Card with Highlight */}
|
||
<div className="bg-white rounded-2xl p-5 shadow-lg border border-white/50 hover:scale-[1.02] transition-transform duration-300 cursor-default group relative overflow-hidden">
|
||
<div className="absolute top-0 right-0 bg-[#F95F62] text-white text-[10px] font-bold px-3 py-1 rounded-bl-xl shadow-sm z-10">POPULAR</div>
|
||
<div className="absolute left-0 top-0 bottom-0 w-1.5 bg-[#F95F62]"></div>
|
||
<div className="flex justify-between items-center mb-2 pl-3">
|
||
<span className="font-poppins font-semibold text-lg text-gray-900 group-hover:text-[#F95F62] transition-colors">Unlimited Card</span>
|
||
<span className="text-[10px] uppercase tracking-wider bg-[#F95F62]/10 px-2 py-1 rounded-md text-[#F95F62] border border-[#F95F62]/20 font-bold">Power User</span>
|
||
</div>
|
||
<p className="text-sm text-gray-600 font-light mb-4 pl-3">Unlimited access for full days of exploration.</p>
|
||
<div className="flex gap-2 flex-wrap pl-3">
|
||
{[2, 3, 5, 7].map(n => (
|
||
<div key={n} className="text-xs bg-white text-[#F95F62] border border-[#F95F62]/20 rounded-lg px-3 py-1.5 font-medium shadow-sm">{n}</div>
|
||
))}
|
||
<span className="text-xs flex items-center text-gray-500 ml-1">Days</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Detailed Features Comparison Table */}
|
||
<Card className="overflow-hidden mb-20 bg-white shadow-lg">
|
||
<CardHeader className="text-center">
|
||
<CardTitle className="font-merchant text-3xl font-bold text-gray-900 mb-2">
|
||
Detailed Feature Comparison
|
||
</CardTitle>
|
||
<CardDescription className="text-lg text-gray-600 font-light">
|
||
See exactly what's included with each pass type
|
||
</CardDescription>
|
||
</CardHeader>
|
||
|
||
<CardContent className="p-0">
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full">
|
||
<thead>
|
||
<tr className="border-b border-gray-200 bg-gray-50">
|
||
<th className="text-left p-6 font-semibold text-gray-900 min-w-[200px]">Features</th>
|
||
<th className="text-center p-6 font-semibold text-gray-900 min-w-[200px]">Flexi Pass</th>
|
||
<th className="text-center p-6 font-semibold text-gray-900 min-w-[200px] bg-primary/5">Unlimited Pass</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
{featureComparison.map((feature, index) => (
|
||
<tr key={feature.key} className={`border-b border-gray-100 ${index % 2 === 0 ? 'bg-white' : 'bg-gray-50/50'}`}>
|
||
<td className="p-6 font-medium text-gray-900">
|
||
<div className="flex items-center">
|
||
<span className="w-2 h-2 bg-primary rounded-full mr-3"></span>
|
||
{feature.label}
|
||
</div>
|
||
</td>
|
||
<td className="p-6 text-center">
|
||
{renderFeatureValue(passTypes[0].tableFeatures[feature.key as keyof typeof passTypes[0]['tableFeatures']])}
|
||
</td>
|
||
<td className="p-6 text-center bg-primary/5">
|
||
{renderFeatureValue(passTypes[1].tableFeatures[feature.key as keyof typeof passTypes[1]['tableFeatures']])}
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Mobile App Promotion Banner */}
|
||
<div className="mb-20">
|
||
<Card className="bg-gray-50 overflow-hidden shadow-lg">
|
||
<CardContent className="p-0">
|
||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||
|
||
{/* Smartphone Mockup */}
|
||
<div className="relative flex justify-center p-8 lg:p-16">
|
||
<div className="relative">
|
||
{/* Phone Frame */}
|
||
<div className="w-64 h-[520px] bg-black rounded-[2.5rem] p-2 shadow-2xl">
|
||
<div className="w-full h-full bg-white rounded-[2rem] relative overflow-hidden">
|
||
|
||
{/* Screen Content */}
|
||
<div className="absolute inset-0 bg-gradient-to-b from-primary/90 to-secondary/90">
|
||
|
||
{/* Status Bar */}
|
||
<div className="flex justify-between items-center px-6 pt-4 pb-2 text-white text-sm">
|
||
<span>9:41</span>
|
||
<div className="flex gap-1">
|
||
<div className="w-4 h-2 bg-white rounded-sm opacity-80"></div>
|
||
<div className="w-4 h-2 bg-white rounded-sm opacity-60"></div>
|
||
<div className="w-4 h-2 bg-white rounded-sm opacity-40"></div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* App Header */}
|
||
<div className="px-6 py-4">
|
||
<div className="flex items-center gap-3 mb-6">
|
||
<div className="w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center backdrop-blur-sm">
|
||
<span className="text-xl">🏙️</span>
|
||
</div>
|
||
<div>
|
||
<h3 className="text-white font-semibold text-lg">CityCards</h3>
|
||
<p className="text-white/70 text-sm">{cityName} Explorer</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Active Pass Card */}
|
||
<div className="mx-6 mb-6">
|
||
<div className="bg-white/15 backdrop-blur-md rounded-2xl p-4 border border-white/20">
|
||
<div className="flex justify-between items-center mb-3">
|
||
<span className="text-white font-semibold">Premium Pass</span>
|
||
<Badge className="bg-green-400 text-green-900 text-xs">Active</Badge>
|
||
</div>
|
||
<div className="h-2 bg-white/20 rounded-full mb-3">
|
||
<div className="h-2 bg-gradient-to-r from-yellow-400 to-orange-500 rounded-full w-3/4"></div>
|
||
</div>
|
||
<p className="text-white/80 text-xs">18 of 24 attractions visited</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Quick Actions */}
|
||
<div className="px-6 mb-6">
|
||
<div className="grid grid-cols-3 gap-3">
|
||
{[
|
||
{ icon: '🗺️', label: 'Map' },
|
||
{ icon: '📍', label: 'Near Me' },
|
||
{ icon: '⭐', label: 'Favorites' }
|
||
].map((action, index) => (
|
||
<div key={index} className="bg-white/10 backdrop-blur-sm rounded-xl p-3 text-center border border-white/20">
|
||
<span className="text-lg mb-1 block">{action.icon}</span>
|
||
<span className="text-white text-xs">{action.label}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Featured Attraction */}
|
||
<div className="mx-6">
|
||
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-4 border border-white/20">
|
||
<h4 className="text-white font-medium mb-2">Today's Highlight</h4>
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-12 h-12 bg-white/20 rounded-lg"></div>
|
||
<div>
|
||
<p className="text-white text-sm font-medium">Royal Botanic Gardens</p>
|
||
<p className="text-white/70 text-xs">2.5km away • Open now</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Bottom Tab Bar */}
|
||
<div className="absolute bottom-6 left-4 right-4">
|
||
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-2 border border-white/20">
|
||
<div className="grid grid-cols-4 gap-2 text-center">
|
||
{['Home', 'Passes', 'Map', 'Profile'].map((tab, index) => (
|
||
<div key={index} className="py-2">
|
||
<span className="text-white text-xs">{tab}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Content */}
|
||
<div className="p-8 lg:p-16">
|
||
<h2 className="heading-dynamic text-4xl text-gray-900 mb-6">
|
||
<span className="font-light">Access all your</span>{' '}
|
||
<span className="font-bold italic text-gradient-primary pr-2">city cards</span>{' '}
|
||
<span className="font-normal">on your</span>{' '}
|
||
<span className="font-semibold text-emphasis">phone</span>
|
||
</h2>
|
||
<p className="text-xl text-gray-600 mb-8 leading-relaxed font-light">
|
||
Download our mobile app for easy access to your passes, maps, and exclusive offers.
|
||
Never worry about losing your tickets again.
|
||
</p>
|
||
|
||
{/* App Features */}
|
||
<div className="space-y-4 mb-8">
|
||
{[
|
||
'Instant pass activation and QR code access',
|
||
'Offline maps and attraction information',
|
||
'Real-time updates and exclusive app-only offers',
|
||
'Track your progress and plan your journey'
|
||
].map((feature, index) => (
|
||
<div key={index} className="flex items-center gap-3">
|
||
<Check className="w-5 h-5 text-green-500" />
|
||
<span className="text-gray-700 font-light">{feature}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Download Buttons */}
|
||
<div className="flex flex-col sm:flex-row gap-4 mb-8">
|
||
<Button className="bg-black text-white hover:bg-gray-800 flex items-center justify-center gap-3 px-6 py-4 rounded-xl font-semibold">
|
||
<Download className="w-5 h-5" />
|
||
Download for iOS
|
||
</Button>
|
||
<Button className="bg-black text-white hover:bg-gray-800 flex items-center justify-center gap-3 px-6 py-4 rounded-xl font-semibold">
|
||
<Download className="w-5 h-5" />
|
||
Get it on Google Play
|
||
</Button>
|
||
</div>
|
||
|
||
{/* QR Code */}
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-16 h-16 bg-black rounded-xl p-2">
|
||
<div className="w-full h-full bg-white rounded-lg flex items-center justify-center">
|
||
<QrCode className="w-8 h-8 text-black" />
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<p className="font-medium text-gray-900">Scan to download</p>
|
||
<p className="text-sm text-gray-600 font-light">Available on iOS and Android</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
|
||
{/* Why Choose Us Section */}
|
||
<div className="mb-20">
|
||
<div className="text-center mb-12">
|
||
<h2 className="heading-dynamic text-4xl text-gray-900 mb-4">
|
||
<span className="font-light">Why Choose</span>{' '}
|
||
<span className="font-bold italic text-gradient-primary pr-2">CityCards</span><span className="font-normal">?</span>
|
||
</h2>
|
||
<p className="text-xl text-gray-600 max-w-3xl mx-auto font-light">
|
||
We're committed to providing the best city exploration experience with unmatched value and service
|
||
</p>
|
||
</div>
|
||
|
||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||
{trustFeatures.map((feature, index) => {
|
||
const IconComponent = feature.icon;
|
||
return (
|
||
<Card key={index} className="text-center p-8 border-gray-200 hover:shadow-lg transition-shadow duration-300">
|
||
<CardContent className="p-0">
|
||
<div className={`w-16 h-16 bg-gradient-to-r ${feature.color} rounded-2xl flex items-center justify-center mx-auto mb-6`}>
|
||
<IconComponent className="w-8 h-8 text-white" />
|
||
</div>
|
||
<h4 className="font-semibold text-gray-900 mb-3">{feature.title}</h4>
|
||
<p className="text-gray-600 mb-4 leading-relaxed font-light">{feature.description}</p>
|
||
<div className="text-2xl font-bold text-primary mb-1">{feature.stat}</div>
|
||
</CardContent>
|
||
</Card>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Enhanced Testimonials Section */}
|
||
<EnhancedTestimonials />
|
||
|
||
{/* Reviews Section */}
|
||
<ReviewsSection />
|
||
|
||
{/* Final CTA Section */}
|
||
<div className="text-center bg-gradient-to-r from-primary to-secondary rounded-3xl p-12 text-white relative overflow-hidden">
|
||
{/* Background Pattern */}
|
||
<div className="absolute inset-0 bg-gradient-to-br from-white/10 to-transparent"></div>
|
||
<div className="absolute top-10 right-10 w-32 h-32 bg-white/5 rounded-full"></div>
|
||
<div className="absolute bottom-10 left-10 w-24 h-24 bg-white/5 rounded-full"></div>
|
||
|
||
<div className="relative z-10">
|
||
<h3 className="heading-dynamic text-4xl mb-4">
|
||
<span className="font-light">Ready to</span>{' '}
|
||
<span className="font-bold italic text-emphasis">explore</span>{' '}
|
||
<span className="font-semibold">{cityName}?</span>
|
||
</h3>
|
||
<p className="text-xl mb-8 max-w-2xl mx-auto opacity-90 font-light">
|
||
Choose your pass and start discovering amazing attractions with skip-the-line access.
|
||
Money-back guarantee included.
|
||
</p>
|
||
|
||
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center mb-8">
|
||
<Button
|
||
size="lg"
|
||
className="bg-white text-primary hover:bg-gray-100 py-4 px-8 rounded-2xl font-semibold text-lg shadow-lg hover:shadow-xl transition-all duration-200"
|
||
>
|
||
Choose Your Pass
|
||
</Button>
|
||
<div className="flex items-center gap-2 text-white/80">
|
||
<Check className="w-5 h-5" />
|
||
<span className="font-light">No hidden fees • Instant confirmation</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex justify-center items-center gap-8 text-sm text-white/70">
|
||
<span className="flex items-center gap-2 font-light">
|
||
<Shield className="w-4 h-4" />
|
||
SSL Secured
|
||
</span>
|
||
<span className="flex items-center gap-2 font-light">
|
||
<Heart className="w-4 h-4" />
|
||
Money Back Guarantee
|
||
</span>
|
||
<span className="flex items-center gap-2 font-light">
|
||
<Clock className="w-4 h-4" />
|
||
24/7 Support
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<LoginModal
|
||
isOpen={isLoginOpen}
|
||
onClose={() => {
|
||
setIsLoginOpen(false);
|
||
}}
|
||
/>
|
||
|
||
</Layout>
|
||
);
|
||
}
|
||
|
||
export default PassesPage; |