'use client'; import Navbar from "@/components/Navbar"; import Footer from "@/components/Footer"; import { buildBookingWhatsAppMessage } from '@/application/messaging/whatsapp-message-builder'; import { isBookingComplete } from '@/domain/booking/validation'; import type { BookingDetails, PreOrderLine } from '@/domain/booking/entities'; import { addOrderLine, calculateLineTotal, calculateOrderTotal, formatLineQuantityLabel, removeOrderLine, updateOrderLineQuantity, } from '@/domain/shared/order-line'; import { buildCartLineFromMenuItem } from '@/application/cart/cart-line-builder'; import type { MenuItem } from '@/domain/menu/entities'; import { getMenuPosterSrc, getMenuPosterCandidates, applyNextImageFallback, SITE_ASSETS, } from '@/lib/assets'; import { container } from '@/infrastructure/di/container'; import { useLanguage } from '@/presentation/providers/language-provider'; import { getTranslation } from '@/presentation/i18n/translations'; import { getMenuItemDescription, getMenuItemName, localizeOrderLineName, } from '@/application/i18n/menu-localization'; import { useCallback, useEffect, useState } from 'react'; import { motion, AnimatePresence } from "framer-motion"; import { MapPin, Calendar, Clock, Users, User, Phone, Mail, MessageCircle, Plus, Minus, X, ArrowRight, Search, CheckCircle, UtensilsCrossed, PartyPopper, Building2, Sparkles } from "lucide-react"; import { useMenu } from "@/presentation/providers/menu-provider"; import { playAddSound, playHoverSound, playSuccessSound } from "@/lib/sounds"; import type { BookingMode, EventTypeId } from '@/domain/booking/event-types'; import { EVENT_GUEST_OPTIONS, TABLE_GUEST_OPTIONS } from '@/domain/booking/event-types'; import { getBookingEventsCopy, getEventTypeLabel, toBookingMessageCopy, } from '@/presentation/i18n/booking-events'; import { getWhatsAppInquiryLanguage, getWhatsAppInquiryTranslation, } from '@/application/messaging/inquiry-language'; import EventTypePicker from '@/components/reserve/EventTypePicker'; import { useCustomerAuth } from '@/presentation/providers/customer-auth-provider'; const RESERVE_DRAFT_STORAGE_KEY = 'shahi-reserve-draft'; function GoogleIcon({ className }: { className?: string }) { return ( ); } /** * ============================================================================= * TABLE RESERVATION / BOOKING PAGE * ============================================================================= * * Beautiful, theme-matched table booking experience with optional pre-order. * - Swap implemented: Reserve Table now in central nav pills, Login is the gold CTA. * - Form first (location, date, time, guests, name, phone, email, notes). * - After form: full menu selector for pre-ordering (reuse menu-data). * - End: builds rich WhatsApp inquiry (booking + pre-order items) using same number as cart. * - Fully translated (sv default + en/hi/ur). * - Mobile-first, large targets, elegant cards matching /login and site theme. * - Uses framer-motion for reveals, lucide icons. * - No backend: client-side only, like staff login and cart WhatsApp. * * Linked from navbar (after swap) and locations. */ const EMPTY_BOOKING: BookingDetails = { bookingMode: 'table', eventType: '', eventTypeOther: '', location: '', date: '', time: '', guests: '', name: '', phone: '', email: '', notes: '', }; export default function ReservePage() { const { categories: menuCategories } = useMenu(); const { language } = useLanguage(); const t = getTranslation(language); const eventsCopy = getBookingEventsCopy(language); const { email: customerEmail, name: customerName, isAuthenticated, isLoading: isAuthLoading } = useCustomerAuth(); const [booking, setBooking] = useState(EMPTY_BOOKING); const [draftRestored, setDraftRestored] = useState(false); const [preOrder, setPreOrder] = useState([]); const [showMenu, setShowMenu] = useState(false); const [search, setSearch] = useState(''); const [sent, setSent] = useState(false); const handleBookingChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setBooking((prev) => ({ ...prev, [name]: value })); }; const persistReserveDraft = useCallback((draft: BookingDetails) => { try { sessionStorage.setItem(RESERVE_DRAFT_STORAGE_KEY, JSON.stringify(draft)); } catch { // Ignore storage errors. } }, []); const handleGoogleSignInForBooking = () => { persistReserveDraft(booking); window.location.href = '/api/auth/google?returnTo=%2Freserve'; }; useEffect(() => { if (draftRestored) return; try { const raw = sessionStorage.getItem(RESERVE_DRAFT_STORAGE_KEY); if (!raw) { setDraftRestored(true); return; } const parsed = JSON.parse(raw) as Partial; setBooking((prev) => ({ ...prev, bookingMode: parsed.bookingMode === 'event' ? 'event' : 'table', eventType: typeof parsed.eventType === 'string' ? parsed.eventType : '', eventTypeOther: typeof parsed.eventTypeOther === 'string' ? parsed.eventTypeOther : '', location: typeof parsed.location === 'string' ? parsed.location : '', date: typeof parsed.date === 'string' ? parsed.date : '', time: typeof parsed.time === 'string' ? parsed.time : '', guests: typeof parsed.guests === 'string' ? parsed.guests : '', name: typeof parsed.name === 'string' ? parsed.name : '', phone: typeof parsed.phone === 'string' ? parsed.phone : '', notes: typeof parsed.notes === 'string' ? parsed.notes : '', })); sessionStorage.removeItem(RESERVE_DRAFT_STORAGE_KEY); } catch { sessionStorage.removeItem(RESERVE_DRAFT_STORAGE_KEY); } finally { setDraftRestored(true); } }, [draftRestored]); useEffect(() => { if (!isAuthenticated || !customerEmail) return; setBooking((prev) => ({ ...prev, email: customerEmail, name: prev.name.trim() ? prev.name : customerName?.trim() ?? prev.name, })); }, [isAuthenticated, customerEmail, customerName]); const validateForm = (): boolean => isBookingComplete(booking); const setBookingMode = (mode: BookingMode) => { setBooking((prev) => ({ ...prev, bookingMode: mode, eventType: mode === 'table' ? '' : prev.eventType, eventTypeOther: mode === 'table' ? '' : prev.eventTypeOther, guests: '', })); }; const selectEventType = (eventType: EventTypeId) => { setBooking((prev) => ({ ...prev, eventType, eventTypeOther: eventType === 'other' ? prev.eventTypeOther : '', })); }; const handleContinue = (e: React.FormEvent) => { e.preventDefault(); if (booking.bookingMode === 'event' && !booking.eventType) { alert(eventsCopy.eventTypeRequired); return; } if (booking.bookingMode === 'event' && booking.eventType === 'other' && !booking.eventTypeOther.trim()) { alert(eventsCopy.eventTypeOtherRequired); return; } if (validateForm()) { setShowMenu(true); setTimeout(() => { const menuEl = document.getElementById('preorder-menu'); if (menuEl) menuEl.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 100); } else { alert('Please fill in all required fields (location, date, time, guests, name, phone).'); } }; // Flatten menu for selector (with search) const allDishes = menuCategories.flatMap((cat) => cat.items); const filteredDishes = allDishes.filter((dish) => { const q = search.toLowerCase(); const localizedName = getMenuItemName(language, dish).toLowerCase(); const localizedDescription = getMenuItemDescription(language, t, dish).toLowerCase(); return ( localizedName.includes(q) || localizedDescription.includes(q) || dish.name.toLowerCase().includes(q) ); }); const addToPreOrder = (dish: MenuItem) => { setPreOrder((prev) => addOrderLine(prev, buildCartLineFromMenuItem(dish))); }; const updatePreOrderQty = (id: string, newQty: number) => { setPreOrder((prev) => updateOrderLineQuantity(prev, id, newQty)); }; const removeFromPreOrder = (id: string) => { setPreOrder((prev) => removeOrderLine(prev, id)); }; const getPreOrderTotal = () => calculateOrderTotal(preOrder); const handleSendInquiry = () => { if (!validateForm()) { alert('Please complete the booking form first.'); return; } playSuccessSound(); const inquiryLang = getWhatsAppInquiryLanguage(language); const inquiryT = getWhatsAppInquiryTranslation(language); const inquiryEventsCopy = getBookingEventsCopy(inquiryLang); const message = buildBookingWhatsAppMessage( booking, preOrder, toBookingMessageCopy(inquiryEventsCopy, { askim: inquiryT.booking.askim, backaplan: inquiryT.booking.backaplan, }), booking.eventType ? getEventTypeLabel(inquiryLang, booking.eventType) : '', inquiryLang, ); if (!message) return; container.messagingGateway.openWhatsApp(message); setSent(true); }; const resetAll = () => { setBooking(EMPTY_BOOKING); setPreOrder([]); setShowMenu(false); setSearch(''); setSent(false); }; const guestOptions = booking.bookingMode === 'event' ? EVENT_GUEST_OPTIONS : TABLE_GUEST_OPTIONS; const editBooking = () => { setShowMenu(false); setSent(false); }; // Location options from translations const locationOptions = [ { value: 'askim', label: t.booking.askim }, { value: 'backaplan', label: t.booking.backaplan }, ]; return (
{/* Hero */}
{eventsCopy.heroBadge}

{eventsCopy.heroTitle}

{eventsCopy.heroSubtitle}

{/* Experience pillars */}
{[ { icon: UtensilsCrossed, title: eventsCopy.experienceDiningTitle, desc: eventsCopy.experienceDiningDesc, accent: 'from-[#0f5a4a]/10 to-[#0f5a4a]/5' }, { icon: PartyPopper, title: eventsCopy.experienceCelebrateTitle, desc: eventsCopy.experienceCelebrateDesc, accent: 'from-[#c99a2e]/15 to-[#fff6dc]' }, { icon: Building2, title: eventsCopy.experienceCorporateTitle, desc: eventsCopy.experienceCorporateDesc, accent: 'from-[#101724]/8 to-[#101724]/3' }, ].map((item) => (

{item.title}

{item.desc}

))}
{!sent ? (
{/* Booking Form Card - now wider (full in max-w-4xl) */}
{/* Booking mode toggle */}
{([ { mode: 'table' as const, label: eventsCopy.modeTable, desc: eventsCopy.modeTableDesc, icon: UtensilsCrossed }, { mode: 'event' as const, label: eventsCopy.modeEvent, desc: eventsCopy.modeEventDesc, icon: PartyPopper }, ]).map((option) => { const active = booking.bookingMode === option.mode; return ( ); })}
{booking.bookingMode === 'event' && ( <> {booking.eventType === 'other' && (
)} )} {isAuthLoading ? (
Checking your account…
) : !isAuthenticated ? (

{t.booking.signInRequiredTitle}

{t.booking.signInRequiredSubtitle}

) : ( <>
{(customerEmail ?? '?').charAt(0).toUpperCase()}

{t.booking.signedInAs}

{customerEmail ?? ''}

{t.booking.formTitle}

{booking.bookingMode === 'event' ? eventsCopy.formSubtitleEvent : eventsCopy.formSubtitleTable}

{/* Location */}
{/* Date */}
{/* Time */}
{/* Guests */}
{/* Name */}
{/* Phone */}
{/* Email */}
{/* Notes */}