# Adopting `@dloizides/ui-nav` ≥ 1.8.0 — per-portal migration map

This is the map the migration agents follow to move each RN-web portal off its
bespoke nav/shell onto the shared package. **Only STYLE varies per app** — layout
structure and behaviour are shared. Nothing from the current shells is lost:
everything is either a component, an optional prop, or a render slot.

> Migrations are a **separate wave** — do NOT edit the apps as part of building the
> package. This doc tells each app which `layout`, which slots, and which
> components to use.

## The one decision: which `layout`?

| Portal | Current shell | `NavShell` `layout` | Notes |
|--------|---------------|---------------------|-------|
| **AML v2** (`consoleChrome` / AuthedShell / PublicShell) | top bar only (dark, radial glow) | `"top"` | `topBar.barTheme` = dark chrome; brand/user/language in `topBar.brand` / `topBar.right`; overflow "…" built-in |
| **Agora** (`AdminShell`) | side rail + top bar (already `AppShell`) | `"side"` | `sideRail` = sections; `header` = `<Topbar logout=… />` |
| **Zygos** (`ZygosShell`) | side rail + top bar (already `AppShell`) | `"side"` | as Agora, plus `Topbar.user` block |
| **Kefi** (`DashboardShell` / `AppHeader` / `DashboardNav`) | top bar + coral pill strip | `"top"` | `header`/`topBar` = brand + account; put `<PillNav minItems={2} />` in the `nav` slot |
| **Erevna** (`ProtectedLayout` + Sidebar/Topbar) | 3-tier rail + top bar | `"side"` **+** `sideRail.collapsed` | full rail → collapsed icon rail → drawer |
| **Katalogos** | byte-identical to erevna | `"side"` **+** `sideRail.collapsed` | same as erevna |

## Slot mapping (what fills each slot)

### AML v2 — `layout="top"`
- `topBar.items` ← `editionNavItems(visibleNavItems(isAdmin), selfHosted)` → `NavItem[]`
  (keep the app-side edition/self-host filtering; pass the result).
- `topBar.brand` ← the `ConsoleBrand` block (brand→marketing-vs-console-home routing
  stays in the app; `brand` is a free-form node).
- `topBar.right` ← `<ShellLanguageSwitcher />` + `<UserChip />` (or the "Sign in" pill
  for anonymous `PublicShell`). Both are already `right`-slot nodes today.
- `topBar.barTheme` ← the navy `useNavTheme()` clone (dark bar over light page).
- `topBar.containerStyle` ← the 1180px cap + the radial-glow `appbarBarStyle` (a web
  `background`/`backgroundImage` gradient cast through `ViewStyle`). **Style only.**
- `topBar.overflowLabel` / `collapseBelow={760}` ← as today.
- External-route split (`isExternalRoute` → `Linking.openURL` vs `router.navigate`)
  stays in the app's `onNavigate`. `ToastHost` stays app-side (it is `@dloizides/ui-feedback`).

### Agora / Zygos — `layout="side"`
- `sideRail.items` ← the localized `NAV_SECTIONS` (`renderIcon` per item).
- `sideRail.title` / `regionLabel` / `navigateHint` ← as today.
- `header` ← `<Topbar logout={…} user={…?} />` (Zygos passes `user`; Agora does not).
- `mobileMenu` ← the drawer open/close labels (Agora passes them; Zygos gets English defaults).
- Nothing else changes — these already consume `AppShell` + `Sidebar` + `Topbar`.

### Kefi — `layout="top"` + `nav` slot
- `header` ← `<Topbar account={{ displayName, onLogout }} left={<Wordmark/>} logout={…} />`
  (the rich `AccountState`; keep omitting tenant/plan to avoid the round-trip).
- `nav` ← `<PillNav items={accessibleNavItems(user, postLoginRouteTable, FM)} minItems={2}
  pathname={…} onNavigate={…} regionLabel={…} />` — this folds in the coral pills AND
  the ≥2-dashboards rule (no more app-local `MIN_DASHBOARDS_FOR_NAV`).
- `gate` / `state` / `banner` / `width` / `chromeAlignment` / `contentPadding` ← exactly
  the `AppShell` props kefi already passes (unchanged).
- Coral colour = theme `palette.primary`; pill radius/font = theme/`containerStyle`. **Style only.**

### Erevna / Katalogos — `layout="side"` + `sideRail.collapsed`
- `sideRail.items` ← `toNavItems(groupSidebarItems(moduleRegistry.getSidebarItemsForRoles(roles)))`
  (role-gated, grouped/expandable — unchanged pipeline).
- `sideRail.header` ← the Home shortcut; `sideRail.footer` ← `<DarkModeControl variant="segmented"
  options={[Light, Dark, System]} value=… onChange={setDarkModePreference} />` + Logout
  (replaces the bespoke `DarkModeToggle`). `sideRail.renderChevron` ← chevronUp/Down.
- `sideRail.collapsed` ← `{ items, header, footer, range: { min, max } }` → the tablet
  **collapsed icon rail** (replaces `MobileSidebarCollapsed`). Its footer can hold the
  `DarkModeControl variant="cycle"` (replaces `MobileDarkModeButton`).
- `header` ← `<Topbar left={<TenantLogo/>} language={…} notificationSlot={<SafeNotificationBell/>}
  user={…} logout={…} account={…?} />`.
- `mobileMenu` ← the drawer open/close labels (the phone drawer re-hosts the full rail).
- `gate` (auth) / `banner` (`VerificationPendingBanner`) / `state` ← the `AppShell` props.
- **Still app-side (not lost, not shared):** the `!isUser` role-conditional rail hide
  (just pass no `sideRail` for plain users), the `SkipNavLink` (compose in `children`),
  and — if the exact right-anchored mobile *account* drawer (user+dark+lang+account) is
  wanted verbatim — keep `MobileTopbar` in the `header` slot; the shared left drawer
  re-hosts the rail, which covers the section-navigation case.
- **Style only:** scrim colour/opacity, slide offsets/durations, segmented-toggle
  highlight, drawer panel colours → theme tokens / `containerStyle`.

## Contract reminders (unchanged)

- No FM / router / icon-set / store imports in the package. Labels are pre-localized
  strings; icons are `renderIcon` render slots; every colour flows through the
  `@dloizides/ui-feedback` `UiProvider` theme.
- `Sidebar` / `Topbar` / `NavBar` / `Nav` / `AppShell` and the role helpers keep their
  exact 1.7.0 signatures — migrate incrementally; a partial migration compiles.
