"use client"; import { useMemo, useState } from "react"; import Link from "next/link"; import type { RequisitionStatus, RequisitionReason } from "@prisma/client"; import { Badge } from "@/components/ui/badge"; import { RaiseRequisitionButton, ConvertReliefButton } from "./requisition-form"; import { STATUS_VARIANT, STATUS_LABEL, REASON_LABEL, ageLabel } from "./requisition-ui"; type RequisitionRow = { id: string; code: string; status: RequisitionStatus; reason: RequisitionReason; autoRaised: boolean; rankName: string; location: string; raisedBy: string; createdAt: string; }; type ReliefRow = { id: string; rankName: string; location: string; note: string | null; requestedBy: string; createdAt: string; }; type Opt = { id: string; name: string }; type RankOpt = { id: string; code: string; name: string }; const INPUT = "rounded-lg border border-neutral-300 px-3 py-2 text-sm focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20"; const STATUS_FILTERS: RequisitionStatus[] = [ "OPEN", "SHORTLISTING", "PROPOSING", "INTERVIEWING", "SELECTED", "FILLED", "CANCELLED", ]; export function RequisitionsManager({ requisitions, reliefRequests, ranks, vessels, sites, canRaise, canConvert, }: { requisitions: RequisitionRow[]; reliefRequests: ReliefRow[]; ranks: RankOpt[]; vessels: Opt[]; sites: Opt[]; canRaise: boolean; canConvert: boolean; }) { const [search, setSearch] = useState(""); const [status, setStatus] = useState<"ALL" | RequisitionStatus>("ALL"); const [location, setLocation] = useState("ALL"); const locations = useMemo( () => Array.from(new Set(requisitions.map((r) => r.location).filter((l) => l !== "—"))).sort(), [requisitions] ); const filtered = useMemo(() => { const q = search.trim().toLowerCase(); return requisitions.filter((r) => { if (status !== "ALL" && r.status !== status) return false; if (location !== "ALL" && r.location !== location) return false; if (q && !`${r.code} ${r.rankName} ${r.location}`.toLowerCase().includes(q)) return false; return true; }); }, [requisitions, search, status, location]); return (

Requisitions

{requisitions.length} requisition{requisitions.length === 1 ? "" : "s"} · vacancies being sourced and filled

{canRaise && }
{/* Filters */}
setSearch(e.target.value)} />
{/* Requisitions table */}
{filtered.length === 0 ? ( ) : ( filtered.map((r) => ( )) )}
Requisition Vessel / site Rank Reason Raised by Status
No requisitions match these filters.
{r.code} {ageLabel(r.createdAt)} ago {r.autoRaised && ( Auto )} {r.location} {r.rankName} {REASON_LABEL[r.reason]} {r.raisedBy} {STATUS_LABEL[r.status]}
{/* Relief requests from sites (spec §8.2 / R3 / R6) */}

Relief requests from sites

Foreseen gaps flagged by site staff. Convert one into a requisition to start sourcing.

{reliefRequests.length === 0 ? ( ) : ( reliefRequests.map((r) => ( )) )}
Vessel / site Rank Note Requested by
No open relief requests.
{r.location} {r.rankName} {r.note ?? "—"} {r.requestedBy} {canConvert && ( )}
); }