feat(berths): bulk price update + per-berth price API
Two new endpoints lift price editing out of the full berth-update form: - `PATCH /api/v1/berths/[id]/price` — single-berth price edit triggered inline from the berth list / detail (no need to open the heavy edit modal just to retag a price). - `POST /api/v1/berths/bulk-update-prices` — multi-row update from a selection in the berth list; transactional, audit-logged per row. Berth list column gets an inline price-edit affordance backed by the single-berth endpoint; the bulk action lives in the row-selection toolbar. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -68,6 +68,9 @@ export type BerthRow = {
|
||||
/** Most-advanced pipeline stage among the berth's active interests. Null
|
||||
* when no active interest is linked. Read-only; computed server-side. */
|
||||
latestInterestStage?: string | null;
|
||||
/** Count of non-terminal, non-archived interests linked to this berth.
|
||||
* Drives the "Active interests" column + the demand sort. */
|
||||
activeInterestCount?: number;
|
||||
/** #67: source of the last status write. 'manual' when a human set it
|
||||
* via the API; 'automated' when a berth-rule fired; null on rows that
|
||||
* haven't been touched since seed. The reconciliation surface treats
|
||||
@@ -85,6 +88,7 @@ export const BERTH_COLUMN_OPTIONS: Array<{ id: string; label: string }> = [
|
||||
{ id: 'area', label: 'Area' },
|
||||
{ id: 'status', label: 'Status' },
|
||||
{ id: 'latestInterestStage', label: 'Latest deal stage' },
|
||||
{ id: 'activeInterestCount', label: 'Active interests' },
|
||||
{ id: 'sidePontoon', label: 'Side / Pontoon' },
|
||||
{ id: 'dimensions', label: 'Dimensions' },
|
||||
{ id: 'nominalBoatSize', label: 'Nominal boat size' },
|
||||
@@ -281,6 +285,16 @@ export const berthColumns: ColumnDef<BerthRow, unknown>[] = [
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'activeInterestCount',
|
||||
accessorKey: 'activeInterestCount',
|
||||
header: 'Active interests',
|
||||
cell: ({ row }) => {
|
||||
const n = row.original.activeInterestCount ?? 0;
|
||||
if (n === 0) return <span className="text-muted-foreground">—</span>;
|
||||
return <span className="font-medium tabular-nums">{n}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sidePontoon',
|
||||
header: 'Side / Pontoon',
|
||||
|
||||
Reference in New Issue
Block a user