replace the registration modal with the registration page
This commit is contained in:
15
package-lock.json
generated
15
package-lock.json
generated
@@ -2257,6 +2257,7 @@
|
|||||||
"version": "9.2.0",
|
"version": "9.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-9.2.0.tgz",
|
||||||
"integrity": "sha512-YSzLC0t6VS9MDdPTynSMqU8IxrItFUjkDORALFT6sSMR/XZ5Vgm3RDp/Gk7z727MC4A9s4MFVel0gF0c7+kdrg==",
|
"integrity": "sha512-YSzLC0t6VS9MDdPTynSMqU8IxrItFUjkDORALFT6sSMR/XZ5Vgm3RDp/Gk7z727MC4A9s4MFVel0gF0c7+kdrg==",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.16"
|
"node": ">=12.16"
|
||||||
}
|
}
|
||||||
@@ -3097,6 +3098,7 @@
|
|||||||
"integrity": "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==",
|
"integrity": "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
@@ -3107,6 +3109,7 @@
|
|||||||
"integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==",
|
"integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
@@ -3117,6 +3120,7 @@
|
|||||||
"integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
|
"integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.0.0"
|
"@types/react": "^19.0.0"
|
||||||
}
|
}
|
||||||
@@ -3386,7 +3390,8 @@
|
|||||||
"version": "8.6.0",
|
"version": "8.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
|
||||||
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
|
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/embla-carousel-react": {
|
"node_modules/embla-carousel-react": {
|
||||||
"version": "8.6.0",
|
"version": "8.6.0",
|
||||||
@@ -3981,6 +3986,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -4038,6 +4044,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
@@ -4064,6 +4071,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"scheduler": "^0.23.2"
|
"scheduler": "^0.23.2"
|
||||||
@@ -4099,6 +4107,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/use-sync-external-store": "^0.0.6",
|
"@types/use-sync-external-store": "^0.0.6",
|
||||||
"use-sync-external-store": "^1.4.0"
|
"use-sync-external-store": "^1.4.0"
|
||||||
@@ -4301,7 +4310,8 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/redux-thunk": {
|
"node_modules/redux-thunk": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
@@ -4564,6 +4574,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||||
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.4.4",
|
"fdir": "^6.4.4",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import { PaymentCancelPage } from './pages/PaymentCancelPage';
|
|||||||
import { ItineraryViewPage } from './pages/ItineraryViewPage';
|
import { ItineraryViewPage } from './pages/ItineraryViewPage';
|
||||||
import { CheckoutPage } from './pages/CheckoutPage';
|
import { CheckoutPage } from './pages/CheckoutPage';
|
||||||
import { CreateMagicItineraryPage } from './pages/CreateMagicIternaryPage';
|
import { CreateMagicItineraryPage } from './pages/CreateMagicIternaryPage';
|
||||||
|
import RegisterPage from './components/RegisterPage';
|
||||||
|
|
||||||
// User type definition
|
// User type definition
|
||||||
interface User {
|
interface User {
|
||||||
@@ -283,6 +284,11 @@ export function AppRouter({
|
|||||||
<CheckoutPage {...commonNavHandlers} />
|
<CheckoutPage {...commonNavHandlers} />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
} />
|
} />
|
||||||
|
<Route path="/register" element={
|
||||||
|
<motion.div key="register" {...pageTransition}>
|
||||||
|
<RegisterPage {...commonNavHandlers} />
|
||||||
|
</motion.div>
|
||||||
|
} />
|
||||||
|
|
||||||
<Route path="/payment/:bookingId" element={
|
<Route path="/payment/:bookingId" element={
|
||||||
<motion.div key="super-savings" {...pageTransition}>
|
<motion.div key="super-savings" {...pageTransition}>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Label } from './ui/label';
|
|||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
import { useLoginMutation, useVerifyOtpMutation } from '../Redux/services/auth.service';
|
import { useLoginMutation, useVerifyOtpMutation } from '../Redux/services/auth.service';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { RegisterModal } from './RegisterModal';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
interface LoginModalProps {
|
interface LoginModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -24,6 +24,7 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) {
|
|||||||
const [showRegisterModal, setShowRegisterModal] = useState(false);
|
const [showRegisterModal, setShowRegisterModal] = useState(false);
|
||||||
|
|
||||||
const { login } = useAuth();
|
const { login } = useAuth();
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const [sendOtp, { isLoading: isSendingOtp }] = useLoginMutation();
|
const [sendOtp, { isLoading: isSendingOtp }] = useLoginMutation();
|
||||||
const [verifyOtp, { isLoading: isVerifying }] = useVerifyOtpMutation();
|
const [verifyOtp, { isLoading: isVerifying }] = useVerifyOtpMutation();
|
||||||
@@ -155,7 +156,11 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
login(userData);
|
login(userData);
|
||||||
toast.success("User Logged in successfully")
|
if (!response?.data?.userExists) {
|
||||||
|
navigate("/register")
|
||||||
|
} else {
|
||||||
|
toast.success("User Logged in successfully")
|
||||||
|
}
|
||||||
onClose();
|
onClose();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err?.data?.message || 'Invalid OTP. Please try again.');
|
setError(err?.data?.message || 'Invalid OTP. Please try again.');
|
||||||
@@ -234,7 +239,7 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowRegisterModal(true)}
|
onClick={() => navigate("/register")}
|
||||||
className="font-poppins text-sm text-gray-600 hover:text-gray-800 transition-colors cursor-pointer"
|
className="font-poppins text-sm text-gray-600 hover:text-gray-800 transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
Don't have an account? <span className="text-primary font-semibold">Register</span>
|
Don't have an account? <span className="text-primary font-semibold">Register</span>
|
||||||
@@ -314,15 +319,6 @@ export function LoginModal({ isOpen, onClose }: LoginModalProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
</AnimatePresence >
|
</AnimatePresence >
|
||||||
<RegisterModal
|
|
||||||
isOpen={showRegisterModal}
|
|
||||||
onClose={() => setShowRegisterModal(false)}
|
|
||||||
onLoginClick={() => {
|
|
||||||
setShowRegisterModal(false);
|
|
||||||
setStep('email');
|
|
||||||
setEmail('');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,391 +0,0 @@
|
|||||||
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 (
|
|
||||||
<AnimatePresence>
|
|
||||||
{isOpen && (
|
|
||||||
<>
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
exit={{ opacity: 0 }}
|
|
||||||
transition={{ duration: 0.2 }}
|
|
||||||
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50"
|
|
||||||
onClick={onClose}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
|
||||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
||||||
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
|
||||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
|
||||||
className="fixed inset-0 flex items-center justify-center z-50 p-4 overflow-y-auto"
|
|
||||||
>
|
|
||||||
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-2xl mx-auto overflow-hidden max-h-[90vh] overflow-y-auto">
|
|
||||||
<div className="relative px-8 pt-8 pb-4 top-0 bg-white z-10">
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="absolute top-6 right-6 w-8 h-8 flex items-center justify-center rounded-full bg-gray-100 hover:bg-gray-200 transition-colors cursor-pointer"
|
|
||||||
>
|
|
||||||
<X className="w-4 h-4 text-gray-600" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<h2 className="font-merchant text-2xl font-semibold text-gray-900 mb-2">
|
|
||||||
Create Account
|
|
||||||
</h2>
|
|
||||||
<p className="font-poppins text-sm text-gray-600">
|
|
||||||
Register to get started with City Cards
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="px-8 pb-8">
|
|
||||||
<div className="space-y-6">
|
|
||||||
{/* Personal Information */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h3 className="font-poppins text-base font-semibold text-gray-800">Personal Information</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
First Name <span className="text-red-500">*</span>
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
placeholder="Enter your first name"
|
|
||||||
value={formData.firstName}
|
|
||||||
onChange={(e) => handleInputChange('firstName', e.target.value)}
|
|
||||||
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
Last Name <span className="text-red-500">*</span>
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
placeholder="Enter your last name"
|
|
||||||
value={formData.lastName}
|
|
||||||
onChange={(e) => handleInputChange('lastName', e.target.value)}
|
|
||||||
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
Email Address <span className="text-red-500">*</span>
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
type="email"
|
|
||||||
placeholder="Enter your email address"
|
|
||||||
value={formData.emailAddress}
|
|
||||||
onChange={(e) => handleInputChange('emailAddress', e.target.value)}
|
|
||||||
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
ISD Code
|
|
||||||
</Label>
|
|
||||||
<Select value={formData.isdCode} onValueChange={(value: any) => handleInputChange('isdCode', value)}>
|
|
||||||
<SelectTrigger className="h-12 bg-gray-50 border-0 rounded-xl cursor-pointer">
|
|
||||||
<SelectValue placeholder="Select code" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="+1">+1 (USA)</SelectItem>
|
|
||||||
<SelectItem value="+44">+44 (UK)</SelectItem>
|
|
||||||
<SelectItem value="+61">+61 (Australia)</SelectItem>
|
|
||||||
<SelectItem value="+91">+91 (India)</SelectItem>
|
|
||||||
<SelectItem value="+86">+86 (China)</SelectItem>
|
|
||||||
<SelectItem value="+81">+81 (Japan)</SelectItem>
|
|
||||||
<SelectItem value="+49">+49 (Germany)</SelectItem>
|
|
||||||
<SelectItem value="+33">+33 (France)</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="md:col-span-2 space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
Mobile Number <span className="text-red-500">*</span>
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
type="tel"
|
|
||||||
placeholder="Enter your mobile number"
|
|
||||||
value={formData.mobileNumber}
|
|
||||||
onChange={(e) => handleInputChange('mobileNumber', e.target.value)}
|
|
||||||
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Address Information */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h3 className="font-poppins text-base font-semibold text-gray-800">Address Information</h3>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
Address Line 1 <span className="text-red-500">*</span>
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
placeholder="Enter street address"
|
|
||||||
value={formData.address1}
|
|
||||||
onChange={(e) => handleInputChange('address1', e.target.value)}
|
|
||||||
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
Address Line 2
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
placeholder="Enter apartment, suite, unit (optional)"
|
|
||||||
value={formData.address2}
|
|
||||||
onChange={(e) => handleInputChange('address2', e.target.value)}
|
|
||||||
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
City <span className="text-red-500">*</span>
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
placeholder="Enter city name"
|
|
||||||
value={formData.city}
|
|
||||||
onChange={(e) => handleInputChange('city', e.target.value)}
|
|
||||||
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
State <span className="text-red-500">*</span>
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
placeholder="Enter state name"
|
|
||||||
value={formData.state}
|
|
||||||
onChange={(e) => handleInputChange('state', e.target.value)}
|
|
||||||
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
Country <span className="text-red-500">*</span>
|
|
||||||
</Label>
|
|
||||||
<Select value={formData.country} onValueChange={(value: any) => handleInputChange('country', value)}>
|
|
||||||
<SelectTrigger className="h-12 bg-gray-50 border-0 rounded-xl cursor-pointer">
|
|
||||||
<SelectValue placeholder="Select country" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="Australia">Australia</SelectItem>
|
|
||||||
<SelectItem value="United States">United States</SelectItem>
|
|
||||||
<SelectItem value="United Kingdom">United Kingdom</SelectItem>
|
|
||||||
<SelectItem value="Canada">Canada</SelectItem>
|
|
||||||
<SelectItem value="India">India</SelectItem>
|
|
||||||
<SelectItem value="Germany">Germany</SelectItem>
|
|
||||||
<SelectItem value="France">France</SelectItem>
|
|
||||||
<SelectItem value="Japan">Japan</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="font-poppins text-sm font-medium text-gray-700">
|
|
||||||
Postal Code <span className="text-red-500">*</span>
|
|
||||||
</Label>
|
|
||||||
<Input
|
|
||||||
placeholder="Enter postal code"
|
|
||||||
value={formData.postalCode}
|
|
||||||
onChange={(e) => handleInputChange('postalCode', e.target.value)}
|
|
||||||
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{helperText && (
|
|
||||||
<p className={`font-poppins text-xs ${helperText.includes('successful') ? 'text-green-600' : 'text-red-500'}`}>
|
|
||||||
{helperText}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onClick={handleRegister}
|
|
||||||
disabled={isLoading || isRegistering}
|
|
||||||
className="w-full h-12 bg-gray-800 hover:bg-gray-900 text-white font-poppins font-semibold rounded-xl transition-colors cursor-pointer"
|
|
||||||
>
|
|
||||||
{isLoading || isRegistering ? (
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="w-4 h-4 border-2 border-white/20 border-t-white rounded-full animate-spin" />
|
|
||||||
Creating Account...
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
Register
|
|
||||||
<svg className="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
||||||
</svg>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<div className="text-center">
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
onLoginClick();
|
|
||||||
onClose();
|
|
||||||
}}
|
|
||||||
className="font-poppins text-sm text-gray-600 hover:text-gray-800 transition-colors cursor-pointer"
|
|
||||||
>
|
|
||||||
Already have an account? <span className="text-primary font-semibold">Login</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
226
src/components/RegisterPage.tsx
Normal file
226
src/components/RegisterPage.tsx
Normal file
@@ -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 (
|
||||||
|
<div className="min-h-screen flex flex-col bg-gray-50 w-full">
|
||||||
|
{/* Navbar */}
|
||||||
|
<Navbar activeCity="" />
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="flex-grow w-full px-6 md:px-10 py-10 mt-20">
|
||||||
|
|
||||||
|
<div className="w-full max-w-5xl mx-auto">
|
||||||
|
|
||||||
|
{/* Header */}
|
||||||
|
<div className="mb-8">
|
||||||
|
<h2 className="font-merchant text-3xl font-semibold text-gray-900 mb-2">
|
||||||
|
Create Account
|
||||||
|
</h2>
|
||||||
|
<p className="font-poppins text-gray-600">
|
||||||
|
Register to get started with City Cards
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Form Container */}
|
||||||
|
<div className="bg-white rounded-2xl border border-gray-200 p-8 space-y-8">
|
||||||
|
|
||||||
|
{/* Personal Info */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h3 className="font-poppins font-semibold text-gray-800 text-lg">
|
||||||
|
Personal Information
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
<Input
|
||||||
|
placeholder="First Name"
|
||||||
|
value={formData.firstName}
|
||||||
|
onChange={(e) => handleInputChange('firstName', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="Last Name"
|
||||||
|
value={formData.lastName}
|
||||||
|
onChange={(e) => handleInputChange('lastName', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
placeholder="Email Address"
|
||||||
|
value={emailAddress}
|
||||||
|
onChange={(e) => handleInputChange('emailAddress', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-3 gap-6">
|
||||||
|
<Input
|
||||||
|
placeholder="ISD Code"
|
||||||
|
value={formData.isdCode}
|
||||||
|
onChange={(e) => handleInputChange('isdCode', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
<div className="md:col-span-2">
|
||||||
|
<Input
|
||||||
|
placeholder="Mobile Number"
|
||||||
|
value={formData.mobileNumber}
|
||||||
|
onChange={(e) => handleInputChange('mobileNumber', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Address */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h3 className="font-poppins font-semibold text-gray-800 text-lg">
|
||||||
|
Address Information
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
placeholder="Address Line 1"
|
||||||
|
value={formData.address1}
|
||||||
|
onChange={(e) => handleInputChange('address1', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
placeholder="Address Line 2"
|
||||||
|
value={formData.address2}
|
||||||
|
onChange={(e) => handleInputChange('address2', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
<Input
|
||||||
|
placeholder="City"
|
||||||
|
value={formData.city}
|
||||||
|
onChange={(e) => handleInputChange('city', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="State"
|
||||||
|
value={formData.state}
|
||||||
|
onChange={(e) => handleInputChange('state', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
<Input
|
||||||
|
placeholder="Country"
|
||||||
|
value={formData.country}
|
||||||
|
onChange={(e) => handleInputChange('country', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="Postal Code"
|
||||||
|
value={formData.postalCode}
|
||||||
|
onChange={(e) => handleInputChange('postalCode', e.target.value)}
|
||||||
|
className="h-12 bg-gray-50 border-0 rounded-xl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{helperText && (
|
||||||
|
<p className="text-sm text-red-500">{helperText}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={handleRegister}
|
||||||
|
disabled={isLoading || isRegistering}
|
||||||
|
className="w-full cursor-pointer bg-gray-800 hover:bg-gray-900 md:px-10 h-12 text-white rounded-xl"
|
||||||
|
>
|
||||||
|
{isLoading || isRegistering ? 'Registering...' : 'Register'}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<div className="mt-auto">
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -60,71 +60,6 @@ interface ProfilePageProps {
|
|||||||
currentPage: string;
|
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({
|
export function ProfilePage({
|
||||||
onBackClick,
|
onBackClick,
|
||||||
onHomeClick,
|
onHomeClick,
|
||||||
@@ -342,21 +277,6 @@ export function ProfilePage({
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="country" className="font-poppins font-light">Country</Label>
|
<Label htmlFor="country" className="font-poppins font-light">Country</Label>
|
||||||
{/* <Select value={formData.country} onValueChange={(value) => handleInputChange('country', value)}>
|
|
||||||
<SelectTrigger className="mt-1">
|
|
||||||
<SelectValue placeholder="Select country" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="us">United States</SelectItem>
|
|
||||||
<SelectItem value="au">Australia</SelectItem>
|
|
||||||
<SelectItem value="uk">United Kingdom</SelectItem>
|
|
||||||
<SelectItem value="ca">Canada</SelectItem>
|
|
||||||
<SelectItem value="de">Germany</SelectItem>
|
|
||||||
<SelectItem value="fr">France</SelectItem>
|
|
||||||
<SelectItem value="in">India</SelectItem>
|
|
||||||
<SelectItem value="jp">Japan</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select> */}
|
|
||||||
<Input
|
<Input
|
||||||
id="country"
|
id="country"
|
||||||
value={formData.country}
|
value={formData.country}
|
||||||
|
|||||||
Reference in New Issue
Block a user