5.2.5 Attachments panel UI on the issue detail page — strip/list, upload (button + drag-drop), download, gated delete, "Show more" paging
Estimate: 35m · Depends on: 5.2.2, 5.2.4
The management surface: an AttachmentsPanel in app/(authed)/issues/[key]/_components/, mounted in the detail page's reserved Epic-5 slot (between Relationships and Activity per the 5.2.4 design). Built on the 5.2.2 service routes; design-gated by 5.2.4.
Build: the section card (count + view toggle + Attach button); strip and list views per the design (the toggle persisted like 5.1.5's sort preference); per-card download (direct blob URL) + delete (confirm popover; hidden without permission; disabled + tooltip on editor-sourced); the whole-panel dropzone + multi-file picker feeding POST /api/work-items/[id]/attachments with per-file progress + inline typed errors (413/415/429 mapped to the localized messages lib/blob/uploadClient.ts already owns — reuse, don't fork); "Show more (N)" driving the cursor read; skeleton / empty / error states; viewer read-only. Mutations via Server Actions / route calls + router.refresh() (the shipped detail-page pattern). Image thumbnails render from the blob URL (unguessable-public — the recorded serving decision); non-images get the MIME-family glyph from the design's map.
A11y: cards/rows keyboard-reachable with text-conveyed state (source, permissions); the dropzone is an enhancement over the always-present labelled Attach button (drag-only upload would be a keyboard hole); confirm popover focus-managed; extends the detail-route strict axe sweep.
Acceptance criteria
- The panel matches
attachments.mock.htmlpanel-for-panel (strip + list + toggle, card anatomy, editor-sourced indicator + disabled delete + tooltip, upload progress, inline errors, "Show more (N)", skeleton/empty/error, delete confirm, viewer read-only). - Upload works via button AND drag-drop (multi-file, per-file progress + per-file error isolation); a viewer sees neither affordance; delete honours the role matrix and the editor-sourced block end-to-end.
- The list is paged (50 + Show-more, no scroll-position loss); an issue with 120+ attachments never triggers an unbounded read.
- Colour/shape only through
--el-*/element tokens; the strict axe sweep over the detail route with a populated panel stays clean. - Component/integration tests: role-matrix rendering, paging, upload error mapping, editor-sourced block; existing detail E2E stays green.
Context refs
design/work-items/attachments.mock.html+ design-notes (5.2.4) — THE layout authority- 5.2.2 routes/DTOs;
lib/blob/uploadClient.ts(error-message mapping to reuse) app/(authed)/issues/[key]/page.tsx+_components/(the reserved slot,ContentSectionCard, the Server-Action + refresh pattern)- 5.1.5 (the sibling section that lands beside it — coordinate placement order if both are in flight); finding #57