fix(audit): A1/A2/A4/A6/A8/A9/A16/A17/A19/A20 from 2026-05-15 sweep
Knocks out 10 of the 13 known issues from yesterday's Playwright audit. A4 — Client form silently rejected submit when a contact row had an empty value. The F19 filter ran in mutationFn after zod's handleSubmit had already short-circuited on min(1). Now wraps the onSubmit to prune empty rows BEFORE handleSubmit/zod sees them. A16 — File upload to documents hub root 400'd because FormData.get returns null for absent fields and zod's .optional() rejects null. Route handler now coerces null/empty → undefined before parse. A17 — Added /api/v1/me/ports endpoint that any authenticated user can hit; client.ts now uses it as the bootstrap port-slug→port-id resolver. Eliminates the wasteful 400s sales-reps and viewers were firing on every page load against the super-admin-gated /admin/ports. A1 — Filter permission_denied actions from the dashboard activity feed. Still in the audit log; just not noise on the dashboard. A2 — New LEGACY_STAGE_REMAP table + canonicalizeStage / stageLabelFor helpers in lib/constants. Activity-feed maps legacy 9-stage enum values (deposit_10pct, contract_sent, etc.) to their 7-stage labels on the way out, so historical audit rows read as "Deposit Paid" not "Deposit 10Pct". A19 — Same-stage write now returns 204 No Content. Service returns a STAGE_NOOP sentinel; the route handler translates it. A9 — Catch-up wizard now derives stage from berth status (under_offer → EOI, sold → contract) with a stageOverride state for explicit user picks. Avoids the set-state-in-effect rule violation. A20 — OwnerPicker shows a "Client / Company" hint chip on the trigger when no value is set, so users know the trigger opens a two-tab picker instead of just a client list. A8 — Migration 0066 normalizes legacy `statusOverrideMode = 'auto'` to NULL so the column lives at strictly 3 states. A6 — file-preview-dialog gets a screen-reader DialogDescription so the Radix "Missing aria-describedby" warning stops firing on every preview. A18 closed as not-a-bug: /api/v1/users genuinely doesn't exist (Next returns 404); /api/v1/admin/audit exists and 403s. A5 (Socket.IO dev noise) + A3 (react-grab CSP) left for a separate pass — both are dev-only cosmetic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -78,7 +78,11 @@ export function CatchUpWizard({ berthId, open, onOpenChange }: CatchUpWizardProp
|
||||
const [newClientEmail, setNewClientEmail] = useState('');
|
||||
const [newClientPhone, setNewClientPhone] = useState('');
|
||||
const [yachtId, setYachtId] = useState<string | null>(null);
|
||||
const [pipelineStage, setPipelineStage] = useState<string>('enquiry');
|
||||
// A9: stageOverride is the user's explicit choice. When null, the
|
||||
// effective stage derives from the loaded berth's status (under_offer
|
||||
// → eoi, sold → contract). Pre-fix this was a useState seeded to
|
||||
// 'enquiry' which never updated when the berth loaded.
|
||||
const [stageOverride, setStageOverride] = useState<string | null>(null);
|
||||
|
||||
// Fetch the berth so the wizard can scope the stage options to what
|
||||
// makes sense for the current manual status. Disabled until open so
|
||||
@@ -95,11 +99,7 @@ export function CatchUpWizard({ berthId, open, onOpenChange }: CatchUpWizardProp
|
||||
// under_offer defaults to eoi since that's the most common pre-deal
|
||||
// status that reps mark manually.
|
||||
const defaultStage = berth?.data.status === 'sold' ? 'contract' : 'eoi';
|
||||
|
||||
// Keep selected stage in sync with the loaded berth's allowed set.
|
||||
if (berth && pipelineStage !== defaultStage && !allowedStages.includes(pipelineStage)) {
|
||||
setPipelineStage(defaultStage);
|
||||
}
|
||||
const pipelineStage = stageOverride ?? defaultStage;
|
||||
|
||||
const submit = useMutation({
|
||||
mutationFn: async () => {
|
||||
@@ -143,7 +143,7 @@ export function CatchUpWizard({ berthId, open, onOpenChange }: CatchUpWizardProp
|
||||
setNewClientEmail('');
|
||||
setNewClientPhone('');
|
||||
setYachtId(null);
|
||||
setPipelineStage('enquiry');
|
||||
setStageOverride(null);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -235,7 +235,7 @@ export function CatchUpWizard({ berthId, open, onOpenChange }: CatchUpWizardProp
|
||||
|
||||
<div className="space-y-1">
|
||||
<Label>Pipeline stage</Label>
|
||||
<Select value={pipelineStage} onValueChange={setPipelineStage}>
|
||||
<Select value={pipelineStage} onValueChange={setStageOverride}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
|
||||
Reference in New Issue
Block a user