Files
CityCards-Website/src/pages/CheckoutPage2.tsx

886 lines
59 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import {
ArrowLeft, Check, Minus, Plus, ChevronDown
} from 'lucide-react';
import Navbar from '../components/Navbar';
import { Footer } from '../components/Footer';
import { ImageWithFallback } from '../components/figma/ImageWithFallback';
import { useNavigate } from 'react-router-dom';
import { useAddCardToCartMutation, useGetCheckoutPageDataQuery } from '../Redux/services/cards.service';
import LoadingSpinner from '../components/LoadingSpinner';
import { toast } from 'sonner';
/* ─── Types ─── */
export interface CartItem {
id: string;
city: string;
cardType: 'Flexi' | 'Unlimited';
days: number;
adults: number;
children: number;
quantity: number;
pricePerUnit: number;
image: string;
}
interface Attraction {
id: string;
name: string;
image: string;
category: string;
included: boolean;
}
/* ─── Data (Same as Original) ─── */
const dayOptions = [3, 6, 12, 18, 24];
const priceTable: Record<string, Record<number, number>> = {
Flexi: { 3: 49.5, 6: 69, 12: 99, 18: 129, 24: 159 },
Unlimited: { 3: 79, 6: 109, 12: 149, 18: 189, 24: 229 },
};
const attractionsData: Record<string, Record<string, Attraction[]>> = {
Melbourne: {
Flexi: [
{ id: 'mel-1', name: 'SEA LIFE Aquarium', image: 'https://images.unsplash.com/photo-1536845111858-bb269af65cb6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjBhcXVhcml1bSUyMHVuZGVyd2F0ZXJ8ZW58MXx8fHwxNzc2MzE5OTcwfDA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Wildlife', included: true },
{ id: 'mel-2', name: 'Melbourne Zoo', image: 'https://images.unsplash.com/photo-1730074888490-31239540bacf?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjB6b28lMjB3aWxkbGlmZXxlbnwxfHx8fDE3NzYzMTk5NzB8MA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Wildlife', included: true },
{ id: 'mel-3', name: 'Royal Botanic Gardens', image: 'https://images.unsplash.com/photo-1585894507208-eeead8cb9a56?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjBib3RhbmljYWwlMjBnYXJkZW4lMjBncmVlbnxlbnwxfHx8fDE3NzYzMTk5NzF8MA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Nature', included: true },
{ id: 'mel-4', name: 'NGV Art Gallery', image: 'https://images.unsplash.com/photo-1752429242469-55ba7ec210d2?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhcnQlMjBnYWxsZXJ5JTIwbXVzZXVtJTIwaW50ZXJpb3J8ZW58MXx8fHwxNzc2MzE5OTczfDA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Culture', included: true },
],
Unlimited: [
{ id: 'mel-1', name: 'SEA LIFE Aquarium', image: 'https://images.unsplash.com/photo-1536845111858-bb269af65cb6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjBhcXVhcml1bSUyMHVuZGVyd2F0ZXJ8ZW58MXx8fHwxNzc2MzE5OTcwfDA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Wildlife', included: true },
{ id: 'mel-2', name: 'Melbourne Zoo', image: 'https://images.unsplash.com/photo-1730074888490-31239540bacf?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjB6b28lMjB3aWxkbGlmZXxlbnwxfHx8fDE3NzYzMTk5NzB8MA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Wildlife', included: true },
{ id: 'mel-3', name: 'Royal Botanic Gardens', image: 'https://images.unsplash.com/photo-1585894507208-eeead8cb9a56?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjBib3RhbmljYWwlMjBnYXJkZW4lMjBncmVlbnxlbnwxfHx8fDE3NzYzMTk5NzF8MA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Nature', included: true },
{ id: 'mel-4', name: 'NGV Art Gallery', image: 'https://images.unsplash.com/photo-1752429242469-55ba7ec210d2?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhcnQlMjBnYWxsZXJ5JTIwbXVzZXVtJTIwaW50ZXJpb3J8ZW58MXx8fHwxNzc2MzE5OTczfDA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Culture', included: true },
{ id: 'mel-5', name: 'Melbourne Star Wheel', image: 'https://images.unsplash.com/photo-1769880659692-fa77e04c5ffa?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxvYnNlcnZhdGlvbiUyMHdoZWVsJTIwYW11c2VtZW50JTIwbmlnaHR8ZW58MXx8fHwxNzc2MzE5OTc2fDA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Experience', included: true },
{ id: 'mel-6', name: 'Penguin Parade', image: 'https://images.unsplash.com/photo-1670391050251-d1cfbc3891c4?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxwZW5ndWlucyUyMHdpbGRsaWZlJTIwbmF0dXJlfGVufDF8fHx8MTc3NjMxOTk3Nnww&ixlib=rb-4.1.0&q=80&w=1080', category: 'Wildlife', included: true },
{ id: 'mel-7', name: 'Yarra River Cruise', image: 'https://images.unsplash.com/photo-1562003914-018a4a6c2171?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxyaXZlciUyMGNydWlzZSUyMGJvYXQlMjBjaXR5fGVufDF8fHx8MTc3NjMxOTk3M3ww&ixlib=rb-4.1.0&q=80&w=1080', category: 'Experience', included: true },
],
},
Sydney: {
Flexi: [
{ id: 'syd-1', name: 'Harbour Bridge Climb', image: 'https://images.unsplash.com/photo-1767974062666-2685a670e353?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxTeWRuZXklMjBoYXJib3VyJTIwYnJpZGdlJTIwY2xpbWJ8ZW58MXx8fHwxNzc2MzE5OTcxfDA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Adventure', included: true },
{ id: 'syd-2', name: 'Taronga Zoo', image: 'https://images.unsplash.com/photo-1704852168456-b70e08441917?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxTeWRuZXklMjB0YXJvbmdhJTIwem9vJTIwYW5pbWFsc3xlbnwxfHx8fDE3NzYzMTk5NzJ8MA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Wildlife', included: true },
{ id: 'syd-3', name: 'Art Gallery NSW', image: 'https://images.unsplash.com/photo-1752429242469-55ba7ec210d2?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhcnQlMjBnYWxsZXJ5JTIwbXVzZXVtJTIwaW50ZXJpb3J8ZW58MXx8fHwxNzc2MzE5OTczfDA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Culture', included: true },
],
Unlimited: [
{ id: 'syd-1', name: 'Harbour Bridge Climb', image: 'https://images.unsplash.com/photo-1767974062666-2685a670e353?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxTeWRuZXklMjBoYXJib3VyJTIwYnJpZGdlJTIwY2xpbWJ8ZW58MXx8fHwxNzc2MzE5OTcxfDA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Adventure', included: true },
{ id: 'syd-2', name: 'Taronga Zoo', image: 'https://images.unsplash.com/photo-1704852168456-b70e08441917?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxTeWRuZXklMjB0YXJvbmdhJTIwem9vJTIwYW5pbWFsc3xlbnwxfHx8fDE3NzYzMTk5NzJ8MA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Wildlife', included: true },
{ id: 'syd-3', name: 'Art Gallery NSW', image: 'https://images.unsplash.com/photo-1752429242469-55ba7ec210d2?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhcnQlMjBnYWxsZXJ5JTIwbXVzZXVtJTIwaW50ZXJpb3J8ZW58MXx8fHwxNzc2MzE5OTczfDA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Culture', included: true },
{ id: 'syd-4', name: 'Sydney Harbour Cruise', image: 'https://images.unsplash.com/photo-1562003914-018a4a6c2171?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxyaXZlciUyMGNydWlzZSUyMGJvYXQlMjBjaXR5fGVufDF8fHx8MTc3NjMxOTk3M3ww&ixlib=rb-4.1.0&q=80&w=1080', category: 'Experience', included: true },
{ id: 'syd-5', name: 'SEA LIFE Aquarium', image: 'https://images.unsplash.com/photo-1536845111858-bb269af65cb6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjBhcXVhcml1bSUyMHVuZGVyd2F0ZXJ8ZW58MXx8fHwxNzc2MzE5OTcwfDA&ixlib=rb-4.1.0&q=80&w=1080', category: 'Wildlife', included: true },
],
},
};
const offersData: Record<string, { title: string; description: string; image: string }[]> = {
Flexi: [
{ title: 'Astor Hotels Ultra Deluxe', description: '15% Discount on all treatments for first-time clients', image: 'https://images.unsplash.com/photo-1715191904112-4a5d9c3089fa?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxsdXh1cnklMjBob3RlbCUyMHJlc29ydCUyMGV4dGVyaW9yfGVufDF8fHx8MTc3NjMyMTM2MXww&ixlib=rb-4.1.0&q=80&w=1080' },
{ title: 'Green Valley Spa Lux', description: '20% Off on membership plans for new members', image: 'https://images.unsplash.com/photo-1759216853079-831ef8c8b327?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzcGElMjB3ZWxsbmVzcyUyMHRyZWF0bWVudCUyMGludGVyaW9yfGVufDF8fHx8MTc3NjMyMTM2M3ww&ixlib=rb-4.1.0&q=80&w=1080' },
{ title: 'Harbour Dining Co.', description: '10% Off your first dining experience at waterfront', image: 'https://images.unsplash.com/photo-1676471932681-45fa972d848a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxyZXN0YXVyYW50JTIwZmluZSUyMGRpbmluZ3xlbnwxfHx8fDE3NzYzMTkxNDl8MA&ixlib=rb-4.1.0&q=80&w=1080' },
{ title: 'National Gallery Exhibition', description: 'Free audio guide with every gallery visit', image: 'https://images.unsplash.com/photo-1569342380852-035f42d9ca41?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtdXNldW0lMjBnYWxsZXJ5JTIwZXhoaWJpdGlvbnxlbnwxfHx8fDE3NzYyNDYwMjh8MA&ixlib=rb-4.1.0&q=80&w=1080' },
{ title: 'Sunset Harbour Cruise', description: 'Complimentary drink on every sunset cruise booking', image: 'https://images.unsplash.com/photo-1765783800962-83d99ff7b158?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxjcnVpc2UlMjBib2F0JTIwaGFyYm9yJTIwdG91cnxlbnwxfHx8fDE3NzYzMjE2MDd8MA&ixlib=rb-4.1.0&q=80&w=1080' },
],
Unlimited: [
{ title: 'SkyView Ferris Wheel', description: 'Complimentary second ride for all pass holders', image: 'https://images.unsplash.com/photo-1626209025747-b41ee6ec191f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxmZXJyaXMlMjB3aGVlbCUyMGFtdXNlbWVudCUyMHBhcmt8ZW58MXx8fHwxNzc2MzE3NDI2fDA&ixlib=rb-4.1.0&q=80&w=1080' },
{ title: 'City Mall Boutique', description: '15% Off at select boutique stores with your pass', image: 'https://images.unsplash.com/photo-1567966689299-819568579d36?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzaG9wcGluZyUyMG1hbGwlMjBib3V0aXF1ZSUyMHJldGFpbHxlbnwxfHx8fDE3NzYzMjEzNjN8MA&ixlib=rb-4.1.0&q=80&w=1080' },
{ title: 'Adventure Outfitters', description: 'Free gear rental on outdoor adventure bookings', image: 'https://images.unsplash.com/photo-1761131221577-0716baffc6ef?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhZHZlbnR1cmUlMjBzcG9ydHMlMjBvdXRkb29yJTIwYWN0aXZpdHl8ZW58MXx8fHwxNzc2MzIxMzYzfDA&ixlib=rb-4.1.0&q=80&w=1080' },
{ title: 'Skyline Rooftop Lounge', description: 'Buy one get one free on signature cocktails', image: 'https://images.unsplash.com/photo-1642114955097-8f3d0e141641?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxyb29mdG9wJTIwYmFyJTIwY2l0eSUyMHNreWxpbmUlMjBuaWdodHxlbnwxfHx8fDE3NzYyNDU2NTl8MA&ixlib=rb-4.1.0&q=80&w=1080' },
{ title: 'Yarra Valley Wines', description: 'Exclusive wine tasting tour with pass holders discount', image: 'https://images.unsplash.com/photo-1764649841527-c8852b63cc53?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx3aW5lJTIwdGFzdGluZyUyMHZpbmV5YXJkJTIwY2VsbGFyfGVufDF8fHx8MTc3NjMyMTYwOHww&ixlib=rb-4.1.0&q=80&w=1080' },
],
};
/* ─── FIGMA CARD PREVIEWS (Exact Copy) ─── */
function FlexiCardPreview({ city, adultPrice, childPrice, isSelected, image }: { city: string; adultPrice: number; childPrice: number; isSelected: boolean, image: string; }) {
return (
<div className={`relative h-[160px] w-full rounded-lg transition-all duration-200 ${isSelected ? 'ring-2 ring-[#F95F62] shadow-md shadow-[#F95F62]/10' : 'hover:shadow-md'
}`}>
{/* Card bg */}
<div className="absolute inset-0 bg-white border border-[rgba(249,95,175,0.2)] rounded-lg shadow-[0px_4px_20px_0px_rgba(0,0,0,0.06)]" />
{/* City image */}
<div className="absolute h-[158px] left-[1px] top-[1px] w-[103px] rounded-bl-[7px] rounded-tl-[7px] overflow-hidden">
<img alt="" className="absolute inset-0 w-full h-full object-cover" src={image} />
</div>
{/* City name - left aligned */}
<div className="absolute left-[112px] top-[12px]">
<p className="font-['Poppins',sans-serif] font-medium text-[16px] text-[#2a2a2a] leading-[22px] whitespace-nowrap">{city}</p>
</div>
{/* Pricing */}
<div className="absolute left-[112px] top-[40px] flex flex-col gap-[6px]">
<div className="flex gap-[2px] items-center">
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.6)] tracking-[0.06px]">From</span>
<span className="font-['Poppins',sans-serif] font-medium text-[24px] text-[#f95f62] tracking-[-0.96px] leading-[1.3]">${adultPrice}</span>
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px]">/Adult</span>
</div>
<div className="flex gap-[2px] items-center">
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.6)] tracking-[0.06px]">and</span>
<span className="font-['Poppins',sans-serif] font-medium text-[24px] text-[#f95f62] tracking-[-0.96px] leading-[1.3]">${childPrice}</span>
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px]">/Child</span>
</div>
</div>
{/* Description */}
<div className="absolute left-[112px] top-[112px] right-[44px]">
<p className="font-['Poppins',sans-serif] text-[11px] text-left text-[rgba(0,0,0,0.4)] tracking-[0.06px] leading-[14px]">
Dive into an extensive selection of thrilling destinations!
</p>
</div>
{/* Side tab - Flexi (pink) */}
<div className="absolute bg-[#f95faf] h-full right-0 top-0 w-[35px] rounded-br-lg rounded-tr-lg flex flex-col items-center justify-center gap-[2px]">
<span className="font-['Poppins',sans-serif] text-[12px] text-white/70 [writing-mode:vertical-rl] rotate-180">Card</span>
<span className="font-['Poppins',sans-serif] text-[16px] text-white [writing-mode:vertical-rl] rotate-180">Flexi</span>
</div>
{/* Selected checkmark */}
{isSelected && (
<div className="absolute top-2 right-[44px] w-6 h-6 rounded-full bg-[#F95F62] flex items-center justify-center z-10">
<Check className="w-3.5 h-3.5 text-white" strokeWidth={3} />
</div>
)}
</div>
);
}
function UnlimitedCardPreview({ city, adultPrice, childPrice, isSelected, image }: { city: string; adultPrice: number; childPrice: number; isSelected: boolean, image: string; }) {
return (
<div className={`relative h-[160px] w-full rounded-lg transition-all duration-200 ${isSelected ? 'ring-2 ring-[#F95F62] shadow-md shadow-[#F95F62]/10' : 'hover:shadow-md'
}`}>
{/* Card bg */}
<div className="absolute inset-0 bg-white border border-[rgba(0,0,0,0.2)] rounded-lg" />
{/* City image */}
<div className="absolute h-[158px] left-[1px] top-[1px] w-[103px] rounded-bl-[7px] rounded-tl-[7px] overflow-hidden">
<img alt="" className="absolute inset-0 w-full h-full object-cover" src={image} />
</div>
{/* City name - left aligned */}
<div className="absolute left-[112px] top-[12px]">
<p className="font-['Poppins',sans-serif] font-medium text-[16px] text-[#2a2a2a] leading-[20px] whitespace-nowrap">{city}</p>
</div>
{/* Pricing */}
<div className="absolute left-[112px] top-[40px] flex flex-col gap-[6px]">
<div className="flex gap-[2px] items-center">
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.6)] tracking-[0.06px]">From</span>
<span className="font-['Poppins',sans-serif] font-medium text-[24px] text-[#f95f62] tracking-[-0.96px] leading-[1.3]">${adultPrice}</span>
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px]">/Adult</span>
</div>
<div className="flex gap-[2px] items-center">
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.6)] tracking-[0.06px]">and</span>
<span className="font-['Poppins',sans-serif] font-medium text-[24px] text-[#f95f62] tracking-[-0.96px] leading-[1.3]">${childPrice}</span>
<span className="font-['Poppins',sans-serif] text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px]">/Child</span>
</div>
</div>
{/* Description */}
<div className="absolute left-[112px] top-[112px] right-[44px]">
<p className="font-['Poppins',sans-serif] text-[11px] text-left text-[rgba(0,0,0,0.4)] tracking-[0.06px] leading-[14px]">
Dive into an extensive selection of thrilling destinations!
</p>
</div>
{/* Side tab - Unlimited (coral) */}
<div className="absolute bg-[#f95f62] h-full right-0 top-0 w-[35px] rounded-br-lg rounded-tr-lg flex flex-col items-center justify-center gap-[2px]">
<span className="font-['Poppins',sans-serif] text-[12px] text-white/70 [writing-mode:vertical-rl] rotate-180">Card</span>
<span className="font-['Poppins',sans-serif] text-[16px] text-white [writing-mode:vertical-rl] rotate-180">Unlimited</span>
</div>
{/* Selected checkmark */}
{isSelected && (
<div className="absolute top-2 right-[44px] w-6 h-6 rounded-full bg-[#F95F62] flex items-center justify-center z-10">
<Check className="w-3.5 h-3.5 text-white" strokeWidth={3} />
</div>
)}
</div>
);
}
/* ─── CheckoutConfigCard (Exact Copy) ─── */
function CheckoutConfigCard({
item,
onProceed,
}: {
item: any;
onProceed: () => void;
}) {
const [dropdownOpen, setDropdownOpen] = useState(false);
const [noOfAdults, setNoOfAdults] = useState(1)
const [noOfChildren, setNoOfChildren] = useState(0)
const [noOfAttractions, setNoOfAttractions] = useState(item?.minNumber);
const [noOfDays, setNoOfDays] = useState(item?.minNumber)
const cityId = localStorage.getItem("cityId")
const cityName = localStorage.getItem("cityName")
const cardTypeId = item?.cardType?.id
const cardId = item?.id
const cardMode = item?.cardType?.name === "selective_pass" ? "flexi" : "unlimited"
const adultPrice = item?.adultPrice * noOfAdults
const childPrice = item?.childPrice * noOfChildren
const basePrice = adultPrice + childPrice
const taxAmount = basePrice * 0.1
const strikedPrice = basePrice + 20
const [addCardToCart] = useAddCardToCartMutation()
useEffect(() => {
setNoOfAttractions(item?.minNumber)
setNoOfDays(item?.minNumber)
}, [item])
const numberArray = Array.from(
{ length: item?.maxNumber - item?.minNumber + 1 },
(_, i) => item?.minNumber + i
);
const navigate = useNavigate();
const cardBookingDetails = {
cityXid: cityId,
cardTypeXid: cardTypeId,
cardXid: cardId,
cardMode, // stays as-is
totalAdult: noOfAdults,
baseAmount: basePrice, // static value
taxAmount,
totalChild: noOfChildren,
noOfAttractions,
noOfDays
};
const handleProceedToPayment = async () => {
try {
console.log("Adding card to cart", cardBookingDetails);
const response = await addCardToCart(cardBookingDetails);
console.log(response)
const bookingId = response?.data?.id
navigate(`/payment/${bookingId}`)
} catch (error) {
console.error("Error adding card to cart:", error);
toast.error("Failed to move forward. Please try again.");
}
}
return (
<div className="bg-white rounded-2xl shadow-[0px_4px_24px_0px_rgba(0,0,0,0.06)] overflow-hidden w-full max-w-[400px]">
<div className="pt-6 pb-2 text-center">
<h4 className="font-poppins text-lg leading-snug font-medium text-[#2a2a2a]">{cityName}</h4>
<div className="mt-2 flex justify-center">
<span className={`inline-flex items-center px-4 py-1 rounded-full font-poppins text-xs font-medium ${item?.cardType?.name === 'selective_pass' ? 'bg-[#f95faf]/10 text-[#f95faf]' : 'bg-[#f95f62]/10 text-[#f95f62]'}`}>
{item?.cardType?.displayName}
</span>
</div>
</div>
<div className="px-6 py-4 space-y-0">
<div className="flex items-center justify-between py-4 border-b border-gray-100">
<span className="font-poppins text-sm font-normal text-[#2a2a2a]">No. of Adults</span>
<div className="flex items-center gap-3">
<button onClick={() => noOfAdults > 1 && setNoOfAdults((prev) => prev - 1)} disabled={noOfAdults <= 1} className={`w-8 h-8 rounded-full flex items-center justify-center transition-colors ${noOfAdults <= 1 ? 'bg-gray-100 text-gray-300 cursor-not-allowed' : 'bg-[#f95f62]/10 text-[#f95f62] hover:bg-[#f95f62]/20'}`}>
<Minus className="w-4 h-4" />
</button>
<span className="font-poppins text-base font-medium text-[#2a2a2a] w-5 text-center tabular-nums">{noOfAdults}</span>
<button onClick={() => setNoOfAdults((prev) => prev + 1)} disabled={noOfAdults >= 15} className={`w-8 h-8 rounded-full flex items-center justify-center transition-colors ${noOfAdults >= 15 ? 'bg-gray-100 text-gray-300 cursor-not-allowed' : 'bg-[#f95f62]/10 text-[#f95f62] hover:bg-[#f95f62]/20'}`}>
<Plus className="w-4 h-4" />
</button>
</div>
</div>
<div className="flex items-center justify-between py-4 border-b border-gray-100">
<span className="font-poppins text-sm font-normal text-[#2a2a2a]">No. of Children</span>
<div className="flex items-center gap-3">
<button onClick={() => noOfChildren > 0 && setNoOfChildren((prev) => prev - 1)} disabled={noOfChildren <= 0} className={`w-8 h-8 rounded-full flex items-center justify-center transition-colors ${noOfChildren <= 0 ? 'bg-gray-100 text-gray-300 cursor-not-allowed' : 'bg-[#f95f62]/10 text-[#f95f62] hover:bg-[#f95f62]/20'}`}>
<Minus className="w-4 h-4" />
</button>
<span className="font-poppins text-base font-medium text-[#2a2a2a] w-5 text-center tabular-nums">{noOfChildren}</span>
<button onClick={() => setNoOfChildren((prev) => prev + 1)} disabled={noOfChildren >= 10} className={`w-8 h-8 rounded-full flex items-center justify-center transition-colors ${noOfChildren >= 10 ? 'bg-gray-100 text-gray-300 cursor-not-allowed' : 'bg-[#f95f62]/10 text-[#f95f62] hover:bg-[#f95f62]/20'}`}>
<Plus className="w-4 h-4" />
</button>
</div>
</div>
<div className="flex items-center justify-between py-4 border-b border-gray-100">
<span className="font-poppins text-sm font-normal text-[#2a2a2a]">
{item?.cardType?.name === 'selective_pass' ? 'No. of Attractions' : 'No. of Days'}
</span>
<div className="relative">
<button onClick={() => setDropdownOpen(!dropdownOpen)} className="flex items-center gap-2 border border-[#f95f62]/30 rounded-lg px-3 py-1.5 min-w-[72px] justify-between hover:border-[#f95f62] transition-colors">
<span className="font-poppins text-base font-medium text-[#f95f62] tabular-nums">{cardMode === "flexi" ? noOfAttractions : noOfDays}</span>
<ChevronDown className={`w-4 h-4 text-[#f95f62] transition-transform ${dropdownOpen ? 'rotate-180' : ''}`} />
</button>
<AnimatePresence>
{dropdownOpen && (
<motion.div
initial={{ opacity: 0, y: -4, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: -4, scale: 0.95 }}
className="absolute right-0 top-full mt-1 bg-white rounded-lg shadow-lg border border-gray-100 z-30 min-w-[72px]
max-h-48 overflow-y-auto"
>
{numberArray.map((i) => (
<button
key={i}
onClick={() => {
cardMode === "flexi" ? setNoOfAttractions(i) : setNoOfDays(i);
setDropdownOpen(false);
}}
className={`w-full px-3 py-2 text-left font-poppins text-sm transition-colors ${(cardMode === "flexi" ? noOfAttractions === i : noOfDays === i)
? "bg-[#f95f62]/10 text-[#f95f62] font-medium"
: "text-[#2a2a2a] hover:bg-gray-50 font-normal"
}`}
>
{i}
</button>
))}
</motion.div>
)}
</AnimatePresence>
</div>
</div>
<div className="flex items-center justify-between py-5">
<span className="font-poppins text-sm font-normal text-[#2a2a2a]">You Pay</span>
<div className="flex items-center gap-2">
<span className="font-poppins text-sm font-normal text-[#aaa] line-through">${strikedPrice}</span>
<span className="font-poppins text-2xl font-medium text-[#f95f62] tracking-tight">${basePrice}</span>
</div>
</div>
</div>
<div className="px-6 pb-6">
<motion.button whileHover={{ scale: 1.01 }} whileTap={{ scale: 0.98 }} onClick={handleProceedToPayment} className="w-full py-4 rounded-full bg-[#f95f62] text-white font-poppins text-base font-medium hover:bg-[#e8545a] transition-colors shadow-lg shadow-[#f95f62]/20 cursor-pointer">
Proceed to Pay
</motion.button>
</div>
</div>
);
}
/* ─── MAIN CHECKOUT PAGE 2 ─── */
export function CheckoutPage2({
onHomeClick,
onPassesClick,
onAttractionsClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onContactUsClick,
onSignInClick,
onSignOutClick,
onProfileClick,
user,
currentPage,
}: any) {
const navigate = useNavigate();
// Default item (you can pass via props later)
const baseUrl = import.meta.env.VITE_BASE_URL;
const cityId = localStorage.getItem("cityId")
const { data: checkoutPageData, isLoading } = useGetCheckoutPageDataQuery(cityId)
const cityName = checkoutPageData?.city?.name ?? ""
const cityImage = checkoutPageData?.city?.heroBanner?.image ?? ""
const cards = checkoutPageData?.cards ?? []
const flexiCard = checkoutPageData?.cards[0] ?? null
const unlimitedCard = checkoutPageData?.cards[1] ?? null
const attractions = checkoutPageData?.attractions ?? [];
const [checkoutItem, setCheckoutItem] = useState(flexiCard);
useEffect(() => {
setCheckoutItem(flexiCard)
}, [cards])
console.log(checkoutItem)
if (isLoading) {
return <LoadingSpinner />
} else {
// console.log(flexiCard)
}
const handleCheckoutItemChange = (cardObject: any) => {
setCheckoutItem(cardObject);
};
return (
<div className="min-h-screen bg-[#fafafa] font-poppins">
<Navbar
activeCity="Melbourne"
onCityChange={() => { }}
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
onPassesClick={onPassesClick}
onCheckoutClick={() => { }}
onHomeClick={onHomeClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={() => { }}
onMagicItineraryClick={() => { }}
onPostCardsClick={() => { }}
onOffersClick={() => { }}
onSuperSavingsClick={() => { }}
onEsimsClick={() => { }}
onHotelDiscountsClick={() => { }}
onCartClick={() => { }}
currentPage={currentPage}
user={user}
/>
<div className="w-full px-4 sm:px-6 lg:px-10 xl:px-16 pt-32 pb-24 max-w-[1440px] mx-auto">
<button onClick={() => navigate(-1)} className="flex items-center gap-2 text-[#8e8e8e] hover:text-[#2a2a2a] transition-colors font-poppins text-sm font-normal mb-8">
<ArrowLeft className="w-4 h-4" />Back
</button>
<div className="mb-10">
<h2 className="font-poppins text-2xl md:text-3xl lg:text-4xl leading-tight">
<span className="font-light">Checkout</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-[#F95F62] to-[#F95FAF] bg-clip-text text-transparent pr-2">{cityName}</span>
</h2>
</div>
<div className="flex flex-col lg:flex-row gap-10">
{/* Left Column */}
<div className="flex-1 space-y-8">
{/* Card Type Selection */}
<div>
<h3 className="font-poppins text-xl md:text-2xl leading-snug font-medium text-[#2a2a2a]">Choose Your Card</h3>
<p className="font-poppins text-sm leading-relaxed font-normal text-[#8e8e8e] mt-1 mb-4">Select the card type that best suits your travel style</p>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-[16px]">
<button onClick={() => handleCheckoutItemChange(flexiCard)}>
<FlexiCardPreview city={cityName} image={cityImage} adultPrice={flexiCard.adultPrice} childPrice={flexiCard.childPrice} isSelected={checkoutItem?.cardType.name === 'selective_pass'} />
</button>
<button onClick={() => handleCheckoutItemChange(unlimitedCard)}>
<UnlimitedCardPreview city={cityName} image={cityImage} adultPrice={unlimitedCard.adultPrice} childPrice={unlimitedCard.childPrice} isSelected={checkoutItem?.cardType.name === 'unlimited_card'} />
</button>
</div>
{/* Features Comparison (Exact Copy) */}
<div className="mt-6 bg-[#f5f5f5] rounded-xl p-4">
<div className="grid grid-cols-[1fr_70px_70px] gap-y-0 items-center">
<p className="font-poppins font-medium text-sm text-[#2a2a2a] py-3">Features</p>
<p className="font-poppins font-medium text-sm text-[#2a2a2a] text-center py-3">Flexi</p>
<p className="font-poppins font-medium text-sm text-[#2a2a2a] text-center py-3">Unlimited</p>
{[
{ feature: 'Access to attractions', flexi: true, unlimited: true },
{ feature: 'Entry to attractions', flexi: true, unlimited: true },
{ feature: 'Access to experiences', flexi: true, unlimited: true },
{ feature: 'Entry to sites', flexi: false, unlimited: true },
{ feature: 'Access to venues', flexi: true, unlimited: true },
{ feature: 'Entry to events', flexi: true, unlimited: true },
{ feature: 'Access to experiences', flexi: false, unlimited: true },
{ feature: 'Access to Itinerary creation', flexi: false, unlimited: true },
{ feature: 'Access to postcard creation', flexi: false, unlimited: true },
].map((row, i) => (
<React.Fragment key={i}>
<p className="font-poppins font-normal text-[13px] text-[#2a2a2a] py-2.5 border-t border-[rgba(0,0,0,0.08)] flex items-center gap-1.5">
<span className="text-[#2a2a2a]"></span> {row.feature}
</p>
<div className="flex justify-center py-2.5 border-t border-[rgba(0,0,0,0.08)]">
{row.flexi ? <div className="w-5 h-5 rounded-full bg-[#F95F62] flex items-center justify-center"><Check className="w-3 h-3 text-white" strokeWidth={3} /></div> : <span className="font-poppins text-[13px] text-[rgba(0,0,0,0.3)]"></span>}
</div>
<div className="flex justify-center py-2.5 border-t border-[rgba(0,0,0,0.08)]">
{row.unlimited ? <div className="w-5 h-5 rounded-full bg-[#F95F62] flex items-center justify-center"><Check className="w-3 h-3 text-white" strokeWidth={3} /></div> : <span className="font-poppins text-[13px] text-[rgba(0,0,0,0.3)]"></span>}
</div>
</React.Fragment>
))}
</div>
</div>
</div>
{/* Offers Section (Exact) */}
<div>
<h3 className="font-poppins text-xl md:text-2xl leading-snug font-medium text-[#2a2a2a]">{checkoutItem?.cardType?.displayName} Offers</h3>
<p className="font-poppins text-sm leading-relaxed font-normal text-[#8e8e8e] mt-1 mb-4">Exclusive deals and discounts included with your {checkoutItem?.cardType.displayName} pass</p>
<div className="flex gap-3 overflow-x-auto pb-2 -mx-4 px-4 snap-x snap-mandatory scrollbar-hide">
{checkoutItem?.offers.map((offer: any) => (
<div key={offer.id} className="relative bg-white rounded-xl shrink-0 w-[180px] h-[260px] snap-start">
<div className="flex flex-col gap-2 items-start overflow-hidden p-3 rounded-xl h-full">
<div className="h-[120px] w-full rounded-lg overflow-hidden shrink-0 relative">
<ImageWithFallback src={`${baseUrl}/${offer.websiteBannerImage}`} alt={offer.title} className="absolute inset-0 w-full h-full object-cover rounded-lg" />
</div>
<div className="w-full h-[44px] overflow-hidden">
<p className="font-['Poppins',sans-serif] font-normal text-[18px] text-black tracking-[-0.72px] leading-[22px] line-clamp-2">{offer.title}</p>
</div>
<div className="w-full flex-1">
<p className="font-['Poppins',sans-serif] font-normal text-[12px] text-[rgba(0,0,0,0.6)] leading-[16px] line-clamp-3">{offer.description}</p>
</div>
</div>
<div className="absolute inset-0 border border-[rgba(249,95,98,0.24)] rounded-xl pointer-events-none" />
</div>
))}
</div>
</div>
{/* Attractions Section (Exact) */}
<div>
<div className="flex items-center justify-between">
<h3 className="font-poppins text-xl md:text-2xl leading-snug font-medium text-[#2a2a2a]">Available Attractions</h3>
<span className="font-poppins text-xs font-medium text-[#F95F62] bg-[#F95F62]/10 px-3 py-1 rounded-full">{attractions.length} included</span>
</div>
<p className="font-poppins text-sm leading-relaxed font-normal text-[#8e8e8e] mt-1 mb-4">Explore all the experiences you can enjoy with your pass</p>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3">
{attractions.map((a: any) => (
<div key={a.id} className="group relative rounded-xl overflow-hidden">
<div className="aspect-[4/3] relative">
<ImageWithFallback src={a.thumbnail} alt={a.title} className="absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-105" />
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-black/10 to-transparent" />
<div className="absolute top-2 right-2">
<span className="inline-flex px-2 py-0.5 rounded-full bg-white/90 backdrop-blur-sm text-[10px] font-poppins font-medium text-[#555]">{a.category}</span>
</div>
<div className="absolute bottom-2 left-2 right-2">
<h6 className="font-poppins text-sm leading-snug font-medium text-white drop-shadow-sm">{a.title}</h6>
</div>
</div>
</div>
))}
</div>
</div>
</div>
{/* Right Column - Config Card */}
<div className="hidden lg:block lg:w-[420px] flex-shrink-0">
<div className="lg:sticky lg:top-28">
<CheckoutConfigCard
item={checkoutItem}
onChange={handleCheckoutItemChange}
onProceed={() => navigate("/payment")}
/>
</div>
</div>
{/* Mobile Config Card */}
{/* <div className="lg:hidden mt-6">
<CheckoutConfigCard
item={checkoutItem}
onChange={handleCheckoutItemChange}
onProceed={() => navigate("/payment")}
/>
</div> */}
</div>
</div>
<Footer
onHomeClick={onHomeClick}
onPassesClick={onPassesClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onContactUsClick={onContactUsClick}
/>
</div>
);
}
// export function CheckoutPage2({
// onHomeClick,
// onPassesClick,
// onAttractionsClick,
// onBlogsClick,
// onHowItWorksClick,
// onFAQClick,
// onPrivacyPolicyClick,
// onAboutUsClick,
// onContactUsClick,
// onSignInClick,
// onSignOutClick,
// onProfileClick,
// user,
// currentPage,
// }: any) {
// const navigate = useNavigate();
// const cityId = localStorage.getItem("cityId");
// const { data: checkoutPageData, isLoading } = useGetCheckoutPageDataQuery(cityId);
// const city = checkoutPageData?.city;
// const allCards = checkoutPageData?.cards ?? [];
// const allAttractions = checkoutPageData?.attractions ?? [];
// const allOffers = checkoutPageData?.offers ?? [];
// const baseUrl = import.meta.env.VITE_BASE_URL;
// // Initialize with first card (Flexi) as default
// const defaultCard = allCards[0] || null;
// const [checkoutItem, setCheckoutItem] = useState<CartItem>({
// id: defaultCard?.id?.toString() || '1',
// city: city?.name || 'Melbourne',
// cardType: defaultCard?.cardType?.displayName || 'Flexi',
// days: defaultCard?.validityDuration || 3,
// adults: 2,
// children: 1,
// quantity: 1,
// pricePerUnit: defaultCard?.adultPrice || 49.5,
// image: city?.heroBanner?.image || '',
// });
// if (isLoading) {
// return <LoadingSpinner />;
// }
// const handleCheckoutItemChange = (updates: Partial<CartItem>) => {
// const updated = { ...checkoutItem, ...updates };
// // If card type changes, update with real card data
// if (updates.cardType) {
// const selectedCard = allCards.find(
// c => c.cardType?.displayName === updates.cardType
// );
// if (selectedCard) {
// updated.id = selectedCard.id.toString();
// updated.days = selectedCard.validityDuration;
// updated.pricePerUnit = selectedCard.adultPrice;
// }
// }
// setCheckoutItem(updated);
// };
// // Get currently selected card
// const selectedCard = allCards.find(c =>
// c.cardType?.displayName === checkoutItem.cardType
// ) || allCards[0];
// // Offers for selected card (fallback to global offers)
// const currentOffers = selectedCard?.offers?.length
// ? selectedCard.offers
// : allOffers;
// return (
// <div className="min-h-screen bg-[#fafafa] font-poppins">
// <Navbar
// activeCity={city?.name || "Melbourne"}
// onCityChange={() => { }}
// onSignInClick={onSignInClick}
// onSignOutClick={onSignOutClick}
// onPassesClick={onPassesClick}
// onCheckoutClick={() => { }}
// onHomeClick={onHomeClick}
// onAttractionsClick={onAttractionsClick}
// onBlogsClick={onBlogsClick}
// onHowItWorksClick={onHowItWorksClick}
// onFAQClick={onFAQClick}
// onPrivacyPolicyClick={onPrivacyPolicyClick}
// onAboutUsClick={onAboutUsClick}
// onProfileClick={onProfileClick}
// onCityCardsClick={() => { }}
// onMagicItineraryClick={() => { }}
// onPostCardsClick={() => { }}
// onOffersClick={() => { }}
// onSuperSavingsClick={() => { }}
// onEsimsClick={() => { }}
// onHotelDiscountsClick={() => { }}
// onCartClick={() => { }}
// currentPage={currentPage}
// user={user}
// />
// <div className="w-full px-4 sm:px-6 lg:px-10 xl:px-16 pt-32 pb-24 max-w-[1440px] mx-auto">
// <button
// onClick={() => navigate(-1)}
// className="flex items-center gap-2 text-[#8e8e8e] hover:text-[#2a2a2a] transition-colors font-poppins text-sm font-normal mb-8"
// >
// <ArrowLeft className="w-4 h-4" />Back
// </button>
// <div className="mb-10">
// <h2 className="font-poppins text-2xl md:text-3xl lg:text-4xl leading-tight">
// <span className="font-light">Checkout</span>{' '}
// <span className="font-bold italic bg-gradient-to-r from-[#F95F62] to-[#F95FAF] bg-clip-text text-transparent pr-3">
// {city?.name || checkoutItem.city}
// </span>
// </h2>
// </div>
// <div className="flex flex-col lg:flex-row gap-10">
// {/* Left Column */}
// <div className="flex-1 space-y-8">
// {/* Card Type Selection */}
// <div>
// <h3 className="font-poppins text-xl md:text-2xl leading-snug font-medium text-[#2a2a2a]">Choose Your Card</h3>
// <p className="font-poppins text-sm leading-relaxed font-normal text-[#8e8e8e] mt-1 mb-4">
// Select the card type that best suits your travel style
// </p>
// <div className="grid grid-cols-1 sm:grid-cols-2 gap-[16px]">
// {allCards.map((card) => (
// <button
// key={card.id}
// onClick={() => handleCheckoutItemChange({
// cardType: card.cardType?.displayName || 'Flexi'
// })}
// >
// {card.cardType?.name === 'selective_pass' ? (
// <FlexiCardPreview
// city={city?.name || checkoutItem.city}
// adultPrice={card.adultPrice}
// childPrice={card.childPrice}
// isSelected={checkoutItem.cardType === card.cardType?.displayName}
// />
// ) : (
// <UnlimitedCardPreview
// city={city?.name || checkoutItem.city}
// adultPrice={card.adultPrice}
// childPrice={card.childPrice}
// isSelected={checkoutItem.cardType === card.cardType?.displayName}
// />
// )}
// </button>
// ))}
// </div>
// {/* Features Comparison - Kept as is (no CSS change) */}
// <div className="mt-6 bg-[#f5f5f5] rounded-xl p-4">
// <div className="grid grid-cols-[1fr_70px_70px] gap-y-0 items-center">
// {/* Header */}
// <p className="font-poppins font-medium text-sm text-[#2a2a2a] py-3">Features</p>
// <p className="font-poppins font-medium text-sm text-[#2a2a2a] text-center py-3">Flexi</p>
// <p className="font-poppins font-medium text-sm text-[#2a2a2a] text-center py-3">Unlimited</p>
// {[
// { feature: 'Access to attractions', flexi: true, unlimited: true },
// { feature: 'Entry to attractions', flexi: true, unlimited: true },
// { feature: 'Access to experiences', flexi: true, unlimited: true },
// { feature: 'Entry to sites', flexi: false, unlimited: true },
// { feature: 'Access to venues', flexi: true, unlimited: true },
// { feature: 'Entry to events', flexi: true, unlimited: true },
// { feature: 'Access to experiences', flexi: false, unlimited: true },
// { feature: 'Access to Itinerary creation', flexi: false, unlimited: true },
// { feature: 'Access to postcard creation', flexi: false, unlimited: true },
// ].map((row, i) => (
// <React.Fragment key={i}>
// <p className="font-poppins font-normal text-[13px] text-[#2a2a2a] py-2.5 border-t border-[rgba(0,0,0,0.08)] flex items-center gap-1.5">
// <span className="text-[#2a2a2a]">•</span> {row.feature}
// </p>
// <div className="flex justify-center py-2.5 border-t border-[rgba(0,0,0,0.08)]">
// {row.flexi ? (
// <div className="w-5 h-5 rounded-full bg-[#F95F62] flex items-center justify-center">
// <Check className="w-3 h-3 text-white" strokeWidth={3} />
// </div>
// ) : (
// <span className="font-poppins text-[13px] text-[rgba(0,0,0,0.3)]"></span>
// )}
// </div>
// <div className="flex justify-center py-2.5 border-t border-[rgba(0,0,0,0.08)]">
// {row.unlimited ? (
// <div className="w-5 h-5 rounded-full bg-[#F95F62] flex items-center justify-center">
// <Check className="w-3 h-3 text-white" strokeWidth={3} />
// </div>
// ) : (
// <span className="font-poppins text-[13px] text-[rgba(0,0,0,0.3)]"></span>
// )}
// </div>
// </React.Fragment>
// ))}
// </div>
// </div>
// </div>
// {/* Offers Section */}
// <div>
// <h3 className="font-poppins text-xl md:text-2xl leading-snug font-medium text-[#2a2a2a]">
// {checkoutItem.cardType} Card Offers
// </h3>
// <p className="font-poppins text-sm leading-relaxed font-normal text-[#8e8e8e] mt-1 mb-4">
// Exclusive deals and discounts included with your {checkoutItem.cardType} pass
// </p>
// <div className="flex gap-3 overflow-x-auto pb-2 -mx-4 px-4 snap-x snap-mandatory scrollbar-hide">
// {currentOffers.map((offer, idx) => (
// <div key={idx} className="relative bg-white rounded-xl shrink-0 w-[180px] h-[260px] snap-start">
// <div className="flex flex-col gap-2 items-start overflow-hidden p-3 rounded-xl h-full">
// <div className="h-[120px] w-full rounded-lg overflow-hidden shrink-0 relative">
// <ImageWithFallback
// src={`${baseUrl}/${offer.websiteBannerImage}` || offer.mobileBannerImage}
// alt={offer.title}
// className="absolute inset-0 w-full h-full object-cover rounded-lg"
// />
// </div>
// <div className="w-full h-[44px] overflow-hidden">
// <p className="font-['Poppins',sans-serif] font-normal text-[18px] text-black tracking-[-0.72px] leading-[22px] line-clamp-2">
// {offer.title}
// </p>
// </div>
// <div className="w-full flex-1">
// <p className="font-['Poppins',sans-serif] font-normal text-[12px] text-[rgba(0,0,0,0.6)] leading-[16px] line-clamp-3">
// {offer.description}
// </p>
// </div>
// </div>
// <div className="absolute inset-0 border border-[rgba(249,95,98,0.24)] rounded-xl pointer-events-none" />
// </div>
// ))}
// </div>
// </div>
// {/* Attractions Section */}
// <div>
// <div className="flex items-center justify-between">
// <h3 className="font-poppins text-xl md:text-2xl leading-snug font-medium text-[#2a2a2a]">Available Attractions</h3>
// <span className="font-poppins text-xs font-medium text-[#F95F62] bg-[#F95F62]/10 px-3 py-1 rounded-full">
// {allAttractions.length} included
// </span>
// </div>
// <p className="font-poppins text-sm leading-relaxed font-normal text-[#8e8e8e] mt-1 mb-4">
// Explore all the experiences you can enjoy with your pass
// </p>
// <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3">
// {allAttractions.map((attraction) => (
// <div key={attraction.id} className="group relative rounded-xl overflow-hidden">
// <div className="aspect-[4/3] relative">
// <ImageWithFallback
// src={attraction.thumbnail}
// alt={attraction.title}
// className="absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
// />
// <div className="absolute inset-0 bg-gradient-to-t from-black/60 via-black/10 to-transparent" />
// <div className="absolute bottom-2 left-2 right-2">
// <h6 className="font-poppins text-sm leading-snug font-medium text-white drop-shadow-sm">
// {attraction.title}
// </h6>
// </div>
// </div>
// </div>
// ))}
// </div>
// </div>
// </div>
// {/* Right Column - Config Card */}
// <div className="hidden lg:block lg:w-[420px] flex-shrink-0">
// <div className="lg:sticky lg:top-28">
// <CheckoutConfigCard
// item={checkoutItem}
// onChange={handleCheckoutItemChange}
// onProceed={() => navigate("/payment")}
// />
// </div>
// </div>
// {/* Mobile Config Card */}
// <div className="lg:hidden mt-6">
// <CheckoutConfigCard
// item={checkoutItem}
// onChange={handleCheckoutItemChange}
// onProceed={() => navigate("/payment")}
// />
// </div>
// </div>
// </div>
// <Footer
// onHomeClick={onHomeClick}
// onPassesClick={onPassesClick}
// onAttractionsClick={onAttractionsClick}
// onBlogsClick={onBlogsClick}
// onHowItWorksClick={onHowItWorksClick}
// onFAQClick={onFAQClick}
// onPrivacyPolicyClick={onPrivacyPolicyClick}
// onAboutUsClick={onAboutUsClick}
// onContactUsClick={onContactUsClick}
// />
// </div>
// );
// }