Merge pull request 'main' (#20) from main into testing
All checks were successful
CityCards-Website / Build-CityCards-Website (push) Successful in 22s
All checks were successful
CityCards-Website / Build-CityCards-Website (push) Successful in 22s
Reviewed-on: #20
This commit is contained in:
@@ -293,7 +293,7 @@ export function AppRouter({
|
||||
<Route path="/payment/:bookingId" element={
|
||||
<motion.div key="super-savings" {...pageTransition}>
|
||||
<PaymentDetailsPage {...commonNavHandlers} />
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
} />
|
||||
<Route path="/super-savings/:id" element={
|
||||
<motion.div key="super-savings" {...pageTransition}>
|
||||
|
||||
@@ -8,8 +8,6 @@ export const itineraryApi = createApi({
|
||||
|
||||
endpoints: (builder) => ({
|
||||
|
||||
|
||||
|
||||
createMagicItinerary: builder.mutation({
|
||||
query: (itineraryDetails) => ({ // keep the name of the variables being passed here same as when calling the mutation hook
|
||||
url: `/website/itinerary`,
|
||||
@@ -24,7 +22,11 @@ export const itineraryApi = createApi({
|
||||
}),
|
||||
|
||||
getUserItineraries: builder.query({
|
||||
query: () => `/website/itinerary/all-initineraries`,
|
||||
query: (cityId) => {
|
||||
const params = new URLSearchParams()
|
||||
params.append('cityId', cityId);
|
||||
return `/website/itinerary/all-initineraries?${params.toString()}`
|
||||
}
|
||||
}),
|
||||
|
||||
})
|
||||
|
||||
@@ -17,13 +17,13 @@ export function FooterBottom({ onPrivacyPolicyClick }: FooterBottomProps) {
|
||||
<div className="flex flex-col lg:flex-row justify-between items-center space-y-6 lg:space-y-0">
|
||||
{/* Copyright */}
|
||||
<p className="text-white/60 text-sm">
|
||||
© 2024 CityCards. All rights reserved.
|
||||
© 2026 CityCards. All rights reserved.
|
||||
</p>
|
||||
|
||||
{/* Right Section - Legal Links and Social Icons */}
|
||||
<div className="flex flex-col md:flex-row items-center space-y-4 md:space-y-0 md:space-x-8">
|
||||
{/* Legal Links */}
|
||||
<div className="flex space-x-6 text-sm">
|
||||
{/* <div className="flex space-x-6 text-sm">
|
||||
<motion.button
|
||||
onClick={onPrivacyPolicyClick}
|
||||
className="text-white/70 hover:text-white transition-colors duration-200"
|
||||
@@ -48,7 +48,7 @@ export function FooterBottom({ onPrivacyPolicyClick }: FooterBottomProps) {
|
||||
>
|
||||
Cookie Policy
|
||||
</motion.a>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* Social Icons - Horizontal Layout */}
|
||||
<div className="flex space-x-3">
|
||||
|
||||
@@ -1,37 +1,21 @@
|
||||
import { motion } from 'motion/react';
|
||||
import { footerSections } from '../utils/footerConstants';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
interface FooterNavigationProps {
|
||||
onHomeClick?: () => void;
|
||||
onMelbourneClick?: () => void;
|
||||
onPassesClick?: () => void;
|
||||
onSignInClick?: () => void;
|
||||
onAttractionsClick?: () => void;
|
||||
onBlogsClick?: () => void;
|
||||
onHowItWorksClick?: () => void;
|
||||
onFAQClick?: () => void;
|
||||
onPrivacyPolicyClick?: () => void;
|
||||
onAboutUsClick?: () => void;
|
||||
onContactUsClick?: () => void;
|
||||
currentPage?: string;
|
||||
}
|
||||
const linkRoutes: Record<string, string> = {
|
||||
'Home': '/',
|
||||
// 'Cancellation policy': '/cancellation-policy',
|
||||
'How It Works': '/how-it-works',
|
||||
'FAQ': '/faq',
|
||||
'Blog': '/blogs',
|
||||
'Contact Us': '/contact-us',
|
||||
'Privacy Policy': '/privacy-policy',
|
||||
// 'Terms of Service': '/terms',
|
||||
};
|
||||
|
||||
export function FooterNavigation({
|
||||
onHomeClick,
|
||||
onMelbourneClick,
|
||||
onPassesClick,
|
||||
onSignInClick,
|
||||
onAttractionsClick,
|
||||
onBlogsClick,
|
||||
onHowItWorksClick,
|
||||
onFAQClick,
|
||||
onPrivacyPolicyClick,
|
||||
onAboutUsClick,
|
||||
onContactUsClick,
|
||||
currentPage
|
||||
}: FooterNavigationProps) {
|
||||
export function FooterNavigation() {
|
||||
return (
|
||||
<div className="lg:col-span-8 grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
<div className="lg:col-span-8 grid grid-cols-2 md:grid-cols-3 gap-8">
|
||||
{Object.entries(footerSections).map(([key, section]) => (
|
||||
<motion.div
|
||||
key={key}
|
||||
@@ -45,50 +29,20 @@ export function FooterNavigation({
|
||||
}}
|
||||
>
|
||||
<h4 className="font-semibold text-white">{section.title}</h4>
|
||||
|
||||
<ul className="space-y-3">
|
||||
{section.links.map((link, index) => {
|
||||
const getClickHandler = () => {
|
||||
switch (link) {
|
||||
case 'Home': return onHomeClick;
|
||||
case 'Melbourne': return onMelbourneClick;
|
||||
case 'Passes': return onPassesClick;
|
||||
case 'Sign In': return onSignInClick;
|
||||
case 'Attractions': return onAttractionsClick;
|
||||
case 'Blog': return onBlogsClick;
|
||||
case 'How It Works': return onHowItWorksClick;
|
||||
case 'FAQ': return onFAQClick;
|
||||
case 'Privacy Policy': return onPrivacyPolicyClick;
|
||||
case 'Contact Us': return onContactUsClick;
|
||||
default: return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const clickHandler = getClickHandler();
|
||||
|
||||
return (
|
||||
<li key={link}>
|
||||
{clickHandler ? (
|
||||
<motion.button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
clickHandler();
|
||||
}}
|
||||
className="text-white/80 hover:text-white transition-colors duration-200 text-sm text-left"
|
||||
whileHover={{ x: 4 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
{link}
|
||||
</motion.button>
|
||||
) : (
|
||||
<motion.span
|
||||
className="text-white/80 cursor-default text-sm"
|
||||
>
|
||||
{link}
|
||||
</motion.span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
{section.links.map((link) => (
|
||||
<li key={link}>
|
||||
<motion.div whileHover={{ x: 4 }} transition={{ duration: 0.2 }}>
|
||||
<Link
|
||||
to={linkRoutes[link] || ""}
|
||||
className="text-white/80 hover:text-white transition-colors duration-200 text-sm"
|
||||
>
|
||||
{link}
|
||||
</Link>
|
||||
</motion.div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</motion.div>
|
||||
))}
|
||||
|
||||
@@ -19,12 +19,12 @@ import {
|
||||
} from "./ui/accordion";
|
||||
|
||||
const faqData = [
|
||||
{
|
||||
id: "refund",
|
||||
question: "Can I get a refund on my Melbourne CityCard?",
|
||||
answer: "Yes, you can cancel your Melbourne CityCard and receive a full refund if you cancel at least 24 hours in advance of your selected start date. For cancellations within 24 hours, refunds are subject to our cancellation policy. Digital cards can be refunded instantly through your account.",
|
||||
icon: CreditCard
|
||||
},
|
||||
// {
|
||||
// id: "refund",
|
||||
// question: "Can I get a refund on my Melbourne CityCard?",
|
||||
// answer: "Yes, you can cancel your Melbourne CityCard and receive a full refund if you cancel at least 24 hours in advance of your selected start date. For cancellations within 24 hours, refunds are subject to our cancellation policy. Digital cards can be refunded instantly through your account.",
|
||||
// icon: CreditCard
|
||||
// },
|
||||
{
|
||||
id: "duration",
|
||||
question: "How long is my Melbourne CityCard valid?",
|
||||
|
||||
@@ -38,19 +38,56 @@ export default function RegisterPage() {
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
// Email
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleRegister = async () => {
|
||||
if (!validateForm()) return;
|
||||
|
||||
@@ -79,182 +116,182 @@ export default function RegisterPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-gray-50 w-full">
|
||||
{/* Navbar */}
|
||||
<Navbar activeCity="" />
|
||||
<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">
|
||||
{/* 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>
|
||||
{/* 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">
|
||||
<div>
|
||||
<Label htmlFor="firstName" className="font-poppins font-light">First Name</Label>
|
||||
<Input
|
||||
id="firstName"
|
||||
value={formData.firstName}
|
||||
onChange={(e) => handleInputChange('firstName', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="lastName" className="font-poppins font-light">Last Name</Label>
|
||||
<Input
|
||||
id="lastName"
|
||||
value={formData.lastName}
|
||||
onChange={(e) => handleInputChange('lastName', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="emailAddress" className="font-poppins font-light">Email Address</Label>
|
||||
<Input
|
||||
id="emailAddress"
|
||||
type="email"
|
||||
value={formData.emailAddress}
|
||||
disabled
|
||||
onChange={(e) => handleInputChange('emailAddress', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
<div>
|
||||
<Label htmlFor="isdCode" className="font-poppins font-light">ISD Code</Label>
|
||||
<Input
|
||||
id="isdCode"
|
||||
placeholder="example: +91"
|
||||
value={formData.isdCode}
|
||||
onChange={(e) => handleInputChange('isdCode', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="mobileNumber" className="font-poppins font-light">Mobile Number</Label>
|
||||
<Input
|
||||
id="mobileNumber"
|
||||
value={formData.mobileNumber}
|
||||
onChange={(e) => handleInputChange('mobileNumber', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Address */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-poppins font-semibold text-gray-800 text-lg">
|
||||
Address Information
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="address1" className="font-poppins font-light">Address Line 1</Label>
|
||||
<Input
|
||||
id="address1"
|
||||
value={formData.address1}
|
||||
onChange={(e) => handleInputChange('address1', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="address2" className="font-poppins font-light">Address Line 2</Label>
|
||||
<Input
|
||||
id="address2"
|
||||
value={formData.address2}
|
||||
onChange={(e) => handleInputChange('address2', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<Label htmlFor="city" className="font-poppins font-light">City</Label>
|
||||
<Input
|
||||
id="city"
|
||||
value={formData.city}
|
||||
onChange={(e) => handleInputChange('city', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="state" className="font-poppins font-light">State</Label>
|
||||
<Input
|
||||
id="state"
|
||||
value={formData.state}
|
||||
onChange={(e) => handleInputChange('state', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<Label htmlFor="country" className="font-poppins font-light">Country</Label>
|
||||
<Input
|
||||
id="country"
|
||||
value={formData.country}
|
||||
onChange={(e) => handleInputChange('country', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="postalCode" className="font-poppins font-light">Postal Code</Label>
|
||||
<Input
|
||||
id="postalCode"
|
||||
value={formData.postalCode}
|
||||
onChange={(e) => handleInputChange('postalCode', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
{/* 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">
|
||||
<div>
|
||||
<Label htmlFor="firstName" className="font-poppins font-light">First Name</Label>
|
||||
<Input
|
||||
id="firstName"
|
||||
value={formData.firstName}
|
||||
onChange={(e) => handleInputChange('firstName', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="lastName" className="font-poppins font-light">Last Name</Label>
|
||||
<Input
|
||||
id="lastName"
|
||||
value={formData.lastName}
|
||||
onChange={(e) => handleInputChange('lastName', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="emailAddress" className="font-poppins font-light">Email Address</Label>
|
||||
<Input
|
||||
id="emailAddress"
|
||||
type="email"
|
||||
value={formData.emailAddress}
|
||||
disabled
|
||||
onChange={(e) => handleInputChange('emailAddress', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
<div>
|
||||
<Label htmlFor="isdCode" className="font-poppins font-light">ISD Code</Label>
|
||||
<Input
|
||||
id="isdCode"
|
||||
placeholder="example: +91"
|
||||
value={formData.isdCode}
|
||||
onChange={(e) => handleInputChange('isdCode', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-2">
|
||||
<Label htmlFor="mobileNumber" className="font-poppins font-light">Mobile Number</Label>
|
||||
<Input
|
||||
id="mobileNumber"
|
||||
value={formData.mobileNumber}
|
||||
onChange={(e) => handleInputChange('mobileNumber', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Address */}
|
||||
<div className="space-y-4">
|
||||
<h3 className="font-poppins font-semibold text-gray-800 text-lg">
|
||||
Address Information
|
||||
</h3>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="address1" className="font-poppins font-light">Address Line 1</Label>
|
||||
<Input
|
||||
id="address1"
|
||||
value={formData.address1}
|
||||
onChange={(e) => handleInputChange('address1', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="address2" className="font-poppins font-light">Address Line 2</Label>
|
||||
<Input
|
||||
id="address2"
|
||||
value={formData.address2}
|
||||
onChange={(e) => handleInputChange('address2', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<Label htmlFor="city" className="font-poppins font-light">City</Label>
|
||||
<Input
|
||||
id="city"
|
||||
value={formData.city}
|
||||
onChange={(e) => handleInputChange('city', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="state" className="font-poppins font-light">State</Label>
|
||||
<Input
|
||||
id="state"
|
||||
value={formData.state}
|
||||
onChange={(e) => handleInputChange('state', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<Label htmlFor="country" className="font-poppins font-light">Country</Label>
|
||||
<Input
|
||||
id="country"
|
||||
value={formData.country}
|
||||
onChange={(e) => handleInputChange('country', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="postalCode" className="font-poppins font-light">Postal Code</Label>
|
||||
<Input
|
||||
id="postalCode"
|
||||
value={formData.postalCode}
|
||||
onChange={(e) => handleInputChange('postalCode', e.target.value)}
|
||||
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
|
||||
/>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-auto">
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-auto">
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -150,9 +150,9 @@ function CheckoutConfigCard({
|
||||
const cardMode = item?.cardType?.name === "selective_pass" ? "flexi" : "unlimited"
|
||||
const adultPrice = item?.adultPrice * noOfAdults
|
||||
const childPrice = item?.childPrice * noOfChildren
|
||||
const basePrice = adultPrice + childPrice
|
||||
const taxAmount = basePrice * 0.1
|
||||
const strikedPrice = basePrice + 20
|
||||
const basePrice = Math.round(adultPrice + childPrice)
|
||||
const taxAmount = Math.round(basePrice * 0.1)
|
||||
const strikedPrice = Math.round(basePrice + 20)
|
||||
|
||||
const [addCardToCart] = useAddCardToCartMutation()
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ export function CreateMagicItineraryPage({
|
||||
const [selectedActivity, setSelectedActivity] = useState<string | null>(null);
|
||||
const [createMagicItinerary] = useCreateMagicItineraryMutation();
|
||||
|
||||
const navigate= useNavigate()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const toggleFavorite = (activityKey: string) => {
|
||||
setFavorites(prev => {
|
||||
@@ -213,17 +213,17 @@ export function CreateMagicItineraryPage({
|
||||
|
||||
const generateItinerary = async () => {
|
||||
try {
|
||||
console.log("creating itinerary...", itineraryDetails);
|
||||
setIsGenerating(true);
|
||||
const response = await createMagicItinerary(itineraryDetails);
|
||||
console.log(response)
|
||||
setGeneratedItinerary(response);
|
||||
setShowResults(true);
|
||||
toast.success("Itinerary created successfully!");
|
||||
navigate(`/itinerary-summary/${response?.data?.id}`)
|
||||
} catch (error) {
|
||||
console.error("Error creating itinerary:", error);
|
||||
toast.error("Failed to create itinerary. Please try again.");
|
||||
if (response?.data?.id) {
|
||||
navigate(`/itinerary-summary/${response?.data?.id}`)
|
||||
toast.success("Itinerary created successfully!");
|
||||
} else {
|
||||
throw new Error(response?.error?.data?.message)
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(error.message);
|
||||
} finally {
|
||||
setIsGenerating(false);
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ export function ProfilePage({
|
||||
const { data: userDetails, isLoading } = useGetUserProfileDetailsQuery(userId)
|
||||
const [updateUserProfileDetails, { isLoading: savingChanges }] = useUpdateUserProfileDetailsMutation();
|
||||
const { data, isLoading: loadingCards } = useGetUserCardsQuery({sort,cityId})
|
||||
const { data: userItineraries, isLoading: loadingItineraries } = useGetUserItinerariesQuery({})
|
||||
const { data: userItineraries, isLoading: loadingItineraries } = useGetUserItinerariesQuery(cityId)
|
||||
|
||||
const cards = data ?? []
|
||||
const itineraries = userItineraries?.itineraries ?? []
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
export const footerSections = {
|
||||
explore: {
|
||||
title: 'Explore',
|
||||
links: ['Home', 'My Adventures', 'Cancellation policy']
|
||||
links: ['Home',
|
||||
'Cancellation policy'
|
||||
]
|
||||
},
|
||||
learn: {
|
||||
title: 'Learn',
|
||||
links: ['How It Works', 'Safety Tips', 'FAQ', 'Blog']
|
||||
},
|
||||
community: {
|
||||
title: 'Community',
|
||||
links: ['Testimonials', 'Partner Stories', 'Events & Meetups', 'Newsletter']
|
||||
links: ['How It Works',
|
||||
// 'Safety Tips',
|
||||
'FAQ', 'Blog']
|
||||
},
|
||||
// community: {
|
||||
// title: 'Community',
|
||||
// links: ['Testimonials', 'Partner Stories', 'Events & Meetups', 'Newsletter']
|
||||
// },
|
||||
support: {
|
||||
title: 'Support',
|
||||
links: ['Contact Us', 'Privacy Policy', 'Terms of Service']
|
||||
|
||||
Reference in New Issue
Block a user