'use client'; import { useParams } from 'next/navigation'; import Link from 'next/link'; import { useQuery } from '@tanstack/react-query'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { EmptyState } from '@/components/shared/empty-state'; import { apiFetch } from '@/lib/api/client'; export interface ReservationRow { id: string; berthId: string; portId: string; clientId: string; yachtId: string; status: 'pending' | 'active' | 'ended' | 'cancelled'; startDate: string; endDate: string | null; tenureType: string; contractFileId: string | null; notes: string | null; createdAt: string; } export interface ReservationListProps { reservations: ReservationRow[]; showBerth?: boolean; portSlug?: string; emptyMessage?: string; } /** * Renders a client's name as a link by fetching the client record. * Uses TanStack Query cache for memoization of repeated clientId queries. */ function ClientLink({ clientId, portSlug }: { clientId: string; portSlug: string }) { const { data } = useQuery<{ fullName: string }>({ queryKey: ['clients', clientId, 'name-only'], queryFn: () => apiFetch<{ data: { fullName: string } }>(`/api/v1/clients/${clientId}`).then((r) => r.data), }); return ( {data?.fullName ?? `Client ${clientId.slice(0, 8)}`} ); } /** * Renders a yacht's name as a link by fetching the yacht record. */ function YachtLink({ yachtId, portSlug }: { yachtId: string; portSlug: string }) { const { data } = useQuery<{ name: string }>({ queryKey: ['yachts', yachtId, 'name-only'], queryFn: () => apiFetch<{ data: { name: string } }>(`/api/v1/yachts/${yachtId}`).then((r) => r.data), }); return ( {data?.name ?? `Yacht ${yachtId.slice(0, 8)}`} ); } /** * Renders a berth's mooring number as a link by fetching the berth record. */ function BerthLink({ berthId, portSlug }: { berthId: string; portSlug: string }) { const { data } = useQuery<{ mooringNumber: string }>({ queryKey: ['berths', berthId, 'name-only'], queryFn: () => apiFetch<{ data: { mooringNumber: string } }>(`/api/v1/berths/${berthId}`).then( (r) => r.data, ), }); return ( {data?.mooringNumber ?? `Berth ${berthId.slice(0, 8)}`} ); } /** * Renders a status badge with appropriate color coding. */ function StatusBadge({ status }: { status: ReservationRow['status'] }) { const colorMap: Record = { pending: 'bg-gray-100 text-gray-800', active: 'bg-green-100 text-green-800', ended: 'bg-blue-100 text-blue-800', cancelled: 'bg-red-100 text-red-800', }; const color = colorMap[status]; const label = status.charAt(0).toUpperCase() + status.slice(1); return ( {label} ); } /** * Pretty-prints tenure type for display. */ function prettyTenure(tenureType: string): string { const tenureMap: Record = { permanent: 'Permanent', fixed_term: 'Fixed term', seasonal: 'Seasonal', }; return tenureMap[tenureType] ?? tenureType; } /** * Formats a date range as "{startDate} → {endDate or 'ongoing'}". */ function formatDateRange(startDate: string, endDate: string | null): string { const start = new Date(startDate).toLocaleDateString(); const end = endDate ? new Date(endDate).toLocaleDateString() : 'ongoing'; return `${start} → ${end}`; } export function ReservationList({ reservations, showBerth = false, portSlug: portSlugProp, emptyMessage, }: ReservationListProps) { const routeParams = useParams<{ portSlug: string }>(); const portSlug = portSlugProp ?? routeParams?.portSlug ?? ''; if (reservations.length === 0) { return ( ); } return (
{showBerth && Berth} Client Yacht Dates Tenure Status Contract {reservations.map((r) => ( {showBerth && ( )} {formatDateRange(r.startDate, r.endDate)} {prettyTenure(r.tenureType)} {r.contractFileId ? ( // TODO: Confirm final file-download endpoint URL when available View contract ) : ( '—' )} ))}
); }