From a651186276230991648b864affe6296ca5d0f54d Mon Sep 17 00:00:00 2001 From: aryabenade Date: Fri, 24 Apr 2026 21:30:04 +0530 Subject: [PATCH 1/5] add validations in forms --- src/components/RegisterPage.tsx | 82 ++++++----- src/pages/PaymentDetailsPage.tsx | 62 +++++--- src/pages/ProfilePage.tsx | 245 +++++++------------------------ 3 files changed, 138 insertions(+), 251 deletions(-) diff --git a/src/components/RegisterPage.tsx b/src/components/RegisterPage.tsx index 31ae9c2..8e754af 100644 --- a/src/components/RegisterPage.tsx +++ b/src/components/RegisterPage.tsx @@ -46,54 +46,60 @@ export default function RegisterPage() { setFormData(prev => ({ ...prev, [field]: value })); }; - const validateForm = () => { - // First Name - if (!formData.firstName.trim()) return toast.error('First name is required'), false; - if (!/^[A-Za-z]+$/.test(formData.firstName)) return toast.error('First name must contain only alphabets'), false; - if (formData.firstName.length < 2 || formData.firstName.length > 50) return toast.error('First name must be between 2 and 50 characters'), false; + const validateForm = () => { + // First Name + if (!formData.firstName.trim()) return toast.error('First name is required'), false; + if (!/^[A-Za-z]+$/.test(formData.firstName)) return toast.error('First name must contain only alphabets'), false; + if (formData.firstName.length < 2 || formData.firstName.length > 50) return toast.error('First name must be between 2 and 50 characters'), false; - // Last Name - if (!formData.lastName.trim()) return toast.error('Last name is required'), false; - if (!/^[A-Za-z]+$/.test(formData.lastName)) return toast.error('Last name must contain only alphabets'), false; - if (formData.lastName.length < 2 || formData.lastName.length > 50) return toast.error('Last name must be between 2 and 50 characters'), false; + // Last Name + if (!formData.lastName.trim()) return toast.error('Last name is required'), false; + if (!/^[A-Za-z]+$/.test(formData.lastName)) return toast.error('Last name must contain only alphabets'), false; + if (formData.lastName.length < 2 || formData.lastName.length > 50) return toast.error('Last name must be between 2 and 50 characters'), false; - // Email - if (!formData.emailAddress.includes('@')) return toast.error('Invalid email address'), false; + // Email (IMPROVED from 1st function) + if (!formData.emailAddress.trim()) return toast.error('Email is required'), false; + if (!/\S+@\S+\.\S+/.test(formData.emailAddress)) return toast.error('Valid email address required'), false; - // ISD Code - if (!formData.isdCode.startsWith("+")) return toast.error("ISD code must start with '+'"), false; - if (!/^\+\d+$/.test(formData.isdCode)) return toast.error("ISD code must contain only numbers after '+'"), false; + // ISD Code + if (!formData.isdCode.trim()) return toast.error('ISD code is required'), false; + if (!formData.isdCode.startsWith("+")) return toast.error("ISD code must start with '+'"), false; + if (!/^\+\d+$/.test(formData.isdCode)) return toast.error("ISD code must contain only numbers after '+'"), false; - // Mobile Number - if (!/^\d+$/.test(formData.mobileNumber)) return toast.error('Invalid mobile number'), false; - if (formData.mobileNumber.length < 7 || formData.mobileNumber.length > 15) return toast.error('Mobile number must be between 7 and 15 digits'), false; + // Mobile Number (IMPROVED with required check) + if (!formData.mobileNumber.trim()) return toast.error('Mobile number is required'), false; + if (!/^\d+$/.test(formData.mobileNumber)) return toast.error('Invalid mobile number'), false; + if (formData.mobileNumber.length < 7 || formData.mobileNumber.length > 15) return toast.error('Mobile number must be between 7 and 15 digits'), false; - // Address Line 1 - if (!formData.address1.trim()) return toast.error('Address required'), false; - if (!/^[A-Za-z0-9\s]+$/.test(formData.address1)) return toast.error('Address must be alphanumeric'), false; - if (formData.address1.length < 5 || formData.address1.length > 100) return toast.error('Address must be between 5 and 100 characters'), false; + // Address Line 1 (UPDATED regex from 3rd function) + if (!formData.address1.trim()) return toast.error('Address required'), false; + if (!/^[A-Za-z0-9\s,\-.]+$/.test(formData.address1)) return toast.error('Address contains invalid characters'), false; + if (formData.address1.length < 5 || formData.address1.length > 100) return toast.error('Address must be between 5 and 100 characters'), false; - // City - if (!formData.city.trim()) return toast.error('City required'), false; - if (!/^[A-Za-z\s]+$/.test(formData.city)) return toast.error('City must contain only alphabets'), false; - if (formData.city.length < 2 || formData.city.length > 50) return toast.error('City must be between 2 and 50 characters'), false; + // City (IMPROVED with no multiple spaces) + if (!formData.city.trim()) return toast.error('City required'), false; + if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.city)) return toast.error('City must contain only alphabets'), false; + if (/\s{2,}/.test(formData.city)) return toast.error('City must not contain multiple consecutive spaces'), false; + if (formData.city.length < 2 || formData.city.length > 50) return toast.error('City must be between 2 and 50 characters'), false; - // State - if (!formData.state.trim()) return toast.error('State required'), false; - if (!/^[A-Za-z\s]+$/.test(formData.state)) return toast.error('State must contain only alphabets'), false; - if (formData.state.length < 2 || formData.state.length > 50) return toast.error('State must be between 2 and 50 characters'), false; + // State (IMPROVED similar to city) + if (!formData.state.trim()) return toast.error('State required'), false; + if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.state)) return toast.error('State must contain only alphabets'), false; + if (/\s{2,}/.test(formData.state)) return toast.error('State must not contain multiple consecutive spaces'), false; + if (formData.state.length < 2 || formData.state.length > 50) return toast.error('State must be between 2 and 50 characters'), false; - // Country - if (!formData.country.trim()) return toast.error('Country required'), false; - if (!/^[A-Za-z\s]+$/.test(formData.country)) return toast.error('Country must contain only alphabets'), false; - if (formData.country.length < 2 || formData.country.length > 50) return toast.error('Country must be between 2 and 50 characters'), false; + // Country (IMPROVED regex from 3rd) + if (!formData.country.trim()) return toast.error('Country required'), false; + if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.country)) return toast.error('Country must contain only alphabets'), false; + if (formData.country.length < 2 || formData.country.length > 50) return toast.error('Country must be between 2 and 50 characters'), false; - // Postal Code - if (!/^\d+$/.test(formData.postalCode)) return toast.error('Postal code should only contain numbers'), false; - if (formData.postalCode.length < 4 || formData.postalCode.length > 10) return toast.error('Postal code must be between 4 and 10 digits'), false; + // Postal Code (IMPROVED from 3rd: alphanumeric allowed) + if (!formData.postalCode.trim()) return toast.error('Postal code is required'), false; + if (!/^[A-Za-z0-9\s\-]+$/.test(formData.postalCode)) return toast.error('Postal code contains invalid characters'), false; + if (formData.postalCode.length < 4 || formData.postalCode.length > 10) return toast.error('Postal code must be between 4 and 10 characters'), false; - return true; - }; + return true; +}; diff --git a/src/pages/PaymentDetailsPage.tsx b/src/pages/PaymentDetailsPage.tsx index b5f438b..24b6187 100644 --- a/src/pages/PaymentDetailsPage.tsx +++ b/src/pages/PaymentDetailsPage.tsx @@ -231,23 +231,51 @@ export function PaymentDetailsPage({ const [errors, setErrors] = useState>({}); const validate = () => { - const e: Record = {}; - if (selectedTab === 'gift') { - if (!giftFirstName.trim()) e.giftFirstName = 'Required'; - if (!giftLastName.trim()) e.giftLastName = 'Required'; - if (!giftIsd.trim()) e.giftIsd = 'Required'; - if (!giftMessage.trim()) e.giftMessage = 'Required'; - if (!giftEmail.trim() || !/\S+@\S+\.\S+/.test(giftEmail)) { - e.giftEmail = 'Valid email required'; - } - if (!giftPhone.trim() || !/^\+?[0-9]{7,15}$/.test(giftPhone)) { - e.giftPhone = 'Valid phone required'; - } - if (!giftCity.trim()) e.giftCity = 'Required'; - if (!giftCountry.trim()) e.giftCountry = 'Required'; - } - return e; - }; + const e: Record = {}; + + if (selectedTab === 'gift') { + // First Name + if (!giftFirstName.trim()) e.giftFirstName = 'Required'; + else if (!/^[A-Za-z]+$/.test(giftFirstName)) e.giftFirstName = 'Only alphabets allowed'; + else if (giftFirstName.length < 2 || giftFirstName.length > 50) e.giftFirstName = 'Must be 2–50 characters'; + + // Last Name + if (!giftLastName.trim()) e.giftLastName = 'Required'; + else if (!/^[A-Za-z]+$/.test(giftLastName)) e.giftLastName = 'Only alphabets allowed'; + else if (giftLastName.length < 2 || giftLastName.length > 50) e.giftLastName = 'Must be 2–50 characters'; + + // ISD Code + if (!giftIsd.trim()) e.giftIsd = 'Required'; + else if (!giftIsd.startsWith('+')) e.giftIsd = "Must start with '+'"; + else if (!/^\+\d+$/.test(giftIsd)) e.giftIsd = "Only numbers allowed after '+'"; + + // Email + if (!giftEmail.trim()) e.giftEmail = 'Required'; + else if (!/\S+@\S+\.\S+/.test(giftEmail)) e.giftEmail = 'Valid email required'; + + // Phone + if (!giftPhone.trim()) e.giftPhone = 'Required'; + else if (!/^\d+$/.test(giftPhone)) e.giftPhone = 'Only numbers allowed'; + else if (giftPhone.length < 7 || giftPhone.length > 15) e.giftPhone = 'Must be 7–15 digits'; + + // Message + if (!giftMessage.trim()) e.giftMessage = 'Required'; + else if (giftMessage.length < 5 || giftMessage.length > 500) e.giftMessage = 'Must be 5–500 characters'; + + // City + if (!giftCity.trim()) e.giftCity = 'Required'; + else if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(giftCity)) e.giftCity = 'Only alphabets allowed'; + else if (/\s{2,}/.test(giftCity)) e.giftCity = 'No multiple spaces'; + else if (giftCity.length < 2 || giftCity.length > 50) e.giftCity = 'Must be 2–50 characters'; + + // Country + if (!giftCountry.trim()) e.giftCountry = 'Required'; + else if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(giftCountry)) e.giftCountry = 'Only alphabets allowed'; + else if (giftCountry.length < 2 || giftCountry.length > 50) e.giftCountry = 'Must be 2–50 characters'; + } + + return e; +}; const [isRedirecting, setIsRedirecting] = useState(false); diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 2353841..e40afa9 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -129,22 +129,60 @@ export function ProfilePage({ }, [userDetails]) + const validateForm = () => { + // First Name (TC-013) + if (!formData.firstName.trim()) return toast.error('First name is required'), false; + if (!/^[A-Za-z]+$/.test(formData.firstName)) return toast.error('First name must contain only alphabets'), false; + if (formData.firstName.length < 2 || formData.firstName.length > 50) return toast.error('First name must be between 2 and 50 characters'), false; + + // Last Name (TC-014) + if (!formData.lastName.trim()) return toast.error('Last name is required'), false; + if (!/^[A-Za-z]+$/.test(formData.lastName)) return toast.error('Last name must contain only alphabets'), false; + if (formData.lastName.length < 2 || formData.lastName.length > 50) return toast.error('Last name must be between 2 and 50 characters'), false; + + // Mobile Number (TC-016) + if (!formData.phone.trim()) return toast.error('Mobile number is required'), false; + if (!/^\d+$/.test(formData.phone)) return toast.error('Mobile number must contain only numbers'), false; + if (formData.phone.length < 7 || formData.phone.length > 15) return toast.error('Mobile number must be between 7 and 15 digits'), false; + + // Address Line 1 (TC-019) + if (!formData.address1.trim()) return toast.error('Address is required'), false; + if (!/^[A-Za-z0-9\s,\-.]+$/.test(formData.address1)) return toast.error('Address contains invalid characters'), false; + if (formData.address1.length < 5 || formData.address1.length > 100) return toast.error('Address must be between 5 and 100 characters'), false; + + // City (TC-018) + if (!formData.city.trim()) return toast.error('City is required'), false; + if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.city)) return toast.error('City must contain only alphabets'), false; + if (/\s{2,}/.test(formData.city)) return toast.error('City must not contain multiple consecutive spaces'), false; + if (formData.city.length < 2 || formData.city.length > 50) return toast.error('City must be between 2 and 50 characters'), false; + + // Country (TC-017) — free text validation since no dropdown yet + if (!formData.country.trim()) return toast.error('Country is required'), false; + if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.country)) return toast.error('Country must contain only alphabets'), false; + if (formData.country.length < 2 || formData.country.length > 50) return toast.error('Country must be between 2 and 50 characters'), false; + + // Postal Code (TC-020) + if (!formData.postalCode.trim()) return toast.error('Postal code is required'), false; + if (!/^[A-Za-z0-9\s\-]+$/.test(formData.postalCode)) return toast.error('Postal code contains invalid characters'), false; + if (formData.postalCode.length < 4 || formData.postalCode.length > 10) return toast.error('Postal code must be between 4 and 10 characters'), false; + + return true; +}; + const handleInputChange = (field: string, value: string) => { setFormData(prev => ({ ...prev, [field]: value })); }; const handleSaveProfile = async () => { - try { - console.log("Saving profile...", formData); - const response = await updateUserProfileDetails({ userDetails: formData, userId }); - console.log(response) - toast.success("Profile updated successfully!"); - } catch (error) { - console.error("Error saving profile:", error); - toast.error("Failed to update profile. Please try again."); - } - }; + if (!validateForm()) return; + try { + const response = await updateUserProfileDetails({ userDetails: formData, userId }); + toast.success("Profile updated successfully!"); + } catch (error) { + toast.error("Failed to update profile. Please try again."); + } +}; const activeCards = cards.filter((card: any) => card.isActive === true); const expiredCards = cards.filter((card: any) => card.isActive === false); @@ -256,6 +294,7 @@ export function ProfilePage({ id="email" type="email" value={formData.email} + disabled onChange={(e) => handleInputChange('email', e.target.value)} className="mt-1 font-poppins font-light" /> @@ -340,192 +379,6 @@ export function ProfilePage({ - {/* App Download Section */} -
- - - - {(() => { - // Determine which pass type to show - const hasUnlimitedPass = activeCards.some((card: any) => card.cardType.cardTypeName === 'selective_pass'); - const hasSelectivePass = activeCards.some((card: any) => card.cardType.cardTypeName === 'unlimited_card'); - - if (hasUnlimitedPass) { - return ( - <> -
-

- Get{' '} - Melbourne Unlimited 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. -

-
- - {/* Card Benefits */} -
-
-
- -
- Unlimited entries to all attractions -
-
-
- -
- Valid for 7 consecutive days -
-
-
- -
- Skip the queue at major venues -
-
- - {/* Purchase CTA */} -
- - -
- - ); - } else if (hasSelectivePass) { - return ( - <> -
-

- Get{' '} - Selective Card - {' '}now -

-

- Choose your own adventure with 12 hand-picked attractions. Perfect for visitors - who want flexibility and value. -

-
- - {/* Card Benefits */} -
-
-
- -
- Choose from 12 curated attractions -
-
-
- -
- Flexible 7-day validity period -
-
-
- -
- Save 40% on combined ticket price -
-
- - {/* Purchase CTA */} -
- - -
- - ); - } else { - return ( - <> -
-

- Get{' '} - CityCards - {' '}now -

-

- Explore Melbourne's best attractions with our flexible card options. - Choose unlimited access or select your favorites. -

-
- - {/* Card Options */} -
-
-
-
-

Unlimited Card

-

25+ attractions, unlimited visits

-
- Popular -
-
$149
-
- -
-
-
-

Selective Card

-

12 attractions of your choice

-
-
-
$89
-
-
- - {/* Purchase CTA */} -
- - -
- - ); - } - })()} -
-
-
-
-- 2.34.1 From 33a782ca5490ed437446b040ebc315bac4b89bae Mon Sep 17 00:00:00 2001 From: aryabenade Date: Fri, 24 Apr 2026 21:45:49 +0530 Subject: [PATCH 2/5] show cityName on basis of citySelected --- src/components/TrustSection.tsx | 2 +- src/pages/MagicItineraryPage.tsx | 2 +- src/pages/PassesPage.tsx | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/TrustSection.tsx b/src/components/TrustSection.tsx index 327f68d..5aa3721 100644 --- a/src/components/TrustSection.tsx +++ b/src/components/TrustSection.tsx @@ -204,7 +204,7 @@ export function TrustSection() { style={{ transform: `rotate(${cardRotation}deg) translateY(${cardOffset}px)`, transformOrigin: 'center center', - minHeight: '480px', + minHeight: '360px', background: ` radial-gradient(circle at 20% 80%, rgba(255, 248, 235, 0.8) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(250, 245, 230, 0.6) 0%, transparent 50%), diff --git a/src/pages/MagicItineraryPage.tsx b/src/pages/MagicItineraryPage.tsx index b9368c9..1564b18 100644 --- a/src/pages/MagicItineraryPage.tsx +++ b/src/pages/MagicItineraryPage.tsx @@ -191,7 +191,7 @@ export function MagicItineraryPage({

- A perfectly planned Melbourne adventure + A perfectly planned {cityName} adventure

diff --git a/src/pages/PassesPage.tsx b/src/pages/PassesPage.tsx index 04dcbb4..3396a39 100644 --- a/src/pages/PassesPage.tsx +++ b/src/pages/PassesPage.tsx @@ -156,6 +156,7 @@ export function PassesPage({ const navigate = useNavigate() const cityId = localStorage.getItem("cityId") + const cityName = localStorage.getItem("cityName") const { data: cityDetails, isLoading: loadingCityDetails } = useGetSelectedCityDetailsQuery(cityId) const cards = cityDetails?.city?.cards ?? [] @@ -597,7 +598,7 @@ export function PassesPage({

CityCards

-

Melbourne Explorer

+

{cityName} Explorer

@@ -770,7 +771,7 @@ export function PassesPage({

Ready to{' '} explore{' '} - Melbourne? + {cityName}?

Choose your pass and start discovering amazing attractions with skip-the-line access. -- 2.34.1 From e3fde4bb17d8ad550e4fd85b5fe6a10f432a4d12 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Fri, 24 Apr 2026 21:58:34 +0530 Subject: [PATCH 3/5] add validations for spaces in the forms --- src/components/RegisterPage.tsx | 58 +++++++++++++++++--------------- src/pages/PaymentDetailsPage.tsx | 54 +++++++++++++++-------------- src/pages/ProfilePage.tsx | 30 ++++++----------- 3 files changed, 69 insertions(+), 73 deletions(-) diff --git a/src/components/RegisterPage.tsx b/src/components/RegisterPage.tsx index 8e754af..8072f8d 100644 --- a/src/components/RegisterPage.tsx +++ b/src/components/RegisterPage.tsx @@ -46,56 +46,58 @@ export default function RegisterPage() { setFormData(prev => ({ ...prev, [field]: value })); }; - const validateForm = () => { + const validateForm = () => { // First Name if (!formData.firstName.trim()) return toast.error('First name is required'), false; - if (!/^[A-Za-z]+$/.test(formData.firstName)) return toast.error('First name must contain only alphabets'), false; + if (/\s/.test(formData.firstName)) return toast.error('First name must not contain spaces'), false; + if (!/^[A-Za-z]+$/.test(formData.firstName)) return toast.error('First name must contain only letters (A–Z)'), false; if (formData.firstName.length < 2 || formData.firstName.length > 50) return toast.error('First name must be between 2 and 50 characters'), false; // Last Name if (!formData.lastName.trim()) return toast.error('Last name is required'), false; - if (!/^[A-Za-z]+$/.test(formData.lastName)) return toast.error('Last name must contain only alphabets'), false; + if (/\s/.test(formData.lastName)) return toast.error('Last name must not contain spaces'), false; + if (!/^[A-Za-z]+$/.test(formData.lastName)) return toast.error('Last name must contain only letters (A–Z)'), false; if (formData.lastName.length < 2 || formData.lastName.length > 50) return toast.error('Last name must be between 2 and 50 characters'), false; - // Email (IMPROVED from 1st function) - if (!formData.emailAddress.trim()) return toast.error('Email is required'), false; - if (!/\S+@\S+\.\S+/.test(formData.emailAddress)) return toast.error('Valid email address required'), false; + // Email + if (!formData.emailAddress.trim()) return toast.error('Email address is required'), false; + if (!/\S+@\S+\.\S+/.test(formData.emailAddress)) return toast.error('Enter a valid email (e.g. name@example.com)'), false; - // ISD Code + // ISD if (!formData.isdCode.trim()) return toast.error('ISD code is required'), false; - if (!formData.isdCode.startsWith("+")) return toast.error("ISD code must start with '+'"), false; - if (!/^\+\d+$/.test(formData.isdCode)) return toast.error("ISD code must contain only numbers after '+'"), false; + if (/\s/.test(formData.isdCode)) return toast.error('ISD code must not contain spaces'), false; + if (!formData.isdCode.startsWith('+')) return toast.error("ISD code must start with '+' (e.g. +91)"), false; + if (!/^\+\d+$/.test(formData.isdCode)) return toast.error("ISD code must contain only digits after '+'"), false; - // Mobile Number (IMPROVED with required check) + // Phone if (!formData.mobileNumber.trim()) return toast.error('Mobile number is required'), false; - if (!/^\d+$/.test(formData.mobileNumber)) return toast.error('Invalid mobile number'), false; + if (/\s/.test(formData.mobileNumber)) return toast.error('Mobile number must not contain spaces'), false; + if (!/^\d+$/.test(formData.mobileNumber)) return toast.error('Mobile number must contain only digits (0–9)'), false; if (formData.mobileNumber.length < 7 || formData.mobileNumber.length > 15) return toast.error('Mobile number must be between 7 and 15 digits'), false; - // Address Line 1 (UPDATED regex from 3rd function) - if (!formData.address1.trim()) return toast.error('Address required'), false; - if (!/^[A-Za-z0-9\s,\-.]+$/.test(formData.address1)) return toast.error('Address contains invalid characters'), false; + // Address + if (!formData.address1.trim()) return toast.error('Address is required'), false; + if (!/^[A-Za-z0-9\s,\-.]+$/.test(formData.address1)) return toast.error('Address can only contain letters, numbers, spaces, commas, dots, and hyphens'), false; if (formData.address1.length < 5 || formData.address1.length > 100) return toast.error('Address must be between 5 and 100 characters'), false; - // City (IMPROVED with no multiple spaces) - if (!formData.city.trim()) return toast.error('City required'), false; - if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.city)) return toast.error('City must contain only alphabets'), false; + // City + if (!formData.city.trim()) return toast.error('City is required'), false; + if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.city)) return toast.error('City can only contain letters and spaces'), false; if (/\s{2,}/.test(formData.city)) return toast.error('City must not contain multiple consecutive spaces'), false; - if (formData.city.length < 2 || formData.city.length > 50) return toast.error('City must be between 2 and 50 characters'), false; - // State (IMPROVED similar to city) - if (!formData.state.trim()) return toast.error('State required'), false; - if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.state)) return toast.error('State must contain only alphabets'), false; + // State + if (!formData.state.trim()) return toast.error('State is required'), false; + if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.state)) return toast.error('State can only contain letters and spaces'), false; if (/\s{2,}/.test(formData.state)) return toast.error('State must not contain multiple consecutive spaces'), false; - if (formData.state.length < 2 || formData.state.length > 50) return toast.error('State must be between 2 and 50 characters'), false; - // Country (IMPROVED regex from 3rd) - if (!formData.country.trim()) return toast.error('Country required'), false; - if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.country)) return toast.error('Country must contain only alphabets'), false; - if (formData.country.length < 2 || formData.country.length > 50) return toast.error('Country must be between 2 and 50 characters'), false; + // Country + if (!formData.country.trim()) return toast.error('Country is required'), false; + if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.country)) return toast.error('Country can only contain letters and spaces'), false; - // Postal Code (IMPROVED from 3rd: alphanumeric allowed) + // Postal Code if (!formData.postalCode.trim()) return toast.error('Postal code is required'), false; - if (!/^[A-Za-z0-9\s\-]+$/.test(formData.postalCode)) return toast.error('Postal code contains invalid characters'), false; + if (/\s/.test(formData.postalCode)) return toast.error('Postal code must not contain spaces'), false; + if (!/^[A-Za-z0-9]+$/.test(formData.postalCode)) return toast.error('Postal code must contain only letters and numbers'), false; if (formData.postalCode.length < 4 || formData.postalCode.length > 10) return toast.error('Postal code must be between 4 and 10 characters'), false; return true; diff --git a/src/pages/PaymentDetailsPage.tsx b/src/pages/PaymentDetailsPage.tsx index 24b6187..cc86e0a 100644 --- a/src/pages/PaymentDetailsPage.tsx +++ b/src/pages/PaymentDetailsPage.tsx @@ -230,53 +230,57 @@ export function PaymentDetailsPage({ const [errors, setErrors] = useState>({}); - const validate = () => { +const validate = () => { const e: Record = {}; if (selectedTab === 'gift') { // First Name - if (!giftFirstName.trim()) e.giftFirstName = 'Required'; - else if (!/^[A-Za-z]+$/.test(giftFirstName)) e.giftFirstName = 'Only alphabets allowed'; - else if (giftFirstName.length < 2 || giftFirstName.length > 50) e.giftFirstName = 'Must be 2–50 characters'; + if (!giftFirstName.trim()) e.giftFirstName = 'First name is required'; + else if (/\s/.test(giftFirstName)) e.giftFirstName = 'First name must not contain spaces'; + else if (!/^[A-Za-z]+$/.test(giftFirstName)) e.giftFirstName = 'First name must contain only letters (A–Z)'; + else if (giftFirstName.length < 2 || giftFirstName.length > 50) e.giftFirstName = 'First name must be between 2 and 50 characters'; // Last Name - if (!giftLastName.trim()) e.giftLastName = 'Required'; - else if (!/^[A-Za-z]+$/.test(giftLastName)) e.giftLastName = 'Only alphabets allowed'; - else if (giftLastName.length < 2 || giftLastName.length > 50) e.giftLastName = 'Must be 2–50 characters'; + if (!giftLastName.trim()) e.giftLastName = 'Last name is required'; + else if (/\s/.test(giftLastName)) e.giftLastName = 'Last name must not contain spaces'; + else if (!/^[A-Za-z]+$/.test(giftLastName)) e.giftLastName = 'Last name must contain only letters (A–Z)'; + else if (giftLastName.length < 2 || giftLastName.length > 50) e.giftLastName = 'Last name must be between 2 and 50 characters'; // ISD Code - if (!giftIsd.trim()) e.giftIsd = 'Required'; - else if (!giftIsd.startsWith('+')) e.giftIsd = "Must start with '+'"; - else if (!/^\+\d+$/.test(giftIsd)) e.giftIsd = "Only numbers allowed after '+'"; + if (!giftIsd.trim()) e.giftIsd = 'ISD code is required'; + else if (/\s/.test(giftIsd)) e.giftIsd = 'ISD code must not contain spaces'; + else if (!giftIsd.startsWith('+')) e.giftIsd = "ISD code must start with '+' (e.g. +91)"; + else if (!/^\+\d+$/.test(giftIsd)) e.giftIsd = "ISD code must contain only digits after '+'"; // Email - if (!giftEmail.trim()) e.giftEmail = 'Required'; - else if (!/\S+@\S+\.\S+/.test(giftEmail)) e.giftEmail = 'Valid email required'; + if (!giftEmail.trim()) e.giftEmail = 'Email address is required'; + else if (!/\S+@\S+\.\S+/.test(giftEmail)) e.giftEmail = 'Enter a valid email (e.g. name@example.com)'; // Phone - if (!giftPhone.trim()) e.giftPhone = 'Required'; - else if (!/^\d+$/.test(giftPhone)) e.giftPhone = 'Only numbers allowed'; - else if (giftPhone.length < 7 || giftPhone.length > 15) e.giftPhone = 'Must be 7–15 digits'; + if (!giftPhone.trim()) e.giftPhone = 'Phone number is required'; + else if (/\s/.test(giftPhone)) e.giftPhone = 'Phone number must not contain spaces'; + else if (!/^\d+$/.test(giftPhone)) e.giftPhone = 'Phone number must contain only digits (0–9)'; + else if (giftPhone.length < 7 || giftPhone.length > 15) e.giftPhone = 'Phone number must be between 7 and 15 digits'; // Message - if (!giftMessage.trim()) e.giftMessage = 'Required'; - else if (giftMessage.length < 5 || giftMessage.length > 500) e.giftMessage = 'Must be 5–500 characters'; + if (!giftMessage.trim()) e.giftMessage = 'Message is required'; + else if (giftMessage.length < 5) e.giftMessage = 'Message must be at least 5 characters long'; + else if (giftMessage.length > 500) e.giftMessage = 'Message must not exceed 500 characters'; // City - if (!giftCity.trim()) e.giftCity = 'Required'; - else if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(giftCity)) e.giftCity = 'Only alphabets allowed'; - else if (/\s{2,}/.test(giftCity)) e.giftCity = 'No multiple spaces'; - else if (giftCity.length < 2 || giftCity.length > 50) e.giftCity = 'Must be 2–50 characters'; + if (!giftCity.trim()) e.giftCity = 'City is required'; + else if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(giftCity)) e.giftCity = 'City can only contain letters and spaces'; + else if (/\s{2,}/.test(giftCity)) e.giftCity = 'City must not contain multiple consecutive spaces'; + else if (giftCity.length < 2 || giftCity.length > 50) e.giftCity = 'City must be between 2 and 50 characters'; // Country - if (!giftCountry.trim()) e.giftCountry = 'Required'; - else if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(giftCountry)) e.giftCountry = 'Only alphabets allowed'; - else if (giftCountry.length < 2 || giftCountry.length > 50) e.giftCountry = 'Must be 2–50 characters'; + if (!giftCountry.trim()) e.giftCountry = 'Country is required'; + else if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(giftCountry)) e.giftCountry = 'Country can only contain letters and spaces'; + else if (giftCountry.length < 2 || giftCountry.length > 50) e.giftCountry = 'Country must be between 2 and 50 characters'; } return e; }; - const [isRedirecting, setIsRedirecting] = useState(false); const handlePayment = async () => { diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index e40afa9..ac78311 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -130,41 +130,31 @@ export function ProfilePage({ }, [userDetails]) const validateForm = () => { - // First Name (TC-013) if (!formData.firstName.trim()) return toast.error('First name is required'), false; - if (!/^[A-Za-z]+$/.test(formData.firstName)) return toast.error('First name must contain only alphabets'), false; - if (formData.firstName.length < 2 || formData.firstName.length > 50) return toast.error('First name must be between 2 and 50 characters'), false; + if (/\s/.test(formData.firstName)) return toast.error('First name must not contain spaces'), false; + if (!/^[A-Za-z]+$/.test(formData.firstName)) return toast.error('First name must contain only letters'), false; - // Last Name (TC-014) if (!formData.lastName.trim()) return toast.error('Last name is required'), false; - if (!/^[A-Za-z]+$/.test(formData.lastName)) return toast.error('Last name must contain only alphabets'), false; - if (formData.lastName.length < 2 || formData.lastName.length > 50) return toast.error('Last name must be between 2 and 50 characters'), false; + if (/\s/.test(formData.lastName)) return toast.error('Last name must not contain spaces'), false; + if (!/^[A-Za-z]+$/.test(formData.lastName)) return toast.error('Last name must contain only letters'), false; - // Mobile Number (TC-016) if (!formData.phone.trim()) return toast.error('Mobile number is required'), false; - if (!/^\d+$/.test(formData.phone)) return toast.error('Mobile number must contain only numbers'), false; - if (formData.phone.length < 7 || formData.phone.length > 15) return toast.error('Mobile number must be between 7 and 15 digits'), false; + if (/\s/.test(formData.phone)) return toast.error('Mobile number must not contain spaces'), false; + if (!/^\d+$/.test(formData.phone)) return toast.error('Mobile number must contain only digits'), false; - // Address Line 1 (TC-019) if (!formData.address1.trim()) return toast.error('Address is required'), false; if (!/^[A-Za-z0-9\s,\-.]+$/.test(formData.address1)) return toast.error('Address contains invalid characters'), false; - if (formData.address1.length < 5 || formData.address1.length > 100) return toast.error('Address must be between 5 and 100 characters'), false; - // City (TC-018) if (!formData.city.trim()) return toast.error('City is required'), false; - if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.city)) return toast.error('City must contain only alphabets'), false; + if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.city)) return toast.error('City can only contain letters and spaces'), false; if (/\s{2,}/.test(formData.city)) return toast.error('City must not contain multiple consecutive spaces'), false; - if (formData.city.length < 2 || formData.city.length > 50) return toast.error('City must be between 2 and 50 characters'), false; - // Country (TC-017) — free text validation since no dropdown yet if (!formData.country.trim()) return toast.error('Country is required'), false; - if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.country)) return toast.error('Country must contain only alphabets'), false; - if (formData.country.length < 2 || formData.country.length > 50) return toast.error('Country must be between 2 and 50 characters'), false; + if (!/^[A-Za-z\s\-'À-ÿ]+$/.test(formData.country)) return toast.error('Country can only contain letters and spaces'), false; - // Postal Code (TC-020) if (!formData.postalCode.trim()) return toast.error('Postal code is required'), false; - if (!/^[A-Za-z0-9\s\-]+$/.test(formData.postalCode)) return toast.error('Postal code contains invalid characters'), false; - if (formData.postalCode.length < 4 || formData.postalCode.length > 10) return toast.error('Postal code must be between 4 and 10 characters'), false; + if (/\s/.test(formData.postalCode)) return toast.error('Postal code must not contain spaces'), false; + if (!/^[A-Za-z0-9]+$/.test(formData.postalCode)) return toast.error('Postal code must contain only letters and numbers'), false; return true; }; -- 2.34.1 From 1be37e098bb233a9af62516366bdbd2f39908226 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Fri, 24 Apr 2026 22:03:11 +0530 Subject: [PATCH 4/5] show login modal when clicked on ctabutton if logged out --- src/components/Navbar.tsx | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index c6972a9..77e2b50 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -94,10 +94,8 @@ export default function Navbar({ const baseUrl = import.meta.env.VITE_BASE_URL; - const protectedPaths = ["/passes", "/whats-included", "/", "/melbourne"]; - const handleOpenLoginModal = () => { - if (!user && protectedPaths.includes(location.pathname)) { + if (!user) { setLoginOpen(true); } else if (!user) { @@ -613,27 +611,7 @@ export default function Navbar({ } /> - {/* Shopping Cart */} - {/* setActiveCartDropdown(prev => !prev)} - items={cartDropdownItems} - title="Shopping Cart" - trigger={ -

- - - {cartItems.length} - -
- } - /> */} - navigate("/cart")} /> + navigate("/cart")} /> {/* Enhanced City Card Button with Source Tracking */}
-- 2.34.1 From d8976a29b45845d8ed3d0bdce9a5fec79382c8a1 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Fri, 24 Apr 2026 22:03:36 +0530 Subject: [PATCH 5/5] remove the booking section in supersavings details page --- src/pages/SuperSavingsDetailsPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/SuperSavingsDetailsPage.tsx b/src/pages/SuperSavingsDetailsPage.tsx index b3b65fb..0960220 100644 --- a/src/pages/SuperSavingsDetailsPage.tsx +++ b/src/pages/SuperSavingsDetailsPage.tsx @@ -345,7 +345,7 @@ export function SuperSavingsDetailsPage({
{/* Right Sidebar - Calendar and Booking (preserved, but you can add a real calendar if needed) */} -
+ {/*

Book This Offer

@@ -379,7 +379,7 @@ export function SuperSavingsDetailsPage({ Proceed to Checkout -
+
*/}
-- 2.34.1