get a city card flow fix

This commit is contained in:
priyanshuvish
2025-11-05 18:57:24 +05:30
parent a55e473e3c
commit fec000e0b0
8 changed files with 249 additions and 165 deletions

View File

@@ -24,22 +24,24 @@ function App() {
const [offersSource, setOffersSource] = useState<'products' | 'passes'>('products');
const [stickyCardType, setStickyCardType] = useState<'unlimited' | 'selective'>('unlimited');
// Login state management
// ✅ Authentication state management
const [user, setUser] = useState<User | null>(null);
const [showLoginModal, setShowLoginModal] = useState(false);
const location = useLocation();
const navigate = useNavigate();
// Login handlers
// Login handlers
const handleLoginSuccess = (userData: User) => {
setUser(userData);
setShowLoginModal(false);
console.log('User logged in successfully:', userData);
};
const handleSignOut = () => {
setUser(null);
navigate('/');
console.log('User signed out');
};
const handleCloseLoginModal = () => {
@@ -50,6 +52,13 @@ function App() {
setShowLoginModal(true);
};
// ✅ Handle checkout (you can expand this later)
const handleCheckoutClick = () => {
console.log('Proceeding to checkout for user:', user);
// Add your checkout logic here
navigate('/checkout');
};
// Detect mobile for optimized animations
useEffect(() => {
const checkMobile = () => {
@@ -108,13 +117,14 @@ function App() {
animate={{ opacity: 1 }}
transition={{ duration: 0.3, ease: easeOutCubic }}
>
<AppRouter
<AppRouter
user={user}
showLoginModal={showLoginModal}
onSignInClick={handleSignInClick}
onSignOutClick={handleSignOut}
onLoginSuccess={handleLoginSuccess}
onCloseLoginModal={handleCloseLoginModal}
onCheckoutClick={handleCheckoutClick} // ✅ Pass checkout handler
offersSource={offersSource}
/>
</motion.div>

View File

@@ -45,6 +45,7 @@ interface AppRouterProps {
onSignOutClick: () => void;
onLoginSuccess: (userData: User) => void;
onCloseLoginModal: () => void;
onCheckoutClick?: () => void;
offersSource: 'products' | 'passes';
}
@@ -55,6 +56,7 @@ export function AppRouter({
onSignOutClick,
onLoginSuccess,
onCloseLoginModal,
onCheckoutClick,
offersSource
}: AppRouterProps) {
const location = useLocation();
@@ -88,9 +90,13 @@ export function AppRouter({
} />
{/* Passes Route */}
<Route path="/passes" element={
<Route path="/passes" element={
<motion.div key="passes" {...pageTransition}>
<PassesPage {...commonNavHandlers} />
<PassesPage
{...commonNavHandlers}
onCheckoutClick={onCheckoutClick} // ✅ Explicitly pass checkout handler
onLoginSuccess={onLoginSuccess} // ✅ Pass login success handler
/>
</motion.div>
} />

View File

@@ -18,32 +18,33 @@ import { ImageWithFallback } from './figma/ImageWithFallback';
import { Layout } from '../Layout';
interface CheckoutPageProps {
onBackClick: () => void;
onHomeClick: () => void;
onMelbourneClick: () => void;
onPassesClick: () => void;
onCheckoutClick: () => void;
onSignInClick: () => void;
onBackClick?: () => void;
onHomeClick?: () => void;
onMelbourneClick?: () => void;
onPassesClick?: () => void;
onCheckoutClick?: () => void;
onSignInClick?: () => void;
onSignOutClick?: () => void;
onAttractionsClick: () => void;
onBlogsClick: () => void;
onHowItWorksClick: () => void;
onFAQClick: () => void;
onPrivacyPolicyClick: () => void;
onAboutUsClick: () => void;
onProfileClick: () => void;
onCityCardsClick: () => void;
onMagicItineraryClick: () => void;
onPostCardsClick: () => void;
onOffersClick: () => void;
onAttractionsClick?: () => void;
onBlogsClick?: () => void;
onHowItWorksClick?: () => void;
onFAQClick?: () => void;
onPrivacyPolicyClick?: () => void;
onAboutUsClick?: () => void;
onProfileClick?: () => void;
onCityCardsClick?: () => void;
onMagicItineraryClick?: () => void;
onPostCardsClick?: () => void;
onOffersClick?: () => void;
onSecureCheckoutClick?: () => void;
onContactUsClick?: () => void;
onEsimsClick?: () => void;
onHotelDiscountsClick?: () => void;
currentPage: string;
currentPage?: string;
user?: { email: string; name: string } | null;
}
// Mock cart data
const mockCartItems = [
{

View File

@@ -1,5 +1,6 @@
// CitySelectionDialog.tsx
import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; // ✅ import navigation hook
import { useNavigate } from 'react-router-dom';
import { Dialog, DialogContent, DialogTitle, DialogDescription } from './ui/dialog';
import { ArrowLeft, Search } from 'lucide-react';
import { Input } from './ui/input';
@@ -15,6 +16,7 @@ interface City {
interface CitySelectionDialogProps {
isOpen: boolean;
onClose: () => void;
onCitySelect?: (city: string) => void; // ✅ New prop for city selection callback
}
const cities: City[] = [
@@ -28,9 +30,13 @@ const cities: City[] = [
{ id: 'louisiana', name: 'Louisiana', imageUrl: 'https://images.unsplash.com/photo-1646508262200-455d62c22182?...' },
];
export function CitySelectionDialog({ isOpen, onClose }: CitySelectionDialogProps) {
export function CitySelectionDialog({
isOpen,
onClose,
onCitySelect
}: CitySelectionDialogProps) {
const [searchQuery, setSearchQuery] = useState('');
const navigate = useNavigate(); // ✅ navigation hook
const navigate = useNavigate();
const filteredCities = cities.filter(city =>
city.name.toLowerCase().includes(searchQuery.toLowerCase())
@@ -38,16 +44,22 @@ export function CitySelectionDialog({ isOpen, onClose }: CitySelectionDialogProp
const handleCityClick = (city: City) => {
console.log('Selected city:', city.name);
// ✅ Call the onCitySelect callback if provided
if (onCitySelect) {
onCitySelect(city.id);
} else {
// ✅ Default behavior: navigate to passes page
navigate(`/passes?city=${encodeURIComponent(city.name)}`);
}
onClose();
// ✅ navigate to /passes with selected city info (optional query param)
navigate(`/passes?city=${encodeURIComponent(city.name)}`);
};
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="max-w-md w-full p-0 gap-0 font-poppins">
{/* Accessible Title */}
{/* ... rest of the component remains the same ... */}
<DialogTitle className="sr-only">Select a City</DialogTitle>
<DialogDescription className="sr-only">
Choose from our available cities to explore attractions and experiences
@@ -86,7 +98,7 @@ export function CitySelectionDialog({ isOpen, onClose }: CitySelectionDialogProp
{filteredCities.map((city, index) => (
<motion.button
key={city.id}
onClick={() => handleCityClick(city)} // ✅ navigate on click
onClick={() => handleCityClick(city)}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
@@ -111,7 +123,6 @@ export function CitySelectionDialog({ isOpen, onClose }: CitySelectionDialogProp
</div>
</AnimatePresence>
{/* No Results */}
{filteredCities.length === 0 && (
<div className="text-center py-8">
<p className="text-gray-500 font-poppins">
@@ -123,4 +134,4 @@ export function CitySelectionDialog({ isOpen, onClose }: CitySelectionDialogProp
</DialogContent>
</Dialog>
);
}
}

View File

@@ -242,18 +242,18 @@ function HeroBannerCarousel({ onCheckoutClick, onPassesClick, onEsimsClick, onHo
}
interface MelbournePageProps {
onBackClick: () => void;
onHomeClick: () => void;
onAttractionsClick: () => void;
onPassesClick: () => void;
onBackClick?: () => void;
onHomeClick?: () => void;
onAttractionsClick?: () => void;
onPassesClick?: () => void;
onCheckoutClick?: () => void;
onSignInClick: () => void;
onSignInClick?: () => void;
onSignOutClick?: () => void;
onBlogsClick: () => void;
onHowItWorksClick: () => void;
onFAQClick: () => void;
onPrivacyPolicyClick: () => void;
onAboutUsClick: () => void;
onBlogsClick?: () => void;
onHowItWorksClick?: () => void;
onFAQClick?: () => void;
onPrivacyPolicyClick?: () => void;
onAboutUsClick?: () => void;
onProfileClick?: () => void;
onCityCardsClick?: () => void;
onMagicItineraryClick?: () => void;
@@ -266,6 +266,7 @@ interface MelbournePageProps {
user?: User | null;
}
export function MelbournePage({
onBackClick,
onHomeClick,

View File

@@ -69,7 +69,7 @@ export default function Navbar({
const location = useLocation();
const navigate = useNavigate();
const handleOpenCityDialog = () => {
const handleOpenCityDialog = () => {
setIsCityDialogOpen(true);
};
@@ -77,6 +77,13 @@ export default function Navbar({
setIsCityDialogOpen(false);
};
// ✅ Handle city selection from dialog
const handleCitySelect = (cityId: string) => {
console.log('City selected in navbar:', cityId);
// Navigate to passes page when city is selected
navigate('/passes');
};
// Available cities
const cities = [
{ id: 'melbourne', label: 'Melbourne' },
@@ -85,6 +92,7 @@ export default function Navbar({
{ id: 'perth', label: 'Perth' }
];
// Check if we're on landing page
const isLandingPage = location.pathname === '/';
@@ -341,7 +349,6 @@ export default function Navbar({
}
className="h-10 w-auto"
/>
</Link>
</motion.div>
@@ -359,7 +366,6 @@ export default function Navbar({
}`}
>
{item.label}
{/* Active indicator */}
<motion.div
className="absolute bottom-0 left-0 h-0.5 bg-gradient-to-r from-primary to-secondary rounded-full"
@@ -374,7 +380,6 @@ export default function Navbar({
}}
transition={{ duration: 0.2 }}
/>
{/* Hover background */}
<motion.div
className="absolute inset-0 bg-gray-100/50 backdrop-blur-sm rounded-lg -z-10"
@@ -384,12 +389,10 @@ export default function Navbar({
/>
</Link>
))}
</div>
{/* Right Section */}
<div className="flex items-center gap-1">
{/* City Dropdown */}
<Dropdown
ref={cityRef}
@@ -421,6 +424,7 @@ export default function Navbar({
</div>
}
/>
{/* Language Dropdown */}
<Dropdown
ref={languageRef}
@@ -437,12 +441,12 @@ export default function Navbar({
}
/>
{/* Shopping Cart - UPDATED: using cartDropdownItems */}
{/* Shopping Cart */}
<Dropdown
ref={cartRef}
isOpen={activeCartDropdown}
onToggle={() => setActiveCartDropdown(prev => !prev)}
items={cartDropdownItems} // Using the updated array with navigation
items={cartDropdownItems}
title="Shopping Cart"
trigger={
<div className="relative text-gray-700 hover:text-gray-900 transition-colors duration-200 rounded-lg hover:bg-gray-50/50 cursor-pointer p-2">
@@ -457,64 +461,86 @@ export default function Navbar({
</div>
}
/>
{/* City Card Button */}
{/* ✅ UPDATED: City Card Button with Proper Authentication Logic */}
<div className="flex items-center gap-3 pl-2">
<div className="relative">
<CTAButton
user={user ?? null}
// 👇 open the city selection dialog on click
onClick={handleOpenCityDialog}
className="hover:scale-105 transition-transform duration-200"
/>
<div className="relative">
{isUserSignedIn && user ? (
// ✅ When user is logged in - show user dropdown
<Dropdown
ref={userRef}
isOpen={activeUserDropdown}
onToggle={() => setActiveUserDropdown(prev => !prev)}
items={[
{
id: 'profile',
label: 'My Profile',
icon: <User className="w-4 h-4" />,
action: () => {
navigate('/profile');
setActiveUserDropdown(false);
}
},
{
id: 'settings',
label: 'Settings',
icon: <Settings className="w-4 h-4" />,
action: () => {
navigate('/comming-soon');
setActiveUserDropdown(false);
}
},
{
id: 'logout',
label: 'Sign Out',
icon: <LogOut className="w-4 h-4" />,
action: () => {
if (onSignOutClick) {
onSignOutClick();
}
setActiveUserDropdown(false);
}
}
]}
title="Account"
trigger={
<div
className="cursor-pointer"
onClick={(e) => {
e.stopPropagation();
setActiveUserDropdown(prev => !prev);
}}
>
<CTAButton
user={user}
onClick={() => { }} // Empty function since we handle click above
className="hover:scale-105 transition-transform duration-200"
/>
</div>
}
/>
) : (
// ✅ When user is NOT logged in - show city selection dialog
<>
<div
className="cursor-pointer"
onClick={handleOpenCityDialog}
>
<CTAButton
user={null}
onClick={() => { }} // Empty function since we handle click above
className="hover:scale-105 transition-transform duration-200"
/>
</div>
{/* ✅ City Selection Dialog */}
<CitySelectionDialog
isOpen={isCityDialogOpen}
onClose={handleCloseCityDialog}
/>
{isUserSignedIn && user && (
<Dropdown
ref={userRef}
isOpen={activeUserDropdown}
onToggle={() => setActiveUserDropdown(prev => !prev)}
items={[
{
id: 'profile',
label: 'My Profile',
icon: <User className="w-4 h-4" />,
action: () => {
navigate('/profile');
setActiveUserDropdown(false);
},
},
{
id: 'settings',
label: 'Settings',
icon: <Settings className="w-4 h-4" />,
action: () => {
navigate('/settings');
setActiveUserDropdown(false);
},
},
{
id: 'logout',
label: 'Sign Out',
icon: <LogOut className="w-4 h-4" />,
action: () => {
if (onSignOutClick) {
onSignOutClick();
}
setActiveUserDropdown(false);
},
},
]}
title="Account"
trigger={null}
/>
)}
</div>
{/* ✅ City Selection Dialog with navigation to passes */}
<CitySelectionDialog
isOpen={isCityDialogOpen}
onClose={handleCloseCityDialog}
onCitySelect={handleCitySelect}
/>
</>
)}
</div>
</div>
</div>
</div>
@@ -522,6 +548,7 @@ export default function Navbar({
</div>
</motion.nav>
{/* Mobile Navbar - Enhanced Glassmorphism */}
<nav className="fixed top-0 w-full z-50 lg:hidden">
<div className={`transition-all duration-500 ease-out border-b shadow-sm ${isScrolled

View File

@@ -5,17 +5,18 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/
import { RadioGroup, RadioGroupItem } from './ui/radio-group';
import { Badge } from './ui/badge';
import Navbar from './Navbar';
import { CitySubmenu } from './CitySubmenu';
import { EnhancedTestimonials } from './EnhancedTestimonials';
import { Footer } from './Footer';
import { ReviewsSection } from './ReviewsSection';
import { Layout } from '../Layout';
import { LoginModal } from './LoginModal';
interface PassesPageProps {
onCheckoutClick?: () => void;
onSignInClick: () => void;
onSignOutClick?: () => void;
user?: { email: string; name: string; } | null;
onLoginSuccess?: (userData: { email: string; name: string }) => void;
}
interface PassType {
@@ -45,7 +46,7 @@ const passTypes: PassType[] = [
{
id: 'selective',
name: 'Selective Pass',
title: 'SELECTIVE PASS',
title: 'Flexi Card',
description: 'Perfect for travelers who want to explore selected attractions at their own pace with essential features.',
price: '$59.99',
originalPrice: '$89.99',
@@ -144,12 +145,38 @@ const trustFeatures = [
];
export function PassesPage({
onCheckoutClick,
onSignInClick,
onCheckoutClick,
onSignInClick,
onSignOutClick,
user
user,
onLoginSuccess
}: PassesPageProps) {
const [selectedPass, setSelectedPass] = useState<string>('unlimited');
const [isLoginOpen, setIsLoginOpen] = useState(false);
const [userData, setUserData] = useState<{ email: string; name: string } | null>(user || null);
// ✅ Handle purchase button click
const handlePurchaseClick = () => {
if (!userData) {
// User not logged in - show login modal
setIsLoginOpen(true);
} else {
// User is logged in - proceed to checkout
onCheckoutClick?.();
}
};
// ✅ Handle successful login
const handleLoginSuccess = (data: { email: string; name: string }) => {
setUserData(data);
setIsLoginOpen(false);
console.log('Logged in user:', data);
// Call parent's onLoginSuccess if provided
if (onLoginSuccess) {
onLoginSuccess(data);
}
};
const renderFeatureValue = (value: boolean | string) => {
if (typeof value === 'boolean') {
@@ -164,18 +191,15 @@ export function PassesPage({
return (
<Layout
activeCity="Melbourne"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
showCitySubmenu={true}
>
activeCity="Melbourne"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={userData} // ✅ Pass the updated user data
>
<div className="container mx-auto px-4 pt-52 pb-12 relative z-10">
{/* Page Header */}
<div className="text-center mb-16">
<div className="mb-6">
<h1 className="font-merchant font-light text-4xl md:text-5xl lg:text-6xl mb-4">
Buy <span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">Passes</span>
</h1>
@@ -187,21 +211,20 @@ export function PassesPage({
{/* Pass Comparison Section */}
<div className="mb-20">
<RadioGroup
value={selectedPass}
<RadioGroup
value={selectedPass}
onValueChange={setSelectedPass}
className="grid md:grid-cols-2 gap-8 max-w-6xl mx-auto"
>
{passTypes.map((pass) => (
<div key={pass.id} className="relative h-full">
<Card className={`relative h-full flex flex-col transition-all duration-300 cursor-pointer ${
pass.popular
? 'ring-2 ring-primary shadow-xl'
: selectedPass === pass.id
? 'ring-2 ring-primary/50 shadow-lg'
: 'border-gray-200 shadow-md hover:shadow-lg hover:border-primary/30'
}`}>
<Card className={`relative h-full flex flex-col transition-all duration-300 cursor-pointer ${pass.popular
? 'ring-2 ring-primary shadow-xl'
: selectedPass === pass.id
? 'ring-2 ring-primary/50 shadow-lg'
: 'border-gray-200 shadow-md hover:shadow-lg hover:border-primary/30'
}`}>
{/* Popular Badge */}
{pass.popular && (
<div className="absolute -top-3 left-1/2 transform -translate-x-1/2 z-10">
@@ -225,7 +248,7 @@ export function PassesPage({
{pass.description}
</CardDescription>
</CardHeader>
{/* Pricing Section - Fixed Height */}
<div className="px-6 pb-6 flex-shrink-0">
<div className="flex items-baseline justify-center gap-2 mb-2">
@@ -241,7 +264,7 @@ export function PassesPage({
)}
</div>
</div>
{/* Content - Flexible Height with Fixed Features Area */}
<CardContent className="pt-0 pb-6 px-6 flex-grow flex flex-col">
{/* Features List - Fixed height */}
@@ -256,19 +279,18 @@ export function PassesPage({
</div>
</div>
{/* CTA Button - Pushed to bottom */}
{/* ✅ UPDATED: CTA Button with login logic */}
<div className="flex-shrink-0 space-y-3">
<Button
className={`w-full h-12 rounded-lg font-semibold transition-all duration-300 font-poppins ${
pass.popular
? 'bg-primary hover:bg-primary/90 text-white shadow-md hover:shadow-lg'
: 'bg-gray-900 hover:bg-gray-800 text-white hover:shadow-md'
}`}
onClick={onCheckoutClick}
<Button
className={`w-full h-12 rounded-lg font-semibold transition-all duration-300 font-poppins ${pass.popular
? 'bg-primary hover:bg-primary/90 text-white shadow-md hover:shadow-lg'
: 'bg-gray-900 hover:bg-gray-800 text-white hover:shadow-md'
}`}
onClick={handlePurchaseClick} // ✅ Use the new handler
>
PURCHASE NOW
</Button>
<p className="text-xs text-gray-500 text-center font-poppins font-normal leading-tight">
Free cancellation up to 24 hours Instant delivery
</p>
@@ -290,7 +312,7 @@ export function PassesPage({
See exactly what's included with each pass type
</CardDescription>
</CardHeader>
<CardContent className="p-0">
<div className="overflow-x-auto">
<table className="w-full">
@@ -301,7 +323,7 @@ export function PassesPage({
<th className="text-center p-6 font-semibold text-gray-900 min-w-[200px] bg-primary/5">Premium Unlimited</th>
</tr>
</thead>
<tbody>
{featureComparison.map((feature, index) => (
<tr key={feature.key} className={`border-b border-gray-100 ${index % 2 === 0 ? 'bg-white' : 'bg-gray-50/50'}`}>
@@ -330,17 +352,17 @@ export function PassesPage({
<Card className="bg-gray-50 overflow-hidden shadow-lg">
<CardContent className="p-0">
<div className="grid lg:grid-cols-2 gap-12 items-center">
{/* Smartphone Mockup */}
<div className="relative flex justify-center p-8 lg:p-16">
<div className="relative">
{/* Phone Frame */}
<div className="w-64 h-[520px] bg-black rounded-[2.5rem] p-2 shadow-2xl">
<div className="w-full h-full bg-white rounded-[2rem] relative overflow-hidden">
{/* Screen Content */}
<div className="absolute inset-0 bg-gradient-to-b from-primary/90 to-secondary/90">
{/* Status Bar */}
<div className="flex justify-between items-center px-6 pt-4 pb-2 text-white text-sm">
<span>9:41</span>
@@ -350,7 +372,7 @@ export function PassesPage({
<div className="w-4 h-2 bg-white rounded-sm opacity-40"></div>
</div>
</div>
{/* App Header */}
<div className="px-6 py-4">
<div className="flex items-center gap-3 mb-6">
@@ -363,7 +385,7 @@ export function PassesPage({
</div>
</div>
</div>
{/* Active Pass Card */}
<div className="mx-6 mb-6">
<div className="bg-white/15 backdrop-blur-md rounded-2xl p-4 border border-white/20">
@@ -377,7 +399,7 @@ export function PassesPage({
<p className="text-white/80 text-xs">18 of 24 attractions visited</p>
</div>
</div>
{/* Quick Actions */}
<div className="px-6 mb-6">
<div className="grid grid-cols-3 gap-3">
@@ -393,7 +415,7 @@ export function PassesPage({
))}
</div>
</div>
{/* Featured Attraction */}
<div className="mx-6">
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-4 border border-white/20">
@@ -407,7 +429,7 @@ export function PassesPage({
</div>
</div>
</div>
{/* Bottom Tab Bar */}
<div className="absolute bottom-6 left-4 right-4">
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-2 border border-white/20">
@@ -425,7 +447,7 @@ export function PassesPage({
</div>
</div>
</div>
{/* Content */}
<div className="p-8 lg:p-16">
<h2 className="heading-dynamic text-4xl text-gray-900 mb-6">
@@ -435,10 +457,10 @@ export function PassesPage({
<span className="font-semibold text-emphasis">phone</span>
</h2>
<p className="text-xl text-gray-600 mb-8 leading-relaxed font-light">
Download our mobile app for easy access to your passes, maps, and exclusive offers.
Download our mobile app for easy access to your passes, maps, and exclusive offers.
Never worry about losing your tickets again.
</p>
{/* App Features */}
<div className="space-y-4 mb-8">
{[
@@ -453,7 +475,7 @@ export function PassesPage({
</div>
))}
</div>
{/* Download Buttons */}
<div className="flex flex-col sm:flex-row gap-4 mb-8">
<Button className="bg-black text-white hover:bg-gray-800 flex items-center justify-center gap-3 px-6 py-4 rounded-xl font-semibold">
@@ -465,7 +487,7 @@ export function PassesPage({
Get it on Google Play
</Button>
</div>
{/* QR Code */}
<div className="flex items-center gap-4">
<div className="w-16 h-16 bg-black rounded-xl p-2">
@@ -495,7 +517,7 @@ export function PassesPage({
We're committed to providing the best city exploration experience with unmatched value and service
</p>
</div>
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-8">
{trustFeatures.map((feature, index) => {
const IconComponent = feature.icon;
@@ -527,7 +549,7 @@ export function PassesPage({
<div className="absolute inset-0 bg-gradient-to-br from-white/10 to-transparent"></div>
<div className="absolute top-10 right-10 w-32 h-32 bg-white/5 rounded-full"></div>
<div className="absolute bottom-10 left-10 w-24 h-24 bg-white/5 rounded-full"></div>
<div className="relative z-10">
<h3 className="heading-dynamic text-4xl mb-4">
<span className="font-light">Ready to</span>{' '}
@@ -535,13 +557,13 @@ export function PassesPage({
<span className="font-semibold">Melbourne?</span>
</h3>
<p className="text-xl mb-8 max-w-2xl mx-auto opacity-90 font-light">
Choose your pass and start discovering amazing attractions with skip-the-line access.
Choose your pass and start discovering amazing attractions with skip-the-line access.
Money-back guarantee included.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center mb-8">
<Button
size="lg"
<Button
size="lg"
className="bg-white text-primary hover:bg-gray-100 py-4 px-8 rounded-2xl font-semibold text-lg shadow-lg hover:shadow-xl transition-all duration-200"
>
Choose Your Pass
@@ -551,7 +573,7 @@ export function PassesPage({
<span className="font-light">No hidden fees Instant confirmation</span>
</div>
</div>
<div className="flex justify-center items-center gap-8 text-sm text-white/70">
<span className="flex items-center gap-2 font-light">
<Shield className="w-4 h-4" />
@@ -570,6 +592,12 @@ export function PassesPage({
</div>
</div>
<LoginModal
isOpen={isLoginOpen}
onClose={() => setIsLoginOpen(false)}
onLoginSuccess={handleLoginSuccess}
/>
</Layout>
);
}

View File

@@ -1,8 +1,8 @@
"use client";
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog@1.1.6";
import { XIcon } from "lucide-react@0.487.0";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";
import { cn } from "./utils";