From 0cb3d6c3266973ab4c4ef5eb5384c3b27af84441 Mon Sep 17 00:00:00 2001 From: aryabenade Date: Thu, 23 Apr 2026 12:38:39 +0530 Subject: [PATCH] replace the registration modal with the registration page --- package-lock.json | 15 +- src/AppRouter.tsx | 6 + src/components/LoginModal.tsx | 20 +- src/components/RegisterModal.tsx | 391 ------------------------------- src/components/RegisterPage.tsx | 226 ++++++++++++++++++ src/pages/ProfilePage.tsx | 80 ------- 6 files changed, 253 insertions(+), 485 deletions(-) delete mode 100644 src/components/RegisterModal.tsx create mode 100644 src/components/RegisterPage.tsx diff --git a/package-lock.json b/package-lock.json index abfa862..af5b3cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2257,6 +2257,7 @@ "version": "9.2.0", "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-9.2.0.tgz", "integrity": "sha512-YSzLC0t6VS9MDdPTynSMqU8IxrItFUjkDORALFT6sSMR/XZ5Vgm3RDp/Gk7z727MC4A9s4MFVel0gF0c7+kdrg==", + "peer": true, "engines": { "node": ">=12.16" } @@ -3097,6 +3098,7 @@ "integrity": "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3107,6 +3109,7 @@ "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -3117,6 +3120,7 @@ "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.0.0" } @@ -3386,7 +3390,8 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/embla-carousel-react": { "version": "8.6.0", @@ -3981,6 +3986,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -4038,6 +4044,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -4064,6 +4071,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -4099,6 +4107,7 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", + "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -4301,7 +4310,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -4564,6 +4574,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx index da7f45d..ed1dd57 100644 --- a/src/AppRouter.tsx +++ b/src/AppRouter.tsx @@ -36,6 +36,7 @@ import { PaymentCancelPage } from './pages/PaymentCancelPage'; import { ItineraryViewPage } from './pages/ItineraryViewPage'; import { CheckoutPage } from './pages/CheckoutPage'; import { CreateMagicItineraryPage } from './pages/CreateMagicIternaryPage'; +import RegisterPage from './components/RegisterPage'; // User type definition interface User { @@ -283,6 +284,11 @@ export function AppRouter({ } /> + + + + } /> diff --git a/src/components/LoginModal.tsx b/src/components/LoginModal.tsx index b15cdc6..0fa4523 100644 --- a/src/components/LoginModal.tsx +++ b/src/components/LoginModal.tsx @@ -7,7 +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'; +import { useNavigate } from 'react-router-dom'; interface LoginModalProps { isOpen: boolean; @@ -24,6 +24,7 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) { const [showRegisterModal, setShowRegisterModal] = useState(false); const { login } = useAuth(); + const navigate = useNavigate() const [sendOtp, { isLoading: isSendingOtp }] = useLoginMutation(); const [verifyOtp, { isLoading: isVerifying }] = useVerifyOtpMutation(); @@ -155,7 +156,11 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) { }; login(userData); - toast.success("User Logged in successfully") + if (!response?.data?.userExists) { + navigate("/register") + } else { + toast.success("User Logged in successfully") + } onClose(); } catch (err: any) { setError(err?.data?.message || 'Invalid OTP. Please try again.'); @@ -234,7 +239,7 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) {
- -

- 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/components/RegisterPage.tsx b/src/components/RegisterPage.tsx new file mode 100644 index 0000000..9908650 --- /dev/null +++ b/src/components/RegisterPage.tsx @@ -0,0 +1,226 @@ +import { useState } from 'react'; +import { Button } from './ui/button'; +import { Input } from './ui/input'; +import { useRegisterMutation } from '../Redux/services/auth.service'; +import { toast } from 'sonner'; +import { useAuth } from '../context/AuthContext'; +import Navbar from './Navbar'; +import { Footer } from './Footer'; +import { useNavigate } from 'react-router-dom'; + +export default function RegisterPage() { + const { login, user } = useAuth(); + + const [formData, setFormData] = useState({ + firstName: '', + lastName: '', + emailAddress: user?.email ?? "", + isdCode: '', + mobileNumber: '', + address1: '', + address2: '', + city: '', + state: '', + country: '', + postalCode: '' + }); + + const [helperText, setHelperText] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const navigate = useNavigate() + + const [register, { isLoading: isRegistering }] = useRegisterMutation(); + + const emailAddress = user?.email + + + const handleInputChange = (field: string, value: string) => { + setFormData(prev => ({ ...prev, [field]: value })); + }; + + const validateForm = () => { + if (!formData.firstName.trim()) return toast.error('First name is required'), false; + if (!formData.lastName.trim()) return toast.error('Last name is required'), false; + if (!formData.emailAddress.includes('@')) return toast.error('Invalid email address'), false; + if (!formData.isdCode.startsWith("+")) toast.error("ISD code must start with '+'"), false; + if (!/^\+\d+$/.test(formData.isdCode)) toast.error("ISD code must contain only numbers after '+'"), false; + if (!/^\d+$/.test(formData.mobileNumber)) return toast.error('Invalid mobile number'), false; + if (!formData.address1.trim()) return toast.error('Address required'), false; + if (!formData.city.trim()) return toast.error('City required'), false; + if (!formData.state.trim()) return toast.error('State required'), false; + if (!/^\d+$/.test(formData.postalCode)) return toast.error('Postal code should only contain numbers'), false; + return true; + }; + + const handleRegister = async () => { + if (!validateForm()) return; + + setIsLoading(true); + setHelperText(''); + + try { + const response = await register(formData).unwrap(); + toast.success('Registration successful!'); + const userData = { + userId: response?.user?.id, + email: response?.email || formData.emailAddress, + name: response?.name || formData.emailAddress.split('@')[0].charAt(0).toUpperCase() + formData.emailAddress.split('@')[0].slice(1), + accessToken: response?.accessToken, + }; + + login(userData); + navigate("/") + } catch (err: any) { + const msg = err?.data?.message || 'Registration failed'; + toast.error(msg); + setHelperText(msg); + } finally { + setIsLoading(false); + } + }; + + return ( +
+ {/* Navbar */} + + {/* Main Content */} +
+ +
+ + {/* Header */} +
+

+ Create Account +

+

+ Register to get started with City Cards +

+
+ + {/* Form Container */} +
+ + {/* Personal Info */} +
+

+ Personal Information +

+ +
+ handleInputChange('firstName', e.target.value)} + className="h-12 bg-gray-50 border-0 rounded-xl" + /> + handleInputChange('lastName', e.target.value)} + className="h-12 bg-gray-50 border-0 rounded-xl" + /> +
+ + handleInputChange('emailAddress', e.target.value)} + className="h-12 bg-gray-50 border-0 rounded-xl" + /> + +
+ handleInputChange('isdCode', e.target.value)} + className="h-12 bg-gray-50 border-0 rounded-xl" + /> +
+ handleInputChange('mobileNumber', e.target.value)} + className="h-12 bg-gray-50 border-0 rounded-xl" + /> +
+
+
+ + {/* Address */} +
+

+ Address Information +

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

{helperText}

+ )} + + + +
+
+
+ + {/* Footer */} +
+
+
+ +
+ ); +} \ No newline at end of file diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 7b1550f..9cf0d70 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -60,71 +60,6 @@ interface ProfilePageProps { currentPage: string; } -// Mock passes data -const mockPasses = [ - { - id: '1', - name: 'Melbourne Unlimited Card', - city: 'Melbourne', - type: 'Unlimited Pass', - status: 'active', - price: 149.00, - originalPrice: 249.00, - discount: 40, - attractions: 25, - validFrom: '2024-01-15', - validUntil: '2024-01-22', - daysRemaining: 3, - image: 'https://images.unsplash.com/photo-1514395462725-fb4566210144?w=400', - usedAttractions: 8 - }, - { - id: '2', - name: 'Melbourne Selective Card', - city: 'Melbourne', - type: 'Flexi Pass', - status: 'active', - price: 89.00, - originalPrice: 149.00, - discount: 40, - attractions: 12, - validFrom: '2024-02-01', - validUntil: '2024-02-08', - daysRemaining: 12, - image: 'https://images.unsplash.com/photo-1502602898536-47ad22581b52?w=400', - usedAttractions: 3 - }, - { - id: '3', - name: 'Sydney Explorer Pass', - city: 'Sydney', - type: 'Standard Pass', - status: 'expired', - price: 89.00, - originalPrice: 149.00, - discount: 40, - attractions: 15, - validFrom: '2023-12-01', - validUntil: '2023-12-08', - daysRemaining: 0, - image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400', - usedAttractions: 12 - } -]; - -// Mock itineraries data -const mockItineraries = [ - { - id: '1', - name: 'Melbourne Unlimited Card', - city: 'Melbourne', - duration: '7 days', - attractions: 25, - createdDate: '2024-01-15', - status: 'active' - } -]; - export function ProfilePage({ onBackClick, onHomeClick, @@ -342,21 +277,6 @@ export function ProfilePage({
- {/* */}