ui phase 10

This commit is contained in:
hamid
2026-07-19 17:13:32 +03:30
parent b638e25a0e
commit b4b8c9ea79
48 changed files with 1643 additions and 290 deletions
@@ -934,3 +934,47 @@ delivers fixes in its own change. **Frontend never edits backend code to "fix" a
- **Proposed shape:** `PatientDto { …, avatarUrl: string | null }` + `POST patients/{id}/avatar` (multipart),
same seam shape as the nurse profile's `uploadAvatar`.
- **Status:** deferred, non-blocking — no UI currently reads a patient `avatarUrl`; initials-only ships either way.
## REQ-059 — Ticket inbox enrichment: last-message preview + author role + an unread-total read — filed by ui-phase-10 — 2026-07-19
- **Need:** Extends REQ-028 (delivered: `unreadCount`/`lastMessageAt` are now real on `TicketSummaryDto`).
Three more additions:
1. `lastMessagePreview: string | null` on `TicketSummaryDto` — the first ~80 chars of the last
**non-internal** message (server-truncated so an internal admin note can never leak into a preview
snippet, same boundary REQ-028/the contract already enforces for the thread).
2. `lastAuthorRole: 'customer' | 'nurse' | 'admin' | 'system' | null` on `TicketSummaryDto` — the last
visible message's author role, so the inbox card can label the preview ("پرستار: …" vs "شما: …").
3. A cheap unread-**total** read for the caller (e.g. `GET tickets/unread_count`, mirroring
`notifications/unread_count`) — a single number summed server-side across the caller's tickets.
- **Why:** The inbox redesign (phase §3.1) turns the ticket list into a real messaging-app inbox: a bold
subject + one-line last-message snippet + unread pill + relative last-activity time, and a small unread
badge on the chrome's support entry (TopBar icon on the customer shell, the sidebar item on the nurse
shell). `unreadCount`/`lastMessageAt` already ship (REQ-028); the preview/author-role fields don't exist
anywhere in the wire summary, and there is no chrome-badge-sized aggregate read at all today (fetching the
whole first page of tickets just to sum `unreadCount` client-side doesn't scale and isn't the badge's job).
The client's `TicketSummary` type carries `lastMessagePreview`/`lastAuthorRole` as optional, mock-tolerant
fields (`services/tickets/types.ts`); the mock computes them from its own store
(`services/tickets/apis/mockApi.ts`); the real `ticketsClientApi` maps them to `null` and the card
degrades to subject + status chip + time — never an empty slot, never a fake "0 unread". The chrome badge
is behind a new `TicketsApi.getUnreadTotal()` seam method — the mock sums its tickets' `unreadCount`, the
real implementation returns `null` (no signal) until this lands, and the badge simply doesn't render.
- **Proposed shape:** `TicketSummaryDto { …, lastMessagePreview: string | null, lastAuthorRole: string | null
}`; `GET tickets/unread_count → { unreadCount: number }` (or fold the total into the existing list envelope
as a `totalUnreadCount` sibling of `items`/`total`, whichever fits the pagination envelope better).
- **Status:** open — mock-only; the real path shows subject + status + Shamsi/relative time with no preview
line, and the support-entry chrome badge renders nothing until a signal exists.
## REQ-060 — Ticket message photo attachments — filed by ui-phase-10 — 2026-07-19
- **Need:** Upload + serve a photo attachment on a ticket message, using the existing object-storage seam
(`IObjectStorage`, already wired for verification documents — REQ-006's avatar upload is the closest
precedent). A message-attachment linkage (one or more attachments per message), server-side size/type
validation (images only, a sane size ceiling), and a signed-URL-style read for displaying them in a thread.
- **Why:** Refund and coordination tickets routinely need photo evidence (a receipt, a care-situation photo)
and today the only channel (tickets, by product design — no free chat) has no way to attach one. The
composer's attachment button + pending-upload chip are **designed but gated** behind
`TICKETS_ATTACHMENTS_ENABLED` (`services/tickets/constants.ts`, default `false`) — no dead button ships in
production; flipping the flag once this lands is the only client change needed.
- **Proposed shape:** `POST tickets/{id}/messages` gains an optional `attachmentIds: string[]` (uploaded
beforehand via a new `POST tickets/{id}/attachments`, multipart, returning an id + a short-lived signed
URL), and `TicketMessageDto` gains `attachments: { id: string, url: string, contentType: string }[]`.
- **Status:** deferred — the affordance is designed and gated off; nothing renders until this lands and the
capability flag flips.