pelagia-portal/App/app/(portal)/approvals/approvals-search.tsx
Hardik 280966a369 refactor: revert cost centre to vessels only, remove vessel-site link
Cost Centre on PO forms now shows only Vessels (plain vesselId field).
Sites are a separate concept and not selectable as cost centres.

- PurchaseOrder.vesselId is required again (NOT NULL restored)
- Vessel.siteId and vessel->site relation removed from schema
- DB migration: drops Vessel.siteId column, restores PO.vesselId NOT NULL
- All PO forms (new/edit/import/manager-edit): plain vessel <select> with
  code-prefixed labels (e.g. "HNR1 — HNR 1")
- History, approvals, dashboard, my-orders, payments: back to vesselId
  filter params and po.vessel.name display
- Admin vessels: removed Site column and site-assignment dropdown
- Admin sites detail page: removed "Assigned Vessels" section
- Sites table: removed Vessels count column (no longer linked)
- seed-prod.ts and seed.ts: vessels created without siteId
- SearchableSelect accounting code picker retained from previous commit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:14:24 +05:30

71 lines
3 KiB
TypeScript

"use client";
import { useRouter, useSearchParams } from "next/navigation";
import { useState } from "react";
interface Props {
vessels: { id: string; name: string }[];
}
export function ApprovalsSearch({ vessels }: Props) {
const router = useRouter();
const sp = useSearchParams();
const [q, setQ] = useState(sp.get("q") ?? "");
const [vesselId, setVesselId] = useState(sp.get("vesselId") ?? "");
const [dateFrom, setDateFrom] = useState(sp.get("dateFrom") ?? "");
function apply() {
const params = new URLSearchParams();
if (q.trim()) params.set("q", q.trim());
if (vesselId) params.set("vesselId", vesselId);
if (dateFrom) params.set("dateFrom", dateFrom);
router.push(`/approvals?${params.toString()}`);
}
function clear() {
setQ(""); setVesselId(""); setDateFrom("");
router.push("/approvals");
}
const hasFilters = q || vesselId || dateFrom;
return (
<div className="mb-4 rounded-lg border border-neutral-200 bg-white p-4">
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
<div className="sm:col-span-2">
<label className="block text-xs font-medium text-neutral-600 mb-1">Search (PO number or submitter)</label>
<input type="text" value={q} onChange={(e) => setQ(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && apply()}
placeholder="e.g. PO-0012 or John…"
className="w-full 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" />
</div>
<div>
<label className="block text-xs font-medium text-neutral-600 mb-1">Cost Centre</label>
<select value={vesselId} onChange={(e) => setVesselId(e.target.value)}
className="w-full 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">
<option value="">All cost centres</option>
{vessels.map((v) => <option key={v.id} value={v.id}>{v.name}</option>)}
</select>
</div>
<div>
<label className="block text-xs font-medium text-neutral-600 mb-1">Submitted from</label>
<input type="date" value={dateFrom} onChange={(e) => setDateFrom(e.target.value)}
className="w-full 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" />
</div>
</div>
<div className="mt-3 flex items-center gap-2">
<button onClick={apply}
className="rounded-lg bg-primary-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-primary-700 transition-colors">
Search
</button>
{hasFilters && (
<button onClick={clear}
className="rounded-lg border border-neutral-300 bg-white px-3 py-1.5 text-sm font-medium text-neutral-600 hover:bg-neutral-50 transition-colors">
Clear
</button>
)}
</div>
</div>
);
}