ui phase 5
This commit is contained in:
@@ -602,6 +602,61 @@ delivers fixes in its own change. **Frontend never edits backend code to "fix" a
|
||||
- **Status:** open (partner login-routing deferred; `/partner` reachable by direct nav + the partnerCenter mock)
|
||||
- **Status:** open
|
||||
|
||||
## REQ-040 — Index-row enrichment: `variantDisplayName` + optional `topReviewTag` — filed by ui-phase-4 — 2026-07-18
|
||||
- **Need:** Two additions to `NurseSearchResultDto` (`GET search/nurses`):
|
||||
1. `variantDisplayName` (required, string) — the variant's display name, so the C2 result card can
|
||||
name the exact service being bought (the row **is** a variant).
|
||||
2. `topReviewTag` (optional, string | null) — a one-line top review tag (e.g. «منظم و دقیق») from the
|
||||
review-tag vocabulary, for an optional one-line trust cue on the card.
|
||||
- **Why:** `NurseResultCard` v2 (ui-phase-4) is the four-second decision unit. Today the index row carries
|
||||
no variant label, so a nurse offering three variants renders as three near-identical cards differing
|
||||
only by price; the card falls back to the row's **category** name (passed in by the page from the cached
|
||||
catalog reference data) until this lands — a genuine wire gap, not a display choice. The client type
|
||||
(`services/search/types.ts` `NurseSearchResult.topReviewTag`) and `NurseResultCard` already render the
|
||||
tag conditionally so this is a zero-code swap once served.
|
||||
- **Proposed shape:** `NurseSearchResultDto { …, variantDisplayName: string, topReviewTag?: string | null }`.
|
||||
- **Status:** open
|
||||
|
||||
## REQ-041 — Free-text search (`q`) over nurse/variant/category names — filed by ui-phase-4 — 2026-07-18
|
||||
- **Need:** A `q` query param on `GET search/nurses` matching against nurse display name, variant display
|
||||
name, and category name (case-insensitive substring or better).
|
||||
- **Why:** The Home search bar was a dead affordance — it pushed `?q=` to C1, which silently discarded it
|
||||
(C1 reads only `category_id`). ui-phase-4 replaced the free-text field with a tappable entry point straight
|
||||
to C1 (decision: the index has no text column, variant names aren't client-queryable, and the only
|
||||
matchable dataset client-side — 5–6 cached category names — is already better served by the category grid
|
||||
directly below; a half-working text field over-promises exactly where trust matters). This REQ is the
|
||||
upgrade path noted in the component's JSDoc: once `q` is served, the Home search bar can become a real
|
||||
typeahead without changing its i18n keys.
|
||||
- **Proposed shape:** `GET search/nurses?...&q=<string>` — matches folded into the existing filter set
|
||||
(AND'd with category/city/gender/price), same `Paginated<NurseSearchResultDto>` response shape.
|
||||
- **Status:** open
|
||||
|
||||
## REQ-042 — `nurseGender` on `NursePublicProfileDto` — filed by ui-phase-4 — 2026-07-18
|
||||
- **Need:** Add `nurseGender` (`'male'|'female'`) to the `GET nurses/{id}/profile` response.
|
||||
- **Why:** C3's header would show a gender chip alongside years-of-experience/rating (same-gender
|
||||
matching is load-bearing throughout the product), but the public profile DTO doesn't serve it — the
|
||||
client's `nurseGender: 'female'` is a **placeholder stub** (explicitly commented "unused by the C3 page").
|
||||
ui-phase-4 keeps the C3 header gender chip **omitted** rather than render the stub as truth; the C2 card
|
||||
and the carried `required_gender` query param already cover the matching flow honestly in the meantime.
|
||||
- **Proposed shape:** `NursePublicProfileDto { …, nurseGender: 'male' | 'female' }`.
|
||||
- **Status:** open
|
||||
|
||||
## REQ-043 — Public per-step verification detail (step codes + decision dates) — filed by ui-phase-4 — 2026-07-18
|
||||
- **Need:** Extend the public `GET nurses/{id}/trust_badge` read (or a sibling public endpoint) with
|
||||
per-step passed-check detail: step `code`, `status`, and `decidedAt`/`passedAt` date — not just the
|
||||
aggregate `credentialTypes[]` + `approvedAt` it serves today.
|
||||
- **Why:** The new shared `VerificationPanel` component (ui-phase-4, `src/components/VerificationPanel/`)
|
||||
is the "what Balinyaar verified" explainer — reused by the C3 profile section, the tappable `TrustBadge`
|
||||
bottom-sheet/dialog, and (per the phase's cross-reference) phase 8's public-profile preview. Today it can
|
||||
only render one row per `credentialTypes[]` entry (identity/license/INO/background-check are folded into
|
||||
three generic credential-type codes) plus the single aggregate `approvedAt` — it cannot list identity
|
||||
check, Shahkar match, and license individually with their own dates. Until this lands the panel stays
|
||||
honest (renders only what's served: the credential-type rows + the one approval date — no invented steps
|
||||
or fake dates).
|
||||
- **Proposed shape:** `TrustBadgeDto { …, steps?: [{ code: string, status: 'passed', decidedAt: string }] }`
|
||||
(only `passed` steps are ever publicly listed — never a pending/failed step's detail).
|
||||
- **Status:** open
|
||||
|
||||
## REQ-039 — WebOTP-conformant OTP SMS template — filed by ui-phase-3 — 2026-07-17
|
||||
- **Need:** the OTP SMS body must end with the origin-bound last line `@<domain> #<code>` (the
|
||||
[WebOTP](https://web.dev/articles/web-otp) / origin-bound one-time-code convention), e.g.:
|
||||
@@ -620,3 +675,38 @@ delivers fixes in its own change. **Frontend never edits backend code to "fix" a
|
||||
- **Proposed shape:** append `\n@<domain> #<code>` as the SMS's last line, where `<domain>` is the site's own
|
||||
host (no scheme, no trailing slash) and `<code>` is the exact code the user must enter.
|
||||
- **Status:** open — client-side WebOTP wiring ships regardless and degrades gracefully until this lands.
|
||||
|
||||
## REQ-044 — Structured `nurseRejectionReasonCode` on `BookingRequestDto` — filed by ui-phase-5 — 2026-07-18
|
||||
- **Need:** A stable, enumerated `nurseRejectionReasonCode` (e.g. `gender_preference` | `outside_coverage` |
|
||||
`schedule_conflict` | `other`) alongside the existing freeform `nurseRejectionReason` text on
|
||||
`BookingRequestDto`.
|
||||
- **Why:** C5's terminal-state recovery (phase §3.2) offers "request again with the same nurse, a different
|
||||
time" only when the rejection reason **isn't** a hard gender/coverage block — otherwise only "similar
|
||||
nurses" is offered, since re-requesting the same nurse would just fail again. `nurseRejectionReason` is
|
||||
nurse-authored free text with no code, so the client can only approximate this with a keyword heuristic
|
||||
over the (possibly translated/paraphrased) string — implemented in
|
||||
`bookings/request/[id]/page.tsx`'s `rejectionAllowsSameNurseRetry()`, matching `gender`/`coverage`/`area`
|
||||
and their Persian equivalents. A real code would replace the heuristic with an exact check.
|
||||
- **Proposed shape:** `BookingRequestDto { …, nurseRejectionReasonCode: 'gender_preference' |
|
||||
'outside_coverage' | 'schedule_conflict' | 'other' | null }`, required whenever `nurseRejectionReason` is
|
||||
set. `reject_dialog`'s reason field would become a category select (+ optional free-text) instead of a
|
||||
single free-text field.
|
||||
- **Status:** open — the client ships the keyword heuristic in the meantime (documented as a known
|
||||
approximation in the phase report), never blocking the recovery UI.
|
||||
|
||||
## REQ-045 — Typed `addressSnapshot`/`variantSnapshot` on `BookingDetailDto` — filed by ui-phase-5 — 2026-07-18
|
||||
- **Need:** Replace (or supplement) `addressSnapshotJson`/`variantSnapshotJson` (opaque JSON strings) with
|
||||
typed fields: `addressSnapshot: { title, cityNameFa, cityNameEn, districtNameFa, districtNameEn,
|
||||
addressLine, postalCode } | null` and `variantSnapshot: { displayName, priceUnit }`.
|
||||
- **Why:** The booking-detail hero (phase §3.4) needed the frozen visit address for its "next visit" card,
|
||||
but the seed fixtures show the snapshot blob has **no stable schema** — field names drift across bookings
|
||||
(`city`/`cityName`/`cityNameFa`, `line`/`addressLine`; see `services/bookings/apis/mockApi.ts`'s `addr5001`
|
||||
vs the booking-5005 fixture). The client (`BookingDetailView.tsx`'s `addressSnapshotLabel()`) currently
|
||||
tries every candidate key and joins whatever resolves — a best-effort parse, not a contract. The nurse view
|
||||
already masks this field to `null` server-side (two-stage disclosure); only the shape within the customer's
|
||||
non-null value is the gap.
|
||||
- **Proposed shape:** as above — the same shape `BookingRequestDisplayContext`'s address fields already use
|
||||
elsewhere in the frontend, so the mapping is a straight carry-over from whatever snapshotting logic
|
||||
produces the JSON blob today.
|
||||
- **Status:** open — the client keeps the defensive multi-key parse until this lands; no user-facing defect
|
||||
today (the address still renders when any recognizable key is present), just an unenforced contract.
|
||||
|
||||
Reference in New Issue
Block a user