Ship leftover Yimaru login/personas, sidebar sections, filter modal, chart/stat clip fixes, and matching feature docs so the polish work is fully committed. Co-authored-by: Cursor <cursoragent@cursor.com>
145 lines
6.4 KiB
Markdown
145 lines
6.4 KiB
Markdown
# 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 ? <PageStatSkeleton cols={4} /> : <StatCardGrid>…</StatCardGrid>}
|
||
<TablePanel loading={isLoading}>…</TablePanel>
|
||
```
|
||
|
||
## 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
|
||
<DetailPageLayout title="…">…</DetailPageLayout>
|
||
|
||
// Create / nested — opt in
|
||
<PageShell title="…" breadcrumb>…</PageShell>
|
||
|
||
// Custom trail (tests / one-offs)
|
||
<Breadcrumbs items={[{ href: "/", label: "Dashboard" }, { label: "Custom" }]} />
|
||
```
|
||
|
||
### 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";
|
||
|
||
<TablePanel
|
||
loading={isLoading}
|
||
loadingLabel={t("common.loading")}
|
||
toolbar={
|
||
<TableToolbar
|
||
search={search}
|
||
onSearchChange={setSearch}
|
||
resultCount={filtered.length}
|
||
resultLabel={t("common.results")}
|
||
/>
|
||
}
|
||
>
|
||
<Table>…</Table>
|
||
</TablePanel>
|
||
```
|
||
|
||
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");
|
||
|
||
<Tabs defaultValue="profile" className="w-full">
|
||
<TabsList>
|
||
<TabsTrigger value="profile">{tTabs("profile")}</TabsTrigger>
|
||
</TabsList>
|
||
<TabsContent value="profile" className="space-y-4 pt-1">…</TabsContent>
|
||
</Tabs>
|
||
```
|
||
|
||
Labels live under root `tabs.*` in `en.json` / `am.json` (`profile`, `entitlement`, `dependants`, `clinical`, `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
|