# SUPERAPP ACADEMY V18 — PANDUAN LENGKAP PEMULA

**Target:** CodeIgniter 4.7.6, PHP 8.2+, MySQL/MariaDB, instalasi melalui cPanel tanpa Composer.

## 1. Apa yang berubah dari V17?

- **Bundel fleksibel:** Produk `bundle` dapat berisi minimal dua konten dari jenis **Bank Soal (`bank`)**, **Quiz/Tryout (`exam`)**, **Materi (`material`)** dan **Kelas Online/Pelatihan (`course`)**. Bisa campur semuanya.
- **Bundel dapat dijual reguler atau menggunakan tier member**. Produk `single` tetap harus memuat tepat satu konten. Satu Tryout yang menggabungkan beberapa bank soal tetap **satu item `exam`**; tetapi Tryout itu juga boleh dimasukkan sebagai **bagian dari bundel** bersama item lain.
- **Bundel regular** sekarang ikut muncul di halaman utama dan katalog.
- **Tema terang/gelap** pada publik, admin, dan dashboard peserta, default terang. Pilihan tema diingat pada perangkat menggunakan `localStorage`.
- **Ikon SVG lokal** pada menu, dashboard, katalog dan kartu. Tidak bergantung pada Font Awesome CDN.
- **Navigasi GET AJAX** untuk halaman daftar utama admin, beranda/katalog/detail produk/keranjang, dan dashboard/daftar peserta. Pencarian & pagination admin V16 tetap AJAX. Form yang secara eksplisit aman untuk AJAX memakai `data-v18-ajax-form`.
- **Fallback bawaan:** navigasi biasa jika JavaScript mati, halaman beda layout, atau AJAX GET gagal.

**Tidak ada perubahan tabel V17 ke V18.** Pengguna database V17 tidak perlu mengimpor SQL baru.

## 2. Instalasi memakai database lama (disarankan jika ada data)

1. Backup database melalui phpMyAdmin → **Export → SQL**.
2. Backup `.env` dan `writable/uploads` termasuk seluruh foto soal.
3. Upload file ZIP V18 ke folder baru dahulu. Jangan hapus konfigurasi server yang sedang aktif.
4. Salin `.env` lama ke folder V18. Bila Anda menimpa semua file, buka `.env` dan kembalikan nilai database, baseURL, Google OAuth dan gateway yang benar.
5. Arahkan document root subdomain ke `public/`.
6. Pastikan ekstensi `intl`, `mbstring`, `mysqli`, `json`, `openssl`, `curl` dan `zip` aktif.
7. **Tidak perlu mengimpor ulang `database/IMPORT_SEKALI.sql`.** Tabel V17 tetap sama.
8. Buka `/panel-admin/penjualan` lalu buat bundel yang berisi Bank+Quiz+Materi+Kelas.
9. Uji keranjang, checkout Rp0, checkout berbayar, akses kedaluwarsa, WAR dan mode gelap.

## 3. Instalasi BARU (database masih kosong)

1. Buat database dan akun database lewat cPanel MySQL Databases.
2. Impor `database/IMPORT_SEKALI.sql` **sekali** menggunakan phpMyAdmin.
3. Edit `.env` sesuai server, termasuk `app.baseURL`.
4. Upload seluruh aplikasi ke folder web; document root harus ke `public/`.
5. Atur Google OAuth redirect URI sesuai domain aplikasi.
6. Login admin, isi tier member, bank soal dan produk untuk percobaan.

## 4. Belajar mengubah kode secara bertahap

### A. Logika bundel

- `app/Services/BundlePolicyService.php` — tempat aturan jenis produk. **Mulailah dari sini** bila ingin mengubah aturan isi bundel.
- `app/Controllers/Admin/ProductController.php` → `payload()` — membaca/memeriksa form, kemudian menyimpan data melalui `persist()`.
- `app/Views/admin/_product_form.php` — tampilan jenis produk, tier dan pemilih isi.
- `app/Controllers/Web/PublicController.php` — daftar bundel dan halaman detail untuk pembeli.
- `app/Services/CartService.php` — keranjang dan snapshot harga di checkout.
- `app/Services/FulfillmentService.php` — memberi akses setelah pembayaran sah/checkout gratis.
- `app/Services/AccessGrantService.php` — memeriksa apakah peserta masih boleh membaca/menjawab.

**Contoh:** Jika produk bundel berisi 1 bank + 1 quiz + 1 kelas, `product_items` memiliki 3 baris dengan `product_id` yang sama, masing-masing memiliki `resource_type` dan `resource_id` sendiri. **Jangan** mencoba menggabungkan soal menjadi bank baru karena tidak diperlukan.

### B. Tema terang/gelap

- `public/assets/v18/theme.css`: ganti variabel `--ink`, `--paper`, `--bg`, `--indigo`, `--sa-accent`. CSS ditulis biasa, tidak minify.
- `public/assets/v18/app.js`: bagian **1. Tema** menyimpan pilihan pada perangkat, default terang.
- `app/Views/layouts/admin.php`, `web.php`, `member.php`: tombol tema dan pemanggilan CSS/JS.
- `app/Helpers/ui_icon_helper.php`: fungsi `sa_icon('home')`, `sa_icon('package')` dan lain-lain.
- `public/assets/v18/icons.svg`: definisi semua ikon SVG lokal.
- `public/assets/v18/admin-base.css` dan `member-base.css`: gabungan CSS lama **tanpa minify**, dibaca dari satu file agar lebih ringan.
- `public/assets/v18/admin-base.js` dan `member-base.js`: gabungan JavaScript lama, juga **tanpa minify**.
- `tools/build_assets.py`: alat pembentuk aset gabungan. Setelah mengubah JS/CSS lama di `v8`, `v9`, ... jalankan `python tools/build_assets.py` untuk memperbarui gabungannya. Jika tidak ada terminal, edit langsung file `*-base.css` / `*-base.js` pada cPanel. Saat edit file hasil gabungan, jangan jalankan build ulang sebelum memindahkan perubahan ke sumbernya, karena hasil build akan menggantikan edit manual.

### C. Interaksi AJAX

- `public/assets/v18/app.js` → `bolehAjax()` menentukan halaman daftar yang aman untuk berpindah tanpa reload.
- Pada layout yang sama, bagian `<div data-v18-view>` atau `<main data-v18-view>` ditukar dari HTML respons.
- `public/assets/v16/admin.js` (disertakan ke `admin-base.js`) menangani pagination/filter dan modal pemilih (server-side AJAX). Event `sa:content-ready` me-rebind komponen setelah navigasi.
- Untuk form POST sederhana, beri atribut `data-v18-ajax-form` **hanya jika aman**, misalnya penghapusan item keranjang atau moderasi ulasan. Endpoint tetap menggunakan POST+CSRF.
- **Jangan** memasang AJAX generik pada login, logout, CAT ujian, gateway, unggah gambar, dan checkout berbayar. Hal-hal tersebut harus tetap mengikuti validasi dan alur transaksi server.
- Jika ingin AJAX penuh untuk halaman editor/ujian, buat modul khusus (bukan memaksa partial navigation) agar tidak menghilangkan state atau mengirim jawaban dua kali.

### D. Menambah modul admin baru

1. Tambahkan rute pada `app/Config/Routes.php` dengan method GET/POST yang tepat.
2. Buat controller di `app/Controllers/Admin/` turunan `AdminBase`.
3. Simpan aturan bisnis dalam `app/Services/` (jangan semuanya ditaruh di view).
4. Buat view di `app/Views/admin/` dan panggil melalui `panel()`.
5. Untuk daftar panjang gunakan `AdminListService` + `_v16_filters.php` + `_v16_pager.php`.
6. Untuk pemilihan data gunakan `_v16_picker.php` + `SelectorController.php` agar modal bisa cari dan pagination.
7. Jika halaman daftar baru perlu pindah tanpa reload, tambahkan route GET-nya di `public/assets/v18/app.js` → `bolehAjax()`.
8. Uji POST tanpa JavaScript juga; validasi server WAJIB tetap ada.

## 5. Keamanan dan kuota

- **Tryout Akbar dalam bundel**: pembelian bundel memberi hak konten, tetapi **tidak** mendaftarkan kursi WAR. Peserta tetap wajib mendaftar via produk satuan/checkout Tryout Akbar saat jadwal dibuka. Ini mencegah kuota 50 terisi hanya karena pembelian paket.
- Sebuah bank soal yang masuk paket **tidak otomatis membuka** semua ujian lain yang memakai bank tersebut.
- Akses kedaluwarsa tetap tersimpan di riwayat tetapi konten terkunci.
- Harga, keabsahan membership, dan hak akses **dihitung di server**; browser hanya tampilan.
- Jangan pernah menghapus file `.env` atau `writable/uploads` lama tanpa backup.
- Jangan mengaktifkan `CI_ENVIRONMENT=development` pada website publik.

## 6. Pengujian dan batasan

Jalankan `php tests/smoke_v18.php` untuk aturan bundel dan pemeriksaan source. Untuk mengecek sintaks gunakan `php -l path/file.php`. Jika ekstensi `intl` dan MySQL tersedia, uji rute melalui browser + database pengujian. Versi ini diuji sintaks dan logika tanpa koneksi MySQL langsung; itu **bukan jaminan bebas error runtime di hosting**.

Skenario wajib: bundel regular dengan bank+tryout+kelas, bundel member Silver/Gold, checkout gratis, transaksi berbayar, masa aktif berakhir, halaman katalog mode gelap, modal pemilih ketika berpindah halaman, dan kuota WAR 50 peserta.
