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.

111 lines
5.3 KiB
Markdown

---
name: cleo-code-review
description: Review struktur dan penulisan kode Cleo Minimarket (Next.js 16, Server Actions, Drizzle, MariaDB MyISAM) terhadap aturan CLAUDE.md dan bug yang pernah ditemukan. Pakai saat diminta "review", "cek kode", "cek commit yang belum dipush", atau sebelum push.
---
# cleo-code-review
Review read-only atas perubahan kode di proyek ini. Tujuannya menangkap bug nyata dan
pelanggaran aturan proyek sebelum di-push. Jangan memperbaiki file kecuali diminta.
## 1. Tentukan cakupan
- Default: commit yang belum di-push plus perubahan yang belum di-commit.
```
git status -sb
git log --oneline '@{u}..HEAD'
git diff --stat '@{u}'
git diff '@{u}'
```
- Kalau user menyebut commit, branch, atau file tertentu, pakai itu.
- Abaikan `old-source/`, `backup/`, `drizzle/`, `components/ui/`, `db/amigo/schema.ts`
(referensi, backup, atau generated), kecuali perubahannya memang di sana dan mencurigakan.
## 2. Jalankan pengecekan otomatis
```
pnpm exec tsc --noEmit
pnpm lint
pnpm exec prettier --check <file yang berubah>
pnpm build
```
Kutip baris error terpendek yang menentukan. Build perlu dijalankan karena `cacheComponents`
membuat error Suspense baru muncul di build, bukan di `tsc`.
## 3. Baca diff dengan checklist ini
Untuk temuan di sekitar kode, baca file utuhnya, jangan hanya potongan diff.
### Auth dan routing
- `proxy.ts` hanya redirect berdasarkan ada/tidaknya cookie. Redirect "sudah login -> /" di
proxy memicu loop saat cookie kedaluwarsa (proxy kirim ke `/`, `getCurrentUser()` kirim ke
`/login`). Redirect seperti itu harus memakai session yang divalidasi
(`redirectIfLoggedIn()` di `data/auth.ts`), dipanggil dari page di dalam `<Suspense>`.
- Matcher proxy harus tetap mengecualikan `login`, `api`, dan aset statis.
- Import dari path internal Next (`next/dist/...`) hampir selalu salah.
- Page dan action yang butuh user memanggil `getCurrentUser()`; layout tidak dipakai untuk cek
session.
- Komponen yang membaca `headers()`/`cookies()`/`searchParams` berada di dalam `<Suspense>`.
### Server Actions
- File bernama `actions.ts` (jamak), diawali `"use server"`, dikolokasikan dengan fitur.
- Setiap action: cek session, validasi zod sebelum menyentuh DB, panggil fungsi `data/`
(bukan query langsung), kembalikan objek kecil, `revalidatePath` sebelum `redirect`.
- Error validasi dikembalikan per field (`z.flattenError(err).fieldErrors`), bukan hanya
`issues[0].message`; kalau tidak, pesan tampil di field yang salah.
- Return yang gagal ikut membawa `values` (isian mentah), karena React 19 me-reset form
setelah action selesai dan ketikan user hilang.
- `console.error` berisi tag nama action, `user.id`, input relevan (tanpa password/secret),
dan error asli sebagai argumen terpisah. Pesan ke UI bahasa Indonesia dan bisa
ditindaklanjuti, tidak pernah pesan DB mentah.
- Hasil `getCurrentUser()` disimpan ke variabel kalau dipakai di log.
### Komponen client
- Jangan menutup sheet/dialog lewat `useEffect(() => { if (state.ok) setOpen(false) },
[state.ok])`: tidak jalan lagi saat sukses kedua (true -> true) dan dilarang lint
`react-hooks/set-state-in-effect`. Bungkus action di `useActionState` dan panggil
`setOpen(false)` di sana.
- Input form memakai `defaultValue={state.values?.[name] ?? dataAwal ?? ""}`; error per field
ditampilkan di `<FieldError>` field itu, error umum di dekat tombol submit.
- Tipe dari modul `data/` (server-only) di-import dengan `import type`.
- Konfirmasi destruktif memakai `AlertDialog`, bukan `Sheet` yang di-override class-nya.
Tombol konfirmasi berupa `Button type="submit"` supaya error action sempat terlihat.
- Prop yang tidak dipakai pemanggil mana pun dibuang.
### Data Access Layer (`data/`)
- Diawali `import "server-only"`.
- Fungsi yang diekspor tapi tidak dipakai di mana pun: tandai untuk dihapus.
- Tipe input diturunkan dari tipe baris (`Omit<...>`) daripada ditulis ulang manual.
- Query lewat Drizzle atau `sql` tag, tidak pernah string concatenation. Pola `LIKE` memakai
escape `\`, `%`, `_`.
- MariaDB `cleo` itu MyISAM: tidak ada transaksi dan `FOR UPDATE` tidak mengunci. Penomoran
otomatis harus tahan bentrok dengan PHP lama (retry saat `ER_DUP_ENTRY`), update stok
atomik satu statement, master + detail divalidasi penuh sebelum statement pertama.
- Tanggal "hari ini" pakai zona `Asia/Jakarta`, bukan `toISOString()` (UTC).
- Tanggal `0000-00-00` tetap string, tidak diubah ke `Date`.
- Batas panjang zod sama dengan panjang kolom di `db/amigo/schema.ts`.
- Aturan bisnis dicocokkan dengan kode lama di `old-source/` (mis. format kode pelanggan).
### Kerapian
- Format sesuai Prettier (titik koma, baris tidak terlalu panjang, newline di akhir file).
- Urutan import: package luar, baris kosong, lalu `@/...`, lalu path relatif.
- Nama file kebab-case, komponen PascalCase, import lintas folder lewat alias `@/`.
## 4. Laporkan
Kalimat pertama berisi kesimpulan: siap di-push atau belum, dan hasil `tsc`/`lint`/`build`.
Lalu temuan dikelompokkan dan diberi nomor berurutan lintas kelompok:
1. **Bug**: perilaku salah yang bisa dialami user. Sertakan skenario konkretnya.
2. **Tidak sesuai aturan proyek**: pelanggaran CLAUDE.md.
3. **Kerapian**: format, kode mati, duplikasi.
Setiap temuan menyebut `file:baris`, apa yang salah, dan arah perbaikannya dalam satu atau
dua kalimat. Tutup dengan daftar singkat hal yang sudah benar, lalu saran urutan perbaikan.