# SUPERAPP ACADEMY V16 — PANEL ADMIN CEPAT, MUDAH DIPELAJARI

Versi: **V16.0.0**  
Dasar aplikasi: V15 (CodeIgniter 4, PHP 8.2+, MySQL/MariaDB).  
Perubahan: daftar admin dengan server-side pagination dan AJAX; modal pemilihan server-side yang reusable, pencarian/status/kategori; komentar Bahasa Indonesia.

> **PENTING:** Paket adalah source lengkap untuk pengujian. Jangan menyimpulkan sudah siap produksi hanya dari tes sintaks. Uji browser, alur pembelian, login Google, dan database MySQL secara nyata lebih dahulu.

## 1. Yang sudah berubah

1. **Paginasi server-side**: 10 / 25 / 50 / 100 per halaman. Query SELECT mengambil baris yang dibutuhkan saja (`LIMIT` dan `OFFSET`). Jumlah data dihitung terpisah (`COUNT`).
2. **Pencarian AJAX**: saat pengguna mengetik, permintaan dikirim setelah 350 ms (`debounce`), request lama dibatalkan, hasil diperbarui tanpa reload seluruh halaman. Jika JavaScript tidak aktif, filter GET tetap berfungsi.
3. **Modal pencarian**: Bank Soal, Quiz/Tryout, Materi, Kursus/Kelas, Peserta, Produk. Pilihan baru dikirim dari server lewat endpoint AJAX (maksimum 100 baris per permintaan).
4. **Multi-pilihan lintas halaman**: checkbox konten produk disimpan sementara di `Map` JavaScript. Saat halaman modal berganti, pilihan sebelumnya **tidak hilang**. Nilai ID baru dibuat menjadi `input type="hidden"` untuk dikirim ke controller.
5. **Filter status/kategori, reset pencarian** pada daftar sesuai jenis datanya. Modal juga memiliki filter status dan pencarian kategori via AJAX untuk konten yang mempunyai kategori.
6. **Komentar Bahasa Indonesia** pada modul V16, pemisahan Controller/Service/View/JavaScript dan contoh pengembangan.

Daftar admin yang menggunakan paginasi V16:

- Daftar bank soal
- Daftar pertanyaan pada detail bank soal
- Daftar quiz/tryout
- Daftar materi pembelajaran dan kelas
- Daftar produk dan bundle
- Daftar paket member
- Daftar peserta
- Daftar pesanan/transaksi
- Daftar ulasan/moderasi
- Daftar koreksi uraian
- Rincian transaksi pada laporan penjualan

**Catatan batas cakupan:** hierarki Bab/Isi Silabus pada halaman Detail Kelas masih menampilkan kurikulum yang sudah dibuat, karena membutuhkan urutan manual dan keterkaitan antarbab. Yang sudah server-side adalah pencarian konten referensi untuk mengisi silabus dan daftar **kelas** pada menu utama. Jika satu kelas memiliki ribuan bab, modul silabus bisa dikembangkan lagi menjadi lazy loading per bab.

## 2. Arsitektur kode yang perlu dipelajari

Pola kerja:

```
URL admin
   ↓
app/Config/Routes.php
   ↓
app/Controllers/Admin/...Controller.php
   ↓
app/Services/AdminListService.php  ← filter + jumlah + LIMIT/OFFSET
   ↓
MySQL / MariaDB
   ↓
app/Views/admin/*.php             ← hasil dan tombol halaman
   ↓
public/assets/v16/admin.js       ← AJAX untuk mengganti hasil
```

### File paling penting

| Berkas | Kegunaan |
|---|---|
| `app/Services/AdminListService.php` | Sanitasi query dan pagination yang dapat dipakai ulang. |
| `app/Controllers/Admin/SelectorController.php` | API daftar pilihan modal (6 jenis). |
| `app/Views/admin/_v16_filters.php` | Pencarian, status, kategori, ukuran halaman. |
| `app/Views/admin/_v16_pager.php` | Nomor halaman beserta query filter GET. |
| `app/Views/admin/_v16_picker.php` | Komponen pemilihan data yang dapat digunakan di form mana saja. |
| `public/assets/v16/admin.js` | Modal, AJAX, multi-pilihan Map, pembatalan fetch lama. |
| `public/assets/v16/admin.css` | Desain modal/daftar responsif, tidak di-minify. |
| `tests/smoke_v16.php` | Uji kontrak fitur V16 tanpa database. |

## 3. Cara belajar: menambah pagination ke menu admin baru

Misalkan Anda membuat tabel `tugas` dengan kolom `id`, `title`, `status`:

**Controller** (contoh konsep, sesuaikan nama tabel dan izin pengguna):

```php
use App\Services\AdminListService;

public function index(): string
{
    // 1. Baca query GET dengan batasan input bawaan.
    $filter = AdminListService::input((array) $this->request->getGet());

    // 2. Buat query SQL aman menggunakan Query Builder.
    $query = db_connect()->table('tugas');

    if ($filter['q'] !== '') {
        $query->like('title', $filter['q']);
    }

    if (in_array($filter['status'], ['draft', 'published'], true)) {
        $query->where('status', $filter['status']);
    }

    // 3. Paginasi: SQL hanya mengambil data halaman sekarang.
    [$rows, $pagination] = AdminListService::paginate($query, $filter);

    // 4. Kirim ke view.
    return $this->panel('tugas', compact('rows', 'pagination'), 'Tugas');
}
```

**View:**

```php
<div data-v16-list>
    <?php echo view('admin/_v16_filters', [
        'filterAction' => site_url('panel-admin/tugas'),
        'pagination' => $pagination,
        'filterStatuses' => ['draft' => 'Draft', 'published' => 'Terbit'],
    ]); ?>

    <div data-v16-result-panel>
        <?php foreach ($rows as $row): ?>
            <p><?= esc($row['title']) ?></p>
        <?php endforeach; ?>

        <?= view('admin/_v16_pager', [
            'pagination' => $pagination,
            'pagerAction' => site_url('panel-admin/tugas'),
        ]) ?>
    </div>
</div>
```

Tanpa membuat JavaScript tambahan, `admin.js` sudah menghubungkan form ke AJAX. Jika pengguna mematikan JavaScript, form GET/tautan nomor halaman tetap berguna.

## 4. Cara belajar: modal pemilihan data

**Contoh pilih satu bank soal**:

```php
<?php
// Jenis yang diperbolehkan: bank, exam, material, course, member, product.
$pickerTypes = 'bank';
$pickerName = 'bank_id';
$pickerMulti = false;
$pickerTitle = 'Pilih Bank Soal';
$pickerAllowEmpty = false;
$pickerSelected = []; // ketika edit, isi dengan ['type'=>'bank', 'id'=>2, 'title'=>'TWK']

echo view('admin/_v16_picker', compact(
    'pickerTypes', 'pickerName', 'pickerMulti',
    'pickerTitle', 'pickerAllowEmpty', 'pickerSelected'
));
?>
```

**Contoh memilih beberapa konten produk**:

```php
$pickerTypes = 'bank,exam,material,course';
$pickerName = 'resources[]';
$pickerMulti = true;
$pickerTitle = 'Isi Produk';
$pickerAllowEmpty = false;
$pickerSelected = [];
```

Nilai yang dikirim pada `resources[]` berupa `bank:12`, `exam:5`, `course:19`, dan lain-lain. **Jangan percaya ID dari browser:** setiap controller harus mengecek ID tersebut ada, boleh digunakan, dan berstatus sesuai aturan publikasi. Controller produk V16 sudah mempertahankan validasi tersebut.

Jika ingin menambahkan jenis data ketujuh, edit whitelist di `SelectorController::search()`, lalu tambahkan label pada `availableTypes` di `public/assets/v16/admin.js`. Jangan memasukkan nama tabel langsung dari URL.

## 5. Memasang seluruh source V16 ke instalasi V15

1. Backup **database** menggunakan phpMyAdmin → Export SQL.
2. Backup file `.env` lama, dan semua file upload pada `writable` (terutama `writable/uploads/academy`).
3. Ekstrak ZIP V16 ke direktori proyek. **Jangan memakai `.env` contoh untuk menimpa konfigurasi server yang benar.** Salin kembali kredensial database/domain/Google OAuth/gateway dari `.env` lama.
4. Tidak ada tabel atau kolom baru pada V16. **Upgrade V15 → V16 tidak perlu menjalankan SQL ALTER**. Jika pengguna database masih V14, lakukan upgrade V14 → V15 dahulu sesuai petunjuk versi sebelumnya.
5. Pastikan ekstensi PHP `intl`, `mbstring`, `mysqli`, `json`, `openssl`, `zip` dan ekstensi lain dari framework aktif. Arahkan document root ke `public/`.
6. Buka menu Bank Soal, Tryout, Materi, Kelas, Penjualan, Peserta, Transaksi; coba cari dan ganti 10/25/50/100.
7. Buat produk Bundle dan pilih 3 konten yang berada di halaman modal berbeda. Tutup modal, buka lagi, pastikan seluruh checkbox dan chip masih dipilih; simpan produk dan periksa hasilnya.
8. Buat tryout gabungan dengan 2 bank soal, lalu atur nilai minimal dan pastikan kedua bank terhubung.
9. Pada Studio Kelas, gunakan modal untuk bank/tryout/materi di dalam Bab. Cek apakah `bank_id`, `exam_id`, atau `material_id` tersimpan.
10. Uji pagination dengan data percobaan **lebih dari 100 baris**, termasuk filter yang menghasilkan 0 baris.

### Instalasi baru dari nol (karena belum produksi)

Buat database kosong, lalu impor **hanya** `database/IMPORT_SEKALI.sql`. Isi `.env` sesuai kredensial di server. Tidak perlu menjalankan berkas upgrade versi lain.

### Jika muncul "Whoops!"

Periksa `writable/logs/log-YYYY-MM-DD.log`, status izin folder `writable`, serta konfigurasi database. Di staging pribadi saja, dapat memakai `CI_ENVIRONMENT = development` sementara untuk melihat pesan rinci. Jangan melakukan itu pada website publik.

## 6. Pengujian teknis

Jalankan pada PHP CLI:

```bash
php tests/smoke_v16.php
```

Pemeriksaan PHP tambahan:

```bash
find app -name '*.php' -exec php -l '{}' \;
```

Perlu diingat bahwa **uji sintaks dan smoke test statis bukan uji sistem secara penuh**. Saat pengujian browser, periksa respons API `/panel-admin/pilih-data?type=bank&q=TWK&page=1&per_page=10`, serta `/panel-admin/pilih-kategori?q=CPNS`, setelah login sebagai admin.

## 7. Prinsip agar aplikasi dapat dikembangkan sendiri

- Cari route di `Routes.php`, lalu cari method controller yang dipanggil.
- Untuk mengubah query pagination, edit controller dan `AdminListService` sesuai kebutuhan; jangan memuat seluruh tabel lalu memotong array di PHP.
- Untuk mengubah tampilan modal, edit `admin.css` dan `_v16_picker.php`, kemudian `admin.js` bila ada perilaku baru.
- Gunakan `esc()` untuk menampilkan data, `csrf_field()` pada setiap form POST, dan Query Builder untuk parameter dari pengguna.
- **Jangan minify saat belajar.** Semua berkas tambahan V16 dapat dibuka melalui editor teks biasa, lengkap dengan komentar Bahasa Indonesia.
- Buat backup sebelum mengubah logika pembelian, hak akses, atau ujian yang sudah dimulai oleh peserta.

---

**Batas pengujian:** sumber ini belum menjalani pengujian transaksi dengan MySQL dan uji browser live di server pengguna. Pengujian integrasi dan load test tetap dibutuhkan sebelum produksi.
