npm package: @isi-ui7/ui-shell
Description: UI Shell component combining header, side navigation, notifications, and switcher.
Latest version: 0.1.3

# @isi-ui7/ui-shell

UI Shell aplikasi berbasis Carbon — header, side navigation, app switcher, panel notifikasi, dan panel profil dalam satu komponen.

## Fitur

- Header dengan branding (product name + prefix), tombol global (notifikasi, profil, app switcher)
- SideNav dengan mode rail/full, responsive, mendukung sub-item
- Panel notifikasi: badge count (cap 99+), lazy fetch sekali, daftar item, mark-all-read
- Panel profil: avatar inisial otomatis, nama/role/email, action callbacks
- Panel app switcher: lazy fetch sekali, highlight current app, grid navigasi
- Integrasi opsional dengan `@isi-ui7/corporate-themes` untuk tema korporat

## Instalasi

```bash
pnpm add @isi-ui7/ui-shell @carbon/react @carbon/icons-react react react-dom
```

## Penggunaan

```tsx
import { UiShell } from "@isi-ui7/ui-shell";

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <UiShell
      productName="Core Banking"
      headerPrefix="BCA Syariah"
      headerVariant="g90"
      navItems={[
        { label: "Dashboard", href: "/dashboard" },
        { label: "Rekening", href: "/rekening", isActive: true },
      ]}
      sideNavItems={[
        { label: "Rekening Tabungan", href: "/rekening/tabungan", isActive: true },
        { label: "Rekening Deposito", href: "/rekening/deposito" },
      ]}
      notifications={{
        badgeCount: 3,
        onFetch: async () => [
          { id: "1", title: "Otorisasi menunggu", time: "5 mnt lalu", read: false },
        ],
        onMarkAllRead: () => console.log("semua dibaca"),
      }}
      profile={{
        name: "Budi Santoso",
        role: "Teller",
        email: "budi@bank.co.id",
        onProfileAction: (action) => console.log(action),
      }}
      apps={{
        currentAppId: "bos",
        onFetch: async () => [
          { id: "bos", label: "BOS", href: "/bos" },
          { id: "cif", label: "CIF", href: "/cif" },
        ],
        onAppSelect: (app) => (window.location.href = app.href),
      }}
    >
      {children}
    </UiShell>
  );
}
```

Dengan `@isi-ui7/corporate-themes`:

```tsx
import { ThemeProvider } from "@isi-ui7/corporate-themes";
import { UiShell } from "@isi-ui7/ui-shell";

<ThemeProvider corporateId="bca-syariah" variant="g90">
  <UiShell productName="Core Banking" headerVariant="g90">
    {children}
  </UiShell>
</ThemeProvider>
```

## Props

| Nama | Tipe | Wajib | Deskripsi |
| --- | --- | --- | --- |
| `productName` | `string` | ya | Judul produk di header |
| `headerPrefix` | `string` | tidak | Teks prefix sebelum productName. Default: `"Platform UI"` |
| `headerVariant` | `"white" \| "g10" \| "g90" \| "g100"` | tidak | Varian tema Carbon untuk header. Default: `"g90"` |
| `headerThemeClassName` | `string` | tidak | Override CSS class tema korporat untuk header |
| `navItems` | `ShellNavItem[]` | tidak | Item navigasi di header |
| `sideNavItems` | `ShellNavItem[]` | tidak | Item navigasi di side nav |
| `notifications` | `object` | tidak | Konfigurasi panel notifikasi |
| `profile` | `object` | tidak | Konfigurasi panel profil |
| `apps` | `object` | tidak | Konfigurasi panel app switcher |
| `children` | `ReactNode` | ya | Konten halaman |

### `notifications`

| Nama | Tipe | Deskripsi |
| --- | --- | --- |
| `badgeCount` | `number` | Jumlah badge (ditampilkan, cap 99+) |
| `onFetch` | `() => Promise<NotifItem[]>` | Dipanggil sekali saat panel pertama dibuka |
| `onItemClick` | `(item) => void` | Callback saat item diklik |
| `onMarkAllRead` | `() => void` | Callback tombol "Tandai semua dibaca" |

### `profile`

| Nama | Tipe | Deskripsi |
| --- | --- | --- |
| `name` | `string` | Nama lengkap pengguna |
| `role` | `string` | Jabatan/role |
| `email` | `string` | Email |
| `initials` | `string` | Override inisial avatar (default: auto dari `name`) |
| `onProfileAction` | `(action: string) => void` | Callback item aksi (edit profil, ganti password, logout) |

### `apps`

| Nama | Tipe | Deskripsi |
| --- | --- | --- |
| `currentAppId` | `string` | ID aplikasi yang sedang aktif |
| `onFetch` | `() => Promise<ShellApp[]>` | Dipanggil sekali saat panel pertama dibuka |
| `onAppSelect` | `(app: ShellApp) => void` | Callback saat app dipilih |

## A11y

- Tombol header menggunakan `aria-expanded` dan `aria-label` yang tepat
- Panel menutup dengan tombol `Escape`
- Membuka panel kedua otomatis menutup panel pertama
- SideNav mendukung keyboard navigation

## Scripts

```bash
pnpm build      # Build ke dist/
pnpm lint       # ESLint
pnpm test       # Vitest
pnpm typecheck  # TypeScript check
```

## Catatan

- Peer deps: React/ReactDOM 19, Next 16, `@carbon/react ^1.97`, `@carbon/icons-react ^11.71`
- Komponen menggunakan `"use client"` — hanya untuk Next.js App Router
- `notifications.onFetch` dan `apps.onFetch` hanya dipanggil sekali (tidak re-fetch saat panel dibuka ulang)