siakad-itm/docs/code-style.md
Yoga Pangestu e6f272bb59
Some checks failed
tests / ci (pull_request) Has been cancelled
feat: refactor controllers and update department service usage for consistency
2026-08-25 19:51:27 +07:00

5.0 KiB

Panduan Gaya Kode

Dokumen ini mencatat konvensi penulisan kode di proyek ini, supaya kontributor (termasuk AI assistant) mengikuti pola yang sama dan tidak menulis ulang solusi yang sudah ada dengan cara berbeda-beda di tiap file.

Setiap bagian punya status:

  • Selesai — sudah diterapkan di seluruh kode yang relevan.
  • 🚧 Sebagian — sudah ada polanya, tapi belum diterapkan di semua tempat.
  • 📝 Rencana — baru berupa kesepakatan, belum diterapkan.

1. Enum

Aturan: setiap backed enum (enum X: string) wajib pakai trait App\Enums\Concerns\HasValues. Jangan pernah menulis array_values(X::cases()) atau array_column(X::cases(), 'value') secara manual — pakai X::values() (atau X::options() kalau butuh label untuk dropdown/select).

// app/Enums/Concerns/HasValues.php
trait HasValues
{
    public static function values(): array   // ['male', 'female']
    public static function options(): array   // [{value: 'male', label: 'Laki-laki'}, ...]
}

Struktur enum standar:

enum Gender: string
{
    use HasValues;

    case Male = 'male';
    case Female = 'female';

    public function label(): string
    {
        return match ($this) {
            self::Male => 'Laki-laki',
            self::Female => 'Perempuan',
        };
    }
}

Sebelum → Sesudah

// ❌ Jangan
'gender' => ['required', 'string', Rule::in(array_values(Gender::cases()))],
'type' => ['required', 'string', Rule::in(array_column(FeedbackType::cases(), 'value'))],

// ✅ Pakai
'gender' => ['required', 'string', Rule::in(Gender::values())],
'type' => ['required', 'string', Rule::in(FeedbackType::values())],

Untuk data yang dikirim ke frontend (mis. isi <Select>), pakai options() alih-alih membangun array {value, label} manual dengan array_map:

// ❌ Jangan
'types' => array_map(fn (FeedbackType $type) => [
    'value' => $type->value,
    'label' => $type->label(),
], FeedbackType::cases()),

// ✅ Pakai
'types' => FeedbackType::options(),

Enum baru wajib langsung pakai use HasValues; sejak awal dibuat, tidak perlu menunggu ada kebutuhan values()/options() baru ditambahkan.


2. Migration

Aturan: kolom enum() di migration wajib pakai EnumClass::values(), konsisten dengan aturan Enum di atas. Kolom ->default() wajib pakai EnumClass::Case->value (string), bukan objek case-nya langsung.

// ❌ Jangan
$table->enum('status', array_values(StudentStatus::cases()))
    ->nullable()
    ->default(StudentStatus::Active); // objek enum, bukan string

// ✅ Pakai
$table->enum('status', StudentStatus::values())
    ->nullable()
    ->default(StudentStatus::Active->value);

3. Bahasa UI

Seluruh teks yang tampil ke pengguna (label, judul halaman, pesan toast, placeholder, pesan error) ditulis dalam Bahasa Indonesia. Istilah teknis dan identifier kode tetap dalam bahasa aslinya (bahasa Inggris), tapi teks yang dibaca pengguna — termasuk teks di halaman auth/settings bawaan starter-kit yang aslinya berbahasa Inggris — harus diterjemahkan supaya konsisten dengan sisa aplikasi.


4. Struktur modul Admin

Semua fitur berada di bawah /admin — tidak ada lagi controller atau halaman yang menggantung di top-level App\Http\Controllers / resources/js/pages/ di luar folder admin/. Setiap fitur admin baru (CRUD) mengikuti struktur namespace yang sama, di salah satu dari lima grup:

Grup Namespace Controller/Request Prefix route
Master data Admin\Master admin/master/*
Data operasional Admin\Manage admin/manage/*
Manajemen pengguna Admin\Users admin/users/*
Pengaturan akun Admin\Settings admin/settings/*
Resource mandiri Admin (langsung, tanpa sub-namespace) admin/<resource>

"Resource mandiri" dipakai untuk fitur yang tidak cocok masuk ke 4 grup di atas — misalnya Admin\FeedbackController di admin/feedback (data personal milik user yang sedang login, bukan data akademik yang dikelola admin atas user lain).

Nama route mengikuti pola admin.<grup>.<resource>.<action> (atau admin.<resource>.<action> untuk resource mandiri), dan file Wayfinder hasil generate mengikuti struktur folder yang sama di resources/js/routes/admin/<grup>/<resource>/.

Halaman CRUD frontend memakai pola: PageHeader (judul + tombol Tambah) → FormDialog (Create/Edit) → DataTable + columns.tsxDeleteConfirmDialog.


5. Wayfinder

Selalu jalankan generate dengan flag form variant, supaya <Form {...Controller.method.form()}> tidak error saat runtime:

php artisan wayfinder:generate --with-form

vite.config.ts sudah diset wayfinder({ formVariants: true }) sehingga dev-server otomatis benar. Flag --with-form hanya wajib diingat kalau generate manual lewat CLI.