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.
135 lines
7.7 KiB
Markdown
135 lines
7.7 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)/actions.ts Server Actions auth (login, logout), dipakai lintas route group
|
|
(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`).
|
|
- `actions.ts` dikolokasikan dengan fitur yang memakainya. Action yang dipakai lintas route group
|
|
(mis. `logout` dipanggil dari `(dashboard)`) ditaruh di level route group pemiliknya, lalu
|
|
di-import lewat alias (`@/app/(auth)/actions`), bukan path relatif `../(auth)/...`.
|
|
|
|
## 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
|
|
pnpm db:seed buat akun admin dari SEED_ADMIN_* di .env (idempotent)
|
|
docker compose up -d nyalakan Postgres lokal (docker-compose.yml)
|
|
```
|
|
|
|
Postgres lokal: service di `docker-compose.yml`, container `cleo-postgres` (image `postgres:17`,
|
|
port 5432, volume `cleo-pgdata`). Setup laptop baru ada di `README.md`.
|