# UI Conventions

## Filosofi desain

- **Bersih dan sederhana** — admin apotek non-teknis adalah pengguna utama.
- **Bahasa Indonesia** — semua label, menu, status, pesan.
- **Responsive** — sidebar slide-out di mobile, tabel scroll horizontal.
- **Konsisten** — gunakan komponen `x-*` dan class `siaga-*` yang sudah ada.

---

## Design tokens

Definisi di `resources/css/app.css` via Tailwind 4 `@theme`:

| Token | Nilai | Penggunaan |
|-------|-------|------------|
| `primary` | `#1E3A5F` | Tombol utama, fokus input |
| `primary-hover` | `#274C77` | Hover primary |
| `accent` | `#F97316` | Aksen orange |
| `sidebar` | `#0c2649` | Background sidebar |
| `sidebar-active` | `#FF7800` | Link aktif sidebar |
| `background` | `#F4F7FB` | Background halaman |
| `surface` | `#FFFFFF` | Card, input |
| `border` | `#E3E9F1` | Border card/tabel |
| `text-primary` | `#1F2937` | Teks utama |
| `text-secondary` | `#6B7280` | Teks sekunder |
| `success` | `#22C55E` | Status positif |
| `warning` | `#FBBF24` | Peringatan |
| `danger` | `#EF4444` | Error/hapus |
| `info` | `#3B82F6` | Informasi |

Font: **Instrument Sans** (via `--font-sans`).

---

## Layout shell

### Authenticated (`layouts/app.blade.php`)

```
┌─────────────────────────────────────────────┐
│ [Sidebar 240px]  │ [Navbar + Page Header]   │
│                  ├──────────────────────────│
│  Nav groups      │ [Alert flash]            │
│                  │ [Main content @yield]    │
│                  │                          │
└─────────────────────────────────────────────┘
```

- Class root: `siaga-app-shell`
- Sidebar: fixed mobile, static desktop (`w-60`)
- Main padding: `p-4 md:p-6 lg:p-7`
- Backdrop gelap saat sidebar mobile terbuka

### Guest (`layouts/guest.blade.php`)

- Halaman login full-screen dengan background image
- Card centered `login-card`
- Class khusus: `login-input`, `login-btn-primary`, `login-btn-secondary`

---

## Sidebar navigation

Definisi di `components/sidebar.blade.php` sebagai array `$links`.

### Grup menu

| Grup | Item |
|------|------|
| — | Dashboard |
| Master Data | Cabang, Obat, Pelanggan, PBF |
| Pembelian | DPB / Pembelian |
| Manajemen Harga | Penentuan Harga Obat |
| Penjualan | Penjualan Medis, Penjualan OTC |
| Stok | Stok Obat & Batch, Mutasi Stok, Transfer Stok (Soon) |
| Keuangan | Kas Tunai, Rekening BCA, Omzet Medis/OTC |
| Supplier / PBF | Tagihan PBF, Rencana Pembayaran |
| Laporan | Daftar Laporan |

### Active state

- Class aktif: `siaga-sidebar-link--active` (background orange)
- Query params dipakai untuk membedakan link yang route-nya sama:
  - `kas.index?rekening=tunai&arus=Medis` vs `rekening=bca` vs `rekening=omzet&arus=Medis` (legacy `sumber_kas` masih di-map)
  - `penjualan.index?jenis=Medis` vs `penjualan.create?jenis=OTC`
  - Hub Stok: `stok.index?tab=obat|batch` (item **Stok Obat & Batch**); Mutasi di `stok.mutasi.index`; Transfer placeholder Soon
  - Item sidebar boleh `disabled` + `badge` (tanpa route) untuk fitur belum siap
  - Item boleh `active_on` (array pola `routeIs`) jika active state lebih sempit dari prefix default

---

## Komponen Blade

### `<x-button>`

| Variant | Penggunaan |
|---------|------------|
| `primary` | Aksi utama (simpan, tambah) |
| `accent` | Aksi sekunder mencolok |
| `secondary` | Aksi netral dengan border |
| `outline` | Aksi alternatif |
| `danger` | Hapus |
| `ghost` | Aksi ringan di tabel |
| `link` | Teks link style |

Sizes: `sm`, `md` (default), `lg`.

```blade
<x-button variant="primary" type="submit">Simpan</x-button>
<x-button variant="ghost" size="sm" href="{{ route('obat.edit', $obat) }}">Edit</x-button>
```

### `<x-card>`

```blade
<x-card title="Judul Section" :hover="true">
    Konten
    <x-slot:footer>Opsional footer</x-slot:footer>
</x-card>
```

Class: `siaga-card`, optional `siaga-card-hover`.

### `<x-badge>`

Variant: `success`, `warning`, `danger`, `info`, `primary`, `secondary`.

```blade
<x-badge variant="warning">{{ $status }}</x-badge>
```

### `<x-table>` + `<x-table-scroll>`

```blade
<x-table-scroll>
    <x-table>
        <x-slot:head>
            <tr><th>Kolom</th></tr>
        </x-slot:head>
        <x-slot:body>
            @forelse($items as $item)
                <tr>...</tr>
            @empty
                <tr><td colspan="N" class="px-4 py-8 text-center text-slate-500">Belum ada data.</td></tr>
            @endforelse
        </x-slot:body>
    </x-table>
</x-table-scroll>
```

Header tabel: class `siaga-table-head`.  
Body: class `siaga-table-body`.

### `<x-form-field>`

```blade
<x-form-field label="Nama Cabang" name="nama_cabang" :value="old('nama_cabang', $cabang->nama_cabang ?? '')" required />

<x-form-field label="Jenis Harga" name="jenis_harga" type="select"
    :options="['OTC' => 'OTC', 'REG' => 'REG', 'VIP' => 'VIP']" required />
```

Types: `text` (default), `number`, `date`, `email`, `password`, `textarea`, `select`.

Input class: `siaga-input`.

### `<x-page-header>`

```blade
@section('page-title', 'Data Obat')

{{-- Di view, via navbar component --}}
<x-page-header title="Data Obat" subtitle="Kelola master obat">
    <x-slot:actions>
        <x-button variant="primary" href="...">Tambah</x-button>
    </x-slot:actions>
</x-page-header>
```

### `<x-alert>`

Otomatis di layout — menampilkan `session('success')` dan `$errors`.

### Searchable select

**Statis** (form sederhana):

```blade
<x-searchable-select name="obat_id" :options="$obatList->pluck('nama_obat', 'id')" />
```

**Alpine** (baris dinamis):

```blade
<x-searchable-select-alpine
    x-bind:name="'detail[' + index + '][obat_id]'"
    :options="$obatOptions"
/>
```

---

## Pola halaman

### Index (list)

1. Page header + tombol "Tambah"
2. Card filter (search `q`, dropdown cabang/status)
3. Tabel dengan pagination `{{ $items->links() }}`
4. Empty state: "Belum ada data." centered, `text-slate-500`

### Create / Edit

1. Page header
2. Form `method="POST"` (+ `@method('PUT')` untuk edit)
3. `@csrf`
4. Grid 1–2 kolom untuk field
5. Tombol "Simpan" + "Batal" (link kembali ke index)

### Show (detail transaksi)

1. Card info header (tanggal, PBF, cabang, total)
2. Tabel detail baris
3. Link kembali ke index

### Transaksi multi-baris (pembelian/penjualan)

1. Section header form (tanggal, PBF/cabang/pelanggan)
2. Alpine `x-data` dengan `rows[]`
3. Tombol "Tambah Baris" / "Hapus Baris"
4. `@push('scripts')` untuk Alpine CDN + init searchable select

---

## Format tampilan data

| Tipe | Format |
|------|--------|
| Uang | `Rp {{ number_format($nilai, 0, ',', '.') }}` |
| Tanggal | `{{ $tanggal->format('d/m/Y') }}` |
| Status stok | Badge: success=Aman, warning=Menipis, danger=Habis |
| Status tagihan | Badge: danger=Belum Lunas, warning=Sebagian, success=Lunas |

---

## Responsive behavior

| Breakpoint | Perilaku |
|------------|----------|
| `< md` | Sidebar hidden, hamburger buka sidebar |
| `>= md` | Sidebar always visible |
| Semua | Tabel scroll horizontal via `x-table-scroll` |

Tombol sidebar: `#sidebar-open` (navbar), `#sidebar-close` (sidebar), `#sidebar-backdrop`.

---

## Assets & branding

| Asset | Path |
|-------|------|
| Logo sidebar | `public/images/brand/siaga-icon.png` |
| Favicon | Generated via `scripts/generate-favicons.php` |
| Login background | `public/images/brand/` (guest layout) |

Vite entrypoints: `resources/css/app.css`, `resources/js/app.js`.

---

## Inkonsistensi yang disengaja (jangan "perbaiki" tanpa diminta)

1. **Dashboard** memakai komponen design system penuh.
2. **Form pembelian/penjualan create** memakai inline Tailwind (`rounded-xl border-slate-200`) bukan `<x-card>`.
3. **Alpine.js** dimuat CDN hanya di halaman transaksi, bukan global di Vite bundle.

Saat menambah halaman baru, **prefer pola index master data** (card + x-table + x-form-field) kecuali form kompleks multi-baris.

---

## Aksesibilitas minimum

- `aria-label` pada tombol tutup sidebar
- `lang="id"` di `<html>`
- Focus ring pada input dan button (`focus:ring-2`)

Belum ada audit a11y formal — jangan mengorbankan kesederhanaan V0.
