# Portal layout conventions (Yimaru / Fortune) Shared shell patterns for list, detail, and create pages. **Workspace:** `/Users/kirukib/Desktop/Yaltopia Project/Gishen-B2B` ## Components | Piece | Path | Role | |-------|------|------| | `PageShell` | `src/components/layout/page-shell.tsx` | Full-width page header + content; optional `breadcrumb` | | `DetailPageLayout` | `src/components/layout/detail-layout.tsx` | Detail wrapper; **breadcrumbs on by default** | | `Breadcrumbs` / `Bc` | `src/components/layout/breadcrumbs.tsx` | Fortune-style trail; locale links via `@/i18n/routing` | | `StatCard` / `StatCardGrid` | `src/components/layout/stat-card.tsx` | Flat MakerSys metrics (thin border, brand mint icon well) | | `TablePanel` | `src/components/table/table-panel.tsx` | Flat white panel (`rounded-xl`, thin border, no shadow) wrapping toolbar + table | | `TableToolbar` | `src/components/table/table-toolbar.tsx` | In-panel search / filter strip (hairline bottom) | | `DataTable` | `src/components/table/data-table.tsx` | Column-driven table inside `TablePanel` | | Table primitives | `src/components/ui/table.tsx` | Yimaru / Fortune density: muted uppercase headers, `px-4` cells, mint hover | | Page loaders | `src/components/layout/page-loading.tsx` | Flat MakerSys skeletons: `PageStatSkeleton`, `PageTableSkeleton`, `PageListSkeleton`, `PageDetailSkeleton`, `PageFormSkeleton`, `PageDashboardSkeleton` | | Sidebar | `src/components/layout/app-sidebar.tsx` + `src/lib/auth/nav.ts` | Role-filtered nav grouped into sections (`nav.sections.*`) | ## Sidebar sections Nav items carry a `section` key. `navSectionsForRole(role)` returns only non-empty groups for that role. | Section | Routes | |---------|--------| | Overview | Dashboard | | Organisation | Organisation, Departments, Verification | | People | Members, Migration | | Benefits | Packages | | Finance | Finance, Approvals, Statements | | Account | Profile (all roles), Prescriptions, My benefits, My orders | Expanded sidebar shows muted uppercase micro labels; collapsed uses thin dividers between groups (labels hidden). Role filtering via `roles` / `navForRole` is unchanged. ## Page loading states Prefer box skeletons over “Loading…” text so list/detail footprints stay flash-free: - **List pages:** `PageStatSkeleton` while stats query loads; `TablePanel loading` renders row skeletons (toolbar stays). - **Detail pages:** `DetailLoading` → `PageDetailSkeleton` (metrics + profile/content panels). - **Dashboard:** `PageDashboardSkeleton`. - **Forms / settings:** `PageFormSkeleton`. ```tsx import { PageStatSkeleton, PageDetailSkeleton } from "@/components/layout/page-loading"; {isLoading ? : …} … ``` ## Breadcrumbs API ```tsx import { Breadcrumbs, Bc } from "@/components/layout/breadcrumbs"; import { PageShell } from "@/components/layout/page-shell"; import { DetailPageLayout } from "@/components/layout/detail-layout"; // Detail — breadcrumbs default true … // Create / nested — opt in … // Custom trail (tests / one-offs) ``` ### Labels - Static paths and dynamic parents map to `bc.*` keys in `src/messages/en.json` + `am.json`. - Path map lives in `PATH_LABEL_KEYS` / `DYNAMIC_PARENT_KEYS` inside `breadcrumbs.tsx`. - To extend: add a path key → `bc` message key, then Amharic/English strings. ## When to use what | Surface | Stats | Tabs | Breadcrumbs | Width | |---------|:-----:|:----:|:-----------:|-------| | List dashboards | ✓ metric strip | — | off | full | | Detail | metrics + sections | shadcn `Tabs` (line) | on | full | | Create / import / nested settings | — | optional | on | full-width form card (no `max-w-*` unless interaction needs it) | ## List tables Use the shared panel pattern so every list matches MakerSys / Yimaru: ```tsx import { TablePanel } from "@/components/table/table-panel"; import { TableToolbar } from "@/components/table/table-toolbar"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; } > …
``` Conventions: - **Panel:** `rounded-xl`, `border-border`, white `bg-card`, **no shadow** — do not wrap tables in padded `Card` / `CardContent`. - **Header row:** muted background, compact uppercase labels (`text-[11px]`, tracking), `px-4`. - **Body:** comfortable `py-4` rows, mint-tinted hover, last row border removed. - **Empty / loading:** empty state cell when idle; `TablePanel loading` shows row skeletons (not text). - **Mobile:** horizontal scroll via the table container (`overflow-x-auto`). - **Detail embeds:** prefer `DetailTableSection` (same panel chrome) with flush table primitives. - **Filters:** prefer `filters: TableFilterDef[]`. When count **> 3** (`inlineFilterLimit`), toolbar shows a Filters button + modal (draft values, Apply / Reset). ≤3 stay inline. Strings under `filters.*` in i18n. Members, finance spend, and migration use the modal path today. ## Charts & metrics - **Stat cards:** `overflow-visible` + `break-words` / `tabular-nums` so large ETB figures are not clipped. - **Charts:** Y-axis uses compact tick formatting (`800K`) with wider left margin; `ChartCard` allows overflow so axis labels are not cut off. ## Tabs Use `@/components/ui/tabs`. Default `TabsList` variant is **line** (Yimaru/Fortune underline). Keep 2–4 sections max. ```tsx const tTabs = useTranslations("tabs"); {tTabs("profile")} … ``` Labels live under root `tabs.*` in `en.json` / `am.json` (`profile`, `entitlement`, `dependants`, `clinical`, `idCard`, `rules`, `perks`, `summary`, `errors`, `breakdown`, `contract`, `people`, `spend`, `info`, `basics`). Prefer `useTranslations("tabs")` so missing keys never render as nested `tabs.tabs.*`. ## Related docs - [i18n.md](i18n.md) — message files - [INDEX.md](INDEX.md) — feature → route map