# Frontend — ASIM Scheduling

## Routing
- Routes defined in `config/routes.ts` (Umi.js file-based routing with explicit config)
- Menu i18n keys: `menu.<routeName>` (flat) or `menu.<parent>.<child>` (nested)
- Locale files: `src/locales/en-US/menu.ts` and `src/locales/id-ID/menu.ts`
- Default locale: `en-US`, `baseNavigator: false` (tidak ikut bahasa browser)
- Access control: `src/access.ts` — `canAsim`, `canPengurus`, `canAdmin`
- Nested route paths MUST start with parent path prefix (React Router v6 requirement)

## API Client
- Axios instance in `src/services/asim/api.ts`
- Bearer token auto-attached via interceptor from `localStorage('asim_token')`
- 401 response → auto-logout (clear localStorage, redirect to `/user/login`)
- 503 response → `history.push('/maintenance')`

## htpasswd + SPA Rules (PENTING — jangan ulangi kesalahan ini)

Server live `asim.goteku.com` menggunakan nginx htpasswd sebagai lapisan pertama sebelum app ASIM.

**Aturan wajib:**
- **DILARANG `window.location.href`** untuk navigasi dalam app — selalu pakai `history.push()` dari `@umijs/max`
  - `window.location.href` = hard navigation = HTTP request baru = nginx cek htpasswd = popup muncul lagi
  - `history.push()` = client-side routing = tidak ada HTTP request = tidak ada popup
- Ini berlaku di: login page redirect, API interceptor 401/503, semua redirect post-action

**Nginx config live:**
- Config: `/etc/nginx/sites-available/asim.conf`
- Document root: `/var/www/html/asim.com/frontend/dist/`
- `/api/` → `auth_basic off` (sudah ada JWT)
- `/uploads/` → `auth_basic off`
- `/` → htpasswd aktif + `Cache-Control: no-store` untuk HTML
- JS/CSS → `expires 1y; immutable`

## Login Page
- Single input field — backend auto-identifies by username / email / phone / no_asim
- No tabs on login page
- After login, checks maintenance status (non-super_admin) via `getMaintenanceStatusAPI()`
- On `must_change_password: true` → redirect to `/change-password?user_id=...`

## Maintenance Mode (Frontend)
- `getMaintenanceStatusAPI()` → `GET /api/settings/maintenance-status` (public, no auth)
- Login page: setelah login berhasil, cek maintenance. Jika aktif (dan bukan super_admin) → `history.push('/maintenance')`
- Route `/maintenance` → halaman statis "sedang maintenance"
- Cache TTL = 30 detik — perubahan setting tidak langsung berlaku

## Seating Diagram Feature

### Frontend Components
- Shared components: `src/components/SeatDiagram/`
  - `SeatLayoutEditor` — admin drag & drop editor (@dnd-kit)
  - `SeatDiagramModal` — ASIM read-only view with popover + print
  - `SeatGrid` — grid rendering untuk view duduk
  - `AltarView` — rendering altar (depan + samping kiri/kanan)
  - `DndParts` — Draggable/Droppable primitives
- Integration points:
  - `TemplateDetail.tsx` — "Denah Duduk" button → Drawer with editor
  - `MassCard.tsx` — "Denah" icon button → Drawer with editor
  - `ScheduleCard.tsx` — "Lihat Denah" button → Modal with ASIM view

### Denah Duduk — Layout Rules
- Grid 6 kolom × N baris, row 0 = paling dekat altar, row N-1 = paling dekat umat
- Setiap posisi punya `side`: kuning atau biru (ditentukan admin, stored per template)
- Warna chip = warna side; warna background cell = tint dari side

### Posisi Altar — Physical Layout & Logic

**Fisik gereja (dari perspektif umat menghadap altar):**
```
Kiri B2 | Kiri B1 │ ALTAR │ Kanan B1 | Kanan B2
                    Depan baris 1
                    Depan baris 2
                    ← UMAT →
```
- **Depan altar**: posisi yang berdiri menghadap umat, horizontal, 2 baris
  - Baris 1 = maju pertama (paling dekat umat, keluar duluan dari barisan)
  - Baris 2 = maju setelah baris 1 (overflow, paling jauh dari umat)
- **Samping altar**: posisi di sisi kiri/kanan altar, vertikal, 2 kolom per sisi
  - B1 = inner (lebih dekat altar), B2 = outer
  - Kanan: baris dibalik (posisi terakhir keluar = paling dekat altar = row 0 teratas)

**Exit order dari denah duduk:**
- Urutan keluar = row DESC, col DESC (baris paling jauh dari altar duluan, kolom paling kanan duluan)

**Generate flow (admin):**
1. **"Isi dari Duduk"** → fill slot Depan Altar dari exit sequence
2. Admin atur divider (← →) untuk tentukan berapa kolom ke kiri vs kanan
3. **"→ Ke Samping"** → distribute ke Kiri B1/B2 dan Kanan B1/B2 (kanan di-reverse)

## Export Excel

### Compact View
- Kolom `Posisi` diulang di awal setiap minggu (Sabtu–Jumat)
- 2 header rows: tanggal (row 1), jam misa (row 2)
- Isi cell: nomor ASIM 2 digit saja (misal `38`)
- File: `src/utils/exportSchedule.ts` → `exportWeeklySchedule(..., compact=true)`

### Detail View
- 1 header row: tanggal + jam per kolom
- Isi cell: `nomor - nama ASIM` lengkap (misal `38 - Tomas Evaristus Ingge`)
- Override ditandai dengan ` *` di akhir nama
