PR: #368: feat(documents): Action Rail multi-file intake upload sheet [Story-1518] by @ankitmaheshwari-com
View PR →
Story-1518 · app lane · documents intake

Action Rail intake upload sheet

The multi-file intake sheet behind the Documents Work Mode's Upload verb. Files upload browser → S3 direct (presigned), each with its own progress / retry / cancel, and land in the Documents Register at processing status the moment they finalize. Mode-1 (deterministic) — AI columns render processing/empty. Frontend only; the backend control plane is pramaan-spec Story-522.

Upload flow

Pick / drop files → Validate (cap + type) → presign-upload → PUT → S3 (progress) → finalize → Register row

Progress requires XMLHttpRequest (fetch cannot report upload progress); the Cancel button maps to xhr.abort() via an AbortSignal. The control plane reuses the existing typed server actions (presignDocumentUploadAction / finalizeDocumentUploadAction), which run through mattersGateway against the generated src/gen contracts (DocumentPresignUploadResponse, DocumentListItem). No pnpm codegen was run.

Per-file states

Each queued file carries one status, rendered as a pill in the sheet:

Processing Uploading Done Failed Rejected

Components (Storybook-first, leaf → composite)

FileRoleStory
lib/matters/upload-queue.tsPure queue model: item shape, status, validation, summary. Unit-tested.upload-queue.test.ts
lib/matters/upload-config.tsConfigurable per-file cap + accepted extensions.—
matters/upload-status-pill.tsxStatus pill (7 states).UploadStatusPill
matters/upload-dropzone.tsxDrag-drop + browse target.UploadDropzone
matters/upload-queue-item.tsxPer-file row: progress bar, retry, cancel, error.UploadQueueItem
matters/intake-upload-sheet.tsxComposes dropzone + queue list in the right-side sheet.IntakeUploadSheet
matters/use-document-upload-queue.tsLive wiring hook: presign → PUT (progress) → finalize, retry/cancel.—

Verification

Files changed

20 file(s), +1180 / -383 lines

Path Role + -
messages/en.jsonConfig+25-11
messages/hi.jsonConfig+25-11
src/components/matters/intake-upload-sheet.stories.tsxComponent+83-0
src/components/matters/intake-upload-sheet.tsxComponent+83-0
src/components/matters/live-matter-documents-page.tsxComponent+33-43
src/components/matters/upload-client.tsComponent+71-10
src/components/matters/upload-dropzone.stories.tsxComponent+29-0
src/components/matters/upload-dropzone.tsxComponent+87-0
src/components/matters/upload-form-fields.tsxComponent+0-81
src/components/matters/upload-form-model.tsComponent+0-65
src/components/matters/upload-form.tsxComponent+0-77
src/components/matters/upload-queue-item.stories.tsxComponent+101-0
src/components/matters/upload-queue-item.tsxComponent+107-0
src/components/matters/upload-sheet.tsxComponent+0-85
src/components/matters/upload-status-pill.stories.tsxComponent+43-0
src/components/matters/upload-status-pill.tsxComponent+39-0
src/components/matters/use-document-upload-queue.tsComponent+151-0
src/lib/matters/upload-config.tsLibrary+59-0
src/lib/matters/upload-queue.test.tsLibrary+114-0
src/lib/matters/upload-queue.tsLibrary+130-0