ui phase 11

This commit is contained in:
hamid
2026-07-19 19:19:44 +03:30
parent b4b8c9ea79
commit 87fa4cd497
74 changed files with 3115 additions and 506 deletions
@@ -978,3 +978,80 @@ delivers fixes in its own change. **Frontend never edits backend code to "fix" a
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.
## REQ-062 — Verification queue enrichment: name/phone search + per-status counts — filed by ui-phase-11 — 2026-07-19
- **Need:** Two additions to `GET admin_verifications` (the b6 admin review-queue list, folded client-side to
one row per nurse — see REQ-034):
1. A **name/phone search** query param (proposed `q`) that filters the queue to nurses whose name or phone
matches (case-insensitive substring), combinable with the existing `status` filter.
2. **Whole-desk per-status counts** in the response envelope — `pending`/`in_review` totals across the
**entire** queue (not the current page or the applied filters), so the status-tab badges never drift
from the true queue size. Optionally also a `submittedAt` sort param (`sort=submittedAt&dir=asc|desc`)
for the new waiting-time column.
- **Why:** The review queue (phase §3.3, the flagship trust-desk screen) previously offered only a 3-value
status filter and no way to find a specific nurse or see how much work is queued per status — the audit's
headline finding. The client's `AdminVerificationQueueFilters` now carries an optional `search` field and
`AdminVerificationQueuePage` an optional `counts: { pending, in_review }` (both in
`client/src/services/verification/types.ts`); the mock (`apis/mockApi.ts`) computes real counts over its
whole unfiltered fixture set and matches `search` against the seeded `nurseName`. The real
`verificationClientApi.listVerificationQueue` (`apis/clientApi.ts`) already sends `search` as `q` on the
query string (a no-op today — the server currently ignores unknown query params rather than 400ing) and
leaves `counts` `undefined`; the queue screen renders the status tabs **without** badge counts until this
lands (mock-tolerant, never a fake count).
- **Proposed shape:** `GET admin_verifications?status=&q=&page=&pageSize=&sort=&dir=` →
`{ items: AdminPendingStepDto[], total, page, pageSize, counts: { pending: number, in_review: number } }`
(`counts` computed over the whole queue, ignoring `status`/`q`/`page`).
- **Status:** open
## REQ-063 — Ticket lifecycle mutations (close/reopen/assign) — filed by ui-phase-11 — 2026-07-19
- **Need:** Close/reopen/assign endpoints for a ticket, plus an `assigneeUserId` field on the admin ticket
DTOs (`AdminTicketSummary`/`AdminTicketDetail`) so the queue and thread can show and change who owns a
case.
- **Why:** Today a ticket has no lifecycle command at all — a resolved ticket can never leave the admin
queue, and there is no way to hand a case to a specific staff member. The ops desk (f15 admin console)
needs both for a working worklist. The client-side controls (`services/tickets` `useCloseTicket`/
`useReopenTicket`/`useAssignTicket`, and the admin thread's close/reopen/"assign to me" affordances) are
built against the mock and gated behind `TICKET_LIFECYCLE_ENABLED` (`services/tickets/constants.ts`,
default `false`) so nothing points at a 404'ing route in production.
- **Proposed shape:** `POST tickets/{id}/close` (no body) → ticket `status: 'closed'`, sets `closedAt`.
`POST tickets/{id}/reopen` (no body) → `status: 'open'`, clears `closedAt`. `POST tickets/{id}/assign`
with `{ ownerUserId: number }` → sets the ticket's assignee. `AdminTicketSummary`/`AdminTicketDetail` gain
`assigneeUserId: number | null`.
- **Status:** open
## REQ-064 — Partner scoped booking detail (read-only summary) — filed by ui-phase-11 — 2026-07-19
- **Need:** A single-sponsored-booking read for the partner portal (`GET centers/me/bookings/{bookingId}`,
sibling of the existing `GET centers/me/bookings` list route), returning everything the list row already
has plus a status timeline for that booking.
- **Why:** §3.7 — the partner portal's sponsored-bookings list (`/partner/bookings`) needs to link each row
to a detail view so a center admin can confirm what happened on a booking without leaving the portal. The
portal is explicitly scoped to read-only, non-clinical data (portal scope boundary, product/business) —
patient display name + dates + status only, never clinical content, address, or money.
- **Proposed shape:** `GET centers/me/bookings/{bookingId}` → `SponsoredBookingDetailDto` = the existing
`SponsoredBookingDto` (`bookingId`, `patientName`, `scheduledDate`, `status`) plus
`timeline: { status: string, occurredAt: string }[]` (server-truth status history, dates only — no
clinical content). The client-side seam (`services/partnerCenter`) is built against this shape (mock
primary, `USE_PARTNER_MOCK`); the real client call is written but unreachable until this route exists.
- **Status:** open
## REQ-061 — Admin user lookup: name/phone search + batch id→label resolve — filed by ui-phase-11 — 2026-07-19
- **Need:** Two endpoints backing a directory of platform users for admin use:
1. `GET admin_users/search?q=&role=` — search by name/phone (min 2 chars), optionally narrowed to one
coarse role (`customer`/`nurse`/`admin`/`partner`), returning up to ~10 matches: `{ id, displayName,
maskedPhone, roles: string[], nurseProfileId?: number }` (`nurseProfileId` set only when `roles`
includes `nurse` — the id `NursePicker` needs, a different id space than the user id).
2. `POST admin_users/lookup` with `{ userIds: number[] }` — batch id→label resolve, returning the same
shape for each found id, so a page with N actor ids (an audit log, a role grid) makes one request
instead of N.
- **Why:** The single scariest wrong-target failure mode in the backoffice — every audited action (role
grants, partner-center admin assignment, sponsored-nurse assignment) previously targeted a hand-typed
raw numeric id with no lookup or name echo-back (phase §3.2, the audit's flagship "high" finding), and
every actor/owner id across the console (audit log, role grid) rendered as a bare `#42` with no way to
find out who that is (§3.6, `AuditLogRow`). `UserPicker`/`NursePicker` (`client/src/components/admin/`)
and the new read-first `/admin/users` directory console (§3.8) are both built against this shape,
mock-primary behind `services/admin`'s existing `USE_ADMIN_MOCK` seam (`searchUsers`/`lookupUsers` on
`AdminApi`) — no page points at a 404'ing route; the mock seeds a small but representative directory
(admin/support/finance staff, nurses, customers, one partner-center contact) across both endpoints.
- **Proposed shape:** see **Need** above; masked phone follows the existing `maskIranMobile` convention
(`"0912•••1234"`, first-4/last-4) — never the full number.
- **Status:** open