'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
) : (
'—'
)}
))}
);
}