This repository has been archived on 2026-08-11. You can view files and clone it, but cannot push or open issues or pull requests.
Gishen-B2B/docs/features/portal-layout.md
kirukib a522916ff1 Close remaining portal polish gaps on create and org pages.
Make prescription create full-width, restore organisation breadcrumbs, and document the idCard tabs key.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 21:59:31 +03:00

6.4 KiB
Raw Blame History

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.
import { PageStatSkeleton, PageDetailSkeleton } from "@/components/layout/page-loading";

{isLoading ? <PageStatSkeleton cols={4} /> : <StatCardGrid>…</StatCardGrid>}
<TablePanel loading={isLoading}>…</TablePanel>

Breadcrumbs API

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:

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.

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, idCard, rules, perks, summary, errors, breakdown, contract, people, spend, info, basics). Prefer useTranslations("tabs") so missing keys never render as nested tabs.tabs.*.