feat(documents-wizard): replace UUID-paste fields with searchable pickers + inline upload

Reps no longer have to copy/paste UUIDs into the New-document wizard.

Three UUID inputs replaced:
- Template id Input → DocumentTemplatePicker (queries /api/v1/document-templates
  with name search; filters to isActive=true)
- Uploaded file id Input → inline FileUploadZone (drop or browse PDF; surfaces
  the uploaded file id directly to the wizard via the new onUploadComplete
  signature)
- Subject id Input → conditional picker: ClientPicker / CompanyPicker /
  YachtPicker / InterestPicker depending on the subject-type dropdown.
  Reservation falls back to Input for now (no ReservationPicker yet).

Other polish in the wizard:
- SIGNER_ROLES labels capitalized in the role select (client → Client, etc.)
  via a formatSignerRole() helper. Internal values stay lowercase.
- Pinned h-9 on Select triggers so the type/subject row + signer-role select
  vertically align with their adjacent inputs.
- Subject-type change now resets subjectId — picker options are type-specific
  and a stale id from a different entity table would be invalid.

Infrastructure for hub uploads (will be consumed in a follow-up dropdown +
drag-drop pass):
- /api/v1/files/upload route now parses folderId from FormData (schema
  already supported it).
- FileUploadZone accepts a folderId prop and forwards it, plus a new
  onUploadComplete(file) callback shape that surfaces { id, filename } on
  each successful upload. Existing per-entity callers (Files tab on clients,
  companies, yachts, interests) ignore the arg, no behaviour change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 15:17:02 +02:00
parent 63f96254e5
commit 880c5cbafc
5 changed files with 337 additions and 23 deletions

View File

@@ -17,6 +17,7 @@ export const POST = withAuth(
const buffer = Buffer.from(await file.arrayBuffer());
const folderIdRaw = formData.get('folderId') as string | undefined;
const metadata = uploadFileSchema.parse({
filename: (formData.get('filename') as string | null) ?? file.name,
clientId: formData.get('clientId') as string | undefined,
@@ -25,6 +26,9 @@ export const POST = withAuth(
category: formData.get('category') as string | undefined,
entityType: formData.get('entityType') as string | undefined,
entityId: formData.get('entityId') as string | undefined,
// Hub uploads pass the current folderId so the file lands inside
// the user's currently-selected folder. Empty string ⇒ root (null).
folderId: folderIdRaw && folderIdRaw.length > 0 ? folderIdRaw : undefined,
});
const result = await uploadFile(