Next.js 14 website with standalone output configured for Docker deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
140 lines
5.1 KiB
TypeScript
140 lines
5.1 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { motion } from "framer-motion";
|
|
import { Experience, WHATSAPP_NUMBER } from "@/lib/constants";
|
|
import { Button } from "@/components/shared/Button";
|
|
|
|
interface BookingWidgetProps {
|
|
experience: Experience;
|
|
}
|
|
|
|
export function BookingWidget({ experience }: BookingWidgetProps) {
|
|
const [guests, setGuests] = useState(2);
|
|
const [selectedDate, setSelectedDate] = useState("");
|
|
const total = experience.price * guests;
|
|
|
|
const dates = Array.from({ length: 7 }, (_, i) => {
|
|
const d = new Date();
|
|
d.setDate(d.getDate() + i + 2);
|
|
return d.toISOString().split("T")[0];
|
|
});
|
|
|
|
const formatDate = (dateStr: string) => {
|
|
const d = new Date(dateStr);
|
|
return d.toLocaleDateString("en-US", {
|
|
weekday: "short",
|
|
month: "short",
|
|
day: "numeric",
|
|
});
|
|
};
|
|
|
|
const whatsappUrl = `https://wa.me/${WHATSAPP_NUMBER.replace("+", "")}?text=${encodeURIComponent(
|
|
`Hi! I'd like to book "${experience.name}" for ${guests} guest(s)${selectedDate ? ` on ${formatDate(selectedDate)}` : ""}. Can you help?`
|
|
)}`;
|
|
|
|
return (
|
|
<section id="booking" className="py-16 md:py-24 section-padding bg-warm-sand">
|
|
<div className="max-w-2xl mx-auto">
|
|
<motion.div
|
|
className="bg-white rounded-3xl p-8 md:p-10 shadow-lg"
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
>
|
|
<h2 className="font-display text-2xl md:text-3xl font-bold text-deep-nazar mb-2 text-center">
|
|
Book {experience.name}
|
|
</h2>
|
|
<p className="text-deep-nazar/60 text-center mb-8">
|
|
Secure your spot — free cancellation up to 48 hours before
|
|
</p>
|
|
|
|
<div className="space-y-6">
|
|
{/* Date selection */}
|
|
<div>
|
|
<label className="block text-sm font-semibold text-deep-nazar mb-3">
|
|
Select a Date
|
|
</label>
|
|
<div className="flex flex-wrap gap-2">
|
|
{dates.map((date) => (
|
|
<button
|
|
key={date}
|
|
onClick={() => setSelectedDate(date)}
|
|
className={`px-4 py-2 rounded-full text-sm font-medium transition-all ${
|
|
selectedDate === date
|
|
? "bg-bosphorus text-white"
|
|
: "bg-deep-nazar/5 text-deep-nazar/70 hover:bg-bosphorus/10"
|
|
}`}
|
|
>
|
|
{formatDate(date)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Guest count */}
|
|
<div>
|
|
<label className="block text-sm font-semibold text-deep-nazar mb-3">
|
|
Number of Guests
|
|
</label>
|
|
<div className="flex items-center gap-4">
|
|
<button
|
|
onClick={() => setGuests(Math.max(1, guests - 1))}
|
|
className="w-10 h-10 rounded-full bg-deep-nazar/5 flex items-center justify-center text-deep-nazar hover:bg-deep-nazar/10 transition-colors"
|
|
>
|
|
-
|
|
</button>
|
|
<span className="text-2xl font-bold text-deep-nazar w-8 text-center">
|
|
{guests}
|
|
</span>
|
|
<button
|
|
onClick={() => setGuests(Math.min(8, guests + 1))}
|
|
className="w-10 h-10 rounded-full bg-deep-nazar/5 flex items-center justify-center text-deep-nazar hover:bg-deep-nazar/10 transition-colors"
|
|
>
|
|
+
|
|
</button>
|
|
<span className="text-sm text-deep-nazar/50">
|
|
Max 8 per group
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Price summary */}
|
|
<div className="bg-warm-sand rounded-2xl p-5">
|
|
<div className="flex justify-between items-center mb-2">
|
|
<span className="text-deep-nazar/70">
|
|
€{experience.price} × {guests} guest{guests > 1 ? "s" : ""}
|
|
</span>
|
|
<span className="font-semibold text-deep-nazar">
|
|
€{total}
|
|
</span>
|
|
</div>
|
|
<div className="flex justify-between items-center pt-3 border-t border-deep-nazar/10">
|
|
<span className="font-bold text-deep-nazar">Total</span>
|
|
<span className="text-2xl font-bold text-deep-nazar">
|
|
€{total}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* CTA */}
|
|
<div className="space-y-3">
|
|
<Button
|
|
href={whatsappUrl}
|
|
external
|
|
variant="coral"
|
|
size="lg"
|
|
className="w-full"
|
|
>
|
|
Reserve Now via WhatsApp
|
|
</Button>
|
|
<p className="text-center text-xs text-deep-nazar/40">
|
|
Instant confirmation · Pay securely online · Free cancellation up to 48h before
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|