import { useCallback, useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Spinner } from "@/components/ui/spinner"; import { useAuth } from "@/context/AuthContext"; import { useAuthStore } from "@/store/authStore"; import { apiGet, apiPatch, apiPost } from "@/lib/api"; import { formatDateTime, formatMoney } from "@/lib/format"; const MENU_CATS = ["FOOD", "BEVERAGE", "EXTRA"] as const; const OFFER_KINDS = ["SPA_SESSION", "SPA_PACKAGE", "GYM_PASS"] as const; const RS_STATUS = [ "PENDING", "PREPARING", "OUT_FOR_DELIVERY", "DELIVERED", "CANCELLED", ] as const; const LAUNDRY_STATUS = [ "REQUESTED", "PICKED_UP", "PROCESSING", "DELIVERED", "CANCELLED", ] as const; const SPA_STATUS = ["PENDING", "CONFIRMED", "COMPLETED", "CANCELLED"] as const; type MenuItem = { id: string; name: string; category: string; unitPrice: string; isAvailable: boolean; }; type RsOrder = { id: string; status: string; total: string; currency?: string; createdAt: string; user?: { name: string; email?: string | null }; booking?: { room?: { name: string }; customer?: { firstName: string; lastName: string }; }; }; type LaundryRow = { id: string; status: string; createdAt: string; user?: { name: string }; booking?: { room?: { name: string } }; }; type Offering = { id: string; kind: string; name: string; price: string; isActive: boolean; }; type SpaBookingRow = { id: string; status: string; total: string; createdAt: string; user?: { name: string }; offering?: { name: string }; }; export function GuestServicesPage() { const { canManageLoyalty, canEditBookings } = useAuth(); const canOps = canManageLoyalty && canEditBookings; const selectedPropertyId = useAuthStore((s) => s.selectedPropertyId); const [menu, setMenu] = useState([]); const [rs, setRs] = useState([]); const [laundry, setLaundry] = useState([]); const [offerings, setOfferings] = useState([]); const [spaBookings, setSpaBookings] = useState([]); const [tab, setTab] = useState("menu"); const [loading, setLoading] = useState(true); const [mOpen, setMOpen] = useState(false); const [mName, setMName] = useState(""); const [mCat, setMCat] = useState<(typeof MENU_CATS)[number]>("FOOD"); const [mPrice, setMPrice] = useState("0"); const [oOpen, setOOpen] = useState(false); const [oKind, setOKind] = useState<(typeof OFFER_KINDS)[number]>("SPA_SESSION"); const [oName, setOName] = useState(""); const [oPrice, setOPrice] = useState("0"); const loadMenu = useCallback(async () => { const r = await apiGet<{ data: MenuItem[] }>("/menu/items"); setMenu(r.data); }, [selectedPropertyId]); const loadRs = useCallback(async () => { const r = await apiGet<{ data: RsOrder[] }>("/room-service/orders"); setRs(r.data); }, [selectedPropertyId]); const loadLaundry = useCallback(async () => { const r = await apiGet<{ data: LaundryRow[] }>("/laundry/requests"); setLaundry(r.data); }, [selectedPropertyId]); const loadSpa = useCallback(async () => { const [o, b] = await Promise.all([ apiGet<{ data: Offering[] }>("/spa/offerings"), apiGet<{ data: SpaBookingRow[] }>("/spa/bookings"), ]); setOfferings(o.data); setSpaBookings(b.data); }, [selectedPropertyId]); const refresh = useCallback(async () => { setLoading(true); try { await Promise.all([loadMenu(), loadRs(), loadLaundry(), loadSpa()]); } finally { setLoading(false); } }, [loadMenu, loadRs, loadLaundry, loadSpa]); useEffect(() => { void refresh(); }, [refresh]); async function addMenu(e: React.FormEvent) { e.preventDefault(); await apiPost("/menu/items", { name: mName, category: mCat, unitPrice: mPrice, isAvailable: true, }); setMOpen(false); setMName(""); setMPrice("0"); void loadMenu(); } async function addOffering(e: React.FormEvent) { e.preventDefault(); await apiPost("/spa/offerings", { kind: oKind, name: oName, price: oPrice, }); setOOpen(false); setOName(""); setOPrice("0"); void loadSpa(); } if (loading) { return (
); } return (

Guest services

Menu Room service Laundry Spa & gym Menu items {canOps && ( New menu item
setMName(e.target.value)} />
setMPrice(e.target.value)} />
)}
Name Category Price Available {canOps && } {menu.map((it) => ( {it.name} {it.category} {formatMoney(Number(it.unitPrice), "ETB")} {it.isAvailable ? "Yes" : "No"} {canOps && ( )} ))}
Room service orders Guest Room Status Total {rs.map((o) => ( {o.user?.name ?? "—"}
{formatDateTime(o.createdAt)}
{o.booking?.room?.name ?? "—"} {canOps ? ( ) : ( {o.status} )} {formatMoney(Number(o.total), o.currency ?? "ETB")}
))}
Laundry Guest Room Status {laundry.map((r) => ( {r.user?.name ?? "—"} {r.booking?.room?.name ?? "—"} {canOps ? ( ) : ( {r.status} )} ))}
Offerings {canOps && ( New offering
setOName(e.target.value)} />
setOPrice(e.target.value)} />
)}
Name Kind Price Active {offerings.map((x) => ( {x.name} {x.kind} {formatMoney(Number(x.price), "ETB")} {x.isActive ? "Yes" : "No"} ))}
Bookings Guest Offering Status Total {spaBookings.map((b) => ( {b.user?.name ?? "—"} {b.offering?.name ?? "—"} {canOps ? ( ) : ( {b.status} )} {formatMoney(Number(b.total), "ETB")} ))}
); }