Files
pn-new-crm/src/app/api/v1/interests/[id]/berths/[berthId]/route.ts

7 lines
288 B
TypeScript
Raw Normal View History

feat(interests): linked berths list with role-flag toggles + EOI bypass Implements plan §5.5: a per-interest "Linked berths" panel mounted above the recommender on the interest detail Overview tab. Each junction row exposes the role-flag controls reps need to manage the M:M `interest_berths` link without the legacy single-berth flow. UI (`src/components/interests/linked-berths-list.tsx`) * Rows ordered with primary first; mooring number links to /berths/[id], with area + a status pill (available/under_offer/sold) and a "Primary" chip. * "Specifically pitching" Switch (writes `is_specific_interest`) with the consequence text from §1: "This berth will appear as under interest on the public map" / "This berth is hidden from the public map". * "Mark in EOI bundle" Switch (writes `is_in_eoi_bundle`). * "Set as primary" button when the row isn't primary - the existing `upsertInterestBerth` helper demotes the prior primary in the same tx. * "Bypass EOI for this berth" with reason textarea, ONLY rendered when the parent interest's `eoiStatus === 'signed'`. Writes the bypass triple (`eoi_bypass_reason`, `eoi_bypassed_by` = caller, `eoi_bypassed_at` = now); also supports clearing. * Remove-from-interest action gated by a confirmation dialog. API (`src/app/api/v1/interests/[id]/berths/...`) * `GET /` - list endpoint returning `listBerthsForInterest` plus the parent interest's `eoiStatus` in `meta.eoiStatus` so the UI can decide whether to show the bypass control. * `PATCH /[berthId]` - partial update of the junction row's flags + bypass fields. Server-side guard: rejects bypass writes when `eoiStatus !== 'signed'` (defence in depth - never trust the UI to gate this). * `DELETE /[berthId]` - calls `removeInterestBerth`. * The existing POST stays unchanged. All routes wrapped with `withAuth(withPermission('interests', view|edit, ...))`. portId from ctx; cross-port reads/writes return 404 for enumeration prevention (§14.10). Service changes (`src/lib/services/interest-berths.service.ts`) * `upsertInterestBerth` now accepts `eoiBypassReason` (tri-state: omit = no change, non-empty = record, null = clear) and `eoiBypassedBy`. The bypass triple moves as a unit, with `eoi_bypassed_at` stamped server-side. * `listBerthsForInterest` now returns berth detail (area, status, dimensions) alongside the junction row, typed as `InterestBerthWithDetails`. Socket: added `interest:berthLinkUpdated` event for live UI refreshes. Tests: 18 new integration tests in `tests/integration/api/interest-berths.test.ts` covering happy paths, primary-demotion in same tx, bypass write/clear, the "requires signed EOI" guard, cross-port 404s, missing-link 404s, empty-body 400, and viewer 403 through the permission gate.
2026-05-05 04:01:56 +02:00
import { withAuth, withPermission } from '@/lib/api/helpers';
import { deleteHandler, patchHandler } from './handlers';
export const PATCH = withAuth(withPermission('interests', 'edit', patchHandler));
export const DELETE = withAuth(withPermission('interests', 'edit', deleteHandler));