Files
baya-monorepo/dev/shared-working-context/reports/frontend-phase-15-report.md
T

10 KiB
Raw Blame History

Frontend Phase 15 (b15) — Admin backoffice & partner-center consoles — report

Date: 2026-07-10 · Track: frontend · Status: complete — this is the final frontend phase; MVP is complete. Gate: npm run check green (0 errors / 0 warnings) · npm run test:ci green (325 tests, +36) · npm run build green (all 18 new routes compile) · admin/partner i18n in sync · i18n key audit clean.


1. What was built

The internal operational cockpit that runs the marketplace: a role-gated admin backoffice in the f0 desktop sidebar shell, plus a separately-scoped partner-center portal. All of it is a read-and-act surface over data other domains own — the client renders the contract's values and issues the sanctioned commands; it never computes eligibility, money decomposition, holiday shifts, the is_verified flip, or the review aggregate (§5).

Two new domain services (the auth-service shape)

  • services/admin — config, holidays, audit, support-alerts, RBAC. 12 hooks; filters + page in every key so each worklist filter/page caches independently. Mock-primary.
  • services/partnerCenter — admin-side center management + the center-scoped portal reads. 12 hooks. Mock-primary.

Admin-endpoint additions to 5 existing domains (the staff lens — not new domains)

  • verification (b6): useVerificationQueue / useVerificationCase / useVerificationDocumentUrl (on-demand signed URL) / useDecideStep / useApproveVerification / useRejectVerification.
  • refunds (b11): useRefundPreview / useInitiateRefund / useApproveRefund / useRejectRefund (ticket-linked).
  • payouts (b13): usePayoutBatches / usePayoutBatchDetail / usePreviewPayoutBatch / useRunPayoutBatch (idempotency-keyed) / useRetryPayout / useRecordTransferReference.
  • reviews (b14): useModerationQueue / useModerateReview.
  • tickets (b15): useAdminTickets / useAdminTicket / useAdminTicketThread / usePostAdminMessage. The admin ticket types carry isInternal; the user-app types deliberately do not — the two surfaces stay distinct so an internal note can never bleed into a user view (§5).

Shared composites — @/components/admin (13, each with a co-located *.test.tsx; 36 tests)

AdminPageHeader, AdminEmptyState, AdminErrorState, AdminPager, ConfirmDialog (optional required-reason), AdminDataTable (generic dense worklist table, horizontal-scroll-contained), ConfigRow, AuditLogRow (expandable diff), SupportAlertCard, PartnerSettlementRow (reconciling commission/VAT breakdown via the f0 PriceBreakdown), DocumentViewer (signed-URL on-demand + expired→re-request), RefundPanel (server-computed preview → initiate → provider-failure retry / reject), AdminMessageBubble (internal-note styled distinctly).

Screens (18 new routes)

  • Admin (/admin/*, role-gated): overview landing · verification (queue + [nurseId] case) · tickets (queue + [id] thread w/ internal-note composer + RefundPanel) · payouts (dashboard + [batchId] detail) · reviews (moderation) · config · holidays · alerts · audit · partners (list + [id] detail) · roles (DEFERRED-IF-MISSING).
  • Partner portal (/partner/*, separate scope): home · nurses · bookings · settlement (MoR-gated).

Foundation

  • useAdminCapabilities() (@/hooks) — a memoized selector over the session's fine-grained roleCodes (added to SessionUser, hydrated from /me by useSessionRoleSync). Drives per-console can* booleans that hide/disable controls a role can't use (server still authorizes every command).
  • PartnerLayout + the (private-routes)/partner/ route group (a separate authz scope).
  • The admin + partner i18n namespaces in both locales (incl. پروانه تأسیس / مسئول فنی / نماد اعتماد الکترونیکی / سامانه مودیان and the enum-label prefixes keyed off stable codes).
  • New AppIcon registry entries; new ROUTES.ADMIN_* / ROUTES.PARTNER_* + path helpers.

2. What is now testable, and exactly how

Run npm run dev, sign in as an admin (mock-primary, so no live backend needed). The §7 acceptance steps:

  1. Verify a nurse. /admin/verification → pick a pending nurse → the case loads each document via a signed URL (DocumentViewer; sentinel documentId 9999 exercises the error→re-request path). Pass the steps (reject one with a required reason), enter a structured credential on a credential-bearing step; Approve is disabled until every step is passed → confirm → the nurse leaves the queue. The client never wrote is_verified.
  2. Process a refund. /admin/tickets → open the refund ticket (bookingId + refundId seeded) → RefundPanel shows the server-computed tiered % + fee/payout decomposition (reconciles to the rial) + channel + BNPL ETA; the post-payout booking shows the read-only clawback notice; the decline sentinel shows retry.
  3. Preview + run a payout batch. /admin/payoutspreview next batch → eligibility breakdown (eligible + no-IBAN-skipped + clawback line + holiday-shifted processing date) → run batch (confirm requires an idempotency key — a double-click can't double-pay) → processing. [batchId] detail → a failed payout → retry; reconcile a transfer_reference.
  4. Moderate a review. /admin/reviews → a pending_moderation review (low-rating flag) → publish/hide/reject (reject/hide require a reason) → it leaves the queue; the client never computed the aggregate.
  5. Edit a config value. /admin/config → edit vat_rate → the input validates 01 (1.5 blocked) → save → the dialog states it is audited, immediate, non-retroactive → the history drawer shows old→new + actor + Shamsi time.
  6. Resolve a support alert. /admin/alerts → filter to openassign to meresolve with a note. It appears in no customer/nurse/partner view.
  7. Holiday manager. /admin/holidays → add a holiday with is_bank_closed on → listed (the client never computes the shift; the payout preview in step 3 shows the server-shifted date).
  8. Audit viewer. /admin/audit → filter by entity/actor/date → paginated; expand a row for the changedFields diff. No edit/delete control exists.
  9. Partner portal. Sign in mapping to a center admin → /partner shows onboarding state + license + MoR; /partner/nurses + /partner/bookings show only that center's data; /partner/settlement renders the commission/VAT invoices + signed-URL PDF + masked IBAN only when merchant-of-record (a non-MoR center shows the "via Balinyaar" state). Back as an admin → /admin/partners → create/verify/activate + assign a nurse; IBAN is write-then-masked (submit full, only last-4 ever shows).
  10. RBAC + i18n + RTL + caching. A support role sees no payout/refund controls; a moderation role sees only moderation (drive via useAdminCapabilities). faen translates every label (incl. the Persian legal terms) and the sidebar mirrors. Switching worklist filters/pages caches separately (filters+page in the key) — no refetch of loaded data.

3. What is mocked, and how to make it real

All f15 data is mock-primary (the real clientApi in each domain maps the live routes and targets proposed slugs for the gaps; the swap is a one-line flag flip per domain — no hook/component change). Full detail in reports/mocks-registry.md (three new/updated rows). Seams + flags:

  • services/adminUSE_ADMIN_MOCK (config/holidays/audit/support-alerts live at b1; RBAC + config audit columns + rich audit filters are gaps → REQ-029/030/031).
  • services/partnerCenterUSE_PARTNER_MOCK (admin CRUD/verify/sponsor live at b15; portal split reads + activate/suspend + invoice total are gaps → REQ-032/033).
  • verification/refunds/payouts/reviews/tickets admin methods — the owning domain's flag (REQ-034/035/036/037).

No backend seams were introducedIObjectStorage/IBankTransferProvider/IBnplProvider/IMoadianClient/ ILicenseVerificationService/the audit interceptor/the notification dispatcher are all server-side (b1/b6/b11/b13/b14/b15); the frontend never touches them.


4. Contracts consumed

messaging-notifications-admin.md (b15, primary) + verification.md (b6) + refunds-invoices.md (b11) + payouts.md (b13) + reviews-records.md (b14) + config-reference.md (b1). All types derived from these — none guessed.

5. Requests filed (frontend/requests/for-backend.md)

REQ-029 config updatedAt/updatedBy · REQ-030 audit actor/action/date filters · REQ-031 RBAC role endpoints · REQ-032 partner-portal split reads + activate/suspend + IBAN write-then-masked · REQ-033 center-scoped invoice list

  • invoice totalIrr · REQ-034 verification nurse-level queue + on-demand doc URL + whole-verification approve/reject · REQ-035 refund preview + explicit approve/reject · REQ-036 payout single-preview + holidayShifted + record-transfer-reference · REQ-037 moderation tagCodes.

6. MVP-complete closeout

This is the final frontend phase — every customer, nurse, admin, and partner surface in the build plan now exists.

  • Deferred-if-missing: the RBAC roles console (/admin/roles) is built against the mock and tagged DEFERRED-IF-MISSING; it activates when the b15 role endpoints land (REQ-031). It is not on the testable acceptance path.
  • Modeled-but-inactive, no UI (as specified): organizations / organization_nurses (employer model), fraud_flags (ML console), recurring_booking_schedules (recurrence) — data-model/13. Also no UI for: full سامانه مودیان e-invoice automation / digital-signature (the portal only views the issued invoice ref + PDF), push/SMS channels, an analytics-warehouse dashboard, on-demand/instant payout, per-nurse payout-frequency settings.
  • Minor i18n substitutions the screen agents flagged (all use existing keys; cosmetic, non-blocking): the payout retry-success toast reuses admin.saved; the partner nurse verified/unverified column borrows center_state_* labels; partner booking-status renders the raw stable status code. Add dedicated keys later if desired.