@@ -0,0 +1,805 @@
import React , { 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 { useGetCheckoutPageDataQuery } from '../Redux/services/cards.service' ;
import LoadingSpinner from '../components/LoadingSpinner' ;
/* ─── 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 } : { city : string ; adultPrice : number ; childPrice : number ; isSelected : boolean } ) {
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' } ` } >
< 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)]" / >
< 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={imgRectangle26} /> */ }
< 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 >
< 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 >
< 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 >
< 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 >
{ 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 } : { city : string ; adultPrice : number ; childPrice : number ; isSelected : boolean } ) {
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' } ` } >
< div className = "absolute inset-0 bg-white border border-[rgba(0,0,0,0.2)] rounded-lg" / >
< 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={imgRectangle26} /> */ }
< 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 >
< 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 >
< 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 >
< 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 >
{ 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 ,
onChange ,
onProceed ,
} : {
item : CartItem ;
onChange : ( updates : Partial < CartItem > ) = > void ;
onProceed : ( ) = > void ;
} ) {
const [ daysOpen , setDaysOpen ] = useState ( false ) ;
const originalPrice = ( item . pricePerUnit * item . quantity * 1.35 ) ;
const totalPrice = item . pricePerUnit * item . quantity ;
const navigate = useNavigate ( ) ;
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]" > { item . city } < / 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 === 'Flexi' ? 'bg-[#f95faf]/10 text-[#f95faf]' : 'bg-[#f95f62]/10 text-[#f95f62]' } ` } >
{ item . cardType } 3
< / 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 = { ( ) = > item . adults > 1 && onChange ( { adults : item.adults - 1 } ) } disabled = { item . adults <= 1 } className = { ` w-8 h-8 rounded-full flex items-center justify-center transition-colors ${ item . adults <= 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" > { item . adults } < / span >
< button onClick = { ( ) = > onChange ( { adults : item.adults + 1 } ) } className = "w-8 h-8 rounded-full bg-[#f95f62]/10 text-[#f95f62] hover:bg-[#f95f62]/20 flex items-center justify-center transition-colors" >
< 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 = { ( ) = > item . children > 0 && onChange ( { children : item.children - 1 } ) } disabled = { item . children <= 0 } className = { ` w-8 h-8 rounded-full flex items-center justify-center transition-colors ${ item . children <= 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" > { item . children } < / span >
< button onClick = { ( ) = > onChange ( { children : item.children + 1 } ) } className = "w-8 h-8 rounded-full bg-[#f95f62]/10 text-[#f95f62] hover:bg-[#f95f62]/20 flex items-center justify-center transition-colors" >
< 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 === 'Flexi' ? 'No. of Attractions' : 'No. of Days' }
< / span >
< div className = "relative" >
< button onClick = { ( ) = > setDaysOpen ( ! daysOpen ) } 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" > { item . days } < / span >
< ChevronDown className = { ` w-4 h-4 text-[#f95f62] transition-transform ${ daysOpen ? 'rotate-180' : '' } ` } / >
< / button >
< AnimatePresence >
{ daysOpen && (
< 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] overflow-hidden" >
{ dayOptions . map ( ( d ) = > (
< button key = { d } onClick = { ( ) = > { onChange ( { days : d } ) ; setDaysOpen ( false ) ; } } className = { ` w-full px-3 py-2 text-left font-poppins text-sm transition-colors ${ item . days === d ? 'bg-[#f95f62]/10 text-[#f95f62] font-medium' : 'text-[#2a2a2a] hover:bg-gray-50 font-normal' } ` } >
{ d }
< / 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" > $ { originalPrice . toFixed ( 0 ) } < / span >
< span className = "font-poppins text-2xl font-medium text-[#f95f62] tracking-tight" > $ { totalPrice . toFixed ( 0 ) } < / span >
< / div >
< / div >
< / div >
< div className = "px-6 pb-6" >
< motion.button whileHover = { { scale : 1.01 } } whileTap = { { scale : 0.98 } } onClick = { onProceed } 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" >
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 [checkoutItem, setCheckoutItem] = useState<CartItem>({
// id: '1',
// city: 'Melbourne',
// cardType: 'Flexi',
// days: 3,
// adults: 2,
// children: 1,
// quantity: 1,
// pricePerUnit: 49.5,
// image: '',
// });
// const cityId = localStorage.getItem("cityId")
// const { data: checkoutPageData, isLoading } = useGetCheckoutPageDataQuery(cityId)
// const cards = checkoutPageData?.cards ?? []
// if (isLoading) {
// return <LoadingSpinner />
// } else {
// console.log(checkoutPageData)
// }
// const handleCheckoutItemChange = (updates: Partial<CartItem>) => {
// const updated = { ...checkoutItem, ...updates };
// const prices = priceTable[updated.cardType];
// if (prices && prices[updated.days] !== undefined) {
// updated.pricePerUnit = prices[updated.days];
// }
// setCheckoutItem(updated);
// };
// const attractions = attractionsData[checkoutItem.city]?.[checkoutItem.cardType] || [];
// const offers = offersData[checkoutItem.cardType] || [];
// 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 to Cart
// </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">{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]">
// <button onClick={() => handleCheckoutItemChange({ cardType: 'Flexi' })}>
// <FlexiCardPreview city={checkoutItem.city} adultPrice={priceTable.Flexi[checkoutItem.days] || 80} childPrice={10} isSelected={checkoutItem.cardType === 'Flexi'} />
// </button>
// <button onClick={() => handleCheckoutItemChange({ cardType: 'Unlimited' })}>
// <UnlimitedCardPreview city={checkoutItem.city} adultPrice={priceTable.Unlimited[checkoutItem.days] || 120} childPrice={20} isSelected={checkoutItem.cardType === 'Unlimited'} />
// </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} 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">
// {offers.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={offer.image} 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) => (
// <div key={a.id} className="group relative rounded-xl overflow-hidden">
// <div className="aspect-[4/3] relative">
// <ImageWithFallback src={a.image} alt={a.name} 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.name}</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 to Cart
< / 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" >
{ 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 >
) ;
}