Merge pull request 'fix : call button' (#26) from feature/email into main
continuous-integration/drone/push Build is passing Details

Reviewed-on: #26
This commit is contained in:
hardik 2025-10-16 10:24:14 +05:30
commit a59f82018d
9 changed files with 405 additions and 374 deletions

File diff suppressed because one or more lines are too long

View File

@ -170,12 +170,12 @@ export function BookingConfirmation({
{bookingData.dropoff.includes("Airport Taxi") ? "Airport Taxi Fee" : "Customize Price"} {bookingData.dropoff.includes("Airport Taxi") ? "Airport Taxi Fee" : "Customize Price"}
</span> </span>
<span className="text-xl font-semibold text-green-600"> <span className="text-xl font-semibold text-green-600">
{bookingData.dropoff.includes("Airport Taxi") ? "₹1099" : "Call for Quote"} {bookingData.dropoff.includes("Airport Taxi") ? "₹999" : "Call for Quote"}
</span> </span>
</div> </div>
<div className="text-sm text-muted-foreground text-center"> <div className="text-sm text-muted-foreground text-center">
{bookingData.dropoff.includes("Airport Taxi") {bookingData.dropoff.includes("Airport Taxi")
? `1099 fixed rate for airport taxi. Driver will contact you at ${bookingData.contactNumber} for pickup details.` ? `999 fixed rate for airport taxi. Driver will contact you at ${bookingData.contactNumber} for pickup details.`
: `Driver will contact you at ${bookingData.contactNumber} for final pricing and trip details.`} : `Driver will contact you at ${bookingData.contactNumber} for final pricing and trip details.`}
</div> </div>
</div> </div>

View File

@ -37,7 +37,7 @@ export function BookingForm({ onSubmit }: BookingFormProps) {
const currentDate = new Date().toISOString().split('T')[0]; const currentDate = new Date().toISOString().split('T')[0];
const currentTime = new Date().toTimeString().slice(0, 5); const currentTime = new Date().toTimeString().slice(0, 5);
return ( return (
@ -48,248 +48,248 @@ export function BookingForm({ onSubmit }: BookingFormProps) {
className="w-full max-w-2xl mx-auto" className="w-full max-w-2xl mx-auto"
> >
<Card className="w-full bg-white border-0 shadow-none"> <Card className="w-full bg-white border-0 shadow-none">
<CardHeader className="text-center pb-4 sm:pb-6 lg:pb-8 bg-transparent px-4 sm:px-6 rounded-[0px]"> <CardHeader className="text-center pb-4 sm:pb-6 lg:pb-8 bg-transparent px-4 sm:px-6 rounded-[0px]">
<motion.div <motion.div
initial={{ opacity: 0, y: -20 }} initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }} transition={{ duration: 0.6 }}
> >
<CardTitle className="flex flex-col sm:flex-row items-center justify-center gap-2 text-center text-lg sm:text-xl lg:text-2xl bg-gradient-to-r from-primary to-primary/80 bg-clip-text text-transparent"> <CardTitle className="flex flex-col sm:flex-row items-center justify-center gap-2 text-center text-lg sm:text-xl lg:text-2xl bg-gradient-to-r from-primary to-primary/80 bg-clip-text text-transparent">
<div className="mb-1 sm:mb-0"> <div className="mb-1 sm:mb-0">
<MapPin className="h-5 w-5 sm:h-6 sm:w-6 text-primary" /> <MapPin className="h-5 w-5 sm:h-6 sm:w-6 text-primary" />
</div> </div>
<span className="font-bold leading-tight text-[32px]">Book Your Airport Taxi Now</span> <span className="font-bold leading-tight text-[32px]">Book Your Airport Taxi Now</span>
</CardTitle> </CardTitle>
</motion.div> </motion.div>
<motion.div <motion.div
className="w-16 sm:w-20 lg:w-24 h-1 bg-gradient-to-r from-primary/30 via-primary to-primary/30 mx-auto mt-2 sm:mt-3 lg:mt-4 rounded-full" className="w-16 sm:w-20 lg:w-24 h-1 bg-gradient-to-r from-primary/30 via-primary to-primary/30 mx-auto mt-2 sm:mt-3 lg:mt-4 rounded-full"
initial={{ scaleX: 0 }} initial={{ scaleX: 0 }}
animate={{ scaleX: 1 }} animate={{ scaleX: 1 }}
transition={{ delay: 0.3, duration: 0.8 }} transition={{ delay: 0.3, duration: 0.8 }}
/> />
</CardHeader> </CardHeader>
<CardContent className="bg-transparent px-4 sm:px-6 pt-[0px] pr-[21px] pb-[21px] pl-[21px]"> <CardContent className="bg-transparent px-4 sm:px-6 pt-[0px] pr-[21px] pb-[21px] pl-[21px]">
<form onSubmit={handleSubmit} className="space-y-4 sm:space-y-6"> <form onSubmit={handleSubmit} className="space-y-4 sm:space-y-6">
{/* Location Inputs */} {/* Location Inputs */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div className="space-y-2">
<Label htmlFor="pickup" className="text-sm sm:text-base">Pickup Location</Label>
<Select value={formData.pickup} onValueChange={(value) => handleInputChange("pickup", value)} required>
<SelectTrigger className="h-10 sm:h-11">
<SelectValue placeholder="Select pickup city" />
</SelectTrigger>
<SelectContent className="max-h-48">
{indiaLocations.map((location) => (
<SelectItem key={location} value={location}>
{location}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="dropoff" className="text-sm sm:text-base">Drop-off Location / Service</Label>
<Select value={formData.dropoff} onValueChange={(value) => handleInputChange("dropoff", value)} required>
<SelectTrigger className="h-10 sm:h-11">
<SelectValue placeholder="Select destination or service" />
</SelectTrigger>
<SelectContent className="max-h-48">
<div className="px-2 py-1 text-xs text-muted-foreground border-b">Services</div>
{serviceTypes.map((service) => (
<SelectItem key={service} value={service}>
{service}
</SelectItem>
))}
<div className="px-2 py-1 text-xs text-muted-foreground border-b">All India Locations</div>
{indiaLocations.map((location) => (
<SelectItem key={location} value={location}>
{location}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
{/* Date and Time */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div className="space-y-2">
<Label htmlFor="date" className="flex items-center gap-2 text-sm sm:text-base">
<Calendar className="h-3 w-3 sm:h-4 sm:w-4" />
Date
</Label>
<Input
id="date"
type="date"
min={currentDate}
value={formData.date}
onChange={(e) => handleInputChange("date", e.target.value)}
className="h-10 sm:h-11"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="time" className="flex items-center gap-2 text-sm sm:text-base">
<Clock className="h-3 w-3 sm:h-4 sm:w-4" />
Time
</Label>
<Input
id="time"
type="time"
value={formData.time}
onChange={(e) => handleInputChange("time", e.target.value)}
className="h-10 sm:h-11"
required
/>
</div>
</div>
{/* Contact Information */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div className="space-y-2">
<Label htmlFor="contactName" className="flex items-center gap-2 text-sm sm:text-base">
<User className="h-3 w-3 sm:h-4 sm:w-4" />
Contact Name
</Label>
<Input
id="contactName"
type="text"
placeholder="Enter your name"
value={formData.contactName}
onChange={(e) => handleInputChange("contactName", e.target.value)}
className="h-10 sm:h-11"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="contactNumber" className="flex items-center gap-2 text-sm sm:text-base">
<Phone className="h-3 w-3 sm:h-4 sm:w-4" />
Contact Number
</Label>
<Input
id="contactNumber"
type="tel"
placeholder="Enter your contact number"
value={formData.contactNumber}
onChange={(e) => handleInputChange("contactNumber", e.target.value)}
className="h-10 sm:h-11"
required
/>
</div>
</div>
{/* Email (Optional) */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="pickup" className="text-sm sm:text-base">Pickup Location</Label> <Label htmlFor="customerEmail" className="flex items-center gap-2 text-sm sm:text-base">
<Select value={formData.pickup} onValueChange={(value) => handleInputChange("pickup", value)} required> <Mail className="h-3 w-3 sm:h-4 sm:w-4" />
<SelectTrigger className="h-10 sm:h-11"> Email Address <span className="text-xs text-muted-foreground">(For booking confirmation)</span>
<SelectValue placeholder="Select pickup city" /> </Label>
</SelectTrigger> <Input
<SelectContent className="max-h-48"> id="customerEmail"
{indiaLocations.map((location) => ( type="email"
<SelectItem key={location} value={location}> placeholder="Enter your email address"
{location} // value={formData.customerEmail}
onChange={(e) => handleInputChange("customerEmail", e.target.value)}
className="h-10 sm:h-11"
/>
</div>
{/* Passengers and Vehicle Type */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div className="space-y-2">
<Label className="flex items-center gap-2 text-sm sm:text-base">
<Users className="h-3 w-3 sm:h-4 sm:w-4" />
Passengers
</Label>
<Select value={formData.passengers} onValueChange={(value) => handleInputChange("passengers", value)}>
<SelectTrigger className="h-10 sm:h-11">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">1 Passenger</SelectItem>
<SelectItem value="2">2 Passengers</SelectItem>
<SelectItem value="3">3 Passengers</SelectItem>
<SelectItem value="4">4 Passengers</SelectItem>
<SelectItem value="5">5 Passengers</SelectItem>
<SelectItem value="6">6 Passengers</SelectItem>
<SelectItem value="7">7 Passengers</SelectItem>
<SelectItem value="8">8+ Passengers</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label className="text-sm sm:text-base">Vehicle Type</Label>
<Select value={formData.vehicleType} onValueChange={(value) => handleInputChange("vehicleType", value)} required>
<SelectTrigger className="h-10 sm:h-11">
<SelectValue placeholder="Select vehicle" />
</SelectTrigger>
<SelectContent>
<SelectItem value="5-seater">
<span className="text-xs sm:text-sm">5 Seater - Airport Taxi </span>
</SelectItem> </SelectItem>
))} <SelectItem value="7-seater">
</SelectContent> <span className="text-xs sm:text-sm">7 Seater (Ertiga) - Airport Taxi </span>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="dropoff" className="text-sm sm:text-base">Drop-off Location / Service</Label>
<Select value={formData.dropoff} onValueChange={(value) => handleInputChange("dropoff", value)} required>
<SelectTrigger className="h-10 sm:h-11">
<SelectValue placeholder="Select destination or service" />
</SelectTrigger>
<SelectContent className="max-h-48">
<div className="px-2 py-1 text-xs text-muted-foreground border-b">Services</div>
{serviceTypes.map((service) => (
<SelectItem key={service} value={service}>
{service}
</SelectItem> </SelectItem>
))} <SelectItem value="innova">
<div className="px-2 py-1 text-xs text-muted-foreground border-b">All India Locations</div> <span className="text-xs sm:text-sm">Innova - Airport Taxi </span>
{indiaLocations.map((location) => (
<SelectItem key={location} value={location}>
{location}
</SelectItem> </SelectItem>
))} </SelectContent>
</SelectContent> </Select>
</Select> </div>
</div> </div>
</div>
{/* Date and Time */} <div className="space-y-3 sm:space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div className="space-y-2">
<Label htmlFor="date" className="flex items-center gap-2 text-sm sm:text-base">
<Calendar className="h-3 w-3 sm:h-4 sm:w-4" />
Date
</Label>
<Input
id="date"
type="date"
min={currentDate}
value={formData.date}
onChange={(e) => handleInputChange("date", e.target.value)}
className="h-10 sm:h-11"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="time" className="flex items-center gap-2 text-sm sm:text-base">
<Clock className="h-3 w-3 sm:h-4 sm:w-4" />
Time
</Label>
<Input
id="time"
type="time"
value={formData.time}
onChange={(e) => handleInputChange("time", e.target.value)}
className="h-10 sm:h-11"
required
/>
</div>
</div>
{/* Contact Information */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div className="space-y-2">
<Label htmlFor="contactName" className="flex items-center gap-2 text-sm sm:text-base">
<User className="h-3 w-3 sm:h-4 sm:w-4" />
Contact Name
</Label>
<Input
id="contactName"
type="text"
placeholder="Enter your name"
value={formData.contactName}
onChange={(e) => handleInputChange("contactName", e.target.value)}
className="h-10 sm:h-11"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="contactNumber" className="flex items-center gap-2 text-sm sm:text-base">
<Phone className="h-3 w-3 sm:h-4 sm:w-4" />
Contact Number
</Label>
<Input
id="contactNumber"
type="tel"
placeholder="Enter your contact number"
value={formData.contactNumber}
onChange={(e) => handleInputChange("contactNumber", e.target.value)}
className="h-10 sm:h-11"
required
/>
</div>
</div>
{/* Email (Optional) */}
<div className="space-y-2">
<Label htmlFor="customerEmail" className="flex items-center gap-2 text-sm sm:text-base">
<Mail className="h-3 w-3 sm:h-4 sm:w-4" />
Email Address <span className="text-xs text-muted-foreground">(For booking confirmation)</span>
</Label>
<Input
id="customerEmail"
type="email"
placeholder="Enter your email address"
value={formData.customerEmail}
onChange={(e) => handleInputChange("customerEmail", e.target.value)}
className="h-10 sm:h-11"
/>
</div>
{/* Passengers and Vehicle Type */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div className="space-y-2">
<Label className="flex items-center gap-2 text-sm sm:text-base">
<Users className="h-3 w-3 sm:h-4 sm:w-4" />
Passengers
</Label>
<Select value={formData.passengers} onValueChange={(value) => handleInputChange("passengers", value)}>
<SelectTrigger className="h-10 sm:h-11">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">1 Passenger</SelectItem>
<SelectItem value="2">2 Passengers</SelectItem>
<SelectItem value="3">3 Passengers</SelectItem>
<SelectItem value="4">4 Passengers</SelectItem>
<SelectItem value="5">5 Passengers</SelectItem>
<SelectItem value="6">6 Passengers</SelectItem>
<SelectItem value="7">7 Passengers</SelectItem>
<SelectItem value="8">8+ Passengers</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label className="text-sm sm:text-base">Vehicle Type</Label>
<Select value={formData.vehicleType} onValueChange={(value) => handleInputChange("vehicleType", value)} required>
<SelectTrigger className="h-10 sm:h-11">
<SelectValue placeholder="Select vehicle" />
</SelectTrigger>
<SelectContent>
<SelectItem value="5-seater">
<span className="text-xs sm:text-sm">5 Seater - Airport Taxi </span>
</SelectItem>
<SelectItem value="7-seater">
<span className="text-xs sm:text-sm">7 Seater (Ertiga) - Airport Taxi </span>
</SelectItem>
<SelectItem value="innova">
<span className="text-xs sm:text-sm">Innova - Airport Taxi </span>
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-3 sm:space-y-4">
<motion.div
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
<Button type="submit" className="w-full h-11 sm:h-12 text-sm sm:text-base shadow-lg hover:shadow-xl transition-all duration-300 rounded-[8px]">
<span className="font-semibold">
Book Your Airport Taxi
</span>
</Button>
</motion.div>
<div className="flex items-center gap-2">
<div className="h-px bg-border flex-1"></div>
<span className="text-xs text-muted-foreground px-2">OR</span>
<div className="h-px bg-border flex-1"></div>
</div>
<motion.div
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className="relative"
>
<motion.div <motion.div
className="absolute inset-0 bg-gradient-to-r from-green-400 to-green-600 rounded-lg" whileHover={{ scale: 1.02 }}
animate={{ whileTap={{ scale: 0.98 }}
opacity: [0.8, 1, 0.8],
scale: [1, 1.02, 1]
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut"
}}
/>
<Button
type="button"
className="w-full h-11 sm:h-12 relative z-10 bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white border-0 shadow-lg"
onClick={() => window.open('tel:+91-7477247488', '_self')}
> >
<Phone className="h-3 w-3 sm:h-4 sm:w-4 mr-2" /> <Button type="submit" className="w-full h-11 sm:h-12 text-sm sm:text-base shadow-lg hover:shadow-xl transition-all duration-300 rounded-[8px]">
<div className="text-center"> <span className="font-semibold">
<div className="font-semibold text-sm sm:text-base">Call to Book Taxi</div> Book Your Airport Taxi
<div className="text-xs opacity-90">7477247488</div> </span>
</div> </Button>
</Button> </motion.div>
</motion.div>
</div> <div className="flex items-center gap-2">
</form> <div className="h-px bg-border flex-1"></div>
</CardContent> <span className="text-xs text-muted-foreground px-2">OR</span>
</Card> <div className="h-px bg-border flex-1"></div>
</div>
<motion.div
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className="relative"
>
<motion.div
className="absolute inset-0 bg-gradient-to-r from-green-400 to-green-600 rounded-lg"
animate={{
opacity: [0.8, 1, 0.8],
scale: [1, 1.02, 1]
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut"
}}
/>
<Button
type="button"
className="w-full h-11 sm:h-12 relative z-10 bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white border-0 shadow-lg"
onClick={() => window.open('tel:+91-7477247488', '_self')}
>
<Phone className="h-3 w-3 sm:h-4 sm:w-4 mr-2" />
<div className="text-center">
<div className="font-semibold text-sm sm:text-base">Call to Book Taxi</div>
<div className="text-xs opacity-90">7477247488</div>
</div>
</Button>
</motion.div>
</div>
</form>
</CardContent>
</Card>
</motion.div> </motion.div>
); );
} }

View File

@ -8,11 +8,11 @@ export function FloatingCallButton() {
className="fixed bottom-4 right-4 sm:bottom-6 sm:right-6 z-50" className="fixed bottom-4 right-4 sm:bottom-6 sm:right-6 z-50"
initial={{ scale: 0, opacity: 0 }} initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }} animate={{ scale: 1, opacity: 1 }}
transition={{ transition={{
delay: 1, delay: 1,
type: "spring", type: "spring",
stiffness: 200, stiffness: 200,
damping: 20 damping: 20,
}} }}
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -21,31 +21,34 @@ export function FloatingCallButton() {
<Button <Button
size="lg" size="lg"
className="bg-transparent hover:bg-white/10 border-0 px-3 py-2 sm:px-4 sm:py-2 h-auto rounded-full" className="bg-transparent hover:bg-white/10 border-0 px-3 py-2 sm:px-4 sm:py-2 h-auto rounded-full"
onClick={() => window.open('tel:+91-7477247488', '_self')} onClick={() => {
// Opens dialpad directly on mobile
window.location.href = "tel:+917477247488";
}}
> >
<div className="mr-1 sm:mr-2"> <div className="mr-1 sm:mr-2">
<Phone className="h-4 w-4 sm:h-5 sm:w-5 text-white" /> <Phone className="h-4 w-4 sm:h-5 sm:w-5 text-white" />
</div> </div>
<div className="text-white"> <div className="text-white text-left">
<div className="text-xs sm:text-sm font-bold">Call to Book</div> <div className="text-xs sm:text-sm font-bold">Call to Book</div>
<div className="text-xs opacity-90">7477247488</div> <div className="text-xs opacity-90">7477247488</div>
</div> </div>
</Button> </Button>
{/* Simple pulse effect */} {/* Simple pulse animation */}
<motion.div <motion.div
className="absolute inset-0 rounded-full bg-green-400/50" className="absolute inset-0 rounded-full bg-green-400/50"
animate={{ animate={{
scale: [1, 1.5, 1], scale: [1, 1.5, 1],
opacity: [0.7, 0, 0.7] opacity: [0.7, 0, 0.7],
}} }}
transition={{ transition={{
duration: 2, duration: 2,
repeat: Infinity, repeat: Infinity,
ease: "easeInOut" ease: "easeInOut",
}} }}
/> />
</div> </div>
</motion.div> </motion.div>
); );
} }

View File

@ -104,7 +104,7 @@ export function HeroSection({ children }: HeroSectionProps) {
transition={{ duration: 0.8, delay: 0.4 }} transition={{ duration: 0.8, delay: 0.4 }}
className="block text-white" className="block text-white"
> >
Durg/Bhilai-Raipur Airport Drop(Oneway) Durg/Bhilai-Raipur Airport Drop(Oneway)
</motion.span> </motion.span>
<motion.span <motion.span
className="block text-yellow-400 text-xl sm:text-3xl md:text-4xl lg:text-5xl" className="block text-yellow-400 text-xl sm:text-3xl md:text-4xl lg:text-5xl"
@ -112,7 +112,7 @@ export function HeroSection({ children }: HeroSectionProps) {
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.6 }} transition={{ duration: 0.8, delay: 0.6 }}
> >
1099 999
</motion.span> </motion.span>
</motion.h1> </motion.h1>

View File

@ -18,8 +18,8 @@ const services = [
icon: Plane, icon: Plane,
title: "Airport Taxi", title: "Airport Taxi",
description: "Comfortable and punctual airport taxi services. Never miss a flight with our dedicated airport service.", description: "Comfortable and punctual airport taxi services. Never miss a flight with our dedicated airport service.",
features: ["Flight monitoring", "Meet & greet service", "Fixed rate ₹1099"], features: ["Flight monitoring", "Meet & greet service", "Fixed rate ₹999"],
price: "₹1099", price: "₹999",
image: "https://images.unsplash.com/photo-1733222012917-e9e74636080c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhaXJwb3J0JTIwdHJhbnNmZXIlMjBjYXIlMjBzZXJ2aWNlfGVufDF8fHx8MTc1NTYwMDgwM3ww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral", image: "https://images.unsplash.com/photo-1733222012917-e9e74636080c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhaXJwb3J0JTIwdHJhbnNmZXIlMjBjYXIlMjBzZXJ2aWNlfGVufDF8fHx8MTc1NTYwMDgwM3ww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
imageAlt: "Premium car at airport terminal" imageAlt: "Premium car at airport terminal"
}, },
@ -91,7 +91,7 @@ export function ServicesSection() {
return ( return (
<div className="py-8 sm:py-12 lg:py-16"> <div className="py-8 sm:py-12 lg:py-16">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<motion.div <motion.div
className="text-center mb-8 sm:mb-10 lg:mb-12" className="text-center mb-8 sm:mb-10 lg:mb-12"
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -100,13 +100,13 @@ export function ServicesSection() {
> >
<h1 className="mb-3 sm:mb-4 text-2xl sm:text-3xl lg:text-4xl font-normal font-bold">Our Services</h1> <h1 className="mb-3 sm:mb-4 text-2xl sm:text-3xl lg:text-4xl font-normal font-bold">Our Services</h1>
<p className="text-base sm:text-lg lg:text-xl text-muted-foreground max-w-3xl mx-auto px-4"> <p className="text-base sm:text-lg lg:text-xl text-muted-foreground max-w-3xl mx-auto px-4">
From daily commutes to special occasions, we provide reliable transportation solutions From daily commutes to special occasions, we provide reliable transportation solutions
tailored to your needs with professional drivers and well-maintained vehicles. tailored to your needs with professional drivers and well-maintained vehicles.
</p> </p>
</motion.div> </motion.div>
{/* Main Services */} {/* Main Services */}
<motion.div <motion.div
className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 sm:gap-6 lg:gap-8 mb-8 sm:mb-10 lg:mb-12" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 sm:gap-6 lg:gap-8 mb-8 sm:mb-10 lg:mb-12"
variants={containerVariants} variants={containerVariants}
initial="hidden" initial="hidden"
@ -117,7 +117,7 @@ export function ServicesSection() {
<motion.div <motion.div
key={index} key={index}
variants={cardVariants} variants={cardVariants}
whileHover={{ whileHover={{
y: -8, y: -8,
transition: { duration: 0.3 } transition: { duration: 0.3 }
}} }}
@ -137,9 +137,9 @@ export function ServicesSection() {
/> />
</motion.div> </motion.div>
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
{/* Icon overlay */} {/* Icon overlay */}
<motion.div <motion.div
className="absolute bottom-4 left-4" className="absolute bottom-4 left-4"
initial={{ scale: 0, opacity: 0 }} initial={{ scale: 0, opacity: 0 }}
whileInView={{ scale: 1, opacity: 1 }} whileInView={{ scale: 1, opacity: 1 }}
@ -148,11 +148,11 @@ export function ServicesSection() {
> >
<div className="w-12 h-12 bg-white/90 backdrop-blur-sm rounded-lg flex items-center justify-center"> <div className="w-12 h-12 bg-white/90 backdrop-blur-sm rounded-lg flex items-center justify-center">
<motion.div <motion.div
animate={{ animate={{
rotate: [0, 10, -10, 0], rotate: [0, 10, -10, 0],
scale: [1, 1.1, 1] scale: [1, 1.1, 1]
}} }}
transition={{ transition={{
duration: 4, duration: 4,
repeat: Infinity, repeat: Infinity,
ease: "easeInOut" ease: "easeInOut"
@ -162,9 +162,9 @@ export function ServicesSection() {
</motion.div> </motion.div>
</div> </div>
</motion.div> </motion.div>
{/* Price badge */} {/* Price badge */}
<motion.div <motion.div
className="absolute top-4 right-4" className="absolute top-4 right-4"
initial={{ x: 50, opacity: 0 }} initial={{ x: 50, opacity: 0 }}
whileInView={{ x: 0, opacity: 1 }} whileInView={{ x: 0, opacity: 1 }}
@ -189,9 +189,9 @@ export function ServicesSection() {
</CardTitle> </CardTitle>
</motion.div> </motion.div>
</CardHeader> </CardHeader>
<CardContent className="pt-0 px-4 sm:px-6"> <CardContent className="pt-0 px-4 sm:px-6">
<motion.p <motion.p
className="text-muted-foreground mb-3 sm:mb-4 text-sm sm:text-base" className="text-muted-foreground mb-3 sm:mb-4 text-sm sm:text-base"
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }} whileInView={{ opacity: 1 }}
@ -200,8 +200,8 @@ export function ServicesSection() {
> >
{service.description} {service.description}
</motion.p> </motion.p>
<motion.div <motion.div
className="space-y-2" className="space-y-2"
initial={{ opacity: 0, y: 10 }} initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -211,21 +211,21 @@ export function ServicesSection() {
<h4>Features:</h4> <h4>Features:</h4>
<ul className="space-y-1"> <ul className="space-y-1">
{service.features.map((feature, featureIndex) => ( {service.features.map((feature, featureIndex) => (
<motion.li <motion.li
key={featureIndex} key={featureIndex}
className="flex items-center gap-2 text-sm text-muted-foreground" className="flex items-center gap-2 text-sm text-muted-foreground"
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ delay: index * 0.2 + 0.7 + featureIndex * 0.1 }} transition={{ delay: index * 0.2 + 0.7 + featureIndex * 0.1 }}
> >
<motion.div <motion.div
className="w-1.5 h-1.5 bg-primary rounded-full" className="w-1.5 h-1.5 bg-primary rounded-full"
animate={{ animate={{
scale: [1, 1.5, 1], scale: [1, 1.5, 1],
opacity: [0.7, 1, 0.7] opacity: [0.7, 1, 0.7]
}} }}
transition={{ transition={{
duration: 2, duration: 2,
repeat: Infinity, repeat: Infinity,
delay: featureIndex * 0.3 delay: featureIndex * 0.3
@ -243,14 +243,14 @@ export function ServicesSection() {
</motion.div> </motion.div>
{/* Additional Services */} {/* Additional Services */}
<motion.div <motion.div
className="bg-muted/50 rounded-lg p-8" className="bg-muted/50 rounded-lg p-8"
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.2 }} transition={{ duration: 0.6, delay: 0.2 }}
> >
<motion.h2 <motion.h2
className="mb-6 text-center" className="mb-6 text-center"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -261,8 +261,8 @@ export function ServicesSection() {
</motion.h2> </motion.h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{additionalServices.map((service, index) => ( {additionalServices.map((service, index) => (
<motion.div <motion.div
key={index} key={index}
className="flex items-center gap-4 p-4 rounded-lg hover:bg-background/50 transition-colors group" className="flex items-center gap-4 p-4 rounded-lg hover:bg-background/50 transition-colors group"
initial={{ opacity: 0, x: index % 2 === 0 ? -20 : 20 }} initial={{ opacity: 0, x: index % 2 === 0 ? -20 : 20 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
@ -270,12 +270,12 @@ export function ServicesSection() {
transition={{ delay: 0.4 + index * 0.1 }} transition={{ delay: 0.4 + index * 0.1 }}
whileHover={{ x: 5 }} whileHover={{ x: 5 }}
> >
<motion.div <motion.div
className="w-10 h-10 bg-primary/10 rounded-lg flex items-center justify-center flex-shrink-0 group-hover:bg-primary/20 transition-colors" className="w-10 h-10 bg-primary/10 rounded-lg flex items-center justify-center flex-shrink-0 group-hover:bg-primary/20 transition-colors"
animate={{ animate={{
rotate: [0, 360], rotate: [0, 360],
}} }}
transition={{ transition={{
duration: 20, duration: 20,
repeat: Infinity, repeat: Infinity,
ease: "linear" ease: "linear"
@ -293,14 +293,14 @@ export function ServicesSection() {
</motion.div> </motion.div>
{/* Service Areas */} {/* Service Areas */}
<motion.div <motion.div
className="mt-12 text-center" className="mt-12 text-center"
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.4 }} transition={{ duration: 0.6, delay: 0.4 }}
> >
<motion.h2 <motion.h2
className="mb-6" className="mb-6"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@ -317,7 +317,7 @@ export function ServicesSection() {
whileInView={{ opacity: 1, scale: 1 }} whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ delay: 0.6 + index * 0.05 }} transition={{ delay: 0.6 + index * 0.05 }}
whileHover={{ whileHover={{
scale: 1.05, scale: 1.05,
transition: { duration: 0.2 } transition: { duration: 0.2 }
}} }}

View File

@ -6,27 +6,27 @@ export const indiaLocations = [
"Jaipur", "Jodhpur", "Udaipur", "Ajmer", "Kota", "Bikaner", "Alwar", "Jaipur", "Jodhpur", "Udaipur", "Ajmer", "Kota", "Bikaner", "Alwar",
"Lucknow", "Kanpur", "Agra", "Varanasi", "Allahabad", "Meerut", "Bareilly", "Aligarh", "Lucknow", "Kanpur", "Agra", "Varanasi", "Allahabad", "Meerut", "Bareilly", "Aligarh",
"Dehradun", "Haridwar", "Rishikesh", "Mussoorie", "Nainital", "Shimla", "Manali", "Dharamshala", "Dehradun", "Haridwar", "Rishikesh", "Mussoorie", "Nainital", "Shimla", "Manali", "Dharamshala",
// East India // East India
"Kolkata", "Howrah", "Durgapur", "Asansol", "Siliguri", "Darjeeling", "Kolkata", "Howrah", "Durgapur", "Asansol", "Siliguri", "Darjeeling",
"Bhubaneswar", "Cuttack", "Rourkela", "Puri", "Bhubaneswar", "Cuttack", "Rourkela", "Puri",
"Patna", "Gaya", "Muzaffarpur", "Bhagalpur", "Darbhanga", "Patna", "Gaya", "Muzaffarpur", "Bhagalpur", "Darbhanga",
"Guwahati", "Silchar", "Dibrugarh", "Jorhat", "Tezpur", "Guwahati", "Silchar", "Dibrugarh", "Jorhat", "Tezpur",
// West India // West India
"Ahmedabad", "Surat", "Vadodara", "Rajkot", "Bhavnagar", "Jamnagar", "Gandhinagar", "Ahmedabad", "Surat", "Vadodara", "Rajkot", "Bhavnagar", "Jamnagar", "Gandhinagar",
"Thane", "Navi Mumbai", "Kalyan", "Vasai-Virar", "Solapur", "Nagpur", "Kolhapur", "Thane", "Navi Mumbai", "Kalyan", "Vasai-Virar", "Solapur", "Nagpur", "Kolhapur",
// South India // South India
"Bangalore", "Mysore", "Mangalore", "Hubli", "Belgaum", "Gulbarga", "Davangere", "Bangalore", "Mysore", "Mangalore", "Hubli", "Belgaum", "Gulbarga", "Davangere",
"Chennai", "Coimbatore", "Madurai", "Tiruchirappalli", "Salem", "Tirunelveli", "Erode", "Tiruppur", "Vellore", "Chennai", "Coimbatore", "Madurai", "Tiruchirappalli", "Salem", "Tirunelveli", "Erode", "Tiruppur", "Vellore",
"Hyderabad", "Secunderabad", "Warangal", "Nizamabad", "Karimnagar", "Khammam", "Ramagundam", "Hyderabad", "Secunderabad", "Warangal", "Nizamabad", "Karimnagar", "Khammam", "Ramagundam",
"Kochi", "Thiruvananthapuram", "Kozhikode", "Kollam", "Thrissur", "Alappuzha", "Palakkad", "Kannur", "Kochi", "Thiruvananthapuram", "Kozhikode", "Kollam", "Thrissur", "Alappuzha", "Palakkad", "Kannur",
// Central India // Central India
"Indore", "Bhopal", "Jabalpur", "Gwalior", "Ujjain", "Sagar", "Dewas", "Satna", "Indore", "Bhopal", "Jabalpur", "Gwalior", "Ujjain", "Sagar", "Dewas", "Satna",
"Raipur", "Bhilai", "Bilaspur", "Korba", "Durg", "Rajnandgaon", "Raipur", "Bhilai", "Bilaspur", "Korba", "Durg", "Rajnandgaon",
// Union Territories // Union Territories
"Pondicherry", "Port Blair", "Panaji", "Margao", "Vasco da Gama", "Daman", "Silvassa" "Pondicherry", "Port Blair", "Panaji", "Margao", "Vasco da Gama", "Daman", "Silvassa"
]; ];
@ -34,7 +34,7 @@ export const indiaLocations = [
// Service types for drop-off options // Service types for drop-off options
export const serviceTypes = [ export const serviceTypes = [
"City Ride - Customize Price", "City Ride - Customize Price",
"Airport Taxi - ₹1099", "Airport Taxi - ₹999",
"Outstation - Customize Price", "Outstation - Customize Price",
"Mover and Packer - Customize Price", "Mover and Packer - Customize Price",
"Corporate Services - Customize Price" "Corporate Services - Customize Price"

View File

@ -5,11 +5,11 @@ export const mockVehicles: Vehicle[] = [
id: "1", id: "1",
type: "5-seater", type: "5-seater",
name: "5 Seater", name: "5 Seater",
price: 1099, price: 999,
capacity: 5, capacity: 5,
rating: 4.5, rating: 4.5,
estimatedTime: "Rider will contact you soon", estimatedTime: "Rider will contact you soon",
features: ["AC", "Music System", "Comfortable", "Airport Taxi ₹1099"], features: ["AC", "Music System", "Comfortable", "Airport Taxi ₹999"],
image: "" image: ""
}, },
{ {

View File

@ -27,8 +27,8 @@ app.get("/make-server-10d555e8/health", (c) => {
// Test email configuration endpoint // Test email configuration endpoint
app.get("/make-server-10d555e8/test-email-config", (c) => { app.get("/make-server-10d555e8/test-email-config", (c) => {
const resendApiKey = Deno.env.get('RESEND_API_KEY'); const resendApiKey = Deno.env.get('RESEND_API_KEY');
return c.json({ return c.json({
emailConfigured: !!resendApiKey, emailConfigured: !!resendApiKey,
apiKeyFormat: resendApiKey ? (resendApiKey.startsWith('re_') ? 'valid' : 'invalid') : 'missing', apiKeyFormat: resendApiKey ? (resendApiKey.startsWith('re_') ? 'valid' : 'invalid') : 'missing',
apiKeyLength: resendApiKey?.length || 0 apiKeyLength: resendApiKey?.length || 0
@ -40,7 +40,7 @@ app.post("/make-server-10d555e8/send-booking-email", async (c) => {
try { try {
const bookingData = await c.req.json(); const bookingData = await c.req.json();
console.log('Received booking data:', JSON.stringify(bookingData, null, 2)); console.log('Received booking data:', JSON.stringify(bookingData, null, 2));
// Store booking in KV store // Store booking in KV store
const bookingId = `booking_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const bookingId = `booking_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
await kv.set(bookingId, { await kv.set(bookingId, {
@ -55,7 +55,7 @@ app.post("/make-server-10d555e8/send-booking-email", async (c) => {
console.log('API Key exists:', !!resendApiKey); console.log('API Key exists:', !!resendApiKey);
console.log('API Key length:', resendApiKey?.length || 0); console.log('API Key length:', resendApiKey?.length || 0);
console.log('API Key prefix:', resendApiKey?.substring(0, 7) || 'none'); console.log('API Key prefix:', resendApiKey?.substring(0, 7) || 'none');
if (!resendApiKey) { if (!resendApiKey) {
console.error('RESEND_API_KEY environment variable not found'); console.error('RESEND_API_KEY environment variable not found');
throw new Error('RESEND_API_KEY not configured - please set up your Resend API key'); throw new Error('RESEND_API_KEY not configured - please set up your Resend API key');
@ -69,9 +69,9 @@ app.post("/make-server-10d555e8/send-booking-email", async (c) => {
// Get vehicle pricing // Get vehicle pricing
const getVehiclePrice = (vehicleType) => { const getVehiclePrice = (vehicleType) => {
switch(vehicleType) { switch (vehicleType) {
case '5-seater': return '₹1099'; case '5-seater': return '₹999';
case '7-seater': return '₹1600'; case '7-seater': return '₹1600';
case 'innova': return '₹2000'; case 'innova': return '₹2000';
default: return 'Contact for pricing'; default: return 'Contact for pricing';
} }
@ -193,8 +193,8 @@ app.post("/make-server-10d555e8/send-booking-email", async (c) => {
console.log('Booking email sent successfully:', bookingId); console.log('Booking email sent successfully:', bookingId);
return c.json({ return c.json({
success: true, success: true,
message: 'Booking confirmed and emails sent successfully', message: 'Booking confirmed and emails sent successfully',
bookingId: bookingId, bookingId: bookingId,
emailsSent: { emailsSent: {
@ -205,19 +205,19 @@ app.post("/make-server-10d555e8/send-booking-email", async (c) => {
} catch (error) { } catch (error) {
console.error('Error processing booking:', error); console.error('Error processing booking:', error);
// If it's an API key issue, provide specific guidance // If it's an API key issue, provide specific guidance
if (error.message.includes('API key') || error.message.includes('validation_error')) { if (error.message.includes('API key') || error.message.includes('validation_error')) {
return c.json({ return c.json({
success: false, success: false,
message: `Email service configuration error: ${error.message}. Please check your Resend API key setup.`, message: `Email service configuration error: ${error.message}. Please check your Resend API key setup.`,
error: 'EMAIL_CONFIG_ERROR' error: 'EMAIL_CONFIG_ERROR'
}, 400); }, 400);
} }
// For other errors, still save the booking but indicate email failure // For other errors, still save the booking but indicate email failure
return c.json({ return c.json({
success: false, success: false,
message: `Booking saved but email notification failed: ${error.message}`, message: `Booking saved but email notification failed: ${error.message}`,
error: 'EMAIL_SEND_FAILED' error: 'EMAIL_SEND_FAILED'
}, 500); }, 500);