# PANDUAN LENGKAP MONEV PBJ
*Bahasa sederhana, langkah demi langkah.*

---

## Gambaran Besar (baca ini dulu, 1 menit)

Sistem ini bekerja seperti dapur restoran:

1. **Master API** = daftar toko bahan (dari mana data diambil)
2. **Master Endpoint** = daftar bahan di tiap toko (data apa saja yang diambil)
3. **Sync** = belanja bahan → disimpan ke gudang (database lokal)
4. **Dataset** = resep masakan (bahan digabung/di-join, dipilih, diberi format)
5. **Menu Frontend & Widget** = menyajikan masakan ke meja (tabel & grafik)

Aturan emas: **frontend tidak pernah menyentuh API langsung**. Semua data
diambil di belakang layar, disimpan lokal, baru ditampilkan. Jadi walau
server LKPP lambat/mati, sistem Anda tetap cepat dan hidup.

---

## A. Instalasi (sekali saja)

1. Salin folder `app/` ke proyek CI4 Anda.
2. Import SQL **berurutan**: `schema.sql` → `engine-schema.sql` →
   `auth-schema.sql` → `notif-menu-schema.sql` → `widget-instansi-schema.sql`
   → `itkp-schema.sql` → `role-update.sql` → (opsional uji coba) `dummy-seed.sql`.
3. Buka `app/Config/Filters.php`, tambahkan di `$aliases`:
   ```php
   'engineauth' => \App\Filters\EngineAuth::class,
   'adminauth'  => \App\Filters\AdminAuth::class,
   ```
4. Buka `app/Config/Routes.php`: hapus route default
   `$routes->get('/', 'Home::index');` lalu tempel seluruh isi
   `app/Config/Routes-monev.txt`.
5. Pasang 2 cron di server:
   ```
   */5 * * * * cd /path/aplikasi && php spark engine:schedule >> writable/logs/engine-schedule.log 2>&1
   *   * * * * cd /path/aplikasi && php spark engine:worker   >> writable/logs/engine-worker.log 2>&1
   ```
   (Di Laragon/lokal belum perlu — ada tombol "Proses Antrean Sekarang".)

## B. Pintu Masuk (siapa masuk lewat mana)

| Alamat | Untuk siapa | Isinya |
|---|---|---|
| `/` | Umum, tanpa login | Grafik ringkasan + angka bergerak |
| `/login` | Operator (dan admin) | Dashboard, Menu Data, ITKP |
| `/admin` | Khusus admin | Seluruh backend |

Login pertama kali: **admin / admin123** (dibuat otomatis).
**Langsung ganti password** dan buat akun operator di menu **Pengguna**.

---

## C. Memasukkan API (5 menit)

Menu **Master API** → **+ Tambah API**:

1. **Nama API**: bebas, misal "INAPROC LKPP".
2. **Base URL**: alamat dasarnya, misal `https://data.inaproc.id`.
3. **Authentication**: pilih *Bearer Token* → tempel token dari LKPP.
   (Untuk uji coba JSONPlaceholder: pilih *Tanpa auth*.)
4. Timeout 60, Retry 3 → **Simpan**.
5. Klik **Test Connection** → harus muncul hijau "Server terjangkau".

> Ingat: token INAPROC terikat IP server. Test Connection harus dijalankan
> dari server yang IP-nya terdaftar di LKPP.

## D. Menambah Endpoint (data apa yang diambil)

Menu **Master Endpoint** → **+ Tambah Endpoint**:

1. Pilih **API induk**.
2. **Nama**: misal "RUP Paket Penyedia".
3. **Path**: salin persis dari API Playground, misal
   `/api/legacy/rup/paket-anggaran-penyedia?tahun={tahun}&kd_klpd={kd_klpd}`.
   Tulisan `{tahun}` dan `{kd_klpd}` otomatis diganti — isi nilainya di
   tombol **Parameter Global**.
4. **Field Kunci Unik**: nama kolom ID di data, misal `kd_rup`.
   Ini membuat sync ulang hanya memperbarui baris yang berubah (hemat & cepat).
5. **Interval**: berapa menit sekali ditarik otomatis (RUP: 1440 = sehari;
   data panas: 60 atau 5).
6. Simpan → klik **Preview**: kalau muncul daftar field + contoh JSON,
   berarti benar. Kalau error, cek path/token.
7. Klik **Sync** → buka menu **Antrean & Log** → **Proses Antrean Sekarang**
   → log harus mencatat "X baru".

Klik **Sync sekali lagi**: log harus mencatat "0 baru, semua sama".
Itu tandanya sistem pintar — dia tahu datanya belum berubah.

## E. Membuat Dataset (menggabungkan data)

Contoh: menempelkan **pagu RUP** ke data **tender** lewat `kd_rup`.

Menu **Dataset Builder** → **+ Dataset Baru** → beri nama "Tender + Pagu RUP":

- **Tab 1 (Endpoint)**: tambah anggota `tender` (centang **utama**) dan `rup`.
  Beri alias pendek: `tender`, `rup`.
- **Tab 2 (Join)**: buat relasi `tender`.`kd_rup` **LEFT** `rup`.`kd_rup`.
  (LEFT = semua tender tampil walau pagu RUP-nya tidak ketemu;
  INNER = hanya yang ketemu pasangan.)
- **Tab 3 (Field Mapping)**: pilih kolom yang mau tampil. Contoh setelan:
  - `tender.nama_paket` → alias "Nama Paket"
  - `rup.pagu` → alias "Pagu RUP", tipe **decimal**, format **Currency**
  - `tender.status_tender` → format **Badge** + centang **Filter**
    → nanti otomatis jadi tab "Semua | Selesai | Berjalan" di halaman data
  - Centang **Cari** pada kolom yang ingin bisa dicari.
- **Simpan Konfigurasi** → kalau ada peringatan kuning, ikuti petunjuknya
  → **Build Dataset** → tunggu antrean selesai → **Lihat Data**.

Kalau hasil join kosong: cek lewat Preview apakah nilai kuncinya
benar-benar sama persis di kedua endpoint (mis. `12345` vs `RUP-12345`
tidak akan ketemu).

## F. Membuat Menu Frontend

Menu **Menu Frontend** → **+ Tambah Menu**:

1. Buat **menu induk** dulu (label "RUP", tanpa dataset, tanpa induk).
2. Buat **sub-menu**: label "Rekap Penyedia", induk = RUP,
   dataset = pilih dataset Anda.
3. Sub-menu juga bisa menunjuk halaman lewat **URL Kustom**,
   misal `engine/itkp` untuk halaman ITKP.

Sidebar bagian **MENU DATA** langsung berubah — untuk semua pengguna login.

## G. Menyusun Grafik Dashboard (Widget)

Menu **Widget Dashboard** → **+ Tambah Widget**:

| Ingin menampilkan | Pilih tipe | Isi apa |
|---|---|---|
| Angka total paket (bergerak) | Kartu — Jumlah Baris | dataset saja |
| Total nilai pagu (Rp, bergerak) | Kartu — Total Nilai | kolom pagu + format Currency |
| Komposisi per kategori | Donut | kolom grup = kategori |
| Perbandingan per satker | Batang | kolom grup = satker |
| Tren per bulan | Garis | kolom grup = kolom tanggal |

Centang **"Tampil di dashboard publik"** kalau boleh dilihat tanpa login.
Widget muncul di: halaman depan `/` (yang publik saja) dan Dashboard
setelah login (semuanya). Angka kartu beranimasi naik otomatis.

## H. ITKP

Menu **ITKP** → tombol **Komponen** → definisikan tiap komponen:
- **Realisasi** (pembilang): dataset + kolom satker + kolom nilai
- **Target** (penyebut): dataset pembanding + kolomnya
- **Bobot**: skor maksimal komponen

Rumus otomatis: `% = realisasi ÷ target × 100 (maks 100)`;
`skor = % × bobot ÷ 100`. Tombol **Skor Minimum** menentukan ambang
tab "Kurang". Penting: pakai **kode satker** (bukan nama) sebagai kolom
satker di semua komponen supaya pencocokan antar sumber akurat.

## I. Notifikasi Telegram

Menu **Notifikasi**:
1. Buat bot lewat **@BotFather** di Telegram → salin token.
2. Cari chat ID Anda lewat **@userinfobot**.
3. **/start** bot Anda dulu (wajib, kalau tidak bot dilarang mengirim).
4. Isi token + chat ID → Simpan → **Tes Kirim**.

Kalau penarikan data gagal permanen (sudah dicoba ulang otomatis 3×),
HP Anda langsung dapat pesan + muncul di lonceng notifikasi.

## J. Kelola Pengguna

Menu **Pengguna** (khusus admin): tambah akun, pilih role
(**admin** = backend penuh, **operator** = frontend saja),
reset password, nonaktifkan, hapus. Sistem menolak menghapus/menurunkan
admin aktif terakhir dan menghapus akun sendiri.

## K. Urutan Uji Coba Pertama Kali (checklist)

- [ ] Login `/admin` (admin/admin123) → ganti password
- [ ] Pengaturan Instansi: nama sistem + logo
- [ ] Jalankan seeder/SQL JSONPlaceholder
- [ ] Master API → Test Connection hijau
- [ ] Master Endpoint → Preview Posts → Sync semua → cek log "baru"
- [ ] Sync ulang → log "sama" semua
- [ ] Dataset "Artikel + Penulis" (join post.userId = user.id) → Build → Lihat Data
- [ ] Widget "Total Artikel" (kartu) + "Tugas per User" (batang)
- [ ] Menu Frontend: induk "Uji Coba" → sub "Artikel"
- [ ] Buat akun operator → logout → login `/login` sebagai operator
      → pastikan hanya melihat Dashboard, ITKP, dan Menu Data
- [ ] Buka `/` tanpa login → grafik publik + angka bergerak tampil
- [ ] Ganti ke API INAPROC asli, hapus API uji coba

## L. Kalau Ada Masalah

| Gejala | Kemungkinan | Solusi |
|---|---|---|
| Test Connection merah | URL salah / server tak terjangkau / IP belum whitelist | Cek base URL; jalankan dari server ber-IP terdaftar |
| Preview error 401/403 | Token salah/kedaluwarsa | Perbarui token di Master API |
| Sync 0 baris | Path/parameter salah | Cocokkan lagi dengan API Playground |
| Join kosong | Nilai kunci tidak sama persis | Bandingkan lewat Preview kedua endpoint |
| Widget "Dataset belum di-build" | Build belum jalan | Menu Antrean → Proses Sekarang |
| Tidak ada alert Telegram | Bot belum di-/start / cron worker mati | /start bot; pastikan cron `engine:worker` jalan |
| Data tidak ter-update otomatis | Cron belum terpasang | Pasang 2 cron di bagian A langkah 5 |
