# PRD - Aplikasi Undangan Digital Tasyakuran NFBS Bogor

Aplikasi undangan digital untuk acara tasyakuran kelulusan santri NFBS Bogor (single-tenant), dikelola per angkatan dan dikirim ke orang tua/wali santri via WhatsApp (`wa.me`).

**Status:** Draft v1.0 — semua keputusan utama sudah final, siap masuk fase desain teknis.
**Target launch:** 12 Juni 2026 (tasyakuran angkatan 7).

---

## 1. Latar Belakang & Tujuan

- Undangan digital tasyakuran kelulusan santri NFBS Bogor.
- Skema angkatan: saat ini angkatan 7, lanjut 8, 9, dst. URL pola `/6`, `/7`, `/8`.
- **Single-tenant** untuk NFBS Bogor saja, tidak direplikasi ke sekolah lain.
- Pengguna utama (penerima): orang tua / wali santri.
- Pengelola: admin & wali kelas.
- Gratis, tanpa monetisasi.
- **Sukses diukur dari dashboard** (jumlah terkirim vs belum terkirim per kelas), bukan KPI formal.

## 2. Target Pengguna & Peran

| Peran | Akses | Kemampuan |
|---|---|---|
| **Orang tua/wali** | Tanpa login, akses via link WA | Buka halaman undangan, lihat nama anak |
| **Wali kelas** | Login (email+password / Google) | Lihat & kirim undangan untuk santri di **1 kelasnya saja**, edit **nomor WA** santri, marking otomatis saat kirim, kirim ulang |
| **Admin** | Login (email+password / Google) | Full CRUD angkatan, kelas, santri, user, mapping kelas↔wali kelas, setting undangan, template WA, akses semua angkatan, lihat audit log |

- **Tidak ada role super-admin** — cukup admin saja.
- **1 wali kelas = 1 kelas** (tidak multi-kelas).
- Estimasi: 200–300 santri per angkatan (gabungan SMP & SMA).

## 3. Use Case Utama

1. Admin menambah angkatan baru.
2. Admin **download template CSV** untuk daftar santri.
3. Admin **upload CSV** (NIS, nama, kelas, nomor WA orang tua) untuk bulk import.
4. Admin membuat user wali kelas dan map ke kelasnya.
5. Admin atur background, backsound (opsional), posisi kolom nama, custom text (rich text), template WA.
6. Wali kelas login → dashboard kelasnya + counter "X/Y terkirim".
7. Wali kelas bisa **edit nomor WA santri** di kelasnya (kalau orang tua ganti nomor / nomor salah).
8. Wali kelas klik **Preview** atau **Test Send** ke nomornya sendiri.
9. Wali kelas klik **Kirim WA** per santri → popup konfirmasi + note "Gunakan HP untuk pengalaman lebih mudah" → buka `wa.me/<nomor>?text=...` → **auto-mark terkirim**.
10. Wali kelas bisa **kirim ulang** ke santri yang sudah terkirim (untuk reminder H-1/H-3 manual) tanpa reset status.
11. Orang tua buka link di HP → lihat nama anak di atas background, custom text, dan backsound looping (kalau diatur).
12. Wali kelas berperan sebagai humas/support — orang tua menghubungi wali kelas jika ada masalah link.

## 4. Fitur & Ruang Lingkup

### MVP (final)
- **Manajemen angkatan** — CRUD, full akses arsip angkatan lama.
- **Manajemen kelas** + mapping ke wali kelas (1:1).
- **Manajemen santri** — field: NIS, nama, kelas, nomor WA orang tua (encrypted at rest).
- **Bulk import CSV** — download template + upload, validasi NIS unik & format nomor WA.
- **Halaman undangan publik** di `/{angkatan}/{slug}` — slug dari nama santri yang di-sanitize (lowercase, hapus diakritik & karakter spesial, ganti spasi ke `-`).
- **Mobile-locked layout** — max-width container ~420px, center di desktop.
- **Setting per angkatan:**
  - Background image (max 10MB).
  - Backsound (opsional, max 10MB, auto-loop, dengan tombol play manual karena auto-play di-block browser).
  - Posisi & style label "Nama Ananda/Aninda" (koordinat/drag, font, warna, ukuran).
  - **Custom text (rich text)** — bold, italic, ukuran, link.
  - **Template pesan WA** dengan placeholder `{nama}`, `{link}`, `{kelas}`.
- **Auth & RBAC** — email+password + Google OAuth, wali kelas terisolasi ke kelasnya.
- **Dashboard** — counter terkirim/belum per kelas & per angkatan.
- **Kirim via `wa.me`** — popup konfirmasi → auto-marking → tab WA terbuka.
- **Note "Gunakan HP"** tampil di UI pengiriman.
- **Kirim ulang** — tombol kirim tetap aktif untuk santri yang sudah ditandai terkirim (kebutuhan reminder).
- **Edit nomor WA** oleh wali kelas (hanya field nomor WA, tidak bisa edit nama/NIS).
- **Preview & Test Send** untuk wali kelas/admin.
- **Reset/undo marking** terkirim.
- **Audit log** untuk aksi admin & wali kelas (tambah/hapus santri, edit nomor WA, kirim, reset, ganti mapping).

### Out of scope
- RSVP / konfirmasi hadir (bisa ditambah nanti).
- Foto santri di undangan.
- Reset password / lupa password (admin reset manual via DB jika perlu).
- Onboarding/tutorial.
- Notifikasi otomatis 100% terkirim.
- Countdown timer.
- Teks Arab / kalender Hijriyah.
- Halaman T&C / Kebijakan Privasi.
- Reminder otomatis (H-1/H-3 dilakukan wali kelas manual via tombol kirim ulang).
- Undangan untuk tamu non-santri.
- CDN.
- Multi-tenant.

## 5. Alur & Pengalaman Pengguna (UX)

- **Orang tua:** akses via link WA, tanpa akun.
- **Wali kelas/admin:** login email+password atau Google OAuth.
- **Layout undangan:** dipaksa mobile view (`max-width: 420px`, `margin: 0 auto`), background image full container.
- **Pengiriman WA:**
  1. Klik tombol "Kirim" di baris santri.
  2. Popup konfirmasi: "Kirim undangan ke [Nama]? *Gunakan HP untuk pengalaman lebih mudah.*"
  3. Klik "Ya" → auto-mark terkirim → buka `wa.me/<nomor>?text=...`.
  4. Bisa klik "Kirim ulang" kapan saja (status tetap terkirim, hanya buka `wa.me` lagi).
  5. Bisa "Reset terkirim" untuk undo marking.

## 6. Konten & Data

### Skema Entitas

**Angkatan**
- `id`, `nomor`, `tahun`, `status` (aktif/arsip)
- `background_url`, `backsound_url` (nullable)
- `nama_label_position` (JSON: x, y, font, color, size, font_weight)
- `custom_text` (HTML rich text, sanitized)
- `wa_template` (string dengan placeholder)
- `created_at`, `updated_at`

**Kelas**
- `id`, `angkatan_id`, `nama`, `jenjang` (SMP/SMA), `wali_kelas_user_id`

**Santri**
- `id`, `kelas_id`, `nis` (unique), `nama`, `slug` (unique per angkatan, generated dari nama), `nomor_wa_ortu_encrypted`
- `status_terkirim` (bool), `terkirim_at`, `terkirim_by_user_id`

**User**
- `id`, `nama`, `email` (unique), `password_hash` (bcrypt), `google_id` (nullable), `role` (admin/wali_kelas)

**AuditLog**
- `id`, `user_id`, `action` (mis. `santri.create`, `santri.update_wa`, `undangan.send`, `undangan.reset`), `entity_type`, `entity_id`, `metadata` (JSON), `created_at`

### Slug santri (auto-generated)
- Lowercase, strip diakritik, ganti karakter non-alfanumerik dengan `-`, collapse multiple dashes.
- Cek unik per angkatan; jika tabrakan, suffix `-2`, `-3`, dst.
- Contoh: `Ahmad Fauzi Al-Hakim` → `ahmad-fauzi-al-hakim`.

### Nomor WA
- Encrypted at-rest dengan AES-256-GCM, key disimpan di env (`ENCRYPTION_KEY`).
- Validasi format: angka, opsional `+` di depan, 10–15 digit. Auto-normalize ke format internasional (`628xxx`).

### Storage file
- Background & backsound disimpan lokal di cPanel: `/public/uploads/angkatan-{id}/`.
- Auto-compress background (target <500KB WebP) untuk hemat disk (1GB total).
- Validasi MIME type & ukuran (max 10MB) saat upload.

## 7. Notifikasi & Komunikasi

- **Channel tunggal:** WhatsApp via `wa.me` (manual klik).
- **Auto-mark terkirim** saat user konfirmasi popup.
- **Kirim ulang** tersedia (status tetap terkirim, hanya buka `wa.me` lagi).
- **Tidak ada** reminder otomatis, notifikasi internal, atau chat antar tamu.
- Wali kelas bertindak sebagai humas → orang tua hubungi wali kelas via WA pribadi jika ada masalah.

## 8. Pembayaran & Monetisasi

Tidak ada.

## 9. Teknis & Arsitektur

### Constraint Hosting (cPanel)
- Disk: **1 GB** total (saat ini 38MB terpakai).
- DB: **MariaDB 10.11.16** (~990MB quota).
- Node.js: LTS available di cPanel.
- Tidak ada CDN, tidak ada Redis.

### Stack Pilihan (paling ringan untuk constraint di atas)

**Rekomendasi utama: Express.js + EJS + HTMX + Drizzle ORM + MariaDB**

Alasan:
- `node_modules` kecil (~80–120MB), aman di disk 1GB.
- SSR dengan EJS = hemat memory, cepat di shared hosting.
- HTMX untuk interaktivitas (auto-mark, popup konfirmasi, dashboard refresh) tanpa SPA framework.
- Drizzle ringan dibanding Prisma (Prisma engine binary ~50MB).
- Tidak ada build step berat seperti Next.js.

**Stack alternatif jika butuh lebih modern:**
- Astro + Node adapter (SSR) — tetap ringan, ada komponen modern.
- Next.js → **tidak direkomendasikan** karena `node_modules` + `.next` build bisa 400MB+, berisiko penuh disk.

### Library/Tooling
- **Web framework:** Express 4.x
- **Template:** EJS
- **Frontend interaktivitas:** HTMX + Alpine.js (untuk popup, drag posisi label)
- **CSS:** Tailwind CSS (purged, JIT) atau Pico.css (lebih ringan)
- **Rich text editor (custom text & WA template):** TipTap atau Quill (loaded di admin saja)
- **ORM:** Drizzle ORM
- **DB driver:** `mysql2`
- **Auth:** `passport` + `passport-local` + `passport-google-oauth20`, session via `express-session` + `connect-mysql`
- **Hash password:** `bcrypt`
- **Encrypt nomor WA:** Node `crypto` (AES-256-GCM)
- **CSV parsing:** `papaparse` atau `csv-parse`
- **Upload file:** `multer`
- **Image compress:** `sharp`
- **Sanitize HTML (custom text):** `DOMPurify` (server-side via `jsdom`) atau `sanitize-html`

### Deployment
- cPanel "Setup Node.js App" → Application root, startup file `app.js`, mode production.
- Upload via Git deploy atau file manager.
- Asset & uploads di `/public`, di-serve langsung Apache/LiteSpeed (lebih cepat dari Express static).

## 10. Keamanan & Privasi

- **Otorisasi wali kelas** via middleware: query santri selalu di-filter `kelas_id = req.user.kelas_id`.
- **Slug undangan** publik (sesuai keputusan), tidak ada token.
- **Password hash** bcrypt cost 10–12.
- **Rate limiting** login (5 attempt / 15 menit) via `express-rate-limit`.
- **Session cookie:** httpOnly, secure (jika HTTPS), sameSite=lax.
- **Encrypt nomor WA at rest** (AES-256-GCM, key di env).
- **CSRF protection** via `csurf` atau token di form.
- **Sanitize rich text** dari custom text & WA template sebelum disimpan & dirender.
- **Audit log** mencatat aksi sensitif (CRUD, edit nomor WA, kirim, reset).
- **Validasi upload:** whitelist MIME (image/jpeg, png, webp; audio/mpeg, mp3, wav).

## 11. Performa & Skalabilitas

- Estimasi traffic puncak: **10–20 concurrent** (sangat ringan).
- Background image auto-compress di server (sharp) → target <500KB.
- Backsound diberi tombol play manual (browser block auto-play).
- Caching:
  - Halaman undangan publik bisa di-cache HTTP (Cache-Control 1 jam) karena jarang berubah.
  - Asset upload pakai filename hash agar cache busting otomatis.
- Tidak perlu Redis, tidak perlu CDN (skala kecil).

## 12. Integrasi Pihak Ketiga

- WhatsApp via `wa.me` (final).
- **Google OAuth** untuk login (selain email+password).
- Tidak ada integrasi SIM sekolah, tidak ada Maps embed (lokasi via custom text).

## 13. Aksesibilitas & Lokalisasi

- Bahasa: Indonesia.
- Tidak ada teks Arab atau kalender Hijriyah.
- Branding NFBS (logo/warna/font) belum confirmed → akan diisi belakangan.

## 14. Hukum & Kepatuhan

- Tidak ada halaman T&C / Kebijakan Privasi.
- Tidak ada persetujuan tertulis dari orang tua.

## 15. Rilis & Roadmap

**Target launch:** 12 Juni 2026 (tasyakuran angkatan 7).
**Rollout:** langsung semua kelas, tidak ada pilot.

| Milestone | Deliverable | Estimasi |
|---|---|---|
| **M1** | Auth (email+password + Google) + CRUD angkatan/kelas/santri/user + bulk import CSV + audit log dasar | ~2 minggu |
| **M2** | Halaman undangan publik + setting background, posisi label, custom text rich text + slug auto-generate | ~2 minggu |
| **M3** | Kirim WA + popup konfirmasi + auto-marking + dashboard counter + kirim ulang + reset marking | ~1 minggu |
| **M4** | Preview & Test Send + backsound upload & loop + template WA per angkatan dengan placeholder + edit nomor WA oleh wali kelas | ~1 minggu |
| **M5** | Polish UI, encrypt nomor WA, audit log lengkap, deploy ke cPanel, smoke test | ~1 minggu |

**Buffer 1–2 minggu** sebelum 12 Juni 2026 untuk QA & input data santri angkatan 7.

## 16. Dukungan & Pemeliharaan

- Wali kelas = humas/support pertama untuk orang tua.
- Admin yang menambah angkatan tiap tahun (developer tidak terlibat operasional).
- Tidak ada PIC teknis khusus saat acara.

## 17. Risiko & Mitigasi

| Risiko | Mitigasi |
|---|---|
| Nomor WA orang tua salah/tidak aktif | Wali kelas bisa edit nomor WA langsung di kelasnya |
| `wa.me` perlu klik manual | Note "Gunakan HP" + popup cepat |
| Disk cPanel 1GB cepat penuh karena upload | Auto-compress image (sharp), batasi 10MB per file, monitor manual |
| Auto-play backsound diblokir browser | Tombol play manual, default muted |
| Slug nama tabrakan/karakter aneh | Sanitize + suffix increment otomatis |
| Lupa password admin/wali kelas | Admin reset manual via DB (sesuai keputusan tidak ada fitur reset) |
| Salah marking terkirim | Tombol reset / undo |
| Branding NFBS belum confirmed | Pakai default netral, ganti saat info tersedia |

---

## Ringkasan Keputusan Final

✅ Single-tenant NFBS, role: admin & wali kelas (1 kelas/wali).
✅ Angkatan 7 launch 12 Juni 2026, 200–300 santri (SMP+SMA).
✅ URL `/{angkatan}/{slug}`, slug dari nama (sanitized).
✅ Bulk import CSV, download template tersedia.
✅ Background & backsound max 10MB, backsound opsional & looping.
✅ Custom text rich text (TipTap/Quill), template WA per angkatan dengan placeholder.
✅ Login email+password + Google OAuth, tanpa reset password.
✅ Halaman undangan publik tanpa token.
✅ Encrypt nomor WA at rest.
✅ Audit log untuk aksi sensitif.
✅ Auto-mark terkirim + popup konfirmasi + note "Gunakan HP".
✅ Kirim ulang & reset marking tersedia.
✅ Wali kelas bisa edit nomor WA santri di kelasnya.
✅ Stack: **Express + EJS + HTMX + Drizzle + MariaDB** (paling ringan untuk cPanel 1GB).
✅ Arsip angkatan lama: full akses.
✅ Rollout langsung semua kelas (tanpa pilot).

## Item yang Belum Tersedia (akan diisi sambil jalan)
- Asset branding NFBS (logo, warna, font).
- Default template WA contoh (akan disusun admin).
- Default custom text contoh (akan disusun admin).
- Daftar santri angkatan 7 (akan diinput admin via CSV menjelang launch).
