'use client'; import Link from 'next/link'; import type { Route } from 'next'; import { useQuery } from '@tanstack/react-query'; import { ArrowLeft, Bell, Download, FileSignature, Mail } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { PageHeader } from '@/components/shared/page-header'; import { StatusPill, type StatusPillStatus } from '@/components/ui/status-pill'; import { EmptyState } from '@/components/ui/empty-state'; import { useRealtimeInvalidation } from '@/hooks/use-realtime-invalidation'; import { apiFetch } from '@/lib/api/client'; interface ReservationDoc { id: string; title: string; status: string; documentType: string; signedFileId: string | null; signers: Array<{ id: string; status: string; signerName: string }>; } interface ReservationData { id: string; status: string; startDate: string; endDate: string | null; tenureType: string; contractFileId: string | null; berthId: string; yachtId: string; clientId: string; notes: string | null; } const RESERVATION_PILL: Record = { pending: 'pending', active: 'active', ended: 'archived', cancelled: 'cancelled', }; interface ReservationDetailProps { reservationId: string; portSlug: string; } export function ReservationDetail({ reservationId, portSlug }: ReservationDetailProps) { const reservation = useQuery<{ data: ReservationData }>({ queryKey: ['reservation', reservationId], queryFn: () => apiFetch(`/api/v1/berth-reservations/${reservationId}`), }); const documentsForRes = useQuery<{ data: ReservationDoc[] }>({ queryKey: ['documents', 'by-reservation', reservationId], queryFn: () => apiFetch( `/api/v1/documents?documentType=reservation_agreement&signatureOnly=true&limit=10`, ).then((res) => { const r = res as { data: ReservationDoc[] & Array<{ reservationId?: string }> }; return { data: r.data.filter( (d: ReservationDoc & { reservationId?: string }) => d.reservationId === reservationId, ), } as { data: ReservationDoc[] }; }), }); useRealtimeInvalidation({ 'document:created': [['documents', 'by-reservation', reservationId]], 'document:completed': [ ['documents', 'by-reservation', reservationId], ['reservation', reservationId], ], 'document:cancelled': [['documents', 'by-reservation', reservationId]], }); if (reservation.isLoading) { return
; } if (reservation.error || !reservation.data) { return ( Back } /> ); } const res = reservation.data.data; const docs = documentsForRes.data?.data ?? []; const activeAgreement = docs.find((d) => ['sent', 'partially_signed'].includes(d.status)); const completedAgreement = docs.find((d) => ['completed', 'signed'].includes(d.status)); const renderAgreementCard = (): React.ReactNode => { if (completedAgreement) { return (

Agreement signed

{completedAgreement.title}

Completed
{completedAgreement.signedFileId ? ( ) : null}

Signed contract attached to this reservation.

); } if (activeAgreement) { const signedCount = activeAgreement.signers.filter((s) => s.status === 'signed').length; return (

Agreement out for signing

{signedCount}/{activeAgreement.signers.length} signed · {activeAgreement.title}

{activeAgreement.status.replace(/_/g, ' ')}
); } return ( } title="No reservation agreement yet" body="Generate an agreement for the parties to sign before activation." actions={ } /> ); }; return (
{res.status} {res.contractFileId ? Contract attached : No contract} } actions={ } variant="gradient" />

Reservation details

Berth
{res.berthId.slice(0, 8)}…
Yacht
{res.yachtId.slice(0, 8)}…
Client
{res.clientId.slice(0, 8)}…
Tenure
{res.tenureType.replace(/_/g, ' ')}
{res.notes ? (
Notes

{res.notes}

) : null}

Agreement

{renderAgreementCard()}
); }