ui phase 13
This commit is contained in:
+21
-5
@@ -105,13 +105,15 @@ client/
|
||||
├── messages/ # Translation files (add keys to BOTH files)
|
||||
│ ├── en.json
|
||||
│ └── fa.json
|
||||
├── middleware.ts # next-intl routing middleware (locale detection + redirect)
|
||||
├── middleware.ts # next-intl routing middleware (locale detection + redirect) + (ui-phase-13) the guest front-door: after the i18n 307/308 early-return, an UNAUTHENTICATED exact-match on '/' is `NextResponse.rewrite()`d to `/{locale}/welcome` (never a redirect, so the URL/SEO canonical stays '/'); an AUTHENTICATED hit on `/welcome` redirects to '/'. The bare-root matcher entry (`'/'`, alongside the existing catch-all regex) is load-bearing — this Next 16/Turbopack build did not reliably invoke middleware for the literal root through the negative-lookahead pattern alone (verified in dev; a production `next build && next start` confirmed the intended behavior end-to-end, so this is a dev-server-only quirk, not a logic bug)
|
||||
├── next.config.mjs # createNextIntlPlugin wires i18n into Next.js
|
||||
└── src/
|
||||
├── app/
|
||||
│ ├── globals.css
|
||||
│ ├── fonts/ # Local font files (woff2) — Mikhak for fa
|
||||
│ ├── global-error.tsx # Special file above [locale] — replaces the root layout on a root-level crash; renders its own <html>, so it CANNOT use next-intl. The one sanctioned static-string exception (minimal, bilingual fa+en).
|
||||
│ ├── robots.ts # (ui-phase-13) replaces the old static `public/robots.txt` (deleted) — allows the public surface, disallows every private route root for every locale, points at sitemap.xml
|
||||
│ ├── sitemap.ts # (ui-phase-13) public routes only ('/', /login, /terms, /privacy) × both locales, with hreflang alternates
|
||||
│ └── [locale]/
|
||||
│ ├── layout.tsx # ROOT RSC: renders <html lang/dir> + fonts + setRequestLocale + NextIntlClientProvider + ThemeProvider + AuthProvider (seeded via getServerAuthState) + generateMetadata (the '%s | برند' title template)
|
||||
│ ├── error.tsx # Branded, localized error boundary for the whole [locale] segment — reset() retries, a "go home" link escapes
|
||||
@@ -222,7 +224,11 @@ client/
|
||||
│ │ ├── page.tsx # Thin RSC — generateMetadata (auth.customer_title) + renders LoginScreen
|
||||
│ │ └── LoginScreen.tsx # 'use client' — the actual LoginFlow body
|
||||
│ ├── terms/page.tsx # /terms — draft Terms of Service (ui-phase-3; DRAFT COPY, needs human/legal review before launch)
|
||||
│ └── privacy/page.tsx # /privacy — draft Privacy Policy (ui-phase-3; DRAFT COPY, needs human/legal review before launch)
|
||||
│ ├── privacy/page.tsx # /privacy — draft Privacy Policy (ui-phase-3; DRAFT COPY, needs human/legal review before launch)
|
||||
│ └── welcome/ # /welcome — ui-phase-13 public front door; middleware REWRITES an unauthenticated '/' here (URL stays '/'), so this is what a guest, and every crawler, actually sees at the root
|
||||
│ ├── page.tsx # Thin RSC — generateMetadata (description/OG/`alternates.canonical` pointing at `/{locale}`; no `title` override, so it inherits the root layout's default "بالینیار"/"Balinyaar" — that's how it's told apart from the customer home's own `shell.customer_app` title in view-source) + renders WelcomeScreen
|
||||
│ ├── WelcomeScreen.tsx # Async Server Component (no 'use client') — hero (BrandMark) + static category grid (CategoryTile via its new `href` prop, §3.1 tier a+b — NOT the live catalog) + how-it-works (step 2 reuses EscrowNotice verbatim) + a static trust/verification explainer + nurse-recruitment CTA + footer; zero query hooks anywhere in the tree — first paint is the whole page
|
||||
│ └── opengraph-image.tsx # `next/og` ImageResponse — a static brand-mark composition (Latin-only; Persian would need an embedded Mikhak font buffer, left for a follow-up) on the brand teal/cream, served as the page's absolute `og:image`
|
||||
├── components/ # Shared UI components (each with .test.tsx if imported >1 place)
|
||||
│ ├── common/ # Foundational primitives (import from @/components or @/components/common)
|
||||
│ │ ├── AppButton/, AppIconButton/, AppIcon/, AppLink/, AppAlert/, AppLoading/ # house-default MUI wrappers (see frontend-designer skill §4)
|
||||
@@ -258,7 +264,7 @@ client/
|
||||
│ ├── PatientForm/ # A4 patient form (first/last name, age/gender/conditions/relation) — reused create+edit; ui-phase-9 split the single full-name field into first/last (lastName falls back to firstName when blank — the wire requires it) and added an `onDirtyChange` prop for `FormDialogShell`'s discard-confirm
|
||||
│ ├── PatientCard/ # E1 care-circle summary card (composes the shared PatientHeader, now with its avatar) + edit/archive actions; ui-phase-9 replaced the invisible tap target with a pressable `ButtonBase` surface (hover/press + a trailing chevron) and added an optional `lastVisitLabel` teaser (never populated without a `patientId` on the cached bookings list — REQ-057)
|
||||
│ ├── BankStatusPanel/ # Nurse bank-account ownership state (pending/verified/mismatch), masked IBAN
|
||||
│ ├── CategoryTile/ # f4 tappable service-category tile (icon+label; `selected` state for the builder) — Home grid + builder step 1 (tested)
|
||||
│ ├── CategoryTile/ # f4 tappable service-category tile (icon+label; `selected` state for the builder) — Home grid + builder step 1; ui-phase-13 added an `href` mode (renders as the anchor itself via `ButtonBase`'s `component` swap to `AppLink` — never a button nested inside a link) for the public landing's static category grid (tested)
|
||||
│ ├── PriceDisplay/ # f4 price renderer: money-util Toman + i18n unit label + unit-aware estimated total (never a total from price alone) (tested)
|
||||
│ ├── VariantCard/ # f4 nurse offering card: display_name, PriceDisplay, active/deactivated distinction, edit/deactivate (no delete); ui-phase-8 added `interactive={false}` for a read-only preview use (the builder's live listing preview + the profile preview) (tested)
|
||||
│ ├── ActivationChecklist/ # ui-phase-8 — the unified go-live tracker (`useActivationChecklist` hook, self-fetching): five already-cached queries folded into rows — two-tier honesty (identity/profile/services/coverage drive search visibility; bank drives "getting paid", labelled separately and never gates search) — collapses to a compact «فعال در جستجو» state once everything passes AND accepting-bookings is on; mounted on `/nurse/services` and the dashboard's `DashboardActivationSlot`, one shared component; `useActivationChecklist` is also consumed directly by `PublishGate` so the go-live gate and the checklist never compute the conditions twice (tested)
|
||||
@@ -430,6 +436,12 @@ root template; `page.tsx` itself never renders `<title>` or touches `document.ti
|
||||
pages (customer home, `/login`, `/search`, `/bookings`, `/nurse`, `/admin`, `/partner`) have adopted this
|
||||
so far — the rest is deferred to the area phases (3–11).
|
||||
|
||||
**`metadataBase` (ui-phase-13):** the root layout's `generateMetadata` sets `metadataBase: new URL(SITE_URL)`
|
||||
(`SITE_URL` — `src/config.ts`, sourced from `NEXT_PUBLIC_SITE_URL`, never hard-coded) so every child
|
||||
page's relative OG/canonical URLs resolve to an absolute one. The public `/welcome` landing is the first
|
||||
consumer: its `generateMetadata` sets `alternates.canonical` + `openGraph`, and a co-located
|
||||
`opengraph-image.tsx` (`next/og`'s `ImageResponse`) supplies `og:image`.
|
||||
|
||||
```tsx
|
||||
import type { Metadata } from 'next';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
@@ -505,10 +517,11 @@ async function MyServerComponent() {
|
||||
- `'legal'` — ui-phase-3's `/terms`/`/privacy` static pages: `terms_title`/`privacy_title`, `draft_banner` (the human/legal-review flag shown on-page), `terms_intro`/`privacy_intro`, and `terms_sections`/`privacy_sections` (arrays of `{title, body}` read via `t.raw`, not flat keys — the one namespace with structured JSON values). Consumed only by the two legal pages
|
||||
- `'admin'` — the f15 backoffice consoles: verification queue/case, refund panel, payout dashboard/detail, review moderation, config editor + change-history, holiday manager, support-alert board, audit viewer, admin ticket queue/thread, RBAC grid, and admin-side partner management. Includes the **Persian legal terms** (پروانه تأسیس / مسئول فنی / نماد اعتماد الکترونیکی) and the enum-label prefixes keyed off the stable code (`step_*`/`agg_*`/`atype_*`/`astatus_*`/`sev_*`/`htype_*`/`dtype_*`/`batch_status_*`/`pstatus_*`/`channel_*`/`rstatus_*`/`mstatus_*`/`center_state_*`/`role_*`/`tcat_*`/`tstatus_*`). Consumed by the `/admin/*` screens + the `@/components/admin` composites
|
||||
- `'partner'` — the f15 partner-center portal (a separate authz scope): center home/onboarding-state, sponsored nurses/bookings, and the merchant-of-record settlement/invoice view (سامانه مودیان, commission/VAT decomposition). Consumed by the `/partner/*` screens + `PartnerSettlementRow`
|
||||
- `'welcome'` — the ui-phase-13 public landing: hero subtitle/CTA, the five static category-grid labels (`category_*`, distinct from the live `catalog`-namespace category names — this grid is marketing copy, not the catalog API), the how-it-works steps, the trust/verification explainer (`trust_*`, a static counterpart to `VerificationPanel`'s live-badge version), the nurse-recruitment CTA, and the footer (`footer_*`, incl. a plain `{year, number}`-formatted copyright line). Consumed only by `WelcomeScreen.tsx`
|
||||
|
||||
**Namespace conventions for the phases to come** (seed each when its feature lands, in both locale
|
||||
files): none — **MVP namespaces complete** (f15 seeded `admin` + `partner`). Keep top-level keys as
|
||||
namespaces and both files in sync.
|
||||
files): none — **MVP namespaces complete** (f15 seeded `admin` + `partner`; ui-phase-13 seeded `welcome`
|
||||
post-MVP). Keep top-level keys as namespaces and both files in sync.
|
||||
|
||||
**Never hard-code UI strings in English.** Any user-visible text must have a translation key in both locale files.
|
||||
|
||||
@@ -984,6 +997,9 @@ PUBLIC_PATHS = [ROUTES.LOGIN, ...] // paths that bypass middleware auth check
|
||||
Import from the barrel: `import { ROUTES, PUBLIC_PATHS } from '@/constants'`.
|
||||
|
||||
To add a new public route, append it to `PUBLIC_PATHS` — the middleware picks it up automatically.
|
||||
**Never append `ROUTES.HOME` ('/') itself** — `PUBLIC_PATHS` is matched with `startsWith`, so `'/'`
|
||||
would silently make every route public. The guest-facing root (ui-phase-13) is handled by an
|
||||
exact-match `NextResponse.rewrite()` in `middleware.ts` instead, to `ROUTES.WELCOME` ('/welcome').
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user