From e98a54635bdaa111184821f9449f5057bbe4f87d Mon Sep 17 00:00:00 2001 From: aryabenade Date: Thu, 2 Apr 2026 12:18:06 +0530 Subject: [PATCH 01/21] change PostCard to Postcard --- src/components/Navbar.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 0dcef92..b5eee1c 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -143,11 +143,11 @@ export default function Navbar({ isShared: false }, { - label: 'Your PostCard', + label: 'Your Postcard', path: '/postcards', isShared: true, - landingLabel: 'Your PostCard', - melbourneLabel: 'Your PostCard' + landingLabel: 'Your Postcard', + melbourneLabel: 'Your Postcard' } ], melbourne: [ From e1fb13d4c043e3f66e132f59fccf9bcffe5048b4 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Thu, 2 Apr 2026 12:47:07 +0530 Subject: [PATCH 02/21] remove the image from postcard hero section --- src/components/Navbar.tsx | 6 +++--- src/components/PostCardsPage.tsx | 6 +++--- src/components/ProfilePage.tsx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index b5eee1c..dad69bf 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -186,11 +186,11 @@ export default function Navbar({ melbourneLabel: 'Your Card' }, { - label: 'Your PostCard', + label: 'Your Postcard', path: '/postcards', isShared: true, - landingLabel: 'Your PostCard', - melbourneLabel: 'Your PostCard' + landingLabel: 'Your Postcard', + melbourneLabel: 'Your Postcard' } ] }; diff --git a/src/components/PostCardsPage.tsx b/src/components/PostCardsPage.tsx index 3eb05f6..8892510 100644 --- a/src/components/PostCardsPage.tsx +++ b/src/components/PostCardsPage.tsx @@ -13,7 +13,7 @@ import { CustomPostcards } from './CustomPostcards'; import { HowItWorks } from './HowItWorks'; import { ImageWithFallback } from './figma/ImageWithFallback'; import { Layout } from '../Layout'; -import front from '../assets/front.jpg' +// import front from '../assets/front.jpg' interface User { @@ -89,7 +89,7 @@ export function PostCardsPage({
*/} - < img src={front} alt='Postcard image' /> + {/* < img src={front} alt='Postcard image' /> */}
{/* Decorative elements */} diff --git a/src/components/ProfilePage.tsx b/src/components/ProfilePage.tsx index 02842d6..0cb4dea 100644 --- a/src/components/ProfilePage.tsx +++ b/src/components/ProfilePage.tsx @@ -225,7 +225,7 @@ export function ProfilePage({ >

My{' '} - Profile + Profile

Manage your account, passes, and travel itineraries From 993ccf2c4748211ed3441179a8e37f080e57a1be Mon Sep 17 00:00:00 2001 From: aryabenade Date: Thu, 2 Apr 2026 13:11:18 +0530 Subject: [PATCH 03/21] add a gradient white shadow effect when showing attractions --- src/components/LandingVarietyOfAdventures.tsx | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/LandingVarietyOfAdventures.tsx b/src/components/LandingVarietyOfAdventures.tsx index 3e7ab94..4af0519 100644 --- a/src/components/LandingVarietyOfAdventures.tsx +++ b/src/components/LandingVarietyOfAdventures.tsx @@ -154,7 +154,7 @@ export function LandingVarietyOfAdventures() {

{/* Header */}
- Experience{' '} for Every Traveller - - From iconic laneways and world-class coffee to stunning gardens and vibrant markets, + From iconic laneways and world-class coffee to stunning gardens and vibrant markets, discover Melbourne's unique character through curated experiences that showcase the city's soul.
@@ -180,7 +180,7 @@ export function LandingVarietyOfAdventures() { {/* Carousel Container - Full Width */}
{/* Scrolling Track */} -
- + {/* Bottom Content Card */}
-
- + {/* Icon */} -
- + {/* Attraction Info */}
@@ -332,19 +332,19 @@ export function LandingVarietyOfAdventures() { {/* Gradient Fade Edges */} -
-
+
+
{/* CTA Button */} - -
- {/* Content */}
{step === 'email' ? ( + // ... Email step (unchanged)
- {/* Email Input */}
setEmail(e.target.value)} - className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" onKeyDown={(e) => e.key === 'Enter' && handleSendOTP()} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" /> - {helperText && ( -

- {helperText} -

- )} + {error &&

{error}

} + {helperText &&

{helperText}

}
- {/* Send OTP Button */}
) : (
{/* Email Display */}
- -
- {email} + +
+ {email}
- {helperText && ( -

- {helperText} -

- )}
- {/* OTP Input */} + {/* OTP Inputs with Paste Support */}
- {/* Countdown */} {countdown > 0 && ( -

- {formatCountdown(countdown)} +

+ Resend OTP in {formatCountdown(countdown)}

)}
- {/* Verify Button */} + {error &&

{error}

} + - {/* Resend OTP */} {countdown === 0 && ( diff --git a/src/main.tsx b/src/main.tsx index 241b0b3..9cf6d51 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,13 +1,14 @@ import { createRoot } from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import App from "./App"; -import "./index.css"; import { Provider } from "react-redux"; import { store } from "./Redux/Store"; +import { Toaster } from "sonner"; createRoot(document.getElementById("root")!).render( + From cdb9c7e734e1ea52a2a4c500e40bdef4db81dca1 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Thu, 9 Apr 2026 13:04:08 +0530 Subject: [PATCH 06/21] integrate the register user api --- src/components/LoginModal.tsx | 268 +++++++++++---------- src/components/RegisterModal.tsx | 391 +++++++++++++++++++++++++++++++ src/global.d.ts | 2 + src/main.tsx | 2 + 4 files changed, 540 insertions(+), 123 deletions(-) create mode 100644 src/components/RegisterModal.tsx diff --git a/src/components/LoginModal.tsx b/src/components/LoginModal.tsx index 5eca180..bff54d3 100644 --- a/src/components/LoginModal.tsx +++ b/src/components/LoginModal.tsx @@ -7,6 +7,7 @@ import { Label } from './ui/label'; import { useAuth } from '../context/AuthContext'; import { useLoginMutation, useVerifyOtpMutation } from '../Redux/services/auth.service'; import { toast } from 'sonner'; +import { RegisterModal } from './RegisterModal'; interface LoginModalProps { isOpen: boolean; @@ -20,6 +21,7 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) { const [countdown, setCountdown] = useState(0); const [helperText, setHelperText] = useState(''); const [error, setError] = useState(''); + const [showRegisterModal, setShowRegisterModal] = useState(false); const { login } = useAuth(); @@ -158,136 +160,156 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) { }; return ( - - {isOpen && ( - <> - + <> + + {isOpen && ( + <> + - -
-
- + +
+
+ -

- Login -

-

- Enter your email and verify with OTP -

-
+

+ Login +

+

+ Enter your email and verify with OTP +

+
-
- {step === 'email' ? ( - // ... Email step (unchanged) -
-
- - setEmail(e.target.value)} - onKeyDown={(e) => e.key === 'Enter' && handleSendOTP()} - className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" - /> - {error &&

{error}

} - {helperText &&

{helperText}

} -
- - -
- ) : ( -
- {/* Email Display */} -
- -
- {email} -
-
- - {/* OTP Inputs with Paste Support */} -
- -
- {otp.map((digit, index) => ( - handleOTPChange(index, e.target.value.replace(/\D/g, ''))} - onKeyDown={(e) => handleOTPKeyDown(index, e)} - onPaste={handlePaste} // ← Paste support added here - data-otp-index={index} - className="w-12 h-12 text-center font-poppins font-semibold text-lg bg-gray-100 border-0 rounded-xl focus:bg-white focus:ring-2 focus:ring-gray-800 focus:outline-none transition-all" - /> - ))} +
+ {step === 'email' ? ( + // ... Email step (unchanged) +
+
+ + setEmail(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && handleSendOTP()} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" + /> + {error &&

{error}

} + {helperText &&

{helperText}

}
- {countdown > 0 && ( -

- Resend OTP in {formatCountdown(countdown)} -

+ +
+ +
+
+ ) : ( +
+ {/* Email Display */} +
+ +
+ {email} +
+
+ + {/* OTP Inputs with Paste Support */} +
+ +
+ {otp.map((digit, index) => ( + handleOTPChange(index, e.target.value.replace(/\D/g, ''))} + onKeyDown={(e) => handleOTPKeyDown(index, e)} + onPaste={handlePaste} // ← Paste support added here + data-otp-index={index} + className="w-12 h-12 text-center font-poppins font-semibold text-lg bg-gray-300 border-0 rounded-xl focus:bg-white focus:ring-2 focus:ring-gray-800 focus:outline-none transition-all" + /> + ))} +
+ + {countdown > 0 && ( +

+ Resend OTP in {formatCountdown(countdown)} +

+ )} +
+ + {error &&

{error}

} + + + + {countdown === 0 && ( + )}
- - {error &&

{error}

} - - - - {countdown === 0 && ( - - )} -
- )} + )} +
-
- - - )} - + + + ) + } + + setShowRegisterModal(false)} + onLoginClick={() => { + setShowRegisterModal(false); + setStep('email'); + setEmail(''); + }} + /> + ); } \ No newline at end of file diff --git a/src/components/RegisterModal.tsx b/src/components/RegisterModal.tsx new file mode 100644 index 0000000..e690a89 --- /dev/null +++ b/src/components/RegisterModal.tsx @@ -0,0 +1,391 @@ +import { useState, useEffect } from 'react'; +import { motion, AnimatePresence } from 'motion/react'; +import { X } from 'lucide-react'; +import { Button } from './ui/button'; +import { Input } from './ui/input'; +import { Label } from './ui/label'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; +import { useRegisterMutation } from '../Redux/services/auth.service'; +import { toast } from 'sonner'; + +interface RegisterModalProps { + isOpen: boolean; + onClose: () => void; + onLoginClick: () => void; +} + +export function RegisterModal({ isOpen, onClose, onLoginClick }: RegisterModalProps) { + const [formData, setFormData] = useState({ + firstName: '', + lastName: '', + emailAddress: '', + isdCode: '+91', + mobileNumber: '', + address1: '', + address2: '', + city: '', + state: '', + country: 'Australia', + postalCode: '' + }); + const [helperText, setHelperText] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const [register, { isLoading: isRegistering }] = useRegisterMutation(); + + useEffect(() => { + if (!isOpen) { + setFormData({ + firstName: '', + lastName: '', + emailAddress: '', + isdCode: '+91', + mobileNumber: '', + address1: '', + address2: '', + city: '', + state: '', + country: 'Australia', + postalCode: '' + }); + setHelperText(''); + } + }, [isOpen]); + + const handleInputChange = (field: string, value: string) => { + setFormData(prev => ({ ...prev, [field]: value })); + }; + + const validateForm = () => { + if (!formData.firstName.trim()) { + toast.error('First name is required'); + return false; + } + if (!formData.lastName.trim()) { + toast.error('Last name is required'); + return false; + } + if (!formData.emailAddress.trim() || !formData.emailAddress.includes('@')) { + toast.error('Please enter a valid email address'); + return false; + } + if (!formData.mobileNumber.trim()) { + toast.error('Mobile number is required'); + return false; + } + if (!/^\d+$/.test(formData.mobileNumber.trim())) { + toast.error('Mobile number must contain only digits'); + return false; + } + if (!formData.address1.trim()) { + toast.error('Address is required'); + return false; + } + if (!formData.city.trim()) { + toast.error('City is required'); + return false; + } + if (!formData.state.trim()) { + toast.error('State is required'); + return false; + } + if (!formData.postalCode.trim()) { + toast.error('Postal code is required'); + return false; + } + if (!/^\d+$/.test(formData.postalCode.trim())) { + toast.error('Postal code must contain only digits'); + return false; + } + return true; + }; + + + const handleRegister = async () => { + if (!validateForm()) { + return; + } + + setHelperText(''); + setIsLoading(true); + + try { + const response = await register(formData).unwrap(); + console.log('Registration response:', response); + + toast.success('Registration successful! Please login.'); + setTimeout(() => { + onLoginClick(); + onClose(); + }, 2000); + } catch (error: any) { + console.error('Registration error:', error); + const errorMessage = error?.data?.message || 'Registration failed. Please try again.'; + toast.error(errorMessage); + setHelperText(errorMessage); + } finally { + setIsLoading(false); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleRegister(); + } + }; + + return ( + + {isOpen && ( + <> + + + +
+
+ + +

+ Create Account +

+

+ Register to get started with City Cards +

+
+ +
+
+ {/* Personal Information */} +
+

Personal Information

+
+
+ + handleInputChange('firstName', e.target.value)} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" + /> +
+ +
+ + handleInputChange('lastName', e.target.value)} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" + /> +
+
+ +
+ + handleInputChange('emailAddress', e.target.value)} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" + /> +
+ +
+
+ + +
+ +
+ + handleInputChange('mobileNumber', e.target.value)} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" + /> +
+
+
+ + {/* Address Information */} +
+

Address Information

+ +
+ + handleInputChange('address1', e.target.value)} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" + /> +
+ +
+ + handleInputChange('address2', e.target.value)} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" + /> +
+ +
+
+ + handleInputChange('city', e.target.value)} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" + /> +
+ +
+ + handleInputChange('state', e.target.value)} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" + /> +
+
+ +
+
+ + +
+ +
+ + handleInputChange('postalCode', e.target.value)} + className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400" + /> +
+
+
+ + {helperText && ( +

+ {helperText} +

+ )} + + + +
+ +
+
+
+
+
+ + )} +
+ ); +} diff --git a/src/global.d.ts b/src/global.d.ts index 903f3f8..eb5a13c 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -32,3 +32,5 @@ declare module '*.mp4' { const src: string; export default src; } + +declare module "*.css"; diff --git a/src/main.tsx b/src/main.tsx index 9cf6d51..6e91c69 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,12 @@ import { createRoot } from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import App from "./App"; +import "./index.css"; import { Provider } from "react-redux"; import { store } from "./Redux/Store"; import { Toaster } from "sonner"; + createRoot(document.getElementById("root")!).render( From 7c9ec2c28587be408de790a8d38f4766eeb3fb77 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Thu, 9 Apr 2026 15:04:27 +0530 Subject: [PATCH 07/21] move the pages from components to pages folder --- src/AppRouter.tsx | 50 ++- src/Redux/Store.tsx | 5 - src/guidelines/Guidelines.md | 345 ------------------ src/{components => pages}/AboutUsPage.tsx | 14 +- .../AttractionDetailsPage.tsx | 8 +- src/{components => pages}/AttractionsPage.tsx | 12 +- src/{components => pages}/BlogDetailsPage.tsx | 14 +- src/{components => pages}/BlogsPage.tsx | 22 +- src/{components => pages}/CheckoutPage.tsx | 24 +- src/{components => pages}/CityCardsPage.tsx | 19 +- src/{components => pages}/ContactUsPage.tsx | 14 +- .../CreateMagicItineraryPage.tsx | 22 +- src/{components => pages}/DiscoverPage.tsx | 8 +- src/{components => pages}/DownloadAppPage.tsx | 12 +- .../HotelDiscountsPage.tsx | 6 +- .../ItineraryViewPage.tsx | 12 +- .../LandingMagicItineraryPage.tsx | 20 +- .../MagicItineraryPage.tsx | 20 +- src/{components => pages}/MelbournePage.tsx | 28 +- src/{components => pages}/OffersPage.tsx | 26 +- src/{components => pages}/PassesPage.tsx | 16 +- src/{components => pages}/PostCardsPage.tsx | 20 +- .../PrivacyPolicyPage.tsx | 12 +- src/{components => pages}/ProfilePage.tsx | 22 +- .../SecureCheckoutPage.tsx | 18 +- .../SuperSavingsPage.tsx | 26 +- src/{components => pages}/WhatsIncluded.tsx | 10 +- 27 files changed, 223 insertions(+), 582 deletions(-) delete mode 100644 src/guidelines/Guidelines.md rename src/{components => pages}/AboutUsPage.tsx (98%) rename src/{components => pages}/AttractionDetailsPage.tsx (98%) rename src/{components => pages}/AttractionsPage.tsx (98%) rename src/{components => pages}/BlogDetailsPage.tsx (98%) rename src/{components => pages}/BlogsPage.tsx (96%) rename src/{components => pages}/CheckoutPage.tsx (97%) rename src/{components => pages}/CityCardsPage.tsx (96%) rename src/{components => pages}/ContactUsPage.tsx (97%) rename src/{components => pages}/CreateMagicItineraryPage.tsx (99%) rename src/{components => pages}/DiscoverPage.tsx (99%) rename src/{components => pages}/DownloadAppPage.tsx (99%) rename src/{components => pages}/HotelDiscountsPage.tsx (98%) rename src/{components => pages}/ItineraryViewPage.tsx (98%) rename src/{components => pages}/LandingMagicItineraryPage.tsx (97%) rename src/{components => pages}/MagicItineraryPage.tsx (97%) rename src/{components => pages}/MelbournePage.tsx (97%) rename src/{components => pages}/OffersPage.tsx (97%) rename src/{components => pages}/PassesPage.tsx (98%) rename src/{components => pages}/PostCardsPage.tsx (93%) rename src/{components => pages}/PrivacyPolicyPage.tsx (97%) rename src/{components => pages}/ProfilePage.tsx (98%) rename src/{components => pages}/SecureCheckoutPage.tsx (97%) rename src/{components => pages}/SuperSavingsPage.tsx (98%) rename src/{components => pages}/WhatsIncluded.tsx (98%) diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx index 1a3c767..28f26f5 100644 --- a/src/AppRouter.tsx +++ b/src/AppRouter.tsx @@ -2,38 +2,36 @@ import { Routes, Route, useParams, useLocation, useNavigate } from 'react-router 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 { MelbournePage } from './pages/MelbournePage'; +import { PassesPage } from './pages/PassesPage'; +import { AttractionsPage } from './pages/AttractionsPage'; +import { AttractionDetailsPage } from './pages/AttractionDetailsPage'; +import { CheckoutPage } from './pages/CheckoutPage'; +import { SecureCheckoutPage } from './pages/SecureCheckoutPage'; +import { BlogsPage } from './pages/BlogsPage'; +import { BlogDetailsPage } from './pages/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 { PrivacyPolicyPage } from './pages/PrivacyPolicyPage'; +import { AboutUsPage } from './pages/AboutUsPage'; +import { ProfilePage } from './pages/ProfilePage'; +import { CreateMagicItineraryPage } from './pages/CreateMagicItineraryPage'; +import { ItineraryViewPage } from './pages/ItineraryViewPage'; +import { OffersPage } from './pages/OffersPage'; +import { CityCardsPage } from './pages/CityCardsPage'; +import { MagicItineraryPage } from './pages/MagicItineraryPage'; +import { PostCardsPage } from './pages/PostCardsPage'; +import { DownloadAppPage } from './pages/DownloadAppPage'; +import { HotelDiscountsPage } from './pages/HotelDiscountsPage'; +import { ContactUsPage } from './pages/ContactUsPage'; import { pageTransition } from './utils/animations'; import { LandingPage } from './pages/landingPage'; import ComingSoonPage from './pages/ComingSoonPage'; -import { SuperSavingsPage } from './components/SuperSavingsPage'; -import { WhatsIncluded } from './components/WhatsIncluded'; -import { LandingMagicItineraryPage } from './components/LandingMagicItineraryPage'; -import { DiscoverPage } from './components/DiscoverPage'; +import { SuperSavingsPage } from './pages/SuperSavingsPage'; +import { WhatsIncluded } from './pages/WhatsIncluded'; +import { LandingMagicItineraryPage } from './pages/LandingMagicItineraryPage'; +import { DiscoverPage } from './pages/DiscoverPage'; // User type definition interface User { diff --git a/src/Redux/Store.tsx b/src/Redux/Store.tsx index 04e0e46..444e72e 100644 --- a/src/Redux/Store.tsx +++ b/src/Redux/Store.tsx @@ -1,23 +1,18 @@ import { configureStore } from "@reduxjs/toolkit"; -import { fakeApi } from "./services/fakeApi.service"; import { attractionsApi } from "./services/attractions.service"; import { citiesApi } from "./services/cities.service"; import { authApi } from "./services/auth.service"; export const store = configureStore({ reducer: { - [fakeApi.reducerPath]: fakeApi.reducer, [attractionsApi.reducerPath]: attractionsApi.reducer, [citiesApi.reducerPath]: citiesApi.reducer, [authApi.reducerPath]:authApi.reducer }, - middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat( - - fakeApi.middleware, attractionsApi.middleware, citiesApi.middleware, authApi.middleware diff --git a/src/guidelines/Guidelines.md b/src/guidelines/Guidelines.md deleted file mode 100644 index 4a00ca3..0000000 --- a/src/guidelines/Guidelines.md +++ /dev/null @@ -1,345 +0,0 @@ -# Image Reference Guidelines - -**IMPORTANT**: When I provide an image for reference, it is for design reference only. Do NOT use that image inside any image section in the code. The image is provided to help understand the layout, styling, and visual direction - not to be embedded as an actual image in the application. - -# CityCards Typography Guidelines - -**Project**: Typography Guidelines for CityCards Travel Website - -## Font System - -### Primary Font -- **Poppins**: Used for all text including headings (H1–H6), body text, buttons, labels, and forms - Clean, readable, and consistent throughout - -### Font Weight Scale & Usage -- **font-light (300)**: Hero headings only - For creating dynamic contrast in H1/H2 -- **font-normal (400)**: Standard body text - Default weight for paragraphs -- **font-medium (500)**: Buttons, navigation links, subtle emphasis -- **font-semibold (600)**: Section headings, primary buttons -- **font-bold (700)**: Hero keywords, strong emphasis - -## Heading Typography Specifications - -### H1 - Hero/Main Page Headings -- **Font**: Poppins -- **Size**: `text-5xl md:text-6xl lg:text-7xl` (48px/60px/72px - targeting ~64px) -- **Line Height**: `leading-tight` -- **Pattern**: Dynamic multi-weight with gradient/italic accents (max 2 emphasis styles) - -```jsx -

- Discover{' '} - - Melbourne's - {' '} - Best Experiences -

-``` - -### H2 - Section Headings -- **Font**: Poppins -- **Size**: `text-2xl md:text-3xl lg:text-4xl` (24px/36px/48px) -- **Line Height**: `leading-tight` -- **Pattern**: Mixed weights with gradient emphasis - -```jsx -

- Explore{' '} - - Amazing - {' '} - Cities -

-``` - -### H3 - Subsection Headings -- **Font**: Poppins -- **Size**: `text-xl md:text-2xl` (24px/30px) -- **Line Height**: `leading-snug` -- **Weight**: `font-semibold` - -```jsx -

- Feature Title -

-``` - -### H4 - Component Headings -- **Font**: Poppins -- **Size**: `text-lg md:text-xl` (20px/24px) -- **Line Height**: `leading-snug` -- **Weight**: `font-medium` or `font-semibold` - -```jsx -

- Component Heading -

-``` - -### H5 - Card/Item Titles -- **Font**: Poppins -- **Size**: `text-lg` (18px) -- **Line Height**: `leading-snug` -- **Weight**: `font-medium` - -```jsx -
- Card Title -
-``` - -### H6 - Small Headings -- **Font**: Poppins -- **Size**: `text-base` (16px) -- **Line Height**: `leading-snug` -- **Weight**: `font-medium` - -```jsx -
- Small Heading -
-``` - -## Body Typography Specifications - -### Large Body Text -- **Font**: Poppins -- **Size**: `text-xl` (20px) -- **Line Height**: `leading-relaxed` -- **Weight**: `font-normal` - -```jsx -

- Large descriptive text for important sections -

-``` - -### Regular Body Text -- **Font**: Poppins -- **Size**: `text-base` (16px) -- **Line Height**: `leading-relaxed` -- **Weight**: `font-normal` - -```jsx -

- Regular body text content -

-``` - -### Small Body Text -- **Font**: Poppins -- **Size**: `text-sm` (14px) -- **Line Height**: `leading-relaxed` -- **Weight**: `font-normal` or `font-light` - -```jsx -

- Caption or meta information -

-``` - -## Interactive Element Typography - -### Buttons -- **Font**: Poppins -- **Primary Weight**: `font-semibold` -- **Secondary Weight**: `font-medium` -- **Min Size**: 16px - -```jsx -// Primary Button - - -// Secondary Button - -``` - -### Navigation Links -- **Font**: Poppins -- **Weight**: `font-medium` -- **Size**: `text-base` (16px) - -```jsx - - Navigation Link - -``` - -### Form Labels -- **Font**: Poppins -- **Weight**: `font-light` or `font-normal` -- **Size**: `text-sm` or `text-base` (14px/16px) - -```jsx - -``` - -### Form Inputs -- **Font**: Poppins -- **Weight**: `font-normal` -- **Size**: `text-base` (16px) - -```jsx - -``` - -## Accessibility Standards - -### Text Size Requirements -- **Minimum Text Size**: 14px -- **Interactive Minimum Size**: 16px -- **Contrast**: WCAG AA or higher -- **Heading Hierarchy**: Maintain semantic order (H1 → H2 → H3 etc.) - -### Implementation Requirements -```jsx -// Always include explicit font and size classes to override defaults -

- Content with explicit styling -

-``` - -## Typography Rules - -### DO ✅ -- Use Poppins for all text (headings and body) -- Apply max 2 emphasis styles per heading -- Use gradient effects sparingly -- Keep line-heights consistent -- Always specify explicit font classes to override component defaults - -### DON'T ❌ -- Don't use font-light in small text -- Don't mix more than 3 weights in one heading -- Don't go below 14px for captions -- Don't override font sizes without Tailwind classes -- Don't break semantic heading hierarchy - -## Implementation Guidelines - -### Component Styling Override -**IMPORTANT**: Always explicitly set typography classes to override component defaults: - -```jsx -// ✅ CORRECT - Explicit typography classes - - - - Card Title - - - -

- Card content with explicit styling -

-
-
- -// ❌ INCORRECT - Relying on defaults - - - Card Title - - -

Card content without explicit styling

-
-
-``` - -### Dynamic Heading Patterns -```jsx -// Pattern 1: Light → Bold (H1/H2 only) -

- Discover{' '} - - Amazing - {' '} - Destinations -

- -// Pattern 2: Normal → Semibold (H3/H4) -

- Experience{' '} - Melbourne's Culture -

-``` - -### Responsive Typography -```jsx -// Mobile-first responsive scaling -

- Responsive Heading -

- -

- Responsive body text -

-``` - -**Add your own guidelines here** - diff --git a/src/components/AboutUsPage.tsx b/src/pages/AboutUsPage.tsx similarity index 98% rename from src/components/AboutUsPage.tsx rename to src/pages/AboutUsPage.tsx index b68a755..29b87fd 100644 --- a/src/components/AboutUsPage.tsx +++ b/src/pages/AboutUsPage.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, Heart, MapPin, Zap, Globe, Users, Camera, Coffee } from 'lucide-react'; -import { Button } from './ui/button'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; -import { MobileAppSection } from './MobileAppSection'; -import { EnhancedTestimonials } from './EnhancedTestimonials'; -import { ReviewsSection } from './ReviewsSection'; +// import { Button } from './ui/button'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; +import { MobileAppSection } from '../components/MobileAppSection'; +import { EnhancedTestimonials } from '../components/EnhancedTestimonials'; +import { ReviewsSection } from '../components/ReviewsSection'; interface User { email: string; diff --git a/src/components/AttractionDetailsPage.tsx b/src/pages/AttractionDetailsPage.tsx similarity index 98% rename from src/components/AttractionDetailsPage.tsx rename to src/pages/AttractionDetailsPage.tsx index d92c3b1..8998f12 100644 --- a/src/components/AttractionDetailsPage.tsx +++ b/src/pages/AttractionDetailsPage.tsx @@ -1,10 +1,10 @@ import { useState } from 'react'; 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, } from './ui/card'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { Button } from '../components/ui/button'; +import { Badge } from '../components/ui/badge'; +import { Card, } from '../components/ui/card'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { Layout } from '../Layout'; import { useParams } from 'react-router-dom'; import { useGetAttractionDetailsByIdQuery } from '../Redux/services/attractions.service'; diff --git a/src/components/AttractionsPage.tsx b/src/pages/AttractionsPage.tsx similarity index 98% rename from src/components/AttractionsPage.tsx rename to src/pages/AttractionsPage.tsx index 79ec7df..588ea14 100644 --- a/src/components/AttractionsPage.tsx +++ b/src/pages/AttractionsPage.tsx @@ -2,12 +2,12 @@ import { useEffect, useState } from 'react'; import { motion } from 'motion/react'; import { Search, Star, Clock } from 'lucide-react'; import { useNavigate, useParams } 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 { ImageWithFallback } from './figma/ImageWithFallback'; +import { Button } from '../components/ui/button'; +import { Input } from '../components/ui/input'; +import { Card, CardContent } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import { Checkbox } from '../components/ui/checkbox'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { Layout } from '../Layout'; import { useGetAttractionFiltersQuery, useGetCustomerAttractionsQuery } from '../Redux/services/attractions.service'; interface User { diff --git a/src/components/BlogDetailsPage.tsx b/src/pages/BlogDetailsPage.tsx similarity index 98% rename from src/components/BlogDetailsPage.tsx rename to src/pages/BlogDetailsPage.tsx index 675c7de..5c935cc 100644 --- a/src/components/BlogDetailsPage.tsx +++ b/src/pages/BlogDetailsPage.tsx @@ -1,13 +1,13 @@ import { useState } from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, Calendar, User, Clock, Share2, BookmarkPlus, ThumbsUp, MessageSquare, Tag, MapPin } 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 Navbar from './Navbar'; -import { Footer } from './Footer'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { Button } from '../components/ui/button'; +import { Badge } from '../components/ui/badge'; +import { Card, CardContent } from '../components/ui/card'; +import { Separator } from '../components/ui/separator'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; interface User { email: string; diff --git a/src/components/BlogsPage.tsx b/src/pages/BlogsPage.tsx similarity index 96% rename from src/components/BlogsPage.tsx rename to src/pages/BlogsPage.tsx index df21c8b..677c947 100644 --- a/src/components/BlogsPage.tsx +++ b/src/pages/BlogsPage.tsx @@ -1,18 +1,18 @@ import { useState } from 'react'; import { motion } from 'motion/react'; import { Calendar, User, Clock, ArrowRight, Search, Tag, CreditCard, MapPin, Check, Smartphone, Star, Heart, Share2 } from 'lucide-react'; -import { Button } from './ui/button'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card'; -import { Badge } from './ui/badge'; -import { Input } from './ui/input'; -import Navbar from './Navbar'; +import { Button } from '../components/ui/button'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import { Input } from '../components/ui/input'; +import Navbar from '../components/Navbar'; // import { CitySubmenu } from './CitySubmenu'; -import { MobileAppSection } from './MobileAppSection'; -import { WhyChooseCityCards } from './WhyChooseCityCards'; -import { EnhancedTestimonials } from './EnhancedTestimonials'; -import { ReviewsSection } from './ReviewsSection'; -import { Footer } from './Footer'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { MobileAppSection } from '../components/MobileAppSection'; +import { WhyChooseCityCards } from '../components/WhyChooseCityCards'; +import { EnhancedTestimonials } from '../components/EnhancedTestimonials'; +import { ReviewsSection } from '../components/ReviewsSection'; +import { Footer } from '../components/Footer'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import imgFrame1597884939 from "figma:asset/5da1b0444c0d21bc7ee776c49e36e2a8ea4d3e12.png"; // Blog Mobile App Section Component diff --git a/src/components/CheckoutPage.tsx b/src/pages/CheckoutPage.tsx similarity index 97% rename from src/components/CheckoutPage.tsx rename to src/pages/CheckoutPage.tsx index 82e5f68..f34b555 100644 --- a/src/components/CheckoutPage.tsx +++ b/src/pages/CheckoutPage.tsx @@ -1,20 +1,20 @@ import { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'motion/react'; import { ArrowLeft, CreditCard, Users, Calendar, MapPin, Shield, Truck, Clock, ChevronRight, Check, ChevronDown, X, Mail, Smartphone } from 'lucide-react'; -import { Button } from './ui/button'; -import { Input } from './ui/input'; -import { Label } from './ui/label'; -import { Card, CardContent, CardHeader, CardTitle } from './ui/card'; -import { Separator } from './ui/separator'; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './ui/dialog'; -import { RadioGroup, RadioGroupItem } from './ui/radio-group'; -import { Checkbox } from './ui/checkbox'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; -import { Badge } from './ui/badge'; -import { Textarea } from './ui/textarea'; +import { Button } from '../components/ui/button'; +import { Input } from '../components/ui/input'; +import { Label } from '../components/ui/label'; +import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/card'; +import { Separator } from '../components/ui/separator'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '../components/ui/dialog'; +import { RadioGroup, RadioGroupItem } from '../components/ui/radio-group'; +import { Checkbox } from '../components/ui/checkbox'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../components/ui/select'; +import { Badge } from '../components/ui/badge'; +import { Textarea } from '../components/ui/textarea'; import Navbar from './Navbar'; import { Footer } from './Footer'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { Layout } from '../Layout'; interface CheckoutPageProps { diff --git a/src/components/CityCardsPage.tsx b/src/pages/CityCardsPage.tsx similarity index 96% rename from src/components/CityCardsPage.tsx rename to src/pages/CityCardsPage.tsx index 1ae32fa..5a27856 100644 --- a/src/components/CityCardsPage.tsx +++ b/src/pages/CityCardsPage.tsx @@ -1,18 +1,15 @@ import React from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, Star, MapPin, Clock, CreditCard, Users, Shield, Smartphone, Check } from 'lucide-react'; -import { Button } from './ui/button'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card'; -import { Badge } from './ui/badge'; -import Navbar from './Navbar'; -// import SubNavbar from './SubNavbar'; -import { Footer } from './Footer'; -import { MobileAppSection } from './MobileAppSection'; -import { EnhancedTestimonials } from './EnhancedTestimonials'; +import { Button } from '../components/ui/button'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import { MobileAppSection } from '../components/MobileAppSection'; +import { EnhancedTestimonials } from '../components/EnhancedTestimonials'; import { FAQPage } from './FAQPage'; -import { HowItWorks } from './HowItWorks'; -import { WhyChooseCityCards } from './WhyChooseCityCards'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { HowItWorks } from '../components/HowItWorks'; +import { WhyChooseCityCards } from '../components/WhyChooseCityCards'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { Layout } from '../Layout'; interface User { diff --git a/src/components/ContactUsPage.tsx b/src/pages/ContactUsPage.tsx similarity index 97% rename from src/components/ContactUsPage.tsx rename to src/pages/ContactUsPage.tsx index d8f9483..46852a3 100644 --- a/src/components/ContactUsPage.tsx +++ b/src/pages/ContactUsPage.tsx @@ -12,13 +12,13 @@ import { ChevronRight, CheckCircle } from 'lucide-react'; -import { Button } from './ui/button'; -import { Card, CardContent, CardHeader, CardTitle } from './ui/card'; -import { Input } from './ui/input'; -import { Label } from './ui/label'; -import { Textarea } from './ui/textarea'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; +import { Button } from '../components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/card'; +import { Input } from '../components/ui/input'; +import { Label } from '../components/ui/label'; +import { Textarea } from '../components/ui/textarea'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; interface ContactUsPageProps { onBackClick: () => void; diff --git a/src/components/CreateMagicItineraryPage.tsx b/src/pages/CreateMagicItineraryPage.tsx similarity index 99% rename from src/components/CreateMagicItineraryPage.tsx rename to src/pages/CreateMagicItineraryPage.tsx index 71c5f21..5735da3 100644 --- a/src/components/CreateMagicItineraryPage.tsx +++ b/src/pages/CreateMagicItineraryPage.tsx @@ -18,17 +18,17 @@ import { ChevronDown, ChevronUp } from 'lucide-react'; -import { Button } from './ui/button'; -import { Card, CardContent } from './ui/card'; -import { Badge } from './ui/badge'; -import { Progress } from './ui/progress'; -import { Calendar as CalendarComponent } from './ui/calendar'; -import { Input } from './ui/input'; -import { Popover, PopoverContent, PopoverTrigger } from './ui/popover'; -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/collapsible'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { Button } from '../components/ui/button'; +import { Card, CardContent } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import { Progress } from '../components/ui/progress'; +import { Calendar as CalendarComponent } from '../components/ui/calendar'; +import { Input } from '../components/ui/input'; +import { Popover, PopoverContent, PopoverTrigger } from '../components/ui/popover'; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../components/ui/collapsible'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; interface User { diff --git a/src/components/DiscoverPage.tsx b/src/pages/DiscoverPage.tsx similarity index 99% rename from src/components/DiscoverPage.tsx rename to src/pages/DiscoverPage.tsx index 2ec1d43..73a9ad0 100644 --- a/src/components/DiscoverPage.tsx +++ b/src/pages/DiscoverPage.tsx @@ -2,10 +2,10 @@ import { ArrowRight, Check, CreditCard, DollarSign, MapPin, Palette, Sparkles, T import { AnimatePresence, motion } from 'motion/react'; import { useEffect, useState } from 'react'; import { Layout } from '../Layout'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { MobileAppSection } from './MobileAppSection'; -import { TrustSection } from './TrustSection'; -import { Button } from './ui/button'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; +import { MobileAppSection } from '../components/MobileAppSection'; +import { TrustSection } from '../components/TrustSection'; +import { Button } from '../components/ui/button'; interface User { email: string; diff --git a/src/components/DownloadAppPage.tsx b/src/pages/DownloadAppPage.tsx similarity index 99% rename from src/components/DownloadAppPage.tsx rename to src/pages/DownloadAppPage.tsx index 7b0c1e0..fe125a4 100644 --- a/src/components/DownloadAppPage.tsx +++ b/src/pages/DownloadAppPage.tsx @@ -1,12 +1,12 @@ import { useState } from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, ChevronRight, QrCode, CreditCard, Calendar, MapPin, Star, CheckCircle, Sparkles, Users, Clock, Gift, Ticket } from 'lucide-react'; -import { Button } from './ui/button'; -import { Card, CardContent } from './ui/card'; -import { Badge } from './ui/badge'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { Button } from '../components/ui/button'; +import { Card, CardContent } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; interface User { email: string; diff --git a/src/components/HotelDiscountsPage.tsx b/src/pages/HotelDiscountsPage.tsx similarity index 98% rename from src/components/HotelDiscountsPage.tsx rename to src/pages/HotelDiscountsPage.tsx index 3811808..798f4f0 100644 --- a/src/components/HotelDiscountsPage.tsx +++ b/src/pages/HotelDiscountsPage.tsx @@ -1,10 +1,6 @@ import { motion } from 'motion/react'; import { BadgePercent, Clock, Crown, Check, ArrowRight } from 'lucide-react'; -import { Button } from './ui/button'; -import Navbar from './Navbar'; -// import { SubNavbar } from './SubNavbar'; -import { Footer } from './Footer'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import cityCardsLogo from '../assets/cityLogo.png'; import marriottHotelImage from '../assets/marriott-hotel.png'; import { Layout } from '../Layout'; diff --git a/src/components/ItineraryViewPage.tsx b/src/pages/ItineraryViewPage.tsx similarity index 98% rename from src/components/ItineraryViewPage.tsx rename to src/pages/ItineraryViewPage.tsx index 0f72882..522604a 100644 --- a/src/components/ItineraryViewPage.tsx +++ b/src/pages/ItineraryViewPage.tsx @@ -1,12 +1,12 @@ import React, { useState } from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, Calendar, Clock, MapPin, Users, Star, Heart, Share2, Download, CheckCircle, Navigation, Cloud, Sun } from 'lucide-react'; -import { Button } from './ui/button'; -import { Card, CardContent } from './ui/card'; -import { Badge } from './ui/badge'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { Button } from '../components/ui/button'; +import { Card, CardContent } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; interface ItineraryViewPageProps { onBackClick: () => void; diff --git a/src/components/LandingMagicItineraryPage.tsx b/src/pages/LandingMagicItineraryPage.tsx similarity index 97% rename from src/components/LandingMagicItineraryPage.tsx rename to src/pages/LandingMagicItineraryPage.tsx index 32fa27c..0c7f059 100644 --- a/src/components/LandingMagicItineraryPage.tsx +++ b/src/pages/LandingMagicItineraryPage.tsx @@ -1,16 +1,16 @@ import React from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, Sparkles, MapPin, Clock, Users, Calendar, Star, Zap, Heart, Camera } from 'lucide-react'; -import { Button } from './ui/button'; -import { Card, CardContent } from './ui/card'; -import { Badge } from './ui/badge'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; -import { MobileAppSection } from './MobileAppSection'; -import { EnhancedTestimonials } from './EnhancedTestimonials'; -import { HowItWorks } from './HowItWorks'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { PersonalizedTourHero } from './PersonalizedTourHero'; +import { Button } from '../components/ui/button'; +import { Card, CardContent } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; +import { MobileAppSection } from '../components/MobileAppSection'; +import { EnhancedTestimonials } from '../components/EnhancedTestimonials'; +import { HowItWorks } from '../components/HowItWorks'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; +import { PersonalizedTourHero } from '../components/PersonalizedTourHero'; import { Layout } from '../Layout'; interface User { diff --git a/src/components/MagicItineraryPage.tsx b/src/pages/MagicItineraryPage.tsx similarity index 97% rename from src/components/MagicItineraryPage.tsx rename to src/pages/MagicItineraryPage.tsx index b6501e9..95eacf0 100644 --- a/src/components/MagicItineraryPage.tsx +++ b/src/pages/MagicItineraryPage.tsx @@ -1,16 +1,16 @@ import React from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, Sparkles, MapPin, Clock, Users, Calendar, Star, Zap, Heart, Camera } from 'lucide-react'; -import { Button } from './ui/button'; -import { Card, CardContent } from './ui/card'; -import { Badge } from './ui/badge'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; -import { MobileAppSection } from './MobileAppSection'; -import { EnhancedTestimonials } from './EnhancedTestimonials'; -import { HowItWorks } from './HowItWorks'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { PersonalizedTourHero } from './PersonalizedTourHero'; +import { Button } from '../components/ui/button'; +import { Card, CardContent } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; +import { MobileAppSection } from '../components/MobileAppSection'; +import { EnhancedTestimonials } from '../components/EnhancedTestimonials'; +import { HowItWorks } from '../components/HowItWorks'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; +import { PersonalizedTourHero } from '../components/PersonalizedTourHero'; import { Layout } from '../Layout'; interface User { diff --git a/src/components/MelbournePage.tsx b/src/pages/MelbournePage.tsx similarity index 97% rename from src/components/MelbournePage.tsx rename to src/pages/MelbournePage.tsx index 4960380..a4dbe82 100644 --- a/src/components/MelbournePage.tsx +++ b/src/pages/MelbournePage.tsx @@ -1,22 +1,22 @@ import { motion, useAnimationControls, AnimatePresence } from 'motion/react'; -import { Button } from './ui/button'; +import { Button } from '../components/ui/button'; import { ArrowRight, Calendar, Thermometer, Eye, MapPin, Clock, Users, Ticket, Wand2, Plane, Sparkles } from 'lucide-react'; import { useEffect, useRef, useState } from 'react'; -import Navbar from './Navbar'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { MelbourneAttractions } from './MelbourneAttractions'; -import { MelbourneCardComparison } from './MelbourneCardComparison'; -import { MelbourneTourOverview } from './MelbourneTourOverview'; -import { MelbourneBlogs } from './MelbourneBlogs'; -import { CustomPostcards } from './CustomPostcards'; -import { EnhancedTestimonials } from './EnhancedTestimonials'; -import { MobileAppPromotion } from './MobileAppPromotion'; -import { MelbourneFAQ } from './MelbourneFAQ'; -import { Footer } from './Footer'; +import Navbar from '../components/Navbar'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; +import { MelbourneAttractions } from '../components/MelbourneAttractions'; +import { MelbourneCardComparison } from '../components/MelbourneCardComparison'; +import { MelbourneTourOverview } from '../components/MelbourneTourOverview'; +import { MelbourneBlogs } from '../components/MelbourneBlogs'; +import { CustomPostcards } from '../components/CustomPostcards'; +import { EnhancedTestimonials } from '../components/EnhancedTestimonials'; +import { MobileAppPromotion } from '../components/MobileAppPromotion'; +import { MelbourneFAQ } from '../components/MelbourneFAQ'; +import { Footer } from '../components/Footer'; // import { MinimalHeroBanner } from './MinimalHeroBanner'; import { Layout } from '../Layout'; -import { HeroBannerCarousel } from './HeroBannerCarousel'; -import { HotelEsimOffers } from './HotelEsimOffers'; +import { HeroBannerCarousel } from '../components/HeroBannerCarousel'; +import { HotelEsimOffers } from '../components/HotelEsimOffers'; interface User { email: string; diff --git a/src/components/OffersPage.tsx b/src/pages/OffersPage.tsx similarity index 97% rename from src/components/OffersPage.tsx rename to src/pages/OffersPage.tsx index c9c5fb1..4e1a26d 100644 --- a/src/components/OffersPage.tsx +++ b/src/pages/OffersPage.tsx @@ -1,20 +1,20 @@ import { useState } from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, Search, Filter, Star, MapPin, Clock, Tag, Heart, Share2, Copy, ChevronDown, ChevronRight, Check, Hotel, Plane, Building2, MapPinned, Home } from 'lucide-react'; -import { Button } from './ui/button'; -import { Input } from './ui/input'; -import { Card, CardContent, CardHeader, CardTitle } from './ui/card'; -import { Badge } from './ui/badge'; -import { Separator } from './ui/separator'; -import { Checkbox } from './ui/checkbox'; -import Navbar from './Navbar'; +import { Button } from '../components/ui/button'; +import { Input } from '../components/ui/input'; +import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import { Separator } from '../components/ui/separator'; +import { Checkbox } from '../components/ui/checkbox'; +import Navbar from '../components/Navbar'; // import SubNavbar from './SubNavbar'; -import { Footer } from './Footer'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { TrustSection } from './TrustSection'; -import { MobileAppSection } from './MobileAppSection'; -import { ReviewsSection } from './ReviewsSection'; -import { TrustedCompanies } from './TrustedCompanies'; +import { Footer } from '../components/Footer'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; +import { TrustSection } from '../components/TrustSection'; +import { MobileAppSection } from '../components/MobileAppSection'; +import { ReviewsSection } from '../components/ReviewsSection'; +import { TrustedCompanies } from '../components/TrustedCompanies'; import { Layout } from '../Layout'; interface OffersPageProps { diff --git a/src/components/PassesPage.tsx b/src/pages/PassesPage.tsx similarity index 98% rename from src/components/PassesPage.tsx rename to src/pages/PassesPage.tsx index cb87517..154ac1e 100644 --- a/src/components/PassesPage.tsx +++ b/src/pages/PassesPage.tsx @@ -1,14 +1,14 @@ import { useState } from 'react'; import { Check, X, Star, Shield, Clock, Smartphone, Download, QrCode, Heart, Users, Award, Headphones } from 'lucide-react'; -import { Button } from './ui/button'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card'; -import { RadioGroup, RadioGroupItem } from './ui/radio-group'; -import { Badge } from './ui/badge'; -import { EnhancedTestimonials } from './EnhancedTestimonials'; -import { ReviewsSection } from './ReviewsSection'; +import { Button } from '../components/ui/button'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/ui/card'; +import { RadioGroup, RadioGroupItem } from '../components/ui/radio-group'; +import { Badge } from '../components/ui/badge'; +import { EnhancedTestimonials } from '../components/EnhancedTestimonials'; +import { ReviewsSection } from '../components/ReviewsSection'; import { Layout } from '../Layout'; -import { LoginModal } from './LoginModal'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { LoginModal } from '../components/LoginModal'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { useAuth } from '../context/AuthContext'; import { useNavigate } from 'react-router-dom'; diff --git a/src/components/PostCardsPage.tsx b/src/pages/PostCardsPage.tsx similarity index 93% rename from src/components/PostCardsPage.tsx rename to src/pages/PostCardsPage.tsx index 8892510..2228d7f 100644 --- a/src/components/PostCardsPage.tsx +++ b/src/pages/PostCardsPage.tsx @@ -1,17 +1,17 @@ import React from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, Camera, Edit3, Upload, Heart, Star, Download, Share2 } from 'lucide-react'; -import { Button } from './ui/button'; -import { Card, CardContent } from './ui/card'; -import { Badge } from './ui/badge'; -import Navbar from './Navbar'; +import { Button } from '../components/ui/button'; +import { Card, CardContent } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import Navbar from '../components/Navbar'; // import SubNavbar from './SubNavbar'; -import { Footer } from './Footer'; -import { MobileAppSection } from './MobileAppSection'; -import { EnhancedTestimonials } from './EnhancedTestimonials'; -import { CustomPostcards } from './CustomPostcards'; -import { HowItWorks } from './HowItWorks'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { Footer } from '../components/Footer'; +import { MobileAppSection } from '../components/MobileAppSection'; +import { EnhancedTestimonials } from '../components/EnhancedTestimonials'; +import { CustomPostcards } from '../components/CustomPostcards'; +import { HowItWorks } from '../components/HowItWorks'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { Layout } from '../Layout'; // import front from '../assets/front.jpg' diff --git a/src/components/PrivacyPolicyPage.tsx b/src/pages/PrivacyPolicyPage.tsx similarity index 97% rename from src/components/PrivacyPolicyPage.tsx rename to src/pages/PrivacyPolicyPage.tsx index 5117319..9787bc2 100644 --- a/src/components/PrivacyPolicyPage.tsx +++ b/src/pages/PrivacyPolicyPage.tsx @@ -1,13 +1,13 @@ import { useState, useEffect } from 'react'; import { motion, useScroll, useTransform } from 'motion/react'; import { ArrowLeft } from 'lucide-react'; -import Navbar from './Navbar'; +import Navbar from '../components/Navbar'; // import { CitySubmenu } from './CitySubmenu'; -import { Footer } from './Footer'; -import { MobileAppSection } from './MobileAppSection'; -import { WhyChooseCityCards } from './WhyChooseCityCards'; -import { EnhancedTestimonials } from './EnhancedTestimonials'; -import { ReviewsSection } from './ReviewsSection'; +import { Footer } from '../components/Footer'; +import { MobileAppSection } from '../components/MobileAppSection'; +import { WhyChooseCityCards } from '../components/WhyChooseCityCards'; +import { EnhancedTestimonials } from '../components/EnhancedTestimonials'; +import { ReviewsSection } from '../components/ReviewsSection'; interface User { email: string; diff --git a/src/components/ProfilePage.tsx b/src/pages/ProfilePage.tsx similarity index 98% rename from src/components/ProfilePage.tsx rename to src/pages/ProfilePage.tsx index 0cb4dea..b7df501 100644 --- a/src/components/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -15,17 +15,17 @@ import { Badge as BadgeIcon, Camera } from 'lucide-react'; -import { Button } from './ui/button'; -import { Input } from './ui/input'; -import { Label } from './ui/label'; -import { Card, CardContent, CardHeader, CardTitle } from './ui/card'; -import { Separator } from './ui/separator'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; -import { Badge } from './ui/badge'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; -import { ImageWithFallback } from './figma/ImageWithFallback'; +import { Button } from '../components/ui/button'; +import { Input } from '../components/ui/input'; +import { Label } from '../components/ui/label'; +import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/card'; +import { Separator } from '../components/ui/separator'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '../components/ui/tabs'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../components/ui/select'; +import { Badge } from '../components/ui/badge'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; interface ProfilePageProps { onBackClick: () => void; diff --git a/src/components/SecureCheckoutPage.tsx b/src/pages/SecureCheckoutPage.tsx similarity index 97% rename from src/components/SecureCheckoutPage.tsx rename to src/pages/SecureCheckoutPage.tsx index 2b61d96..e987298 100644 --- a/src/components/SecureCheckoutPage.tsx +++ b/src/pages/SecureCheckoutPage.tsx @@ -1,15 +1,15 @@ import { useState } from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, Lock, Shield, CreditCard, Check, X, Tag } from 'lucide-react'; -import { Button } from './ui/button'; -import { Input } from './ui/input'; -import { Label } from './ui/label'; -import { Card, CardContent, CardHeader, CardTitle } from './ui/card'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs'; -import { Separator } from './ui/separator'; -import { Badge } from './ui/badge'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; +import { Button } from '../components/ui/button'; +import { Input } from '../components/ui/input'; +import { Label } from '../components/ui/label'; +import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/card'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '../components/ui/tabs'; +import { Separator } from '../components/ui/separator'; +import { Badge } from '../components/ui/badge'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; interface User { email: string; diff --git a/src/components/SuperSavingsPage.tsx b/src/pages/SuperSavingsPage.tsx similarity index 98% rename from src/components/SuperSavingsPage.tsx rename to src/pages/SuperSavingsPage.tsx index 6743d5d..eb8263d 100644 --- a/src/components/SuperSavingsPage.tsx +++ b/src/pages/SuperSavingsPage.tsx @@ -1,19 +1,19 @@ import { useState } from 'react'; import { motion } from 'motion/react'; import { ArrowLeft, Search, Filter, Star, MapPin, Clock, Tag, Heart, Share2, ChevronDown, ChevronRight, Check, Hotel, Plane, Building2, MapPinned, Home, Gift, Percent } from 'lucide-react'; -import { Button } from './ui/button'; -import { Input } from './ui/input'; -import { Card, CardContent, CardHeader, CardTitle } from './ui/card'; -import { Badge } from './ui/badge'; -import { Separator } from './ui/separator'; -import { Checkbox } from './ui/checkbox'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { TrustSection } from './TrustSection'; -import { MobileAppSection } from './MobileAppSection'; -import { ReviewsSection } from './ReviewsSection'; -import { TrustedCompanies } from './TrustedCompanies'; +import { Button } from '../components/ui/button'; +import { Input } from '../components/ui/input'; +import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/card'; +import { Badge } from '../components/ui/badge'; +import { Separator } from '../components/ui/separator'; +import { Checkbox } from '../components/ui/checkbox'; +import Navbar from '../components/Navbar'; +import { Footer } from '../components/Footer'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; +import { TrustSection } from '../components/TrustSection'; +import { MobileAppSection } from '../components/MobileAppSection'; +import { ReviewsSection } from '../components/ReviewsSection'; +import { TrustedCompanies } from '../components/TrustedCompanies'; import { Layout } from '../Layout'; interface SuperSavingsPageProps { diff --git a/src/components/WhatsIncluded.tsx b/src/pages/WhatsIncluded.tsx similarity index 98% rename from src/components/WhatsIncluded.tsx rename to src/pages/WhatsIncluded.tsx index a31114f..0bdec15 100644 --- a/src/components/WhatsIncluded.tsx +++ b/src/pages/WhatsIncluded.tsx @@ -1,11 +1,11 @@ import { ArrowRight, Hotel, Mail, MapPin, Sparkles, Ticket, Wifi } from 'lucide-react'; import { motion } from 'motion/react'; import { Layout } from '../Layout'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { SmartSaving } from './SmartSaving'; -import { Badge } from './ui/badge'; -import { Button } from './ui/button'; -import { WhatsIncludedHero } from './WhatsIncludedHero'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; +import { SmartSaving } from '../components/SmartSaving'; +import { Badge } from '../components/ui/badge'; +import { Button } from '../components/ui/button'; +import { WhatsIncludedHero } from '../components/WhatsIncludedHero'; interface User { email: string; From 169a29f63c85935c97126e8f78da0f9e2141c570 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Thu, 9 Apr 2026 15:29:17 +0530 Subject: [PATCH 08/21] change the route according to cityName instead of id --- src/components/CitySelectionDialog.tsx | 2 +- src/components/Navbar.tsx | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/CitySelectionDialog.tsx b/src/components/CitySelectionDialog.tsx index 203aa72..07f2ff2 100644 --- a/src/components/CitySelectionDialog.tsx +++ b/src/components/CitySelectionDialog.tsx @@ -40,7 +40,7 @@ export function CitySelectionDialog({ // ✅ Call the onCitySelect callback if provided (passing cityId) if (onCitySelect) { - onCitySelect(String(city.id)); + onCitySelect(String(city.cityName)); } else { // ✅ Default behavior: navigate to passes page navigate(`/passes?city=${encodeURIComponent(city.cityName)}`); diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 9374a6a..ca6e863 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -299,11 +299,11 @@ export default function Navbar({ setDialogSource('navbar'); }; - const handleCitySelectFromNavbar = (cityId: string) => { - console.log('City selected from navbar:', cityId); - onCityChange(cityId); + const handleCitySelectFromNavbar = (cityName: string) => { + console.log('City selected from navbar:', cityName); + onCityChange(cityName); - if (cityId.toLowerCase() === '1') { + if (cityName.toLowerCase() === 'melbourne') { setNavigationSource('melbourne'); navigate('/melbourne'); } else { @@ -334,11 +334,11 @@ export default function Navbar({ handleCloseCityDialog(); }; - const handleCitySelect = (cityId: string) => { + const handleCitySelect = (cityName: string) => { if (dialogSource === 'cta') { - handleCitySelectFromCTA(cityId); + handleCitySelectFromCTA(cityName); } else { - handleCitySelectFromNavbar(cityId); + handleCitySelectFromNavbar(cityName); } }; From f3b16e8e1a588b542ecde328d4bba79d7e521c97 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Thu, 9 Apr 2026 16:06:15 +0530 Subject: [PATCH 09/21] change the duration of the toaster to 2s --- src/{imports => components}/AfterLogin.tsx | 0 src/{imports => components}/BeforeLogin.tsx | 0 src/components/CTAButton.tsx | 4 ++-- src/main.tsx | 3 +-- 4 files changed, 3 insertions(+), 4 deletions(-) rename src/{imports => components}/AfterLogin.tsx (100%) rename src/{imports => components}/BeforeLogin.tsx (100%) diff --git a/src/imports/AfterLogin.tsx b/src/components/AfterLogin.tsx similarity index 100% rename from src/imports/AfterLogin.tsx rename to src/components/AfterLogin.tsx diff --git a/src/imports/BeforeLogin.tsx b/src/components/BeforeLogin.tsx similarity index 100% rename from src/imports/BeforeLogin.tsx rename to src/components/BeforeLogin.tsx diff --git a/src/components/CTAButton.tsx b/src/components/CTAButton.tsx index 9b80d9f..0c16431 100644 --- a/src/components/CTAButton.tsx +++ b/src/components/CTAButton.tsx @@ -1,6 +1,6 @@ import { motion } from 'motion/react'; -import BeforeLogin from '../imports/BeforeLogin'; -import AfterLogin from '../imports/AfterLogin'; +import BeforeLogin from './BeforeLogin'; +import AfterLogin from './AfterLogin'; interface User { email: string; diff --git a/src/main.tsx b/src/main.tsx index 6e91c69..b8619fc 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -6,11 +6,10 @@ import { Provider } from "react-redux"; import { store } from "./Redux/Store"; import { Toaster } from "sonner"; - createRoot(document.getElementById("root")!).render( - + From 439ad4b2640a7485659b4ea870299d2d9b23ec7e Mon Sep 17 00:00:00 2001 From: aryabenade Date: Mon, 13 Apr 2026 16:12:05 +0530 Subject: [PATCH 10/21] send accessToken to the backend after login --- index.html | 2 +- src/Redux/baseQuery.ts | 4 ++-- src/components/LoginModal.tsx | 9 +++++++-- src/context/AuthContext.tsx | 8 +++++++- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/index.html b/index.html index 000821d..daa01f8 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - CityCards Travel 22-8-2025 + CityCards Customer-web diff --git a/src/Redux/baseQuery.ts b/src/Redux/baseQuery.ts index 7ffc8d8..36c9339 100644 --- a/src/Redux/baseQuery.ts +++ b/src/Redux/baseQuery.ts @@ -8,9 +8,9 @@ export const baseQuery = fetchBaseQuery({ const token = localStorage.getItem("accessToken"); if (token) { headers.set("Authorization", `Bearer ${token}`); - // headers.set("access-token", token); + headers.set("access-token", token); } // headers.set("Content-Type", "application/json"); return headers; }, -}); +}); \ No newline at end of file diff --git a/src/components/LoginModal.tsx b/src/components/LoginModal.tsx index bff54d3..b4378d0 100644 --- a/src/components/LoginModal.tsx +++ b/src/components/LoginModal.tsx @@ -140,8 +140,10 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) { }).unwrap(); const userData = { + userId:response?.user?.id, email: response?.email || email, name: response?.name || email.split('@')[0].charAt(0).toUpperCase() + email.split('@')[0].slice(1), + accessToken:response?.accessToken, }; login(userData); @@ -285,11 +287,14 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) { onClick={() => { setStep('email'); setOtp(['', '', '', '', '', '']); + setHelperText("") setError(''); }} - className="w-full text-sm text-gray-600 hover:text-gray-800 font-poppins" + className="w-full text-sm text-gray-600 hover:text-gray-800 font-poppins cursor-pointer" > - Didn't receive OTP? Send again + Didn't receive OTP? + Send again + {/* Send again */} )}
diff --git a/src/context/AuthContext.tsx b/src/context/AuthContext.tsx index 8cabeb8..50dba2a 100644 --- a/src/context/AuthContext.tsx +++ b/src/context/AuthContext.tsx @@ -3,7 +3,9 @@ import { useNavigate } from 'react-router-dom'; interface User { email: string; - name: string + name: string; + accessToken:string; + userId:string; } interface AuthContextType { @@ -29,11 +31,15 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { const login = (userData: User) => { setUser(userData) localStorage.setItem("user", JSON.stringify(userData)) + localStorage.setItem("accessToken", userData?.accessToken) + localStorage.setItem("userId", userData?.userId) } const logout = () => { setUser(null) localStorage.removeItem("user") + localStorage.removeItem("accessToken") + localStorage.removeItem("userId") navigate("/") } From 0a0cb9f6f13dbe7e2359d5e87f4d5dee445faa3e Mon Sep 17 00:00:00 2001 From: aryabenade Date: Mon, 13 Apr 2026 16:13:09 +0530 Subject: [PATCH 11/21] add loading spinners --- src/Redux/services/fakeApi.service.ts | 19 ------------------- src/components/CitySelectionDialog.tsx | 9 ++++++++- src/components/LandingUpcomingCities.tsx | 11 +++++++++-- src/pages/AttractionDetailsPage.tsx | 17 ++++++++++++----- src/pages/AttractionsPage.tsx | 19 +++++++++++++------ 5 files changed, 42 insertions(+), 33 deletions(-) delete mode 100644 src/Redux/services/fakeApi.service.ts diff --git a/src/Redux/services/fakeApi.service.ts b/src/Redux/services/fakeApi.service.ts deleted file mode 100644 index b5f90ae..0000000 --- a/src/Redux/services/fakeApi.service.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' - -export const fakeApi = createApi({ - reducerPath: 'fakeApi', - baseQuery: fetchBaseQuery({ - baseUrl: " https://fakestoreapi.com", - - }), - endpoints: (builder) => ({ - getProducts: builder.query({ - query: () => ({ - url: 'products', - method: 'GET', - }), - }), - }), -}) - -export const { useGetProductsQuery} = fakeApi diff --git a/src/components/CitySelectionDialog.tsx b/src/components/CitySelectionDialog.tsx index 07f2ff2..3ede930 100644 --- a/src/components/CitySelectionDialog.tsx +++ b/src/components/CitySelectionDialog.tsx @@ -31,7 +31,14 @@ export function CitySelectionDialog({ const { data: cities, isLoading } = useGetCityListWithBannerQuery({ search }) if (isLoading) { - return
Loading...
+ return ( +
+
+
+

Loading...

+
+
+ ); } diff --git a/src/components/LandingUpcomingCities.tsx b/src/components/LandingUpcomingCities.tsx index 2d724a0..cd15393 100644 --- a/src/components/LandingUpcomingCities.tsx +++ b/src/components/LandingUpcomingCities.tsx @@ -113,8 +113,15 @@ export function LandingUpcomingCities() { const { data, isLoading } = useGetUpcomingCitiesQuery(listType) - if(isLoading){ - return
Loading...
+ if (isLoading) { + return ( +
+
+
+

Loading...

+
+
+ ); } const handleMouseDown = (e: React.MouseEvent) => { diff --git a/src/pages/AttractionDetailsPage.tsx b/src/pages/AttractionDetailsPage.tsx index 8998f12..34fd0d6 100644 --- a/src/pages/AttractionDetailsPage.tsx +++ b/src/pages/AttractionDetailsPage.tsx @@ -31,7 +31,14 @@ export function AttractionDetailsPage({ const { data: attraction, isLoading } = useGetAttractionDetailsByIdQuery(Number(attractionId)); if (isLoading) { - return
loading...
+ return ( +
+
+
+

Loading...

+
+
+ ); } return ( @@ -40,7 +47,7 @@ export function AttractionDetailsPage({ onSignInClick={onSignInClick} onSignOutClick={onSignOutClick} user={user} - // showCitySubmenu={false} + // showCitySubmenu={false} >
{/* Back Button */} @@ -82,7 +89,7 @@ export function AttractionDetailsPage({ {attraction.title} {' '} - Day Trip by {attraction.partner.businessName} + Day Trip by {attraction.partner.businessName}
@@ -99,10 +106,10 @@ export function AttractionDetailsPage({ {/* Gallery images */} - {attraction.attractionGalleries.slice().map((image:any) => ( + {attraction.attractionGalleries.slice().map((image: any) => (
diff --git a/src/pages/AttractionsPage.tsx b/src/pages/AttractionsPage.tsx index 588ea14..4a7d35b 100644 --- a/src/pages/AttractionsPage.tsx +++ b/src/pages/AttractionsPage.tsx @@ -230,7 +230,7 @@ export function AttractionsPage({ const [selectedPassType, setSelectedPassType] = useState(null); const cityId = 1 - + const { data: filterData, isLoading } = useGetAttractionFiltersQuery(cityId) const { data: attractions } = useGetCustomerAttractionsQuery({ cityId, // required @@ -239,9 +239,16 @@ export function AttractionsPage({ cardType: selectedPassType, // optional search, // optional }); - + if (isLoading) { - return
Loading...
+ return ( +
+
+
+

Loading...

+
+
+ ); } const handleAttractionClick = (attractionId: string) => { @@ -254,7 +261,7 @@ export function AttractionsPage({ const showingFrom = 1; const showingTo = Math.min(12, attractions?.length); const totalItems = attractions?.length; - + function handlePassTypeSelection(key: string, checked: boolean) { if (checked) { setSelectedPassType(key); // only keep the newly selected one @@ -403,7 +410,7 @@ export function AttractionsPage({ htmlFor={key} className="font-poppins text-sm text-[#414141] cursor-pointer flex-1 font-normal" > - {key==="selective_pass" ?"Selective":"Unlimited"} ({count as number}) + {key === "selective_pass" ? "Selective" : "Unlimited"} ({count as number})
))} @@ -493,7 +500,7 @@ export function AttractionsPage({ @@ -384,7 +454,7 @@ export function ProfilePage({ // Determine which pass type to show const hasUnlimitedPass = activePasses.some(pass => pass.type === 'Unlimited Pass'); const hasSelectivePass = activePasses.some(pass => pass.type === 'Flexi Pass'); - + if (hasUnlimitedPass) { return ( <> @@ -394,7 +464,7 @@ export function ProfilePage({ Melbourne Unlimited Card

- Unlimited access to 25+ attractions. Visit as many places as you want with one simple card. + Unlimited access to 25+ attractions. Visit as many places as you want with one simple card. Save up to 40% compared to individual tickets.

@@ -423,13 +493,13 @@ export function ProfilePage({ {/* Purchase CTA */}
- -
@@ -478,13 +548,13 @@ export function ProfilePage({ {/* Purchase CTA */}
- -
@@ -535,13 +605,13 @@ export function ProfilePage({ {/* Purchase CTA */}
- -
- +
Attractions: @@ -616,10 +686,10 @@ export function ProfilePage({ ))}
- + {/* Offers Button */}
-
- +
Attractions visited: @@ -689,7 +759,7 @@ export function ProfilePage({ >

My Itineraries

-
- +
@@ -728,8 +798,8 @@ export function ProfilePage({
- diff --git a/src/main.tsx b/src/main.tsx index b8619fc..cfff6d7 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -9,7 +9,7 @@ import { Toaster } from "sonner"; createRoot(document.getElementById("root")!).render( - + diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 91c0d1c..adf6491 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -28,6 +28,7 @@ import { Footer } from '../components/Footer'; import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { useGetUserProfileDetailsQuery, useUpdateUserProfileDetailsMutation } from '../Redux/services/profile.service'; import { toast } from 'sonner'; +import { useNavigate } from 'react-router-dom'; interface ProfilePageProps { onBackClick: () => void; @@ -57,18 +58,6 @@ interface ProfilePageProps { currentPage: string; } -// Mock user data -const mockUserData = { - firstName: 'John', - lastName: 'Doe', - email: 'john.doe@example.com', - phone: '+1 (555) 123-4567', - country: 'us', - address: '123 Main Street', - city: 'New York', - postalCode: '10001' -}; - // Mock passes data const mockPasses = [ { @@ -174,8 +163,11 @@ export function ProfilePage({ postalCode: '' }); + const navigate = useNavigate() const userId = localStorage.getItem("userId") const { data: userDetails, isLoading } = useGetUserProfileDetailsQuery(userId) + const [updateUserProfileDetails, { isLoading: savingChanges }] = useUpdateUserProfileDetailsMutation(); + const { data: passes, isLoading: loadingPasses } = useGetUserProfileDetailsQuery(userId) useEffect(() => { if (userDetails) { @@ -194,7 +186,6 @@ export function ProfilePage({ }, [userDetails]) - const [updateUserProfileDetails] = useUpdateUserProfileDetailsMutation(); const handleInputChange = (field: string, value: string) => { setFormData(prev => ({ ...prev, [field]: value })); @@ -215,7 +206,7 @@ export function ProfilePage({ const activePasses = mockPasses.filter(pass => pass.status === 'active'); const expiredPasses = mockPasses.filter(pass => pass.status === 'expired'); - if (isLoading) { + if (isLoading && loadingPasses) { return (
@@ -259,8 +250,8 @@ export function ProfilePage({
{/* Back Button */} navigate(-1)} + className="flex items-center gap-2 text-gray-600 hover:text-gray-900 mb-6 transition-colors duration-200 cursor-pointer" initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} transition={{ duration: 0.5 }} @@ -434,7 +425,7 @@ export function ProfilePage({ onClick={handleSaveProfile} className="w-full bg-gradient-to-r from-primary to-secondary hover:from-primary/90 hover:to-secondary/90 text-white font-normal py-3 font-poppins cursor-pointer" > - Save Changes + {savingChanges ? "Saving Changes..." : "Save Changes"} From 729f9ffa0d2273b42ff8997bdb6f9ce8701961dc Mon Sep 17 00:00:00 2001 From: aryabenade Date: Mon, 13 Apr 2026 22:04:14 +0530 Subject: [PATCH 14/21] solve errors in tsconfig --- src/pages/ProfilePage.tsx | 21 +++------------------ tsconfig.json | 4 ++-- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index adf6491..d8f91c8 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -222,28 +222,13 @@ export function ProfilePage({ {/* Navbar */} { }} - onHomeClick={onHomeClick} - onMelbourneClick={onMelbourneClick} - onPassesClick={onPassesClick} - onCheckoutClick={onCheckoutClick} onSignInClick={onSignInClick} onSignOutClick={onSignOutClick} - onAttractionsClick={onAttractionsClick} - onBlogsClick={onBlogsClick} - onHowItWorksClick={onHowItWorksClick} - onFAQClick={onFAQClick} - onPrivacyPolicyClick={onPrivacyPolicyClick} - onAboutUsClick={onAboutUsClick} - onProfileClick={onProfileClick} - onCityCardsClick={onCityCardsClick} - onMagicItineraryClick={onMagicItineraryClick} - onPostCardsClick={onPostCardsClick} - onOffersClick={onOffersClick} - currentPage={currentPage} isUserSignedIn={true} user={{ email: "user@example.com", name: "John Doe" }} - /> + onCityChange={function (city: string): void { + throw new Error('Function not implemented.'); + }} /> {/* Header Section */}
diff --git a/tsconfig.json b/tsconfig.json index 3d0a51a..e444793 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,12 +5,12 @@ "lib": ["DOM", "DOM.Iterable", "ESNext"], "allowJs": false, "skipLibCheck": true, - "esModuleInterop": false, + "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "ESNext", - "moduleResolution": "Node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, From dbdf974475cdc34d54301f71918e9ea2eda548e4 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Tue, 14 Apr 2026 18:14:13 +0530 Subject: [PATCH 15/21] save the selected city to localstorage --- src/AppRouter.tsx | 6 +- src/components/CitySelectionDialog.tsx | 11 +- src/components/Navbar.tsx | 26 ++-- src/pages/AttractionsPage.tsx | 192 +------------------------ 4 files changed, 33 insertions(+), 202 deletions(-) diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx index 28f26f5..52bf4f0 100644 --- a/src/AppRouter.tsx +++ b/src/AppRouter.tsx @@ -88,11 +88,11 @@ export function AppRouter({ } /> {/* Home Route */} - - } /> + } /> {/* Passes Route */} {/* Attractions Routes */} - diff --git a/src/components/CitySelectionDialog.tsx b/src/components/CitySelectionDialog.tsx index 3ede930..e4d6503 100644 --- a/src/components/CitySelectionDialog.tsx +++ b/src/components/CitySelectionDialog.tsx @@ -20,6 +20,9 @@ interface CitySelectionDialogProps { onCitySelect?: (cityId: string) => void; // ✅ Updated to pass cityId } +export const slugify = (name: string | null) => + name?.toLowerCase().replace(/\s+/g, '-'); + export function CitySelectionDialog({ isOpen, onClose, @@ -41,13 +44,17 @@ export function CitySelectionDialog({ ); } - const handleCityClick = (city: City) => { console.log('Selected city:', city.cityName); // ✅ Call the onCitySelect callback if provided (passing cityId) if (onCitySelect) { - onCitySelect(String(city.cityName)); + // onCitySelect(String(city.cityName)); + // navigate(`/${city.cityName}/${city.id}`) + + navigate(`/${slugify(city.cityName)}/${city.id}`); + localStorage.setItem("cityId", String(city.id)) + localStorage.setItem("cityName", String(city.cityName)) } else { // ✅ Default behavior: navigate to passes page navigate(`/passes?city=${encodeURIComponent(city.cityName)}`); diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index ca6e863..36980ca 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -8,7 +8,7 @@ import { ImageWithFallback } from './figma/ImageWithFallback'; import { CTAButton } from './CTAButton'; import logoImage from '../assets/cit-logo.png'; import melbourneLogo from '../assets/melbourne-logo.png'; -import { CitySelectionDialog } from './CitySelectionDialog'; +import { CitySelectionDialog, slugify } from './CitySelectionDialog'; import { useAuth } from '../context/AuthContext'; import { LoginModal } from './LoginModal'; @@ -104,6 +104,8 @@ export default function Navbar({ } }; + const cityId = localStorage.getItem("cityId") + const cityName = localStorage.getItem("cityName") // More flexible navigation configuration const navigationConfig = { @@ -129,20 +131,20 @@ export default function Navbar({ isShared: false }, // Position 4 - Shared item - { - label: 'Your Card', - path: '/passes', - isShared: true, - landingLabel: 'Your Card', - melbourneLabel: 'Your Card' - }, + // { + // label: 'Your Card', + // path: '/passes', + // isShared: true, + // landingLabel: 'Your Card', + // melbourneLabel: 'Your Card' + // }, // Position 5 { label: 'FAQ', path: '/faq', isShared: false }, - { + { label: 'Your Postcard', path: '/postcards', isShared: true, @@ -154,7 +156,7 @@ export default function Navbar({ // Position 1 { label: 'Attractions', - path: '/attractions', + path: `/${slugify(cityName)}/${cityId}/attractions`, isShared: false }, // Position 2 @@ -185,7 +187,7 @@ export default function Navbar({ landingLabel: 'Your Card', melbourneLabel: 'Your Card' }, - { + { label: 'Your Postcard', path: '/postcards', isShared: true, @@ -559,7 +561,7 @@ export default function Navbar({ whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > - + void; onSignOutClick?: () => void; @@ -229,7 +48,10 @@ export function AttractionsPage({ const [selectedCategory, setSelectedCategory] = useState(null); const [selectedPassType, setSelectedPassType] = useState(null); - const cityId = 1 + const { cityId } = useParams() + const cityName = localStorage.getItem("cityName") + + console.log(cityName) const { data: filterData, isLoading } = useGetAttractionFiltersQuery(cityId) const { data: attractions } = useGetCustomerAttractionsQuery({ @@ -308,12 +130,12 @@ export function AttractionsPage({

Discover{' '} - Melbourne's{' '} + {cityName}'s{' '} Best{' '} Attractions

- Skip the lines and explore Melbourne's most iconic destinations with your CityCard pass + Skip the lines and explore {cityName}'s most iconic destinations with your CityCard pass

{/* City Card Promotional Banner */} @@ -423,7 +245,7 @@ export function AttractionsPage({
{/* Header */}
-

Attractions in Melbourne

+

Attractions in {cityName}

{/* Results count */}

Showing {showingFrom}-{showingTo} of {totalItems} Item(s) From 743462d088ab14f73b2c465d9bae2150933874ad Mon Sep 17 00:00:00 2001 From: aryabenade Date: Wed, 15 Apr 2026 11:07:13 +0530 Subject: [PATCH 16/21] make the card comparison section in selected city page dynamic --- src/Redux/services/cities.service.ts | 6 +- src/components/MelbourneCardComparison.tsx | 156 ++++++++++++++------- src/components/Navbar.tsx | 19 ++- src/pages/MelbournePage.tsx | 26 ++-- 4 files changed, 137 insertions(+), 70 deletions(-) diff --git a/src/Redux/services/cities.service.ts b/src/Redux/services/cities.service.ts index e1dfb06..9754016 100644 --- a/src/Redux/services/cities.service.ts +++ b/src/Redux/services/cities.service.ts @@ -23,8 +23,12 @@ export const citiesApi = createApi({ query: (listType) => `/cities/list/all?listType=${listType}`, + }), + + getSelectedCityDetails:builder.query({ + query: (cityId) => `/website/${cityId}`, }) }), }); -export const { useGetCityListWithBannerQuery,useGetUpcomingCitiesQuery } = citiesApi; \ No newline at end of file +export const { useGetCityListWithBannerQuery,useGetUpcomingCitiesQuery,useGetSelectedCityDetailsQuery } = citiesApi; \ No newline at end of file diff --git a/src/components/MelbourneCardComparison.tsx b/src/components/MelbourneCardComparison.tsx index d12ac03..e32bdaa 100644 --- a/src/components/MelbourneCardComparison.tsx +++ b/src/components/MelbourneCardComparison.tsx @@ -3,52 +3,52 @@ import { Check, X, Star, Users, MapPin, Calendar, Clock, Zap, Eye } from 'lucide import { Button } from './ui/button'; import { motion } from 'motion/react'; -const cardOptions = [ - { - id: 'selective', - name: 'Flexi Card', - subtitle: 'Pick 5-10 things to do from a choice of 102 attractions tours and activities', - priceRange: '$89-159', - duration: '3-7 days', - popular: false, - color: 'from-blue-500 to-cyan-500', - features: { - passCategory: 'Selective Card', - accessToAttractions: true, - entryToAttractions: true, - accessToExperiences: true, - entryToSites: true, - accessToVenues: false, - entryToEvents: 'Pass Category', - accessToLocations: 'Pass Category', - entryToActivities: true, - accessToExhibits: true, - entryToActivitiesSecond: true - } - }, - { - id: 'unlimited', - name: 'Melbourne Unlimited Card', - subtitle: 'Pick 5-30 things to do from a choice of 102 attractions tours and activities', - priceRange: '$159-299', - duration: '3-7 days', - popular: true, - color: 'from-purple-500 to-pink-500', - features: { - passCategory: 'Pass Category', - accessToAttractions: true, - entryToAttractions: true, - accessToExperiences: true, - entryToSites: true, - accessToVenues: true, - entryToEvents: 'Pass Category', - accessToLocations: 'Pass Category', - entryToActivities: true, - accessToExhibits: true, - entryToActivitiesSecond: true - } - } -]; +// const cardOptions = [ +// { +// id: 'selective', +// name: 'Flexi Card', +// subtitle: 'Pick 5-10 things to do from a choice of 102 attractions tours and activities', +// priceRange: '$89-159', +// duration: '3-7 days', +// popular: false, +// color: 'from-blue-500 to-cyan-500', +// features: { +// passCategory: 'Selective Card', +// accessToAttractions: true, +// entryToAttractions: true, +// accessToExperiences: true, +// entryToSites: true, +// accessToVenues: false, +// entryToEvents: 'Pass Category', +// accessToLocations: 'Pass Category', +// entryToActivities: true, +// accessToExhibits: true, +// entryToActivitiesSecond: true +// } +// }, +// { +// id: 'unlimited', +// name: 'Melbourne Unlimited Card', +// subtitle: 'Pick 5-30 things to do from a choice of 102 attractions tours and activities', +// priceRange: '$159-299', +// duration: '3-7 days', +// popular: true, +// color: 'from-purple-500 to-pink-500', +// features: { +// passCategory: 'Pass Category', +// accessToAttractions: true, +// entryToAttractions: true, +// accessToExperiences: true, +// entryToSites: true, +// accessToVenues: true, +// entryToEvents: 'Pass Category', +// accessToLocations: 'Pass Category', +// entryToActivities: true, +// accessToExhibits: true, +// entryToActivitiesSecond: true +// } +// } +// ]; const features = [ { key: 'passCategory', label: 'Pass Category', icon: Star }, @@ -71,11 +71,59 @@ const FeatureIcon = ({ feature }: { feature: typeof features[0] }) => { interface MelbourneCardComparisonProps { onCheckoutClick?: () => void; + cards: any[] } -export function MelbourneCardComparison({ onCheckoutClick }: MelbourneCardComparisonProps) { +export function MelbourneCardComparison({ onCheckoutClick,cards }: MelbourneCardComparisonProps) { const [selectedCard, setSelectedCard] = useState('unlimited'); + const cardOptions = [ + { + id: cards[0]?.id, + name: cards[0]?.title, + subtitle: cards[0]?.description, + priceRange: `$${cards[0]?.adultPrice}`, + duration: '3-7 days', + popular: false, + color: 'from-blue-500 to-cyan-500', + features: { + passCategory: 'Selective Card', + accessToAttractions: true, + entryToAttractions: true, + accessToExperiences: true, + entryToSites: true, + accessToVenues: false, + entryToEvents: 'Pass Category', + accessToLocations: 'Pass Category', + entryToActivities: true, + accessToExhibits: true, + entryToActivitiesSecond: true + } + }, + { + id: cards[1]?.id, + name: cards[1]?.title, + subtitle: cards[1]?.description, + priceRange: `$${cards[1]?.adultPrice}`, + duration: '3-7 days', + popular: true, + color: 'from-purple-500 to-pink-500', + features: { + passCategory: 'Pass Category', + accessToAttractions: true, + entryToAttractions: true, + accessToExperiences: true, + entryToSites: true, + accessToVenues: true, + entryToEvents: 'Pass Category', + accessToLocations: 'Pass Category', + entryToActivities: true, + accessToExhibits: true, + entryToActivitiesSecond: true + } + } + ]; + const renderFeatureValue = (value: boolean | string, cardId: string) => { if (typeof value === 'boolean') { return value ? ( @@ -92,7 +140,7 @@ export function MelbourneCardComparison({ onCheckoutClick }: MelbourneCardCompar

); } - + return (
{value} @@ -122,17 +170,17 @@ export function MelbourneCardComparison({ onCheckoutClick }: MelbourneCardCompar Choose Your Adventure
- +

Buy {' '} Now

- +

- Melbourne is a must-visit cultural epicenter, and this spectacular trip unlocks - your access around the city in one easy. Save over the cost of visiting Melbourne's + Melbourne is a must-visit cultural epicenter, and this spectacular trip unlocks + your access around the city in one easy. Save over the cost of visiting Melbourne's landmarks, have lunch at Phi Phi Leh, snorkel at Bamboo Island, and visit Monkey Beach.

@@ -179,7 +227,7 @@ export function MelbourneCardComparison({ onCheckoutClick }: MelbourneCardCompar {feature.label}
- + {cardOptions.map((card) => (
{renderFeatureValue(card.features[feature.key as keyof typeof card.features], card.id)} @@ -196,7 +244,7 @@ export function MelbourneCardComparison({ onCheckoutClick }: MelbourneCardCompar
Ready to explore?
Compare features above
- + {cardOptions.map((card) => (
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 36980ca..9190dfc 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -91,6 +91,10 @@ export default function Navbar({ const { user, login, logout } = useAuth(); // from AuthContext + const cityLogo = sessionStorage.getItem("cityLogo") || "" + + const baseUrl = import.meta.env.VITE_BASE_URL; + const protectedPaths = ["/passes", "/whats-included", "/", "/melbourne"]; const handleOpenLoginModal = () => { @@ -563,8 +567,7 @@ export default function Navbar({ >
{/* Enhanced Navigation Items with source tracking */} {navigationItems.map((item) => { @@ -625,12 +628,13 @@ export default function Navbar({ onClick={handleOpenCityDialogFromNavbar} > - {!activeCity || activeCity === 'shared' + {/* {!activeCity || activeCity === 'shared' ? 'City' : ['landing', 'landingpage'].includes(activeCity.toLowerCase()) ? 'City' : activeCity.charAt(0).toUpperCase() + activeCity.slice(1) - } + } */} + {cityName ? cityName : "City"} @@ -786,10 +790,11 @@ export default function Navbar({ trigger={
- {activeCity && activeCity !== 'shared' ? + {/* {activeCity && activeCity !== 'shared' ? activeCity.charAt(0).toUpperCase() + activeCity.slice(1) : currentSource === 'melbourne' ? 'Melbourne' : 'Select City' - } + } */} + {cityName ? cityName : "City"}
diff --git a/src/pages/MelbournePage.tsx b/src/pages/MelbournePage.tsx index a4dbe82..64c8c11 100644 --- a/src/pages/MelbournePage.tsx +++ b/src/pages/MelbournePage.tsx @@ -1,8 +1,7 @@ import { motion, useAnimationControls, AnimatePresence } from 'motion/react'; import { Button } from '../components/ui/button'; import { ArrowRight, Calendar, Thermometer, Eye, MapPin, Clock, Users, Ticket, Wand2, Plane, Sparkles } from 'lucide-react'; -import { useEffect, useRef, useState } from 'react'; -import Navbar from '../components/Navbar'; +import { useState } from 'react'; import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { MelbourneAttractions } from '../components/MelbourneAttractions'; import { MelbourneCardComparison } from '../components/MelbourneCardComparison'; @@ -12,11 +11,10 @@ import { CustomPostcards } from '../components/CustomPostcards'; import { EnhancedTestimonials } from '../components/EnhancedTestimonials'; import { MobileAppPromotion } from '../components/MobileAppPromotion'; import { MelbourneFAQ } from '../components/MelbourneFAQ'; -import { Footer } from '../components/Footer'; -// import { MinimalHeroBanner } from './MinimalHeroBanner'; import { Layout } from '../Layout'; import { HeroBannerCarousel } from '../components/HeroBannerCarousel'; import { HotelEsimOffers } from '../components/HotelEsimOffers'; +import { useGetSelectedCityDetailsQuery } from '../Redux/services/cities.service'; interface User { email: string; @@ -149,6 +147,18 @@ export function MelbournePage({ const [currentCardIndex, setCurrentCardIndex] = useState(0); const [isAnimating, setIsAnimating] = useState(false); + const cityId = localStorage.getItem("cityId") + + const { data: cityDetails, isLoading: loadingCityDetails } = useGetSelectedCityDetailsQuery(cityId) + + + if (loadingCityDetails) { + return
Loading...
+ } + + const cards = cityDetails?.city?.cards + sessionStorage.setItem("cityLogo", String(cityDetails?.city?.cityIconPath)) + const currentCard = itineraryCards[currentCardIndex]; const nextCard = itineraryCards[(currentCardIndex + 1) % itineraryCards.length]; const thirdCard = itineraryCards[(currentCardIndex + 2) % itineraryCards.length]; @@ -257,12 +267,12 @@ export function MelbournePage({ {/* Attractions Section */}
- +
{/* Pass Comparison */}
- +
{/* Tour Overview */} @@ -740,8 +750,8 @@ export function MelbournePage({ }, 400); }} className={`font-poppins group relative transition-all duration-300 px-4 py-2 rounded-full font-medium ${idx === currentCardIndex - ? 'bg-gradient-to-r from-primary to-orange-500 text-white shadow-lg scale-110' - : 'bg-white/80 backdrop-blur-sm text-gray-600 hover:text-primary hover:bg-white border border-gray-200 hover:border-primary/30 hover:scale-105' + ? 'bg-gradient-to-r from-primary to-orange-500 text-white shadow-lg scale-110' + : 'bg-white/80 backdrop-blur-sm text-gray-600 hover:text-primary hover:bg-white border border-gray-200 hover:border-primary/30 hover:scale-105' }`} whileHover={{ y: -2 }} whileTap={{ scale: 0.95 }} From 929359d7e214b74e4884575450a9dc32cef1446a Mon Sep 17 00:00:00 2001 From: aryabenade Date: Wed, 15 Apr 2026 11:34:44 +0530 Subject: [PATCH 17/21] make loading spinner component and pass it to all loading states --- src/components/CitySelectionDialog.tsx | 8 ++------ src/components/LandingUpcomingCities.tsx | 8 ++------ src/components/LoadingSpinner.tsx | 12 ++++++++++++ src/components/MelbourneCardComparison.tsx | 2 +- src/pages/AttractionDetailsPage.tsx | 8 ++------ src/pages/AttractionsPage.tsx | 8 ++------ src/pages/MelbournePage.tsx | 9 ++++++--- src/pages/ProfilePage.tsx | 8 ++------ 8 files changed, 29 insertions(+), 34 deletions(-) create mode 100644 src/components/LoadingSpinner.tsx diff --git a/src/components/CitySelectionDialog.tsx b/src/components/CitySelectionDialog.tsx index e4d6503..e087e4e 100644 --- a/src/components/CitySelectionDialog.tsx +++ b/src/components/CitySelectionDialog.tsx @@ -7,6 +7,7 @@ import { Input } from './ui/input'; import { motion, AnimatePresence } from 'motion/react'; import { ImageWithFallback } from './figma/ImageWithFallback'; import { useGetCityListWithBannerQuery } from '../Redux/services/cities.service'; +import LoadingSpinner from './LoadingSpinner'; interface City { id: number; @@ -35,12 +36,7 @@ export function CitySelectionDialog({ if (isLoading) { return ( -
-
-
-

Loading...

-
-
+ ); } diff --git a/src/components/LandingUpcomingCities.tsx b/src/components/LandingUpcomingCities.tsx index cd15393..c5c70a1 100644 --- a/src/components/LandingUpcomingCities.tsx +++ b/src/components/LandingUpcomingCities.tsx @@ -4,6 +4,7 @@ import { Button } from './ui/button'; import { useRef, useState, useEffect } from 'react'; import Image592Traced from '../imports/Image592Traced-5025-559'; import { useGetUpcomingCitiesQuery } from '../Redux/services/cities.service'; +import LoadingSpinner from './LoadingSpinner'; // const upcomingCities = [ // { @@ -115,12 +116,7 @@ export function LandingUpcomingCities() { if (isLoading) { return ( -
-
-
-

Loading...

-
-
+ ); } diff --git a/src/components/LoadingSpinner.tsx b/src/components/LoadingSpinner.tsx new file mode 100644 index 0000000..bde2706 --- /dev/null +++ b/src/components/LoadingSpinner.tsx @@ -0,0 +1,12 @@ +const LoadingSpinner = () => { + return ( +
+
+
+

Loading...

+
+
+ ) +} + +export default LoadingSpinner \ No newline at end of file diff --git a/src/components/MelbourneCardComparison.tsx b/src/components/MelbourneCardComparison.tsx index e32bdaa..5258954 100644 --- a/src/components/MelbourneCardComparison.tsx +++ b/src/components/MelbourneCardComparison.tsx @@ -253,7 +253,7 @@ export function MelbourneCardComparison({ onCheckoutClick,cards }: MelbourneCard
- -
+ Start Saving Now + +
+
- {/* Decorative elements */} -
-
- + {/* Decorative elements */} +
+
+ - {/* Trusted By Companies Section */} -
-
-
-
-

- Trusted by the - world's best -

-

- Join thousands of savvy travelers enjoying massive savings on premium experiences -

-
- -
-
-
- - {/* Featured Super Savings Section */} -
-
- -

- Featured{' '} - - Super Savings - + {/* Trusted By Companies Section */} +
+
+
+
+

+ Trusted by the + world's best

-

- Check out our biggest discounts and start saving on premium experiences +

+ Join thousands of savvy travelers enjoying massive savings on premium experiences

- +
+ +
+
+
-
-
- {/* Left Sidebar - Filters */} -
- -
- {/* Search by header */} -
-
-

Search by

-
+ {/* Featured Super Savings Section */} +
+
+ +

+ Featured{' '} + + Super Savings + +

+

+ Check out our biggest discounts and start saving on premium experiences +

+
- {/* Filter categories */} -
- {filterCategories.map(category => ( -
- toggleCategory(category.value)} - className="border-gray-400" - /> - -
- ))} -
+
+
+ {/* Left Sidebar - Filters */} +
+ +
+ {/* Search by header */} +
+
+

Search by

- -
- {/* Main Content */} -
- {/* Breadcrumb */} -
-

- {fromSource === 'passes' ? ( - <> - My Profile{'>'}My passes{'>'} - Super Savings - - ) : ( - <> - Our Products{'>'} - Super Savings - - )} -

-
- - {/* Header Section */} -
-

- Super Savings -

-

- Exclusive discounts up to 65% off on premium experiences -

-
- - {/* Savings Grid */} -
- {displayedSavings.map((saving, index) => ( - - - {/* Image */} -
- - - - {/* Discount Badge */} -
- {saving.discount} -
-
- - - {/* Business Name */} -
-
- {saving.business} -
- - {/* Title */} -

- {saving.title} -

- - {/* Saved Amount Display */} -
-
- - - {saving.savedAmount} - -
-
-
-
-
- ))} -
- - {/* Minimal Pagination */} -
-
- - -
- {[1, 2, 3].map((page) => ( - - ))} -
- - + {category.categoryName} ({category.offerCount}) + +
+ ))}
+
+
+ + {/* Main Content */} +
+ {/* Breadcrumb */} +
+

+ {fromSource === 'passes' ? ( + <> + My Profile{'>'}My passes{'>'} + Super Savings + + ) : ( + <> + Our Products{'>'} + Super Savings + + )} +

+
+ + {/* Header Section */} +
+

+ Super Savings +

+

+ Exclusive discounts up to 65% off on premium experiences +

+
+ + {/* Offers Grid */} +
+ {offers.map((offer: any, index: number) => ( + + + {/* Image */} +
+ + {/* */} + + {/* Discount Badge */} +
+ {offer.offerCode} +
+
+ + + {/* Business Name */} +
+
+ {offer.partnerName} +
+ + {/* Title */} +

+ {offer.description} +

+ + {/* Saved Amount Display */} +
+
+ {/* */} + + {offer.title} + +
+
+
+
+
+ ))} +
+ + {/* Minimal Pagination */} +
+
+ {/* Previous button */} + + + {/* Page numbers */} +
+ {Array.from({ length: totalPages }, (_, i) => i + 1).map(p => ( + + ))} +
+ + {/* Next button */} + +
- -
- -
-
- {/* How It Works Section */} -
- {/* Background decorative elements */} -
-
- -
- +
-
+
+

- {/* Categories Section */} -
- {/* Abstract Travel Patterns */} -
+ {/* How It Works Section */} +
+ {/* Background decorative elements */} +
+
-
- {/* Section Header */} -
- -
-
- Explore Collections -
-

- Curated for the Modern Traveler -

-

- Discover exclusive savings across our most sought-after travel categories. -

-
- - - - +
+ +
+
+ Simple Process +
+

+ Start Saving in Minutes +

+

+ Your journey to smarter travel and bigger savings begins with three simple steps. +

+
- {/* Bento Grid Layout */} -
- {categoriesData.map((category, index) => ( +
+ {/* Connecting line for desktop */} +
+ +
+ {[ + { + step: '01', + title: 'Unlock Access', + description: 'Get your CityCards pass to instantly activate membership perks.', + icon: MapPinned + }, + { + step: '02', + title: 'Discover Deals', + description: 'Browse exclusive offers on hotels, flights, and experiences.', + icon: Search + }, + { + step: '03', + title: 'Enjoy Savings', + description: 'Redeem discounts instantly and watch your travel budget grow.', + icon: Percent + } + ].map((item, index) => ( -
+ {/* Icon Container */} +
+
+
+ +
+
+ {index + 1} +
+
+ +

+ {item.title} +

+

+ {item.description} +

+
+
+ ))} +
+
+ + +
+
+ + {/* Categories Section */} +
+ {/* Abstract Travel Patterns */} +
+ +
+ {/* Section Header */} +
+ +
+
+ Explore Collections +
+

+ Curated for the Modern Traveler +

+

+ Discover exclusive savings across our most sought-after travel categories. +

+
+ + + + +
+ + {/* Bento Grid Layout */} +
+ {categoriesData.map((category, index) => ( + +
- {/* Background Gradient Hover */} -
+ > + {/* Background Gradient Hover */} +
- {/* Large Watermark Icon for visual depth */} - + {/* Large Watermark Icon for visual depth */} + -
-
-
+
+
- -
- - {category.savings} - +
- -

- {category.title} -

-

- {category.description} -

+ + {category.savings} +
-
- Explore Deals - -
+

+ {category.title} +

+

+ {category.description} +

- - ))} -
- {/* Mobile View All Button */} -
- -
+
+ Explore Deals + +
+
+
+ ))}
-
- {/* Access Your CityCards Section */} -
- -
+ {/* Mobile View All Button */} +
+ +
+
+ + + {/* Access Your CityCards Section */} +
+ +
+ +
+ + ); -
- - ); - } } From ce3c095727b588b728fc7f1038deb51346c95343 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Fri, 17 Apr 2026 14:45:03 +0530 Subject: [PATCH 20/21] show cards on the passes page coming from backend --- src/AppRouter.tsx | 4 +- src/components/Navbar.tsx | 12 +- src/pages/CheckoutPage.tsx | 2 - src/pages/PassesPage.tsx | 288 ++++++++++++++++++++++--------------- 4 files changed, 184 insertions(+), 122 deletions(-) diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx index b3e28a0..12945be 100644 --- a/src/AppRouter.tsx +++ b/src/AppRouter.tsx @@ -106,7 +106,7 @@ export function AppRouter({ } /> {/* Attractions Routes */} - @@ -265,7 +265,7 @@ export function AppRouter({ } /> - diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 5568776..7e421a3 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -162,25 +162,25 @@ export default function Navbar({ // Position 1 { label: 'Attractions', - path: `/${slugify(cityName)}/attractions`, + path: `/attractions`, isShared: false }, // Position 2 { label: 'Magic Itinerary', - path: `/${slugify(cityName)}/magic-itinerary`, + path: `/magic-itinerary`, isShared: false }, // Position 3 { label: 'Super Savings', - path: `/${slugify(cityName)}/super-savings`, + path: `/super-savings`, isShared: false }, // Position 4 - Shared item { label: 'How It Works', - path: `/${slugify(cityName)}/how-it-works`, + path: `/how-it-works`, isShared: true, landingLabel: 'Discover', melbourneLabel: 'How It Works' @@ -188,14 +188,14 @@ export default function Navbar({ // Position 5 - Shared item { label: 'Your Card', - path: `/${slugify(cityName)}/passes`, + path: `/passes`, isShared: true, landingLabel: 'Your Card', melbourneLabel: 'Your Card' }, { label: 'Your Postcard', - path: `/${slugify(cityName)}/postcards`, + path: `/postcards`, isShared: true, landingLabel: 'Your Postcard', melbourneLabel: 'Your Postcard' diff --git a/src/pages/CheckoutPage.tsx b/src/pages/CheckoutPage.tsx index f34b555..235aa7e 100644 --- a/src/pages/CheckoutPage.tsx +++ b/src/pages/CheckoutPage.tsx @@ -12,8 +12,6 @@ import { Checkbox } from '../components/ui/checkbox'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../components/ui/select'; import { Badge } from '../components/ui/badge'; import { Textarea } from '../components/ui/textarea'; -import Navbar from './Navbar'; -import { Footer } from './Footer'; import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { Layout } from '../Layout'; diff --git a/src/pages/PassesPage.tsx b/src/pages/PassesPage.tsx index 154ac1e..171ca2e 100644 --- a/src/pages/PassesPage.tsx +++ b/src/pages/PassesPage.tsx @@ -11,6 +11,8 @@ import { LoginModal } from '../components/LoginModal'; import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { useAuth } from '../context/AuthContext'; import { useNavigate } from 'react-router-dom'; +import { useGetSelectedCityDetailsQuery } from '../Redux/services/cities.service'; +import LoadingSpinner from '../components/LoadingSpinner'; interface PassesPageProps { onCheckoutClick?: () => void; @@ -148,21 +150,29 @@ export function PassesPage({ onSignInClick, onSignOutClick, }: PassesPageProps) { - const [selectedPass, setSelectedPass] = useState('unlimited'); + const [selectedPass, setSelectedPass] = useState(passTypes[1].id); const [isLoginOpen, setIsLoginOpen] = useState(false); const { user } = useAuth(); // from AuthContext - const navigate= useNavigate() + const navigate = useNavigate() + const cityId = localStorage.getItem("cityId") + + const { data: cityDetails, isLoading: loadingCityDetails } = useGetSelectedCityDetailsQuery(cityId) + const cards = cityDetails?.city?.cards ?? [] + console.log(cards) + + if (loadingCityDetails) { + return () + } - - const handleCheckoutClick = () => { + const handleCheckoutClick = () => { console.log('Proceeding to checkout for user:', user); // Add your checkout logic here navigate('/checkout'); }; - const handleSignInClick = () => { + const handleSignInClick = () => { setIsLoginOpen(true); }; @@ -189,7 +199,7 @@ export function PassesPage({

- Buy Passes + Buy Cards

Skip the lines, save money, and explore more with our flexible city cards designed for modern travelers @@ -200,130 +210,184 @@ export function PassesPage({ {/* Pass Comparison Section */}

- {passTypes.map((pass) => ( -
- + {/* Flexi Pass Card */} +
+ setSelectedPass(passTypes[0].id)} + > +
+ {/* */} +
- {/* Popular Badge */} - {pass.popular && ( -
- - Most Popular - -
- )} + + + {cards[0].title} + + + {cards[0].description} + + - {/* Radio Button */} -
- + {/* Pricing */} +
+
+ + ${cards[0].adultPrice} + + + / {passTypes[0].period} +
- - {/* Header - Fixed Height */} - - - {pass.title} - - - {pass.description} - - - - {/* Attraction Images Grid */} -
-
-
- +
+ {cards[0].adultPrice && ( +
+ {/* Strikethrough price = originalPrice + $5 */} + + ${parseFloat(cards[0].adultPrice) + 5} + + + Save{" "} + {Math.round( + ((5) / (parseFloat(cards[0].adultPrice) + 5)) * 100 + )} + % +
-
- -
-
- -
-
- -
-
+ )}
+
- {/* Pricing Section - Fixed Height */} -
-
- {pass.price} - / {pass.period} -
-
- {pass.originalPrice && ( -
- {pass.originalPrice} - Save {Math.round(((parseFloat(pass.originalPrice.slice(1)) - parseFloat(pass.price.slice(1))) / parseFloat(pass.originalPrice.slice(1))) * 100)}% + + +
+
+ {passTypes[0].features.map((feature, index) => ( +
+ + {feature}
- )} + ))}
- {/* Content - Flexible Height with Fixed Features Area */} - - {/* Features List - Fixed height */} -
-
- {pass.features.slice(0, 6).map((feature, index) => ( -
- - {feature} -
- ))} +
+ +

+ ✓ Free cancellation up to 24 hours • Instant delivery +

+
+ + +
+ + {/* Unlimited Pass Card */} +
+ setSelectedPass(passTypes[1].id)} + + > + {passTypes[1].popular && ( +
+ + Most Popular + +
+ )} + +
+ {/* */} +
+ + + + {cards[1].title} + + + {cards[1].description} + + + + {/* Pricing */} +
+
+ ${cards[1].adultPrice} + / {passTypes[1].period} +
+
+ {cards[1].adultPrice && ( +
+ {/* Strikethrough price = originalPrice + $5 */} + + ${parseFloat(cards[1].adultPrice) + 5} + + + Save{" "} + {Math.round( + ((5) / (parseFloat(cards[1].adultPrice) + 5)) * 100 + )} + % +
-
+ )} +
+
- {/* CTA Button - Pushed to bottom */} -
- - -

- ✓ Free cancellation up to 24 hours • Instant delivery -

+ +
+
+ {passTypes[1].features.map((feature, index) => ( +
+ + {feature} +
+ ))}
- - -
- ))} +
+ +
+ +

+ ✓ Free cancellation up to 24 hours • Instant delivery +

+
+ + +
+ {/* Good to Know Section */}
From cdadee5df40960af2263c2e5e6f4a8f234b7d5b0 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Fri, 17 Apr 2026 15:56:35 +0530 Subject: [PATCH 21/21] remove calendar from attraction details page --- src/pages/AttractionDetailsPage.tsx | 126 +--------------------------- 1 file changed, 1 insertion(+), 125 deletions(-) diff --git a/src/pages/AttractionDetailsPage.tsx b/src/pages/AttractionDetailsPage.tsx index 53770f6..4c9f90c 100644 --- a/src/pages/AttractionDetailsPage.tsx +++ b/src/pages/AttractionDetailsPage.tsx @@ -258,131 +258,7 @@ export function AttractionDetailsPage({
{/* Right Sidebar - Calendar and Booking */} -
- {/* Calendar Widget with Custom Design */} - -
-

Select Date

-

Choose your preferred visit date

-
- - {/* Custom Calendar Design */} -
- {/* Calendar Header */} -
- - September 2025 - -
- - {/* Days of week */} -
-
Su
-
Mo
-
Tu
-
We
-
Th
-
Fr
-
Sa
-
- - {/* Calendar Grid */} -
- {/* Previous month */} - - - {/* Current month */} - {Array.from({ length: 30 }, (_, i) => { - const day = i + 1; - const isSelected = day === 27; - const isToday = day === 15; - return ( - - ); - })} - - {/* Next month */} - {Array.from({ length: 4 }, (_, i) => ( - - ))} -
-
- - {/* Selected Date Display */} -
-
- -
-

Selected Date

-

September 27, 2025

-
-
-
-
- - {/* Pricing Card */} - -
-
- Adult Ticket - {attraction.ticketPriceAdult} -
-
- Service Fee - $5 -
-
-
- Total - ${attraction.ticketPriceAdult + 5} -
-
-
-
- - {/* Confirm Booking Button */} - - - {/* Trust Indicators */} -
-
- - Instant Confirmation -
-
- - Free Cancellation -
-
-
+