Initial Commit

This commit is contained in:
tanshu committed 2026-09-02 16:29:03 +00:00
commit 1a9c69fc9d
34 files changed
+7479

No files matched your search

@@ -0,0 +1,247 @@
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>
);
}