Repository navigation
Slice 4 (Module 02): Doctor "Shared with me" view + document download #12
Copy link
Copy link
Closed
Labels
afkEligible for the main agent loopEligible for the main agent looppriority:2Priority 2Priority 2qa-readyPR opened, awaiting human QAPR opened, awaiting human QAsliceVertical tracer-bullet sliceVertical tracer-bullet slice
Description
What to build
Give doctors a view of all documents shared with them and the ability to download any shared file. Expose the doctor-facing component as a named export so Module 04 (appointments) can embed it without reimplementing any document logic.
getSharedWithMeuse case: returns documents shared with the authenticated doctor, grouped by owning patient (patientId,patientDisplayName,documents[]).downloadDocumentuse case: verifies adocument_sharesrow exists for (documentId, doctorId) before streaming — 403 ACCESS_DENIED if absent. Streams viacreateReadStream(no full-file buffering).GET /documents/shared-with-me→ 200 +Array<{ patientId, patientDisplayName, documents: Array<{ id, filename, mimeType, size, uploadedAt }> }>behindrequireAuth()+requireRole(['doctor']). Note: this path must be registered beforeGET /documents/:id/filein the Hono router.GET /documents/:id/file→ 200 + binary stream (Content-Type: stored mimeType,Content-Disposition: attachment; filename="<original>") behindrequireAuth()+requireRole(['doctor']). Returns 403 ACCESS_DENIED if not shared with this doctor; 404 DOCUMENT_NOT_FOUND if document does not exist.- Fill in these two endpoints in
packages/contracts/src/documents.ts. SharedDocumentsListReact component (named export): self-contained, no required props, renders grouped documents with download links using theuseSharedDocumentshook.useSharedDocumentsTanStack Query hook (named export): callsGET /documents/shared-with-me.- Both exports live under
apps/web/src/features/documents/and are importable by Module 04 without any document logic being reimplemented there.
Acceptance criteria
-
GET /documents/shared-with-mereturns 200 with documents grouped by patient when shares exist; returns[]when no documents are shared with the doctor. -
GET /documents/:id/filestreams the file with correctContent-TypeandContent-Disposition: attachment; filename="<original>"headers when the document is shared with the calling doctor. -
GET /documents/:id/filereturns 403{ error: "ACCESS_DENIED", ... }when nodocument_sharesrow exists for the (documentId, doctorId) pair; zero file bytes are written to the response. -
GET /documents/:id/filereturns 404{ error: "DOCUMENT_NOT_FOUND", ... }for an unknown document id. - Both endpoints return 401 without a valid JWT and 403 with a patient JWT.
-
SharedDocumentsListrenders the grouped document list when imported and rendered in isolation (Vitest component test against a mocked contract). -
useSharedDocumentsis a named export callable from outside the documents feature folder. -
pnpm verifyexits 0.
User stories covered
- US feat: Slice 3 — RBAC middleware, probe routes, /login redirect guard #13: As a doctor, I want to see all documents shared with me, grouped by patient.
- US feat: Slice 4 — auth failure modes & token lifecycle hardening #14: As a doctor with no shared documents, I want the list to return an empty array rather than an error.
- US feat: Slice 1 (Module 02) — Document upload & list foundation #15: As a doctor, I want to download a document that has been explicitly shared with me.
- US feat: Slice 2 (Module 02) — document delete with atomic cascade #16: As a doctor, I want every download to be authorization-checked before any file byte is sent.
- US Module 02 Slice 3: per-doctor document sharing #17: As a doctor, I want an explicit 403 when I try to download a document not shared with me.
- US Module 02 Slice 4: doctor shared-with-me + document download #18: As a developer building Module 04, I want to import
SharedDocumentsListanduseSharedDocumentsfrom Module 02.
Blocked by
- Slice 1 (Module 02): Document upload & list — foundation #9 (code); meaningful demo requires Slice 3 (Module 02): Per-doctor sharing management (grant / revoke / state) #11 to have granted access first
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
afkEligible for the main agent loopEligible for the main agent looppriority:2Priority 2Priority 2qa-readyPR opened, awaiting human QAPR opened, awaiting human QAsliceVertical tracer-bullet sliceVertical tracer-bullet slice
Activity
mGasiorek998 commentedon May 7, 2026
Plan: two new doctor-side endpoints; the shared-with-me list path must be registered BEFORE /documents/:id/file so the param route doesn't shadow it.
Files touched:
Tests: integration AC1–AC5 in documents.test.ts under a new describeWithDb block; pre-seed two patients + two doctors + two docs + a couple of share rows; assertions on grouping, streaming headers + body, 403 unshared, 404 unknown id, auth/role guards. UI named-export component test deferred (no apps/web vitest infra yet — same convention as Slices 1–3).
mGasiorek998 commentedon May 7, 2026
Light review advisory (warning, not blocker): SharedDocumentsList.tsx uses raw fetch for the binary download because the shared apiClient hardcodes response.json() and cannot return a Blob. The bypass is documented inline. Follow-up cleanup option (out of scope here): extend apiClient with a binary-response branch, or move the download helper into a feature-level fetch wrapper, so the architecture-rules ban on direct fetch in components has no exceptions.
mGasiorek998 commentedon May 7, 2026
Implementation PR opened: #18. 1 red→green cycle, 9 new integration tests added (AC1–AC5). Backend covers GET /documents/shared-with-me (grouped) and GET /documents/:id/file (streamed) with the literal path registered before the param so ordering is correct. Web SharedDocumentsList + useSharedDocuments are named exports under apps/web/src/features/documents/, ready for Module 04 to import without reimplementing document logic. pnpm verify clean (77 tests).
feat(green): doctor shared-with-me list + document download (Module 0…