248 lines
11 KiB
TypeScript
248 lines
11 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { createReservation, type ReservationData } from '../lib/pocketbase';
|
|
|
|
interface ReservationModalProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export default function ReservationModal({ isOpen, onClose }: ReservationModalProps) {
|
|
const [formData, setFormData] = useState<ReservationData>({
|
|
name: '',
|
|
email: '',
|
|
phone: '',
|
|
date: new Date().toISOString().split('T')[0],
|
|
time: '19:30',
|
|
guests: 2,
|
|
location: 'Chandigarh (Sector 9)',
|
|
special_requests: '',
|
|
});
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
const [confirmedId, setConfirmedId] = useState<string | null>(null);
|
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
|
|
if (!isOpen) return null;
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
setErrorMessage(null);
|
|
|
|
const res = await createReservation(formData);
|
|
setLoading(false);
|
|
|
|
if (res.success && res.id) {
|
|
setConfirmedId(res.id);
|
|
} else {
|
|
setErrorMessage(res.error || 'Failed to submit reservation. Please try again.');
|
|
}
|
|
};
|
|
|
|
const handleReset = () => {
|
|
setConfirmedId(null);
|
|
setErrorMessage(null);
|
|
onClose();
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-md animate-fade-in">
|
|
<div
|
|
className="bg-[#1c1b1b] border border-[#ffb77b]/40 rounded-xl max-w-lg w-full p-6 sm:p-8 relative shadow-2xl overflow-y-auto max-h-[90vh]"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
{/* Close button */}
|
|
<button
|
|
onClick={handleReset}
|
|
className="absolute top-4 right-4 text-[#a08d80] hover:text-[#ffb77b] transition-colors p-2 rounded-full hover:bg-[#201f1f]"
|
|
>
|
|
<span className="material-symbols-outlined text-2xl">close</span>
|
|
</button>
|
|
|
|
{confirmedId ? (
|
|
<div className="text-center py-8 space-y-4">
|
|
<div className="w-16 h-16 rounded-full bg-[#234f3b] text-[#ffb77b] flex items-center justify-center mx-auto border border-[#ffb77b]/30">
|
|
<span className="material-symbols-outlined text-3xl">check_circle</span>
|
|
</div>
|
|
<h3 className="font-['Playfair_Display'] text-2xl font-bold text-[#e5e2e1]">
|
|
Table Reserved!
|
|
</h3>
|
|
<p className="text-[#d8c3b4] text-sm max-w-sm mx-auto">
|
|
We look forward to welcoming you to Hops n Grains ({formData.location}). A confirmation message will be sent to <strong>{formData.phone}</strong>.
|
|
</p>
|
|
<div className="bg-[#201f1f] p-3 rounded-lg border border-[#524439]/40 inline-block text-xs font-mono text-[#ffb77b]">
|
|
Booking ID: #{confirmedId}
|
|
</div>
|
|
<div className="pt-4">
|
|
<button
|
|
onClick={handleReset}
|
|
className="bg-[#ffb77b] text-[#4d2700] font-semibold px-6 py-2.5 rounded hover:bg-[#ffdcc2] transition-colors inner-shadow-copper"
|
|
>
|
|
Done
|
|
</button>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div>
|
|
<div className="mb-6">
|
|
<span className="text-xs uppercase tracking-widest text-[#ffb77b] font-semibold">
|
|
Table Booking
|
|
</span>
|
|
<h2 className="font-['Playfair_Display'] text-2xl sm:text-3xl font-bold text-[#e5e2e1] mt-1">
|
|
Reserve Your Experience
|
|
</h2>
|
|
<p className="text-[#d8c3b4] text-sm mt-1">
|
|
Handcrafted beer & artisanal dining at Chandigarh & Panchkula.
|
|
</p>
|
|
</div>
|
|
|
|
{errorMessage && (
|
|
<div className="mb-4 p-3 rounded bg-red-950/50 border border-red-800 text-red-300 text-xs">
|
|
{errorMessage}
|
|
</div>
|
|
)}
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
{/* Location selector */}
|
|
<div>
|
|
<label className="block text-xs uppercase tracking-wider text-[#a08d80] font-semibold mb-1">
|
|
Brewery Branch *
|
|
</label>
|
|
<select
|
|
value={formData.location}
|
|
onChange={(e) => setFormData({ ...formData, location: e.target.value })}
|
|
className="w-full bg-[#201f1f] border border-[#524439]/60 rounded-md px-3.5 py-2.5 text-sm text-[#e5e2e1] focus:outline-none focus:border-[#ffb77b]"
|
|
required
|
|
>
|
|
<option value="Chandigarh (Sector 9)">Hops n Grains — Sector 9, Chandigarh</option>
|
|
<option value="Panchkula (Sector 5)">Hops n Grains — Sector 5, Panchkula</option>
|
|
</select>
|
|
</div>
|
|
|
|
{/* Name & Phone */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-xs uppercase tracking-wider text-[#a08d80] font-semibold mb-1">
|
|
Your Name *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
required
|
|
placeholder="e.g. Tanshu"
|
|
value={formData.name}
|
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
|
className="w-full bg-[#201f1f] border border-[#524439]/60 rounded-md px-3.5 py-2.5 text-sm text-[#e5e2e1] focus:outline-none focus:border-[#ffb77b]"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-xs uppercase tracking-wider text-[#a08d80] font-semibold mb-1">
|
|
Phone Number *
|
|
</label>
|
|
<input
|
|
type="tel"
|
|
required
|
|
placeholder="+91 98765 43210"
|
|
value={formData.phone}
|
|
onChange={(e) => setFormData({ ...formData, phone: e.target.value })}
|
|
className="w-full bg-[#201f1f] border border-[#524439]/60 rounded-md px-3.5 py-2.5 text-sm text-[#e5e2e1] focus:outline-none focus:border-[#ffb77b]"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Email */}
|
|
<div>
|
|
<label className="block text-xs uppercase tracking-wider text-[#a08d80] font-semibold mb-1">
|
|
Email Address *
|
|
</label>
|
|
<input
|
|
type="email"
|
|
required
|
|
placeholder="name@example.com"
|
|
value={formData.email}
|
|
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
|
className="w-full bg-[#201f1f] border border-[#524439]/60 rounded-md px-3.5 py-2.5 text-sm text-[#e5e2e1] focus:outline-none focus:border-[#ffb77b]"
|
|
/>
|
|
</div>
|
|
|
|
{/* Date, Time, Guests */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
|
<div>
|
|
<label className="block text-xs uppercase tracking-wider text-[#a08d80] font-semibold mb-1">
|
|
Date *
|
|
</label>
|
|
<input
|
|
type="date"
|
|
required
|
|
value={formData.date}
|
|
onChange={(e) => setFormData({ ...formData, date: e.target.value })}
|
|
className="w-full bg-[#201f1f] border border-[#524439]/60 rounded-md px-3 py-2 text-sm text-[#e5e2e1] focus:outline-none focus:border-[#ffb77b]"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-xs uppercase tracking-wider text-[#a08d80] font-semibold mb-1">
|
|
Time *
|
|
</label>
|
|
<input
|
|
type="time"
|
|
required
|
|
value={formData.time}
|
|
onChange={(e) => setFormData({ ...formData, time: e.target.value })}
|
|
className="w-full bg-[#201f1f] border border-[#524439]/60 rounded-md px-3 py-2 text-sm text-[#e5e2e1] focus:outline-none focus:border-[#ffb77b]"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-xs uppercase tracking-wider text-[#a08d80] font-semibold mb-1">
|
|
Guests *
|
|
</label>
|
|
<select
|
|
value={formData.guests}
|
|
onChange={(e) => setFormData({ ...formData, guests: Number(e.target.value) })}
|
|
className="w-full bg-[#201f1f] border border-[#524439]/60 rounded-md px-3 py-2 text-sm text-[#e5e2e1] focus:outline-none focus:border-[#ffb77b]"
|
|
>
|
|
{[1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 16, 20].map((n) => (
|
|
<option key={n} value={n}>
|
|
{n} {n === 1 ? 'Guest' : 'Guests'}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Special Requests */}
|
|
<div>
|
|
<label className="block text-xs uppercase tracking-wider text-[#a08d80] font-semibold mb-1">
|
|
Special Requests (Optional)
|
|
</label>
|
|
<textarea
|
|
rows={2}
|
|
placeholder="e.g., Table near copper brewing tanks, birthday anniversary celebration"
|
|
value={formData.special_requests}
|
|
onChange={(e) => setFormData({ ...formData, special_requests: e.target.value })}
|
|
className="w-full bg-[#201f1f] border border-[#524439]/60 rounded-md px-3.5 py-2 text-sm text-[#e5e2e1] focus:outline-none focus:border-[#ffb77b]"
|
|
/>
|
|
</div>
|
|
|
|
<div className="pt-3">
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="w-full bg-[#ffb77b] text-[#4d2700] font-semibold px-6 py-3.5 rounded-md hover:bg-[#ffdcc2] transition-colors inner-shadow-copper flex items-center justify-center gap-2 shadow-lg disabled:opacity-50 cursor-pointer"
|
|
>
|
|
{loading ? (
|
|
<>
|
|
<span className="w-4 h-4 border-2 border-[#4d2700] border-t-transparent rounded-full animate-spin" />
|
|
Securing Table...
|
|
</>
|
|
) : (
|
|
'Confirm Table Reservation'
|
|
)}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|