"use client"; import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useLocale, useTranslations } from "next-intl"; import { Link } from "@/i18n/routing"; import { Trash2 } from "lucide-react"; import { apiDelete, apiGet, apiPatch, apiPost } from "@/lib/api/client"; import { ConfirmDialog } from "@/components/ui/confirm-dialog"; import { notify } from "@/lib/notify"; import { useApiError } from "@/lib/use-api-error"; import { useAuthStore } from "@/lib/stores/auth.store"; import { formatNumber } from "@/lib/format"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { JalaliDateField, formatIsoDateJalali } from "@/components/ui/jalali-date-field"; import { LabeledField } from "@/components/ui/labeled-field"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; import type { Table } from "@/lib/api/types"; import { Can } from "@/components/auth/can"; type ReservationStatus = | "Pending" | "Confirmed" | "Cancelled" | "Seated" | "Completed"; interface Reservation { id: string; tableId?: string; tableNumber?: string; guestName: string; guestPhone: string; date: string; time: string; partySize: number; status: ReservationStatus; notes?: string; } const statusStyle: Record = { Pending: "bg-amber-50 text-[#BA7517] border-amber-200", Confirmed: "bg-[#E1F5EE] text-[#0F6E56] border-[#0F6E56]/30", Seated: "bg-blue-50 text-blue-800 border-blue-200", Completed: "bg-muted text-muted-foreground border-border", Cancelled: "bg-red-50 text-[#A32D2D] border-red-200", }; export function ReservationsScreen() { const t = useTranslations("reservations"); const tCommon = useTranslations("common"); const locale = useLocale(); const apiError = useApiError(); const cafeId = useAuthStore((s) => s.user?.cafeId); const queryClient = useQueryClient(); const [deleteTarget, setDeleteTarget] = useState(null); const [guestName, setGuestName] = useState(""); const [guestPhone, setGuestPhone] = useState("09121234567"); const [date, setDate] = useState(() => new Date().toISOString().slice(0, 10)); const [time, setTime] = useState("19:00"); const [partySize, setPartySize] = useState("2"); const [tableId, setTableId] = useState(""); const [notes, setNotes] = useState(""); const { data: list = [], isLoading } = useQuery({ queryKey: ["reservations", cafeId], queryFn: () => apiGet(`/api/cafes/${cafeId}/reservations`), enabled: !!cafeId, }); const { data: tables = [] } = useQuery({ queryKey: ["tables", cafeId], queryFn: () => apiGet(`/api/cafes/${cafeId}/tables`), enabled: !!cafeId, }); const createReservation = useMutation({ mutationFn: () => apiPost(`/api/cafes/${cafeId}/reservations`, { guestName: guestName.trim(), guestPhone: guestPhone.trim(), date, time: time.length === 5 ? `${time}:00` : time, partySize: Number(partySize), tableId: tableId || null, notes: notes.trim() || null, }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["reservations", cafeId] }); setGuestName(""); setNotes(""); }, }); const updateStatus = useMutation({ mutationFn: ({ id, status }: { id: string; status: ReservationStatus }) => apiPatch(`/api/cafes/${cafeId}/reservations/${id}/status`, { status }), onSuccess: () => queryClient.invalidateQueries({ queryKey: ["reservations", cafeId] }), }); const deleteReservation = useMutation({ mutationFn: (id: string) => apiDelete(`/api/cafes/${cafeId}/reservations/${id}`), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["reservations", cafeId] }); setDeleteTarget(null); notify.success(t("deleted")); }, onError: (err) => notify.error(apiError(err)), }); if (!cafeId) return null; const posHref = (r: Reservation) => { const params = new URLSearchParams({ reservationId: r.id }); if (r.tableId) params.set("tableId", r.tableId); params.set("guestName", r.guestName); return `/pos?${params.toString()}`; }; return (

{t("title")}

{t("newReservation")}

{t("newReservationHint")}

setGuestName(e.target.value)} /> setGuestPhone(e.target.value)} dir="ltr" className="text-end" /> setTime(e.target.value)} dir="ltr" className="text-end" /> setPartySize(e.target.value)} dir="ltr" className="text-end" /> setNotes(e.target.value)} />
{isLoading ? (

...

) : list.length === 0 ? (

{t("empty")}

) : (
    {list.map((r) => (
  • {r.guestName}

    {formatIsoDateJalali(r.date, locale)} {r.time.slice(0, 5)} · {formatNumber(r.partySize)} {t("party")} {r.tableNumber ? ` · ${t("tableNumber", { number: r.tableNumber })}` : ""}

    {r.guestPhone}

    {t(`status.${r.status}`)}
    {r.status === "Pending" && ( <> )} {(r.status === "Confirmed" || r.status === "Seated") && ( )} {r.status === "Seated" && ( )}
  • ))}
)} { if (!o) setDeleteTarget(null); }} title={t("deleteConfirmTitle")} description={ deleteTarget ? t("deleteConfirmDesc", { name: deleteTarget.guestName }) : undefined } busy={deleteReservation.isPending} onConfirm={() => deleteTarget && deleteReservation.mutate(deleteTarget.id)} />
); }