You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
7.1 KiB
Markdown

@AGENTS.md
# Cleo Minimarket
Aplikasi Next.js 16 (App Router) untuk menggantikan aplikasi back-office minimarket lama berbasis
PHP di `old-source/`. Kode baru ditulis ulang dari nol; `old-source/` hanya referensi.
## Stack
- Next.js 16.4 + React 19, TypeScript strict, Tailwind CSS 4, pnpm 11.
- Drizzle ORM + `pg` untuk database aplikasi (Postgres).
- Better Auth 1.7 untuk login: Drizzle adapter ke Postgres, plugin `username`, sign-up publik
ditutup (`disableSignUp`). CLI-nya package `auth` (bukan `@better-auth/cli` yang berhenti di 1.4).
- shadcn/ui 4 preset `base-nova` (di atas Base UI, bukan Radix). `cn()` dari package `cn`
milik shadcn, pengganti `clsx` + `tailwind-merge`.
## Dua database
| Env var | Database | Isi |
| -------------------- | ------------------------- | ----------------------------------------- |
| `DATABASE_URL` | Postgres (Docker lokal) | Milik aplikasi ini: user, session, auth. |
| `AMIGO_DATABASE_URL` | MariaDB 10.4 `cleo` (LAN) | Data bisnis lama: barang, transaksi, dll. |
- Postgres dikelola penuh oleh Drizzle migration (`db:generate` lalu `db:migrate`).
- MariaDB `cleo` masih dipakai aplikasi PHP lama. **Jangan pernah** `generate`, `migrate`, atau
`push` ke database ini. Schema Drizzle-nya hanya boleh berasal dari `drizzle-kit pull`
(introspeksi), dengan config terpisah.
- Server MariaDB 10.4, bukan MySQL 8: hindari fitur yang tidak didukung (mis. sebagian fungsi
JSON dan CTE tertentu). Driver: `mysql2`.
## Struktur folder
Tanpa folder `src/`. Kode aplikasi di root, sejajar config.
```
app/
(auth)/login/page.tsx halaman publik (belum login)
(dashboard)/layout.tsx kerangka UI halaman yang wajib login (bukan tempat cek session)
(dashboard)/<fitur>/
page.tsx Server Component: ambil data lewat data/, render
actions.ts Server Actions fitur ini ('use server')
_components/ komponen khusus fitur ini
api/auth/[...all]/route.ts handler Better Auth (satu-satunya route handler yang wajar)
components/
ui/ komponen shadcn (generated, jangan diedit manual kecuali perlu)
<nama>.tsx komponen yang dipakai lintas fitur
data/ Data Access Layer: fungsi baca/tulis DB, selalu 'server-only'
db/
index.ts, schema.ts koneksi + schema Postgres
amigo/index.ts, amigo/schema.ts koneksi + schema MariaDB (hasil `drizzle-kit pull`)
lib/
auth.ts config Better Auth (server)
auth-client.ts client Better Auth
utils.ts helper umum (dibuat shadcn)
drizzle/ file migrasi Postgres (generated, ikut di-commit)
proxy.ts pengganti middleware di Next 16; hanya cek ringan (redirect)
```
- Nama folder route dan file: kebab-case. Komponen React: PascalCase untuk nama fungsinya.
- Folder berawalan `_` tidak menjadi route; pakai untuk komponen yang dikolokasikan.
- Route group `(nama)` hanya untuk mengelompokkan layout, tidak muncul di URL.
- Import pakai alias `@/` (mis. `@/db`, `@/data/barang`).
## Aturan data dan Server Actions
- **Mutasi (create/update/delete, submit form) wajib lewat Server Action.** Tidak membuat
route handler `app/api/*` untuk hal yang bisa dikerjakan Server Action.
- **Membaca data tidak lewat Server Action.** Server Action adalah POST dan dijalankan berurutan,
jadi tidak cocok untuk fetch. Data dibaca di Server Component dengan memanggil fungsi di `data/`.
- Setiap file `actions.ts` diawali `'use server'`. Setiap action:
1. Cek session dan hak akses di dalam action itu sendiri. Menyembunyikan tombol di UI bukan
pengaman, karena action bisa dipanggil langsung lewat POST.
2. Validasi input (`FormData`/argumen) dengan zod sebelum menyentuh DB.
3. Panggil fungsi di `data/`, bukan query langsung dari action.
4. Kembalikan objek kecil yang dibutuhkan UI (mis. `{ ok: true }` atau
`{ ok: false, error: string }`), bukan baris DB mentah.
5. Segarkan cache dengan `revalidatePath`/`updateTag` sebelum `redirect`, karena `redirect`
melempar exception dan kode sesudahnya tidak jalan.
- Form di client memakai `useActionState` untuk menampilkan error/pending dari action.
- File di `data/` diawali `import "server-only"` supaya tidak bisa ter-import ke client
component. `db/` dan `lib/auth.ts` sengaja tanpa itu karena dibaca CLI `auth` di luar Next.js
(package `server-only` tidak di-install; Next.js menanganinya sendiri). Hanya `data/`, `db/`,
dan `lib/auth.ts` yang membaca `process.env` untuk secret.
- Session dicek lewat `getCurrentUser()` di `data/auth.ts` (redirect ke `/login` kalau tidak ada),
dipanggil oleh page dan action yang butuh user. Layout tidak dipakai untuk cek session: layout
tidak di-render ulang saat navigasi antar halaman di dalamnya.
- `cacheComponents` aktif: komponen yang membaca session (`headers()`/`cookies()`) wajib berada
di dalam `<Suspense>`, kalau tidak build gagal. Jangan `await` session di level atas layout.
- `proxy.ts` hanya untuk redirect cepat berdasarkan ada/tidaknya cookie session, bukan pengganti
`getCurrentUser()`.
- Query selalu lewat Drizzle query builder atau `sql` template tag (parameterized). Jangan
menyusun SQL dengan string concatenation.
## Kode lama (`old-source/`)
Read-only, jangan diedit dan jangan di-import. Dipakai untuk memahami fitur dan aturan bisnis.
- PHP prosedural + jQuery, akses DB lewat `mysqli`/`mysql_*` langsung di tiap halaman.
Banyak query disusun dengan string concatenation (rentan SQL injection); jangan tiru polanya.
- Peta fitur ada di `old-source/menu.php`: Data Master (pelanggan, karyawan, group/sub group
product, rak, supplier, satuan), Barang, Pengadaan, Barcode, Retur Pembelian, Transfer Barang,
Revisi, Laporan Persediaan/Beli/Jual, BON, Pembayaran.
- Helper bisnis di `old-source/func_lib.php`. Hal yang tidak terlihat dari namanya:
- Login lama mencocokkan `karyawan.username` dan `karyawan.password` (hash MD5), lalu
mengisi session `useracc`, `nik`, `job` (`id_pekerjaan`), `namatoko`.
- Jabatan dibaca dari `karyawan.id_pekerjaan` berupa teks: `Pemimpin Toko`,
`Asisten Pemimpin Toko`, `Petugas Administrasi`.
- Harga pokok ditampilkan dalam kode huruf: `convHrgP`/`DeconvHrgP` memetakan angka 1-0 ke
`B R A W N F O X E S`; profit dipetakan `convProfit` ke `D J U N G K A R E T`.
- Format tanggal tampilan `dd-mm-yyyy` (`encTgl`), DB `yyyy-mm-dd` (`decTgl`).
- `bukaKoneksiPusat()` merujuk konstanta `*_PUSAT` yang tidak didefinisikan di
`db_login.inc.php`; fitur koneksi ke DB pusat kemungkinan sudah mati.
- File `*~` dan `(copy)` adalah sisa backup editor, abaikan.
## Perintah
```
pnpm dev jalankan dev server
pnpm build build production
pnpm lint eslint
pnpm exec tsc --noEmit type-check
pnpm db:generate buat migrasi dari db/schema.ts (Postgres)
pnpm db:migrate jalankan migrasi ke Postgres
pnpm db:studio lihat isi Postgres di https://local.drizzle.studio
```
Postgres lokal: container Docker `cleo-postgres` (image `postgres:17`, port 5432).