diff --git a/package-lock.json b/package-lock.json index af7afbd..e5ecc9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.55.0", "react-resizable-panels": "^2.1.7", + "react-router-dom": "^7.9.4", "recharts": "^2.15.2", "sonner": "^2.0.3", "tailwind-merge": "*", @@ -2901,6 +2902,15 @@ "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -3837,6 +3847,44 @@ "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, + "node_modules/react-router": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz", + "integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.4.tgz", + "integrity": "sha512-f30P6bIkmYvnHHa5Gcu65deIXoA2+r3Eb6PJIAddvsT9aGlchMatJ51GgpU470aSqRRbFX22T70yQNUGuW3DfA==", + "license": "MIT", + "dependencies": { + "react-router": "7.9.4" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/react-smooth": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", @@ -3972,6 +4020,12 @@ "loose-envify": "^1.1.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/sonner": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", diff --git a/package.json b/package.json index e9d93e0..fe1c23d 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "react-dom": "^18.3.1", "react-hook-form": "^7.55.0", "react-resizable-panels": "^2.1.7", + "react-router-dom": "^7.9.4", "recharts": "^2.15.2", "sonner": "^2.0.3", "tailwind-merge": "*", diff --git a/src/App.tsx b/src/App.tsx index e5a673b..fe376f4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,50 +1,13 @@ import { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'motion/react'; -import { QrCode, X, Smartphone, CreditCard, Ticket } from 'lucide-react'; +import { QrCode, X } from 'lucide-react'; +import { useLocation, useNavigate } from 'react-router-dom'; import cityCardsLogo from './assets/cit-logo.png'; import imgRectangle18609 from "./assets/84c8cfb942f3c96e9f5fbb29459a9861138f61d8.png"; -import { LoginModal } from './components/LoginModal'; -import { CTAButton } from './components/CTAButton'; -import { HomePage } from './components/HomePage'; -import { PassesPage } from './components/PassesPage'; -import { MelbournePage } from './components/MelbournePage'; -import { AttractionsPage } from './components/AttractionsPage'; -import { AttractionDetailsPage } from './components/AttractionDetailsPage'; -import { CheckoutPage } from './components/CheckoutPage'; -import { SecureCheckoutPage } from './components/SecureCheckoutPage'; -import { BlogsPage } from './components/BlogsPage'; -import { BlogDetailsPage } from './components/BlogDetailsPage'; -import { HowItWorksPage } from './components/HowItWorksPage'; -import { FAQPage } from './components/FAQPage'; -import { PrivacyPolicyPage } from './components/PrivacyPolicyPage'; -import { AboutUsPage } from './components/AboutUsPage'; -import { ProfilePage } from './components/ProfilePage'; -import { CreateMagicItineraryPage } from './components/CreateMagicItineraryPage'; -import { ItineraryViewPage } from './components/ItineraryViewPage'; -import { OffersPage } from './components/OffersPage'; -import { CityCardsPage } from './components/CityCardsPage'; -import { MagicItineraryPage } from './components/MagicItineraryPage'; -import { PostCardsPage } from './components/PostCardsPage'; -import { DownloadAppPage } from './components/DownloadAppPage'; -import { EsimsPage } from './components/EsimsPage'; -import { HotelDiscountsPage } from './components/HotelDiscountsPage'; -import { ContactUsPage } from './components/ContactUsPage'; - +import { AppRouter } from './AppRouter'; import { - heroVariants, - sectionVariants, - cardVariants, - staggerContainer, - fastStaggerContainer, - headingVariants, - textVariants, - ctaVariants, - iconVariants, - buttonHoverVariants, - cardHoverVariants, pageTransition, - easeOutQuart, easeOutExpo, easeOutCubic } from './utils/animations'; @@ -56,10 +19,6 @@ interface User { } function App() { - const [selectedCity, setSelectedCity] = useState(''); - const [currentPage, setCurrentPage] = useState<'home' | 'melbourne' | 'passes' | 'attractions' | 'attraction-details' | 'checkout' | 'secure-checkout' | 'blogs' | 'blog-details' | 'how-it-works' | 'faq' | 'privacy-policy' | 'about-us' | 'profile' | 'create-itinerary' | 'itinerary-view' | 'offers' | 'citycards' | 'magic-itinerary' | 'postcards' | 'download-app' | 'esims' | 'hotel-discounts' | 'contact-us'>('home'); - const [selectedAttractionId, setSelectedAttractionId] = useState(''); - const [selectedBlogId, setSelectedBlogId] = useState(''); const [isMobile, setIsMobile] = useState(false); const [showQRCard, setShowQRCard] = useState(false); const [offersSource, setOffersSource] = useState<'products' | 'passes'>('products'); @@ -69,6 +28,9 @@ function App() { const [user, setUser] = useState(null); const [showLoginModal, setShowLoginModal] = useState(false); + const location = useLocation(); + const navigate = useNavigate(); + // Login handlers const handleLoginSuccess = (userData: User) => { setUser(userData); @@ -77,17 +39,15 @@ function App() { const handleSignOut = () => { setUser(null); - setCurrentPage('home'); + navigate('/'); }; - const handleCTAClick = () => { - if (user) { - // If logged in, navigate to profile or passes - setCurrentPage('profile'); - } else { - // If not logged in, show login modal - setShowLoginModal(true); - } + const handleCloseLoginModal = () => { + setShowLoginModal(false); + }; + + const handleSignInClick = () => { + setShowLoginModal(true); }; // Detect mobile for optimized animations @@ -103,29 +63,24 @@ function App() { // Generate a realistic QR code pattern const generateQRPattern = () => { - const size = 27; // Even larger QR code size for the bigger widget + const size = 27; const pattern = []; for (let i = 0; i < size * size; i++) { const row = Math.floor(i / size); const col = i % size; - // Corner squares (7x7 for larger QR) const isCornerSquare = - (row < 7 && col < 7) || // Top-left - (row < 7 && col >= 20) || // Top-right - (row >= 20 && col < 7); // Bottom-left + (row < 7 && col < 7) || + (row < 7 && col >= 20) || + (row >= 20 && col < 7); - // Finder patterns within corner squares const isFinderPattern = isCornerSquare && ( (row === 0 || row === 6 || col === 0 || col === 6) || (row >= 2 && row <= 4 && col >= 2 && col <= 4) ); - // Timing patterns const isTimingPattern = (row === 6 && col >= 8 && col <= 18) || (col === 6 && row >= 8 && row <= 18); - - // Random data pattern for other areas const isDataPattern = !isCornerSquare && !isTimingPattern && Math.random() > 0.38; pattern.push(isFinderPattern || isTimingPattern || isDataPattern); @@ -136,872 +91,16 @@ function App() { const qrPattern = generateQRPattern(); - const renderPage = () => { - switch (currentPage) { - case 'home': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onAttractionsClick={() => setCurrentPage('attractions')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'passes': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('passes'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'melbourne': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onAttractionsClick={() => setCurrentPage('attractions')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'attractions': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - onAttractionClick={(attractionId: string) => { - setSelectedAttractionId(attractionId); - setCurrentPage('attraction-details'); - }} - currentPage={currentPage} - user={user} - /> - - ); - case 'attraction-details': - return ( - - setCurrentPage('attractions')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'checkout': - return ( - - setCurrentPage('passes')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - onSecureCheckoutClick={() => setCurrentPage('secure-checkout')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'secure-checkout': - return ( - - setCurrentPage('passes')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'blogs': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - onBlogClick={(blogId: string) => { - setSelectedBlogId(blogId); - setCurrentPage('blog-details'); - }} - currentPage={currentPage} - user={user} - /> - - ); - - case 'blog-details': - return ( - - setCurrentPage('blogs')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'how-it-works': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'faq': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'privacy-policy': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'about-us': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'profile': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('passes'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - onCreateItineraryClick={() => setCurrentPage('create-itinerary')} - onViewItineraryClick={() => setCurrentPage('itinerary-view')} - onDownloadAppClick={() => setCurrentPage('download-app')} - currentPage={currentPage} - /> - - ); - - case 'create-itinerary': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('home')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'itinerary-view': - return ( - - setCurrentPage('magic-itinerary')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - onCreateItineraryClick={() => setCurrentPage('create-itinerary')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'offers': - return ( - - setCurrentPage('profile')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => setCurrentPage('offers')} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - fromSource={offersSource} - currentPage={currentPage} - user={user} - /> - - ); - - case 'citycards': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'magic-itinerary': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - onCreateItineraryClick={() => setCurrentPage('create-itinerary')} - onViewItineraryClick={() => setCurrentPage('itinerary-view')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'postcards': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'download-app': - return ( - - setCurrentPage('profile')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'esims': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'hotel-discounts': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onMelbourneClick={() => setCurrentPage('melbourne')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); - - case 'contact-us': - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onAttractionsClick={() => setCurrentPage('attractions')} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - currentPage={currentPage} - user={user} - /> - - ); - - default: - return ( - - setCurrentPage('home')} - onHomeClick={() => setCurrentPage('home')} - onAttractionsClick={() => setCurrentPage('attractions')} - onPassesClick={() => setCurrentPage('passes')} - onCheckoutClick={() => setCurrentPage('checkout')} - onSignInClick={() => setShowLoginModal(true)} - onSignOutClick={handleSignOut} - onBlogsClick={() => setCurrentPage('blogs')} - onHowItWorksClick={() => setCurrentPage('how-it-works')} - onFAQClick={() => setCurrentPage('faq')} - onPrivacyPolicyClick={() => setCurrentPage('privacy-policy')} - onAboutUsClick={() => setCurrentPage('about-us')} - onProfileClick={() => setCurrentPage('profile')} - onCityCardsClick={() => setCurrentPage('citycards')} - onMagicItineraryClick={() => setCurrentPage('magic-itinerary')} - onPostCardsClick={() => setCurrentPage('postcards')} - onOffersClick={() => { - setOffersSource('products'); - setCurrentPage('offers'); - }} - onEsimsClick={() => setCurrentPage('esims')} - onHotelDiscountsClick={() => setCurrentPage('hotel-discounts')} - onContactUsClick={() => setCurrentPage('contact-us')} - currentPage={currentPage} - user={user} - /> - - ); + const handleStickyWidgetClick = () => { + if (location.pathname === '/attractions') { + navigate('/checkout'); + } else { + setShowQRCard(true); } }; return (
- - {/* Global Animation Context Provider */} - - {renderPage()} - + - {/* Login Modal */} - setShowLoginModal(false)} - onLoginSuccess={handleLoginSuccess} - /> - - {/* Sticky Widget - Dynamic based on page */} + {/* Sticky Widget */}
{!showQRCard && ( @@ -1032,21 +130,15 @@ function App() { transition={{ duration: 0.3, ease: easeOutExpo }} whileHover={{ scale: 1.05, y: -2 }} whileTap={{ scale: 0.95 }} - onClick={() => { - if (currentPage === 'attractions') { - setCurrentPage('checkout'); - } else { - setShowQRCard(true); - } - }} + onClick={handleStickyWidgetClick} className={`relative shadow-2xl flex items-center justify-center rounded-2xl transition-all duration-300 overflow-hidden group ${ - currentPage === 'attractions' + location.pathname === '/attractions' ? 'w-[244px] h-36' : 'w-36 h-36 bg-black text-white' }`} - aria-label={currentPage === 'attractions' ? 'Get CityCard' : 'Show QR Code'} + aria-label={location.pathname === '/attractions' ? 'Get CityCard' : 'Show QR Code'} > - {currentPage === 'attractions' ? ( + {location.pathname === '/attractions' ? (
{/* Header Image */} @@ -1084,10 +176,7 @@ function App() {
) : (
- {/* QR Icon */} - - {/* Text */}
Scan QR @@ -1099,14 +188,13 @@ function App() {
)} - {/* Subtle Hover Effect */}
)} - {showQRCard && currentPage !== 'attractions' && ( + {showQRCard && location.pathname !== '/attractions' && ( - {/* Close Button */} - {/* QR Code - Much Larger */}
- {/* QR Code Pattern */}
{qrPattern.map((filled, index) => ( - {/* Center Logo - Much Larger */}
- {/* Text Content - Larger */}

Scan QR for app

@@ -1164,7 +247,6 @@ function App() {

- {/* Static Border */}
)} @@ -1178,7 +260,6 @@ function App() { animate={{ opacity: 0 }} transition={{ duration: 0.4, delay: 0.1 }} onAnimationComplete={() => { - // Remove from DOM after animation const element = document.querySelector('[data-preload]'); element?.remove(); }} @@ -1188,22 +269,4 @@ function App() { ); } -// Export animation variants for use in components -export { - heroVariants, - sectionVariants, - cardVariants, - staggerContainer, - fastStaggerContainer, - headingVariants, - textVariants, - ctaVariants, - iconVariants, - buttonHoverVariants, - cardHoverVariants, - easeOutQuart, - easeOutExpo, - easeOutCubic -}; - export default App; \ No newline at end of file diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx new file mode 100644 index 0000000..e4cd05d --- /dev/null +++ b/src/AppRouter.tsx @@ -0,0 +1,261 @@ +import { Routes, Route, useParams, useLocation, useNavigate } from 'react-router-dom'; +import { motion, AnimatePresence } from 'motion/react'; + +// Import all your pages +import { LoginModal } from './components/LoginModal'; +import { MelbournePage } from './components/MelbournePage'; +import { PassesPage } from './components/PassesPage'; +import { AttractionsPage } from './components/AttractionsPage'; +import { AttractionDetailsPage } from './components/AttractionDetailsPage'; +import { CheckoutPage } from './components/CheckoutPage'; +import { SecureCheckoutPage } from './components/SecureCheckoutPage'; +import { BlogsPage } from './components/BlogsPage'; +import { BlogDetailsPage } from './components/BlogDetailsPage'; +import { HowItWorksPage } from './components/HowItWorksPage'; +import { FAQPage } from './components/FAQPage'; +import { PrivacyPolicyPage } from './components/PrivacyPolicyPage'; +import { AboutUsPage } from './components/AboutUsPage'; +import { ProfilePage } from './components/ProfilePage'; +import { CreateMagicItineraryPage } from './components/CreateMagicItineraryPage'; +import { ItineraryViewPage } from './components/ItineraryViewPage'; +import { OffersPage } from './components/OffersPage'; +import { CityCardsPage } from './components/CityCardsPage'; +import { MagicItineraryPage } from './components/MagicItineraryPage'; +import { PostCardsPage } from './components/PostCardsPage'; +import { DownloadAppPage } from './components/DownloadAppPage'; +import { EsimsPage } from './components/EsimsPage'; +import { HotelDiscountsPage } from './components/HotelDiscountsPage'; +import { ContactUsPage } from './components/ContactUsPage'; + +import { pageTransition } from './utils/animations'; +import { LandingPage } from './pages/landingPage'; +import ComingSoonPage from './pages/ComingSoonPage'; + +// User type definition +interface User { + email: string; + name: string; +} + +interface AppRouterProps { + user: User | null; + showLoginModal: boolean; + onSignInClick: () => void; + onSignOutClick: () => void; + onLoginSuccess: (userData: User) => void; + onCloseLoginModal: () => void; + offersSource: 'products' | 'passes'; +} + +export function AppRouter({ + user, + showLoginModal, + onSignInClick, + onSignOutClick, + onLoginSuccess, + onCloseLoginModal, + offersSource +}: AppRouterProps) { + const location = useLocation(); + const { attractionId, blogId } = useParams(); + + // Common navigation handlers for all pages + const commonNavHandlers = { + onSignInClick, + onSignOutClick, + user, + }; + + const navigate = useNavigate(); + + return ( + <> + + + {/* Landing Page Route */} + + + + } /> + + {/* Home Route */} + + + + } /> + + {/* Passes Route */} + + + + } /> + + {/* Melbourne Route */} + + + + } /> + + {/* Attractions Routes */} + + + + } /> + + + navigate(-1)} + onCheckoutClick={() => navigate('/checkout')} + /> + + } /> + + {/* Checkout Routes */} + + + + } /> + + + + + } /> + + {/* Blog Routes */} + + + + } /> + + + + + } /> + + {/* Information Pages */} + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + + {/* User Routes */} + + + + } /> + + {/* Itinerary Routes */} + + + + } /> + + + + + } /> + + + + + } /> + + {/* Products Routes */} + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + + + + + } /> + + + + + + + + ); +} \ No newline at end of file diff --git a/src/Layout.tsx b/src/Layout.tsx new file mode 100644 index 0000000..9048fdb --- /dev/null +++ b/src/Layout.tsx @@ -0,0 +1,54 @@ +import { ReactNode } from 'react'; +import Navbar from './components/Navbar'; +import { CitySubmenu } from './components/CitySubmenu'; +import { Footer } from './components/Footer'; + +interface User { + email: string; + name: string; +} + +interface LayoutProps { + children: ReactNode; + activeCity?: string; + showCitySubmenu?: boolean; + onSignInClick: () => void; + onSignOutClick?: () => void; + user?: User | null; +} + +export function Layout({ + children, + activeCity = 'Melbourne', + showCitySubmenu = false, + onSignInClick, + onSignOutClick, + user +}: LayoutProps) { + return ( +
+ {/* Navbar */} + { + // Handle city change if needed + }} + onSignInClick={onSignInClick} + onSignOutClick={onSignOutClick} + isUserSignedIn={!!user} + user={user} + /> + + {/* City Submenu - Conditionally rendered */} + {showCitySubmenu && {}} />} + + {/* Main Content */} +
+ {children} +
+ + {/* Footer */} +
+
+ ); +} \ No newline at end of file diff --git a/src/assets/landing-hero.png b/src/assets/landing-hero.png new file mode 100644 index 0000000..1c295df Binary files /dev/null and b/src/assets/landing-hero.png differ diff --git a/src/assets/marriott-hotel.png b/src/assets/marriott-hotel.png new file mode 100644 index 0000000..1e5fedb Binary files /dev/null and b/src/assets/marriott-hotel.png differ diff --git a/src/components/AttractionDetailsPage.tsx b/src/components/AttractionDetailsPage.tsx index 834e875..ceac7f1 100644 --- a/src/components/AttractionDetailsPage.tsx +++ b/src/components/AttractionDetailsPage.tsx @@ -3,64 +3,24 @@ import { motion } from 'motion/react'; import { ArrowLeft, Clock, Users, Calendar, MapPin, Star, Check, X, ChevronLeft, ChevronRight } from 'lucide-react'; import { Button } from './ui/button'; import { Badge } from './ui/badge'; -import { Card, CardContent } from './ui/card'; -import { Separator } from './ui/separator'; -import { Calendar as CalendarComponent } from './ui/calendar'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; +import { Card, } from './ui/card'; import { ImageWithFallback } from './figma/ImageWithFallback'; +import { Layout } from '../Layout'; interface AttractionDetailsPageProps { - attractionId: string; 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; - onContactUsClick?: () => void; - onEsimsClick?: () => void; - onHotelDiscountsClick?: () => void; - currentPage: string; user?: { email: string; name: string } | null; + attractionId: string; } export function AttractionDetailsPage({ - attractionId, onBackClick, - onHomeClick, - onMelbourneClick, - onPassesClick, onCheckoutClick, onSignInClick, onSignOutClick, - onAttractionsClick, - onBlogsClick, - onHowItWorksClick, - onFAQClick, - onPrivacyPolicyClick, - onAboutUsClick, - onProfileClick, - onCityCardsClick, - onMagicItineraryClick, - onPostCardsClick, - onOffersClick, - onContactUsClick, - onEsimsClick, - onHotelDiscountsClick, - currentPage, user, }: AttractionDetailsPageProps) { const [date, setDate] = useState(new Date()); @@ -114,35 +74,13 @@ export function AttractionDetailsPage({ }; return ( -
- { - if (city === 'Melbourne') { - onMelbourneClick(); - } - }} - onSignInClick={onSignInClick} - onSignOutClick={onSignOutClick} - onPassesClick={onPassesClick} - onCheckoutClick={onCheckoutClick} - onHomeClick={onHomeClick} - onAttractionsClick={onAttractionsClick} - onBlogsClick={onBlogsClick} - onHowItWorksClick={onHowItWorksClick} - onFAQClick={onFAQClick} - onPrivacyPolicyClick={onPrivacyPolicyClick} - onAboutUsClick={onAboutUsClick} - onProfileClick={onProfileClick} - onCityCardsClick={onCityCardsClick} - onMagicItineraryClick={onMagicItineraryClick} - onPostCardsClick={onPostCardsClick} - onOffersClick={onOffersClick} - currentPage="attractions" - isUserSignedIn={!!user} - user={user} - /> - +
{/* Back Button */}
-
-
+ + ); } \ No newline at end of file diff --git a/src/components/AttractionsPage.tsx b/src/components/AttractionsPage.tsx index 1e7eee8..cf92e16 100644 --- a/src/components/AttractionsPage.tsx +++ b/src/components/AttractionsPage.tsx @@ -1,15 +1,14 @@ import { useState } from 'react'; import { motion } from 'motion/react'; -import { Search, Star, Clock, ChevronRight } from 'lucide-react'; +import { Search, Star, Clock } from 'lucide-react'; +import { useNavigate } from 'react-router-dom'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Card, CardContent } from './ui/card'; import { Badge } from './ui/badge'; import { Checkbox } from './ui/checkbox'; -import Navbar from './Navbar'; -import { CitySubmenu } from './CitySubmenu'; -import { Footer } from './Footer'; import { ImageWithFallback } from './figma/ImageWithFallback'; +import { Layout } from '../Layout'; interface User { email: string; @@ -217,145 +216,75 @@ const passTypeCategories = [ ]; interface AttractionsPageProps { - 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; - onContactUsClick?: () => void; - onEsimsClick?: () => void; - onHotelDiscountsClick?: () => void; - onAttractionClick?: (attractionId: string) => void; - currentPage: string; - user: User | null; + onSignOutClick?: () => void; + user?: User | null; } export function AttractionsPage({ - onBackClick, - onHomeClick, - onMelbourneClick, - onPassesClick, - onCheckoutClick, onSignInClick, onSignOutClick, - onAttractionsClick, - onBlogsClick, - onHowItWorksClick, - onFAQClick, - onPrivacyPolicyClick, - onAboutUsClick, - onProfileClick, - onCityCardsClick, - onMagicItineraryClick, - onPostCardsClick, - onOffersClick, - onContactUsClick, - onEsimsClick, - onHotelDiscountsClick, - onAttractionClick, - currentPage, user }: AttractionsPageProps) { + const navigate = useNavigate(); const [searchQuery, setSearchQuery] = useState(''); const [selectedCategories, setSelectedCategories] = useState([]); const [selectedPassTypes, setSelectedPassTypes] = useState([]); const filteredAttractions = attractions.filter(attraction => { const matchesSearch = attraction.name.toLowerCase().includes(searchQuery.toLowerCase()) || - attraction.description.toLowerCase().includes(searchQuery.toLowerCase()); - - const matchesCategory = selectedCategories.length === 0 || - selectedCategories.some(cat => { - if (cat === 'with-reservation') return attraction.hasReservation; - if (cat === 'without-reservation') return !attraction.hasReservation; - return attraction.category === cat; - }); - - const matchesPassType = selectedPassTypes.length === 0 || - selectedPassTypes.includes(attraction.passType); - + attraction.description.toLowerCase().includes(searchQuery.toLowerCase()); + + const matchesCategory = selectedCategories.length === 0 || + selectedCategories.some(cat => { + if (cat === 'with-reservation') return attraction.hasReservation; + if (cat === 'without-reservation') return !attraction.hasReservation; + return attraction.category === cat; + }); + + const matchesPassType = selectedPassTypes.length === 0 || + selectedPassTypes.includes(attraction.passType); + return matchesSearch && matchesCategory && matchesPassType; }); const toggleCategory = (category: string) => { - setSelectedCategories(prev => - prev.includes(category) + setSelectedCategories(prev => + prev.includes(category) ? prev.filter(c => c !== category) : [...prev, category] ); }; const togglePassType = (passType: string) => { - setSelectedPassTypes(prev => - prev.includes(passType) + setSelectedPassTypes(prev => + prev.includes(passType) ? prev.filter(p => p !== passType) : [...prev, passType] ); }; + const handleAttractionClick = (attractionId: string) => { + navigate(`/attractions/${attractionId}`); + }; + + const handleCheckoutClick = () => { + navigate('/checkout'); + }; + const showingFrom = 1; const showingTo = Math.min(12, filteredAttractions.length); const totalItems = filteredAttractions.length; return ( -
- { - if (city === 'Melbourne') { - onMelbourneClick(); - } - }} - onSignInClick={onSignInClick} - onSignOutClick={onSignOutClick} - onPassesClick={onPassesClick} - onCheckoutClick={onCheckoutClick} - onHomeClick={onHomeClick} - onAttractionsClick={onAttractionsClick} - onBlogsClick={onBlogsClick} - onHowItWorksClick={onHowItWorksClick} - onFAQClick={onFAQClick} - onPrivacyPolicyClick={onPrivacyPolicyClick} - onAboutUsClick={onAboutUsClick} - onProfileClick={onProfileClick} - onCityCardsClick={onCityCardsClick} - onMagicItineraryClick={onMagicItineraryClick} - onPostCardsClick={onPostCardsClick} - onOffersClick={onOffersClick} - currentPage="attractions" - isUserSignedIn={!!user} - user={user} - /> - - {/* City Submenu */} - {}} // Empty function since submenu always shows on this page - onHomeClick={onHomeClick} - onMelbourneClick={onMelbourneClick} - onAttractionsClick={onAttractionsClick} - onPassesClick={onPassesClick} - onBlogsClick={onBlogsClick} - onHowItWorksClick={onHowItWorksClick} - /> - +
- {/* Breadcrumb */} - - {/* Page Header */}

@@ -376,7 +305,7 @@ export function AttractionsPage({

Find Your Perfect Adventure

- + {/* Search Bar and Button Container */}
{/* Search Bar */} @@ -389,17 +318,17 @@ export function AttractionsPage({ />
- + {/* Call-to-Action Button */} -
- + {/* Decorative background elements */}
@@ -474,7 +403,7 @@ export function AttractionsPage({ {/* Header */}

Attractions in Melbourne

- + {/* Results count */}

Showing {showingFrom}-{showingTo} of {totalItems} Item(s) @@ -491,9 +420,9 @@ export function AttractionsPage({ transition={{ duration: 0.3 }} className="h-full" > - onAttractionClick?.(attraction.id)} + onClick={() => handleAttractionClick(attraction.id)} >

( ))} @@ -542,7 +470,7 @@ export function AttractionsPage({
- + {/* Pricing and Pass Info */}
@@ -557,7 +485,7 @@ export function AttractionsPage({
- + {/* Included with Pass CTA */}
@@ -573,7 +501,7 @@ export function AttractionsPage({ className="bg-primary hover:bg-primary/90 text-white font-poppins font-semibold text-xs px-4 min-h-[44px] min-w-[44px] h-[44px] whitespace-nowrap" onClick={(e) => { e.stopPropagation(); - onCheckoutClick(); + handleCheckoutClick(); }} > Get Pass @@ -589,16 +517,6 @@ export function AttractionsPage({
- -
-
+ ); } \ No newline at end of file diff --git a/src/components/CitySubmenu.tsx b/src/components/CitySubmenu.tsx index 55839a8..eb36948 100644 --- a/src/components/CitySubmenu.tsx +++ b/src/components/CitySubmenu.tsx @@ -1,38 +1,55 @@ import { useState, useEffect } from 'react'; import { motion } from 'motion/react'; +import { useLocation, useNavigate } from 'react-router-dom'; interface CitySubmenuProps { onClose: () => void; - currentPage?: string; - onHomeClick?: () => void; - onMelbourneClick?: () => void; - onAttractionsClick?: () => void; - onPassesClick?: () => void; - onBlogsClick?: () => void; - onHowItWorksClick?: () => void; } interface SubmenuItem { id: string; label: string; - isCity?: boolean; + path?: string; action?: () => void; } -export function CitySubmenu({ - onClose, - currentPage, - onHomeClick, - onMelbourneClick, - onAttractionsClick, - onPassesClick, - onBlogsClick, - onHowItWorksClick -}: CitySubmenuProps) { - const [selectedItem, setSelectedItem] = useState(null); +export function CitySubmenu({ onClose }: CitySubmenuProps) { const [isScrolled, setIsScrolled] = useState(false); + const [activeItem, setActiveItem] = useState(null); - // Handle scroll effects to match main navbar behavior + const location = useLocation(); + const navigate = useNavigate(); + + // Direct submenu items for Melbourne + const submenuItems: SubmenuItem[] = [ + { + id: 'melbourne', + label: 'Melbourne', + path: '/melbourne' + }, + { + id: 'attractions', + label: 'Attractions', + path: '/attractions' + }, + { + id: 'buy-now', + label: 'Buy Now', + path: '/passes' + }, + { + id: 'blogs', + label: 'Blogs', + path: '/blogs' + }, + { + id: 'how-it-works', + label: 'How It Works', + path: '/how-it-works' + } + ]; + + // Handle scroll effects useEffect(() => { const handleScroll = () => { const scrolled = window.scrollY > 20; @@ -43,83 +60,57 @@ export function CitySubmenu({ return () => window.removeEventListener('scroll', handleScroll); }, []); - const submenuItems: SubmenuItem[] = [ - { - id: 'melbourne', - label: 'Melbourne', - isCity: true, - action: () => { - onMelbourneClick?.(); - onClose(); - } - }, - { - id: 'attractions', - label: 'Attractions', - action: () => { - onAttractionsClick?.(); - onClose(); - } - }, - - { - id: 'buy-now', - label: 'Buy Now', - action: () => { - onPassesClick?.(); - onClose(); - } - }, - { - id: 'blogs', - label: 'Blogs', - action: () => { - onBlogsClick?.(); - onClose(); - } - }, - { - id: 'how-it-works', - label: 'How It Works', - action: () => { - onHowItWorksClick?.(); - onClose(); - } - } - ]; - - // Update selection based on current page + // Determine active item based on current route useEffect(() => { - if (currentPage === 'melbourne') { - setSelectedItem('melbourne'); - } else if (currentPage === 'attractions') { - setSelectedItem('attractions'); - } else if (currentPage === 'blogs') { - setSelectedItem('blogs'); - } else if (currentPage === 'how-it-works') { - setSelectedItem('how-it-works'); - } else if (currentPage === 'passes') { - setSelectedItem('buy-now'); - } else { - setSelectedItem(null); - } - }, [currentPage]); + const currentItem = submenuItems.find(item => + item.path && location.pathname === item.path + ); + setActiveItem(currentItem?.id || null); + }, [location.pathname]); - const handleItemClick = (item: SubmenuItem) => { - if (item.isCity) { - setSelectedItem(item.id); + const handleSubmenuItemClick = (item: SubmenuItem) => { + if (item.path) { + navigate(item.path); } + setActiveItem(item.id); item.action?.(); + onClose(); }; - const isItemActive = (item: SubmenuItem) => { - return (item.isCity && currentPage === 'melbourne' && item.id === 'melbourne') || - (item.id === 'attractions' && currentPage === 'attractions') || - (item.id === 'buy-now' && currentPage === 'passes') || - (item.id === 'blogs' && currentPage === 'blogs') || - (item.id === 'how-it-works' && currentPage === 'how-it-works'); + const isSubmenuItemActive = (itemId: string) => { + return activeItem === itemId; }; + // Render direct submenu items + const renderSubmenu = () => ( +
+ {submenuItems.map((item) => ( + handleSubmenuItemClick(item)} + className={`font-poppins font-medium text-base relative px-4 py-2.5 transition-all duration-300 whitespace-nowrap rounded-full ${ + isSubmenuItemActive(item.id) + ? 'bg-primary text-white shadow-md' + : 'text-gray-700 hover:text-white hover:bg-gray-800' + }`} + whileHover={{ scale: 1.02 }} + whileTap={{ scale: 0.98 }} + > + {item.label} + + {!isSubmenuItemActive(item.id) && ( + + )} + + ))} +
+ ); + return ( <> {/* Desktop Submenu */} @@ -145,33 +136,7 @@ export function CitySubmenu({ }} transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }} > -
- {submenuItems.map((item) => ( - handleItemClick(item)} - className={`font-poppins font-medium text-base relative px-4 py-2.5 transition-all duration-300 whitespace-nowrap rounded-full ${ - isItemActive(item) - ? 'bg-primary text-white shadow-md' - : 'text-gray-700 hover:text-white hover:bg-gray-800' - }`} - whileHover={{ scale: 1.02 }} - whileTap={{ scale: 0.98 }} - > - {item.label} - - {/* Hover effect for non-active items */} - {!isItemActive(item) && ( - - )} - - ))} -
+ {renderSubmenu()} @@ -202,9 +167,9 @@ export function CitySubmenu({ {submenuItems.map((item) => ( handleItemClick(item)} + onClick={() => handleSubmenuItemClick(item)} className={`relative px-3 py-2 text-sm font-medium transition-all duration-300 whitespace-nowrap rounded-xl flex-shrink-0 ${ - isItemActive(item) + isSubmenuItemActive(item.id) ? 'bg-primary text-white shadow-md' : 'text-gray-700 hover:text-white hover:bg-gray-800' }`} @@ -213,8 +178,7 @@ export function CitySubmenu({ > {item.label} - {/* Hover effect for non-active items */} - {!isItemActive(item) && ( + {!isSubmenuItemActive(item.id) && ( -
- {submenuItems.map((item) => ( - handleItemClick(item)} - className={`relative px-4 py-2.5 text-sm font-medium transition-all duration-300 whitespace-nowrap rounded-full ${ - isItemActive(item) - ? 'bg-primary text-white shadow-md' - : 'text-gray-700 hover:text-white hover:bg-gray-800' - }`} - whileHover={{ scale: 1.02 }} - whileTap={{ scale: 0.98 }} - > - {item.label} - - {/* Hover effect for non-active items */} - {!isItemActive(item) && ( - - )} - - ))} -
+ {renderSubmenu()}
diff --git a/src/components/CityTabsNavbar.tsx b/src/components/CityTabsNavbar.tsx new file mode 100644 index 0000000..36c3730 --- /dev/null +++ b/src/components/CityTabsNavbar.tsx @@ -0,0 +1,82 @@ +import React, { useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; + +interface CityTabsNavbarProps { + onPageSelect: (page: string) => void; +} + +const CityTabsNavbar: React.FC = ({ onPageSelect }) => { + const [selectedCity, setSelectedCity] = useState(null); + + const cities = ["Melbourne", "Sydney", "Brisbane"]; + + // City-specific submenus + const cityMenus: Record = { + Melbourne: ["Attractions", "Passes", "Deals", "Events"], + Sydney: ["Attractions", "Tours", "Food & Drinks"], + Brisbane: ["Passes", "Nature", "Culture"] + }; + + const handleCityClick = (city: string) => { + setSelectedCity(city); + }; + + const handleBackToCities = () => { + setSelectedCity(null); + }; + + return ( +
+
+ + {!selectedCity ? ( + + {cities.map((city) => ( + + ))} + + ) : ( + + + + {cityMenus[selectedCity].map((menu) => ( + + ))} + + )} + +
+
+ ); +}; + +export default CityTabsNavbar; diff --git a/src/components/CustomPostcards.tsx b/src/components/CustomPostcards.tsx index f30bc5a..f214a65 100644 --- a/src/components/CustomPostcards.tsx +++ b/src/components/CustomPostcards.tsx @@ -1,10 +1,10 @@ import { useState, useRef, useEffect } from 'react'; -import { Camera, ArrowRight, Edit3, Upload, Type, Calendar } from 'lucide-react'; +import { Camera, ArrowRight, Edit3, Upload, Type, Calendar, Palette, Edit, Stamp } from 'lucide-react'; import { Button } from './ui/button'; import { ImageWithFallback } from './figma/ImageWithFallback'; import { motion, useMotionValue, useSpring, useTransform, useInView } from 'motion/react'; import { HandwrittenText, useHandwrittenText } from './HandwrittenText'; -import vintageVector from 'figma:asset/e8091276ed2c976b5c975f21687b0d1702bd9c90.png'; +import postcardImage from '../assets/eaf15191e9a315d2d4b384ffcb22910687c3d328.png'; interface EditableCardProps { isEditing: boolean; @@ -66,7 +66,7 @@ export function CustomPostcards() { const [postcardData, setPostcardData] = useState({ photo: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=600&fit=crop&crop=center", - message: "Greetings from paradise!\nThe beaches here are absolutely\nbreathtaking. Wish you were\nhere to enjoy this amazing\nsunset with me.", + message: "Greetings from paradise!\\nThe beaches here are absolutely\\nbreathtaking. Wish you were\\nhere to enjoy this amazing\\nsunset with me.", date: "July 2024", addressLabel: "POSTCARD" }); @@ -108,7 +108,7 @@ export function CustomPostcards() { }} whileTap={{ scale: 0.98 }} animate={isEditing ? { - boxShadow: "0 0 0 2px rgba(99, 102, 241, 0.5), 0 8px 16px rgba(99, 102, 241, 0.3)" + boxShadow: "0 0 0 2px rgba(249, 95, 98, 0.5), 0 8px 16px rgba(249, 95, 98, 0.3)" } : {}} > {children} @@ -146,7 +146,7 @@ export function CustomPostcards() { @@ -270,8 +270,8 @@ export function CustomPostcards() { ease: "easeInOut" }} > - Vintage logo - {/* Editable Photo Card with realistic mounting - Responsive */} - setEditingCard(editingCard === 'photo' ? null : 'photo')} + {/* For Correspondence Text */} +
} > -
- - {/* Photo aging overlay */} -
-
- - + For correspondence +

+
+ {/* Realistic vertical divider with ink bleeding - Responsive */}
{/* Vintage Stamps */} -
-
-
+
+
+
{/* Paper Textures */} -
-
+
+
{/* Ink Splatters */} -
-
-
+
+
+
{/* Header */} -
+
- + Custom Memories

- How to create your + The Only Card That Sends Your - - custom + + Holiday {' '} - postcard? + Home.

-

- Transform your travel experiences into beautiful, personalized postcards with authentic handwritten messages. Share your journey in a way that feels truly personal. +

+ Transform your travel memories into beautiful, personalized postcards that capture the essence of your adventures.

- {/* Main Content - Responsive Grid */} -
- - {/* Left Column - Interactive Postcard with 3D Effect */} + {/* Centered Postcard Preview - Enhanced with Animations */} +
setEditingCard(null)} + className="relative group w-full max-w-4xl [perspective:2000px]" + initial={{ opacity: 0, y: 30, scale: 0.95 }} + animate={{ opacity: 1, y: 0, scale: 1 }} + transition={{ + duration: 0.6, + ease: [0.25, 0.1, 0.25, 1], + delay: 0.2 + }} > - {/* Main Postcard Container with 3D Transform */} - - + + {/* Subtle glow effect */} + + + + {/* Back Side - Full Image */} + + + {/* Subtle gradient overlay */} +
- {/* Floating Elements around postcard */} - - - - - - {/* Right Column - Features and CTA */} -
- - {/* Features List */} -
- {[ - { - icon: Camera, - title: "Capture & Customize", - description: "Upload your travel photos and customize every detail of your postcard." - }, - { - icon: Edit3, - title: "Handwritten Messages", - description: "Add personal messages that appear in beautiful handwritten script." - }, - { - icon: ArrowRight, - title: "Share Instantly", - description: "Send digital postcards or order physical prints delivered worldwide." - } - ].map((feature, index) => ( - -
- -
- -
-

{feature.title}

-

{feature.description}

-
-
- ))} -
- - {/* CTA Section */} -
- - -

- Turn your travel memories into timeless keepsakes. Click anywhere on the postcard above to start customizing. -

-
- - {/* Interactive Hint */} - {!editingCard && ( - - - ✨ - - Hover over the postcard and click any element to edit it! + Click on any element to edit it + +
)} -
+
+
+ + {/* Animated Editing Panel */} + {editingCard && ( + +

Edit {editingCard}

+ {editingCard === 'photo' && ( +
+ + setPostcardData(prev => ({ ...prev, photo: e.target.value }))} + className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary" + placeholder="Enter image URL" + /> +
+ )} + {editingCard === 'message' && ( +
+ +