Seed: restaurant template (17 files)

This commit is contained in:
2026-07-23 22:10:20 +00:00
parent a030079d92
commit ffb3992377
+156
View File
@@ -0,0 +1,156 @@
import { useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { ArrowRight, Wine, Clock, Shirt, Car, AlertCircle, MapPin, CheckCircle2 } from "lucide-react";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { useSaasMutation } from "@/hooks/use-tygarun";
export default function ReservationsPage() {
const [time, setTime] = useState("7:00 PM");
const [guests, setGuests] = useState("2");
const [occasion, setOccasion] = useState("No special occasion");
const { mutate: bookTable, loading: booking } = useSaasMutation("scheduling", "/bookings");
const handleReserve = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const form = e.currentTarget;
const fd = new FormData(form);
try {
await bookTable({
name: fd.get("name"),
email: fd.get("email"),
phone: fd.get("phone"),
date: fd.get("date"),
time,
partySize: guests,
occasion,
requests: fd.get("requests"),
});
toast.success("Reservation confirmed! Check your email for details.");
form.reset();
} catch {
toast.error("Could not confirm reservation. Please try again.");
}
};
return (
<div className="min-h-screen bg-background text-foreground antialiased">
<Header />
<section className="relative pt-32 pb-16 bg-gradient-to-b from-stone-950 to-background">
<div className="relative z-10 mx-auto max-w-4xl px-6 text-center">
<div className="flex items-center justify-center gap-3 mb-4 text-amber-600/60 text-sm tracking-[0.3em]">
<span></span><Wine className="h-4 w-4" /><span></span>
</div>
<h1 className="text-4xl sm:text-5xl font-serif tracking-wide text-white animate-fade-up">Make a Reservation</h1>
<p className="mt-4 text-stone-400 animate-fade-up">Join us for an unforgettable evening of exceptional cuisine</p>
</div>
</section>
<section className="py-24">
<div className="mx-auto max-w-6xl px-6 grid lg:grid-cols-3 gap-12">
{/* ── FORM ── */}
<div className="lg:col-span-2">
<div className="flex items-center justify-center gap-3 mb-8 text-amber-600/40 text-sm tracking-[0.3em]">
<span></span><span></span><span></span>
</div>
<Card className="border-stone-200 dark:border-stone-800 rounded-none shadow-lg">
<CardContent className="p-8">
<form onSubmit={handleReserve} className="space-y-6">
<div className="grid sm:grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-2 block">Name</label>
<Input name="name" required placeholder="Your full name" className="rounded-none border-stone-300 dark:border-stone-700 focus-visible:ring-amber-600" />
</div>
<div>
<label className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-2 block">Email</label>
<Input name="email" required type="email" placeholder="your@email.com" className="rounded-none border-stone-300 dark:border-stone-700 focus-visible:ring-amber-600" />
</div>
</div>
<div>
<label className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-2 block">Phone</label>
<Input name="phone" type="tel" placeholder="+44 20 7946 0000" className="rounded-none border-stone-300 dark:border-stone-700 focus-visible:ring-amber-600" />
</div>
<div className="grid sm:grid-cols-3 gap-4">
<div>
<label className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-2 block">Date</label>
<Input name="date" required type="date" className="rounded-none border-stone-300 dark:border-stone-700 focus-visible:ring-amber-600" />
</div>
<div>
<label className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-2 block">Time</label>
<select value={time} onChange={(e) => setTime(e.target.value)} className="flex h-10 w-full rounded-none border border-stone-300 dark:border-stone-700 bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-600">
{["6:00 PM","6:30 PM","7:00 PM","7:30 PM","8:00 PM","8:30 PM","9:00 PM","9:30 PM"].map((t) => (
<option key={t} value={t}>{t}</option>
))}
</select>
<p className="text-xs text-amber-600/60 mt-1">Most popular: 7:00 PM 8:30 PM</p>
</div>
<div>
<label className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-2 block">Guests</label>
<select value={guests} onChange={(e) => setGuests(e.target.value)} className="flex h-10 w-full rounded-none border border-stone-300 dark:border-stone-700 bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-600">
{[1,2,3,4,5,6,7,8].map((n) => (
<option key={n} value={String(n)}>{n} {n === 1 ? "Guest" : "Guests"}</option>
))}
</select>
</div>
</div>
<div>
<label className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-2 block">Occasion</label>
<select value={occasion} onChange={(e) => setOccasion(e.target.value)} className="flex h-10 w-full rounded-none border border-stone-300 dark:border-stone-700 bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-600">
{["No special occasion", "Birthday", "Anniversary", "Business Dinner", "Date Night", "Celebration", "Other"].map((o) => (
<option key={o} value={o}>{o}</option>
))}
</select>
</div>
<div>
<label className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-2 block">Special Requests</label>
<Textarea name="requests" placeholder="Allergies, dietary requirements, special occasions..." className="rounded-none border-stone-300 dark:border-stone-700 focus-visible:ring-amber-600 min-h-[100px]" />
</div>
<Button type="submit" disabled={booking} className="w-full rounded-none bg-amber-700 hover:bg-amber-600 text-white tracking-widest uppercase text-sm py-6 active:scale-[0.98]">
{booking ? "Confirming..." : "Confirm Reservation"} <ArrowRight className="ml-2 h-4 w-4" />
</Button>
<p className="text-xs text-center text-muted-foreground mt-3 flex items-center justify-center gap-1.5"><CheckCircle2 className="h-3.5 w-3.5 text-amber-600/60" /> Confirmation sent to your email</p>
</form>
</CardContent>
</Card>
</div>
{/* ── SIDEBAR INFO ── */}
<div className="space-y-8">
{[
{ icon: Clock, title: "Opening Hours", lines: ["Tuesday Thursday: 6:00 PM 10:00 PM", "Friday Saturday: 6:00 PM 11:00 PM", "Sunday Monday: Closed"] },
{ icon: Shirt, title: "Dress Code", lines: ["Smart casual. Jackets preferred for gentlemen in the main dining room."] },
{ icon: Car, title: "Parking", lines: ["Complimentary valet parking available. Underground garage on Golden Lane."] },
{ icon: AlertCircle, title: "Cancellation Policy", lines: ["Cancellations accepted up to 24 hours before your reservation. Late cancellations may incur a fee."] },
].map((info, i) => (
<div key={i}>
<div className="flex items-center gap-2 mb-2">
<info.icon className="h-4 w-4 text-amber-600" />
<h3 className="font-medium text-sm uppercase tracking-wider">{info.title}</h3>
</div>
{info.lines.map((line, j) => (
<p key={j} className="text-sm text-muted-foreground">{line}</p>
))}
</div>
))}
</div>
</div>
</section>
{/* ── MAP PLACEHOLDER ── */}
<section className="py-24 bg-muted/30">
<div className="mx-auto max-w-6xl px-6">
<div className="aspect-[3/1] rounded-sm bg-stone-200 dark:bg-stone-800 flex items-center justify-center">
<MapPin className="h-12 w-12 text-muted-foreground/30" />
</div>
</div>
</section>
<Footer />
</div>
);
}