# Panduan Implementasi: Persetujuan Dokumen Pendaftar (Admin)

Sistem: *Sistem Informasi Pendaftaran Mahasiswa Baru (PMB)*. Dokumen ini menjelaskan
mekanisme persetujuan (*approve*) unggahan dokumen mahasiswa yang diintegrasikan ke
dashboard admin (`admin-dashboard.html`).

---

## 1. Masalah & Akar Penyebab

Panel **Verifikasi Dokumen** (`data-panel="docverify"`) sudah ada di HTML, tetapi:

1. **Tidak ada modul frontend** — tidak ada fungsi `setupDocVerify()` di `js/admin-dashboard.js`,
   sehingga panel `#docVerifyBody` kosong dan tidak ada tombol **Setujui/Tolak**.
2. **Tidak ada CSS** — kelas `.dv-list` dirujuk di HTML tapi tidak didefinisikan di
   `css/admin-dashboard.css`, jadi panel tidak memiliki tata letak.
3. **Backend belum punya antrean dokumen agregat** — endpoint yang ada hanya mengambil
   dokumen *per mahasiswa* (`GET /students/:id/documents`); admin butuh daftar semua
   dokumen menunggu dalam satu layar, plus cara menampilkan file aslinya.

Solusi di bawah menambal ketiga celah tersebut.

---

## 2. Arsitektur Alur Kerja (Backend)

```
Mahasiswa upload (student.js: POST /api/student/upload-document)
        │  INSERT documents(is_verified=0, ...)
        ▼
Admin buka "Verifikasi Dokumen"
        │  GET  /api/admin/documents?filter=waiting   ← BARU
        ▼
Admin pratinjau file
        │  GET  /api/admin/documents/:id/file          ← BARU (serve file)
        ▼
Admin klik "Setujui Dokumen" / "Tolak"
        │  PUT  /api/admin/documents/:id/verify
        │       body: { is_verified: 1|0, admin_note }
        ▼
Semua dokumen disetujui → tombol "Setujui Pendaftaran"
        │  PUT  /api/admin/students/:id/status
        │       body: { status: "approved", admin_comment }
        ▼
profiles.status_pendaftaran = accepted  →  NIM dibuat (frontend/worker)
```

### 2.1 Endpoint Baru di `backend/routes/admin.js`

**Antrean dokumen (agregat):**
```http
GET /api/admin/documents?filter=waiting|verified|all
Authorization: Bearer <admin_jwt>
```
Mengembalikan `students: [{ student_id, full_name, student_email, jurusan,
status_pendaftaran, registration_status, documents: [{ id, document_type,
file_path, is_verified, admin_note, verified_at }] }]`. Diurutkan dokumen
belum diverifikasi lebih dulu.

**Pratinjau file asli:**
```http
GET /api/admin/documents/:documentId/file
Authorization: Bearer <admin_jwt>
```
Mengembalikan file (`sendFile`). Dilindungi dari *path traversal* — path yang
di-resolve harus berada di dalam direktori `uploads/`.

**Verifikasi / penolakan dokumen (sudah ada, digunakan):**
```http
PUT /api/admin/documents/:documentId/verify
Authorization: Bearer <admin_jwt>
{ "is_verified": 1, "admin_note": "Sesuai" }
```

**Persetujuan pendaftaran (perbaikan mapping status):**
```http
PUT /api/admin/students/:studentId/status
Authorization: Bearer <admin_jwt>
{ "status": "approved", "admin_comment": "Dokumen lengkap" }
```
`status` sekarang menerima enum `registration_status`
(`draft|submitted|under_review|approved|rejected`) **dan** dipetakan otomatis ke
`profiles.status_pendaftaran` (`pending|processing|accepted|rejected`) lewat
konstanta `STATUS_MAP`. Sebelumnya hanya menerima 4 nilai dan menulis nilai
mentah ke kedua tabel (inkonsisten).

> Catatan: `backend/app.js` memiliki typo `app.use(lerimiter)` yang menghentikan
> server. Sudah diperbaiki menjadi `app.use(limiter)` agar backend bisa berjalan.

---

## 3. Komponen UI (Frontend)

### 3.1 HTML — `admin-dashboard.html`
- Panel `#docVerifyBody` (`.dv-list`) + filter `#docVerifyFilter` (sudah ada).
- Modal pratinjau baru `#docReviewModal` dengan: info mahasiswa, area pratinjau
  (`#docPreviewWrap`), textarea catatan `#docReviewNote`, dan dua tombol aksi
  `#docApproveBtn` / `#docRejectBtn`.

### 3.2 CSS — `css/admin-dashboard.css`
Kelas baru yang ditambahkan (tanpa mengubah yang lain):
`.dv-list`, `.dv-card`, `.dv-card-head`, `.dv-docs`, `.dv-doc`
(`.is-verified` / `.is-rejected`), `.dv-doc-ic`, `.dv-doc-actions`,
`.dv-card-foot`, `.dv-progress`, `.dv-summary`, dan `#docReviewModal` (pratinjau
gambar/iframe + link file). Semua memakai variabel tema (`--brand-600`,
`--border`, `var(--card)`, dll.) sehingga mendukung mode gelap.

### 3.3 JS — `js/admin-dashboard.js`
Fungsi utama `setupDocVerify()` (dipanggil dari `init()`):

- **Data layer dokumen** (localStorage, konsisten dengan pola simulasi dashboard):
  `getDocs()/saveDocs()`, `docsFor(userId)`, `pendingDocStudents()`,
  `setDocDecision(userId, docId, decision, note)`, `setStudentRegistration(...)`.
- **Seed**: `seedDocuments()` mengisi setiap mahasiswa demo dengan 6 dokumen
  (`DOCS`: ijazah, ktp, kk, foto, sehat, skl) dengan `decision` mengikuti status
  pendaftarannya (pending/approved/rejected).
- **Render**: kartu per mahasiswa berisi grid dokumen. Setiap dokumen menampilkan
  ikon tipe, nama, badge status, dan aksi:
  - `decision === 'pending'` → tombol **Tinjau** (buka modal) + **Setujui**.
  - sudah diputuskan → badge *Disetujui* / *Ditolak* + catatan admin.
- **Setujui Pendaftaran**: muncul di footer kartu bila **semua** dokumen
  `approved` dan status pendaftaran masih `pending`. Menyetujui membuat NIM &
  mencatat ke log aktivitas (sama seperti alur *Manajemen Siswa*).
- **Badge nav**: `#navDocCount` menampilkan jumlah mahasiswa yang masih punya
  dokumen menunggu (diperbarui lewat `renderNavCounts()`).

#### State dokumen
```js
// decision: 'pending' | 'approved' | 'rejected'
{ id, userId, type, name, decision, admin_note, created_at }
```

#### Handler aksi inti
```js
// Setujui satu dokumen
setDocDecision(userId, docId, 'approved', note);

// Tolak satu dokumen (wajib ada catatan)
setDocDecision(userId, docId, 'rejected', note);

// Setujui seluruh pendaftaran setelah dokumen lengkap
setStudentRegistration(userId, true);
```

---

## 4. Cara Mengaktifkan (Backend Riil)

Frontend saat ini berjalan dalam mode simulasi (localStorage) agar dashboard
langsung interaktif tanpa DB. Untuk menyambungkan ke backend riil:

1. Jalankan migrasi `backend/config/database.sql` di MySQL.
2. `cd backend && npm install && npm start` (perbaiki `JWT_SECRET` di `.env`).
3. Ganti lapisan data `getDocs()/setDocDecision()/setStudentRegistration()` di
   `js/admin-dashboard.js` dengan pemanggilan `fetch` ke endpoint di §2, mis.:

```js
async function fetchDocQueue(filter) {
  const r = await fetch(`/api/admin/documents?filter=${filter}`, {
    headers: { Authorization: 'Bearer ' + localStorage.getItem('adminToken') }
  });
  return (await r.json()).students;
}
async function verifyDoc(docId, isVerified, note) {
  await fetch(`/api/admin/documents/${docId}/verify`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + localStorage.getItem('adminToken') },
    body: JSON.stringify({ is_verified: isVerified ? 1 : 0, admin_note: note })
  });
}
```
Pratinjau file cukup mengarahkan `<img>/<iframe>` ke
`/api/admin/documents/${id}/file` (dengan header otorisasi via cookie/http-only
jika bukan mode Bearer).

---

## 5. Ringkasan File yang Diubah

| File | Perubahan |
|------|-----------|
| `backend/routes/admin.js` | `GET /documents` (antrean agregat), `GET /documents/:id/file` (pratinjau), mapping `STATUS_MAP` pada `PUT /students/:id/status` |
| `backend/app.js` | Perbaiki typo `lerimiter` → `limiter` |
| `css/admin-dashboard.css` | Tambah gaya `.dv-*` & `#docReviewModal` |
| `admin-dashboard.html` | Tambah `#docReviewModal` |
| `js/admin-dashboard.js` | `seedDocuments()`, lapisan data dokumen, `setupDocVerify()`, handler modal, badge `#navDocCount` |

Setelah perubahan, admin dapat: membuka **Verifikasi Dokumen**, mempratinjau &
**menyetujui/menolak** tiap dokumen, lalu **menyetujui pendaftaran** (membuat NIM)
begitu seluruh dokumen diverifikasi.
