main #7

Merged
Hemant.Vishwakarma merged 24 commits from main into testing 2026-04-22 11:23:27 +00:00
4 changed files with 826 additions and 8 deletions
Showing only changes of commit fca33f55a7 - Show all commits

View File

@@ -34,6 +34,8 @@ import { LandingMagicItineraryPage } from './pages/LandingMagicItineraryPage';
import { DiscoverPage } from './pages/DiscoverPage';
import { CartPage } from './pages/CartPage';
import { PaymentDetailsPage } from './pages/PaymentDetailsPage';
import { CartPageDesign } from './pages/CartPageDesign';
import { CheckoutPage2 } from './pages/CheckoutPage2';
// User type definition
interface User {
@@ -126,11 +128,11 @@ export function AppRouter({
} />
{/* Checkout Routes */}
<Route path="/checkout" element={
{/* <Route path="/checkout" element={
<motion.div key="checkout" {...pageTransition}>
<CheckoutPage {...commonNavHandlers} />
</motion.div>
} />
} /> */}
<Route path="/secure-checkout" element={
<motion.div key="secure-checkout" {...pageTransition}>
@@ -278,7 +280,17 @@ export function AppRouter({
<CartPage {...commonNavHandlers} />
</motion.div>
} />
<Route path="/payment" element={
<Route path="/checkout" element={
<motion.div key="super-savings" {...pageTransition}>
<CheckoutPage2 {...commonNavHandlers} />
</motion.div>
} />
<Route path="/cart-design" element={
<motion.div key="super-savings" {...pageTransition}>
<CartPageDesign {...commonNavHandlers} />
</motion.div>
} />
<Route path="/payment/:bookingId" element={
<motion.div key="super-savings" {...pageTransition}>
<PaymentDetailsPage {...commonNavHandlers} />
</motion.div>

View File

@@ -95,7 +95,8 @@ export default function Navbar({
const cityId = localStorage.getItem("cityId")
const cityName = localStorage.getItem("cityName")
const citySelected = location.pathname.includes(slugify(cityName) || "")
// const citySelected = location.pathname.includes(slugify(cityName) || "")
const citySelected = cityName
const baseUrl = import.meta.env.VITE_BASE_URL;
@@ -675,7 +676,7 @@ export default function Navbar({
</div>
}
/> */}
<ShoppingBag className="w-6 h-6" onClick={() => navigate("/cart-page")} />
<ShoppingBag className="w-6 h-6" onClick={() => navigate("/cart")} />
{/* Enhanced City Card Button with Source Tracking */}
<div className="flex items-center gap-3 pl-2">

View File

@@ -446,14 +446,14 @@ export function CartPage({
setSelectedCardId(prev => (prev === id ? null : id));
};
const handleGoToCheckout = () => {
const handleGoToCheckout = (selectedItemId:number) => {
// const item = cartItems.find(i => i.id === selectedCardId);
// if (item) {
// setCheckoutItem({ ...item });
// setView('checkout');
// window.scrollTo({ top: 0, behavior: 'smooth' });
// }
navigate("/payment")
navigate(`/payment/${selectedItemId}`)
};
const handleBackToCart = () => {
@@ -641,7 +641,7 @@ export function CartPage({
<motion.button
whileHover={selectedItem ? { scale: 1.02 } : {}}
whileTap={selectedItem ? { scale: 0.98 } : {}}
onClick={handleGoToCheckout}
onClick={()=>handleGoToCheckout(selectedItem.id)}
disabled={!selectedItem}
className={`sm:w-auto px-8 py-3.5 rounded-xl font-poppins text-base font-medium flex items-center justify-center gap-2 transition-all duration-200 ${selectedItem ? 'bg-[#F95F62] text-white hover:bg-[#e8545a] shadow-lg shadow-[#F95F62]/20' : 'bg-gray-100 text-gray-400 cursor-not-allowed'
}`}

805
src/pages/CheckoutPage2.tsx Normal file
View File

@@ -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>
);
}