# Overview — Arsitektur & Konvensi Umum

> Dokumen ini menjelaskan bagaimana setiap modul di **web_manager_frontend** bekerja:
> input apa yang ditangkap, validasi apa yang jalan di frontend, endpoint apa yang
> dipanggil, dan apa yang diharapkan sebagai response. Tujuannya supaya tim bisa
> ambil keputusan cepat tanpa harus baca ulang seluruh kode setiap kali.

## Stack

- **Frontend:** React 18 + Vite + Tailwind CSS
- **Routing:** React Router
- **Data fetching:** TanStack Query (`useQuery` untuk read, `useMutation` untuk write)
- **Form & validasi:** `react-hook-form` + `zod` (`zodResolver`) — sebagian halaman lama masih pakai `useState` manual (lihat catatan per modul)
- **HTTP client:** Axios (instance custom di [src/services/api.js](../../src/services/api.js))
- **Global state:** Zustand (`authStore`, `forbiddenStore`)
- **Notifikasi:** `react-hot-toast`

## Backend

- **Framework:** Laravel 12, **PostgreSQL**, multi-tenant via `TenantGlobalScope`
- **Auth:** JWT (`tymon/jwt-auth`) — token disimpan di `localStorage` key `autopos_token`
- **Base URL:** `VITE_API_URL` (default `http://localhost:8000/api`, dev biasa pakai `http://127.0.0.1:8765/api`)
- **Format response standar:** `{ success: bool, message: string, data: any }` — semua service mengambil `data.data`
- **Validation error (422):** `{ success: false, message, errors: { field: [msg, ...] } }`

## Mode dummy vs backend asli

Ditentukan oleh `VITE_USE_DUMMY` di `.env`:
- `true` → semua service pakai data dummy hardcoded di file service masing-masing (delay simulasi 400–1000ms)
- `false` → service memanggil `api` (axios) ke backend Laravel sungguhan

File [src/services/api.js](../../src/services/api.js) mengekspor flag `USE_DUMMY` ini, dipakai sebagai gate `if (USE_DUMMY) { ...return dummy } else { ...call api }` di **setiap** service.

## Axios interceptor — perilaku global (api.js)

**Request interceptor:** ambil token dari `localStorage.autopos_token`, attach sebagai header `Authorization: Bearer <token>` ke semua request.

**Response interceptor** menangani error berdasarkan HTTP status, secara global (di luar try/catch tiap halaman):

| Status | Perilaku global otomatis |
|---|---|
| `401` | Jika sebelumnya ada token tersimpan → dianggap "sesi habis": hapus token & user dari localStorage, redirect ke `/login`, toast "Sesi habis, silakan login kembali". Jika tidak ada token (mis. gagal login dengan password salah) → tidak ada side effect global, pesan error diteruskan apa adanya ke pemanggil. |
| `403` | Set `forbiddenStore.forbidden = true` (dipakai untuk render halaman "akses ditolak" di layout) |
| `422` | Tidak ditangani secara global — dilempar ke `catch` masing-masing form, biasanya ditampilkan via toast atau pesan field-level |
| `500` | Toast generik "Terjadi kesalahan server, coba lagi nanti" |
| `404` | Tidak ditangani secara global — masing-masing service yang menentukan (biasanya dianggap "data tidak ditemukan") |
| lainnya | Toast `message` dari response, atau toast generik "Terjadi kesalahan, coba lagi" |

**Implikasi penting:** karena 401/403/500 sudah ditangani global, kode di level halaman/form **tidak perlu** menampilkan toast khusus untuk status-status ini — fokus penanganan di level halaman biasanya untuk pesan sukses, validasi 422 spesifik field, dan pesan custom dari backend.

## Pola umum per jenis halaman

### Service file
```js
import api, { USE_DUMMY } from './api'
const delay = (ms = 400) => new Promise((r) => setTimeout(r, ms))
const myService = {
  index: async () => {
    if (USE_DUMMY) { await delay(); return dummyData }
    const { data } = await api.get('/v1/endpoint')
    return data.data
  },
}
```

### Halaman List (pagination client-side)
Backend mengembalikan **semua data sekaligus** (tidak ada server-side pagination untuk sebagian besar modul), frontend yang slice per halaman:
```jsx
const PER_PAGE = 10
const filtered = items.filter(...)
const paged = filtered.slice((page-1)*PER_PAGE, page*PER_PAGE)
```

### Halaman Form (create/edit)
```jsx
const { data: detail, isLoading } = useQuery({ queryKey: ['key', id], queryFn: () => service.show(id), enabled: isEdit })
useEffect(() => { if (detail) setForm({...}) }, [detail])
```
Submit pakai `useMutation`, `onSuccess` → toast sukses + `invalidateQueries` + navigate balik ke list. `onError` → ambil `err.response.data.message` atau `errors` (422) untuk ditampilkan.

## Daftar dokumen modul

- [01-auth.md](01-auth.md) — Login, Register, Forgot/Reset Password, Change Password
- [02-dashboard.md](02-dashboard.md)
- [03-master-data.md](03-master-data.md) — Branch, Category, Unit, Modifier, Table, Customer
- [04-products.md](04-products.md)
- [05-inventory.md](05-inventory.md) — Material, Stock
- [06-discounts.md](06-discounts.md)
- [07-sales.md](07-sales.md) — Shifts, Reports, Transactions
- [08-users-roles.md](08-users-roles.md)
- [09-settings.md](09-settings.md) — Store, Payment, Tax, Printer

## Catatan status integrasi (per Juni 2026)

- **Masih dummy / belum ada endpoint BE:** Users/Roles (full CRUD), Settings Tax (CRUD pajak custom), Sales ReportDetail (detail transaksi individual), Dashboard (statistik agregat)
- Semua modul lain sudah terhubung ke backend asli.
