cleanup phases 6
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# Frontend Phase 5 — Nurse verification flow (trust engine) — Report (2026-07-09)
|
||||
|
||||
Builds the trust engine's front end: the staged, platform-owned verification a nurse walks before any
|
||||
service can go live. A nurse lands on a **status checklist (B3)**, submits **identity (B4)**, submits
|
||||
**professional credentials (B5)**, and waits on **under-review (B6)** until an admin decides — then a
|
||||
**trust badge** renders and the **publish gate** unlocks. Consumes the b6 `verification` contract. Unlocks
|
||||
a bookable verified nurse + the `<TrustBadge>` f6 reuses.
|
||||
|
||||
## What was built
|
||||
|
||||
### `services/verification` domain (`client/src/services/verification/`)
|
||||
- **`types.ts`** — DTOs mirrored from [`verification.md`](../../contracts/domains/verification.md)
|
||||
(**camelCase** wire): `VerificationStatus` (aggregate `status` + `isBookable` + `blockingSteps` +
|
||||
ordered `steps[]`), `VerificationStep`, `RunStepResult`, `UploadUrlResult`, `DocumentConfirmedResult`,
|
||||
`VerificationDocument` (metadata only), `NurseCredential`, `TrustBadge`, the `VerificationApi` seam, and
|
||||
the string-literal enums (aggregate/per-step status, the six step codes, credential type, verification
|
||||
method, `BadgeState`). Helpers: `isApproved`, `ownBadgeState` (own-profile, computes `expired`),
|
||||
`publicBadgeState` (public/search — verified/unverified only), `SPECIALTY_PRESETS`.
|
||||
- **`validation.ts`** — `isValidNationalId` (10-digit mod-11 checksum; rejects all-same-digit).
|
||||
- **`keys.ts`** — `verificationKeys.status()` (the single cached source B3+B6 read), `.documents(code)`,
|
||||
`.badge(nurseId)`.
|
||||
- **`constants.ts`** — `USE_VERIFICATION_MOCK` (**default true**), the status `staleTime` (30 s) + badge
|
||||
`staleTime`/`gcTime`, the document type/size caps (jpg/png/pdf, 5 MB), `NATIONAL_ID_LENGTH`.
|
||||
- **`apis/`** — `clientApi.ts` (real HTTP, action-style routes, XHR signed-URL PUT for upload progress +
|
||||
SHA-256 integrity hash), `mockApi.ts` (**primary**, full journey + dev-only admin sim), selecting
|
||||
`index.ts`. Both implement `VerificationApi`; swap is one line.
|
||||
- **`hooks/`** — one per file: `useVerificationStatus` (query), `useStartVerification` (setQueryData),
|
||||
`useSubmitIdentity` (runs KYC → chained Shahkar), `useRunBankVerification`, `useUploadVerificationDocument`
|
||||
(progress via vars), `useSubmitCredentials`, `useNurseTrustBadge`. **Every mutation invalidates
|
||||
`status()`** (badge where relevant), so the checklist re-renders from cache with no manual refetch.
|
||||
|
||||
### Screens — nurse verification route subtree (`app/[locale]/(private-routes)/nurse/verification/`)
|
||||
- **B3 `page.tsx`** — the hub: loading skeleton, error+retry, `not_started` start-CTA, the in-progress
|
||||
**"X از Y" meter + data-driven checklist** (`VerificationChecklist` reusing the shared `StatusChip`), a
|
||||
**blocking summary**, a single **continue CTA** that routes to the next actionable step (calls
|
||||
`useStartVerification` first when `not_started`), and the terminal **`approved`** state (publish link).
|
||||
- **B4 `identity/page.tsx`** — national-ID field (checksum) + national-ID card + liveness selfie **local
|
||||
captures** (identity stores no server document — they feed the automated KYC), the honest auto-registry
|
||||
note, submit → `useSubmitIdentity`. Handles national-ID mismatch (`failed`) and the **non-accusatory
|
||||
shared-SIM** Shahkar failure distinctly.
|
||||
- **B5 `credentials/page.tsx`** — INO number + specialty chips (presets + add-your-own) + **a
|
||||
`<DocumentUpload>` per manual credential step (data-driven from `status`)** each moving its step to
|
||||
`in_review`, + an optional education-cert local upload + optional registry fields; submit →
|
||||
`useSubmitCredentials`, lands on B6. Copy reflects **manual review**, never an automated authority check.
|
||||
- **B6 `review/page.tsx`** — the under-review resting screen: "در حال بررسی" + the 24–48h note + a
|
||||
**condensed mini-checklist** (reusing `StatusChip`) — a focused **second view of the same cached
|
||||
`status()` query**, not a second fetch. CTA back to B3.
|
||||
- **`verificationSteps.ts`** — the data-driven glue: `displaySteps` (prepends a synthetic passed **mobile**
|
||||
step), `progressCounts`, `stepLabelKey`/`stepDescriptionKey`, `stepStatusChip` (the green/amber/grey/red
|
||||
legend), `routeForStep`, `nextActionRoute`/`nextActionableIndex`.
|
||||
- **Journey stepper:** B4/B5/B6 reuse the shared `StepperHeader` (identity → credentials → review).
|
||||
|
||||
### Shared components (with co-located tests)
|
||||
- **`<DocumentUpload>`** — the reusable uploader: client type/size validation **before** upload, the full
|
||||
idle → uploading(%) → success(✓ + name / local image preview) → error(retry) machine, re-upload on
|
||||
reject, server-metadata as the "uploaded" truth, and a **local-capture mode** (B4). Its state chrome uses
|
||||
the `verification` namespace; the caller passes the field label/hint.
|
||||
- **`<TrustBadge state=…>`** — verified / unverified / expired off `--bal-*` tokens. Rendered on the nurse's
|
||||
own profile; **exported so f6 reuses it** in search results + the public profile.
|
||||
|
||||
### Wiring
|
||||
- **Publish gate** — `nurse/services/PublishGate.tsx` (in `MyServicesList`): the go-live CTA is **disabled
|
||||
with a blocked-until-verified explanation** (+ link to B3) until the aggregate is `approved`. Mirrors the
|
||||
server's guarded `is_verified` flip.
|
||||
- **Trust badge on the nurse profile** — sourced from the own `VerificationStatus` via `ownBadgeState`; the
|
||||
unverified banner now shows only until `verified`.
|
||||
- **6 AppIcons** (`upload/document/refresh/identity/license/publish`), **4 route constants**, the
|
||||
`verification` **i18n namespace (123 keys)** in both locales in sync.
|
||||
|
||||
## What is now testable, and exactly how
|
||||
Run `npm run dev`, sign in as a **nurse** (mock auth code `123456` if `USE_AUTH_MOCK`), open
|
||||
**/nurse/verification**:
|
||||
1. **Checklist:** B3 shows "X از Y" with the mobile step already green, the rest `not_started`. The continue
|
||||
CTA calls `start` (seeds steps) then routes to B4.
|
||||
2. **Identity (B4):** a valid کد ملی + card image (watch the uploader progress → ✓) + selfie → submit → the
|
||||
`identity_kyc` + `shahkar_match` steps update on B3 **without a refresh**. National-ID `0000000000` →
|
||||
failed KYC; national-ID `1111111111` → the non-accusatory **shared-SIM** Shahkar message.
|
||||
3. **Credentials (B5):** INO number + upload each manual document (→ `in_review`) + specialty chips → submit
|
||||
→ lands on **B6** ("در حال بررسی", 24–48h, mini-checklist).
|
||||
4. **Approval flips verified:** the **dev-only "Simulate admin review" panel** on B3/B6 (mock-flag-gated —
|
||||
stands in for the deferred f15 admin queue) → *Approve all* → B3 shows `approved`, the **trust badge**
|
||||
on the nurse profile shows **verified**, and the **publish CTA on /nurse/services is enabled**.
|
||||
5. **Rejected step:** *Reject a document* → the step shows its **rejection reason** + a working re-upload
|
||||
path; the publish CTA stays blocked.
|
||||
6. React Query Devtools shows **one `verification.status` query** feeding B3 + B6 and invalidating on each
|
||||
mutation. Toggle `/en`↔`/fa` (RTL) and dark mode — strings + layout flip.
|
||||
|
||||
## What is mocked / waiting on a real service
|
||||
`services/verification` runs behind **`verificationMockApi`** (`USE_VERIFICATION_MOCK = true`) — b6 isn't
|
||||
reachable in this environment, so the mock drives the full journey (identity/Shahkar/bank runs, document
|
||||
uploads, admin-decision sim). The **swap is one line** (`USE_VERIFICATION_MOCK = false`): `verificationClientApi`
|
||||
is wired to every b6 route (`nurse_verification/*`, `nurses/{id}/trust_badge`), same hook signatures + query
|
||||
keys, no call-site change. See the mocks-registry row for the deterministic test triggers. Vendor calls
|
||||
(KYC/Shahkar/credential/IBAN/object-storage) are mocked **server-side** by b6 — the front end consumes them
|
||||
as if real.
|
||||
|
||||
## Contracts consumed + gaps filed
|
||||
- **Consumed:** [`verification.md`](../../contracts/domains/verification.md) + `openapi/swagger.v1.json` (b6).
|
||||
- **Filed:** **REQ-011** — a nurse-facing endpoint for the structured credential details (INO number,
|
||||
specialties, license fields) B5 collects; the contract records these only on the admin `decide`, so the
|
||||
real `submitCredentialDetails` no-ops until it lands (the document uploads are contract-backed). Also
|
||||
flagged: `VerificationStepDto` has no `isRequired` (the client treats every seeded step as required).
|
||||
|
||||
## Follow-ups for later phases
|
||||
- **f6** reuses `<TrustBadge>` (import from `@/components`) on search results (C2) + the public nurse profile
|
||||
(C3), sourced from `useNurseTrustBadge(nurseId)` / `publicBadgeState`. The `expired` state is own-profile
|
||||
only (the public badge payload carries `isVerified` only).
|
||||
- **f12 payout** depends on the `bank_account_verification` step (this phase deep-links it to the f2 bank screen).
|
||||
- The **admin verification review queue** (pass/reject, doc viewer, credential entry) is **f15** — the
|
||||
dev-only mock sim here is a stand-in, gated on `USE_VERIFICATION_MOCK`.
|
||||
- Deliver **REQ-011** then wire `submitCredentialDetails` + flip `USE_VERIFICATION_MOCK=false`.
|
||||
Reference in New Issue
Block a user