Versi dokumen: 1.3
Sumber: Project Brief D-Form v2 (PDF), diselaraskan dengan keadaan repositori, keputusan stack frontend, dan skema basis data terbaru (database/migrations/, termasuk perubahan Mei 2026). Revisi 1.3 menambahkan pendaftaran bundle (satu pendaftaran → banyak peserta) di §4.4.
Dokumen ini mendefinisikan apa yang dibangun dan kriteria utamanya. Detail implementasi mengikuti pedoman front-end, pedoman back-end, dan struktur folder. Tahapan pengiriman diuraikan di milestone.md.
D-Form v2 adalah aplikasi web untuk membuat, mengelola, dan memproses formulir pendaftaran event secara dinamis. Admin mendefinisikan event dan form tanpa mengubah kode; peserta mendaftar melalui form tersebut, menerima email konfirmasi berisi QR Code, dan kehadiran dicatat dengan pemindaian QR saat event berlangsung.
- Menyediakan platform manajemen event berbasis web yang dinamis dan dapat dikembangkan (scalable secara arsitektur).
- Memudahkan admin membuat form event dengan input yang sepenuhnya dapat dikustomisasi (label, tipe field, validasi, urutan).
- Mempercepat pendaftaran oleh peserta melalui alur yang jelas (pilih event → isi form → konfirmasi).
- Mengotomatisasi email konfirmasi pendaftaran beserta QR unik untuk absensi.
- Menyediakan absensi berbasis QR yang terukur (waktu, event, petugas scan).
- Menegakkan pemisahan peran Admin dan Member untuk keamanan dan pembatasan akses.
- Membuat dan mengatur event (metadata dan jadwal pendaftaran).
- Mendesain form pendaftaran dinamis per event, termasuk (jika dipakai) mode tim,
team_size/max_team_sizedi metadata form, mode bundle (registration_mode), penandaan fieldduplicatabledi metadata field, dan penandaan fieldis_appenduntuk data yang boleh disunting anggota saat konfirmasi. - (Fase lanjutan) Mengirim siaran email terkait event mendatang atau perubahan informasi.
- Melihat daftar peserta dan statistik kehadiran.
- Melakukan pemindaian QR untuk absensi (kamera perangkat melalui web app).
- Mendaftar ke event yang dibuka.
- Mengisi form dinamis sesuai konfigurasi admin.
- Pada mode pendaftaran tim (§4.2): dapat diundang oleh leader tim (pemilik submission utama); menerima notifikasi undangan; membuka halaman khusus untuk meninjau, mengoreksi (pada field yang diizinkan), dan memvalidasi / menyetujui data
form_answeryang awalnya diisi leader. - Pada mode bundle (§4.3): leader mendaftarkan banyak peserta dalam satu pengajuan; setiap anggota memiliki
form_answersendiri dan menerima undangan untuk memverifikasi data serta menerima atau menolak sebelum alur review admin (untuk anggota: biasanya setelah status accepted). - Menerima email konfirmasi (detail event + QR) sesuai alur produk (biasanya setelah submission diterima admin, untuk jalur individu atau leader tim).
- Menghadiri event dan menunjukkan QR untuk discan.
| Area | Deskripsi |
|---|---|
| Autentikasi & peran | Multi-peran: Admin dan Member. Alur login, registrasi akun, lupa password; akun dapat menautkan Google dan/atau GitHub (google_id, github_id pada users). Akses dibatasi middleware berdasarkan peran / permission. |
| Manajemen event | Admin membuat event dengan: judul, deskripsi, lokasi, rentang tanggal event, jendela buka–tutup pendaftaran, kuota, harga (price), banner, status, kategori dan sesi/informasi sesi (kolom teks panjang di skema), serta registered_count (counter denormalisasi jumlah pendaftar). Halaman daftar event, detail event, dan indikator status pendaftaran (buka/tutup/kuota). |
| Form per event | Satu event dapat memiliki satu atau lebih entri forms: judul/deskripsi form, closed_at (penutupan form), visible_for (siapa yang boleh melihat/mengisi — disimpan sebagai JSON dipetakan ke enum aplikasi), opsional banner form (banner_url berbasis teks panjang untuk URL/data URL, plus banner_caption), dan metadata JSON untuk registration_mode (single | bundle dan varian mode tim), max_team_size / team_size, dan parameter terkait; soft delete. |
| Form builder dinamis | Per form: label, nama field internal, input_type di basis data: input, selectInput, textarea, datePicker, fileUpload, radio, checkbox (perluasan enum di migrasi MySQL/MariaDB; SQLite mengikuti definisi tabel), deskripsi field opsional, metadata JSON untuk opsi, validasi, duplicatable (bundle: field diulang per anggota), perilaku tambahan, is_append (opsional: field yang boleh disunting anggota pada alur konfirmasi tim), serta order urutan field. |
| Pendaftaran (Member) | Pemilihan event dan form yang relevan, render form otomatis, penyimpanan jawaban dalam kolom JSON answers pada form_answers, upload file jika ada field file, validasi sesuai aturan admin. Constraint unik (user_id, form_id): satu pengguna hanya boleh satu submission per form (pendaftaran ganda dicegah di basis data). Mode tim: satu pengajuan leader dapat membuat beberapa form_answer (loop / batch), dengan anggota memiliki status_confirmation_member false sampai memvalidasi di halaman khusus; notifikasi undangan leader → anggota dikirim sesuai desain teknis. |
| Notifikasi email | Email sukses pendaftaran berisi detail event, ringkasan data peserta, dan QR unik terikat pada submission (form_answers); serta (pada mode tim) notifikasi undangan dari leader ke anggota sesuai PRD §4.2. Pengiriman melalui SMTP. Pengiriman non-blocking memakai antrean (queue). Audit pengiriman pada tabel email_logs (untuk email yang dilog-kan di sistem): tautan ke form_answer_id (nullable jika submission dihapus), event_id, user_id, alamat penerima, status, pesan error opsional, sent_at. |
| Absensi QR | Satu QR unik per submission (form_answers); pemindaian untuk kehadiran merupakan sasaran produk (waktu, event, admin yang memproses scan). Catatan skema: belum ada tabel dedikasi event_attendances (atau setara) di migrasi saat ini — implementasi pencatatan per scan dapat mengikuti milestone teknis terpisah. |
| Dasbor & pelaporan | Admin melihat jumlah pendaftar per event, statistik kehadiran, data peserta, dan log/rekaman absensi (minimal tampilan tabular; ekspor dapat berupa CSV pada MVP). |
Fitur ini memperluas Pendaftaran (Member) ketika form dikonfigurasi untuk mode tim.
| Aspek | Requirement |
|---|---|
Metadata form (forms.metadata) |
Kolom JSON di tabel forms menyimpan konfigurasi mode pendaftaran, minimal: registration_mode (mis. individu vs tim) dan team_size (ukuran tim / jumlah peserta per satu pengajuan tim, sesuai keputusan implementasi). |
Field is_append (form_fields) |
Flag is_append menyatakan apakah suatu field boleh diubah/ditambahkan oleh anggota saat memvalidasi data yang awalnya diisi leader — dipakai untuk validasi server-side dan batasan UI di halaman konfirmasi anggota. |
Penyimpanan banyak form_answer sekaligus |
Satu aksi simpan dari leader menghasilkan beberapa baris form_answers (satu per pengguna terlibat, dalam transaksi / loop yang terkontrol). Leader memiliki baris utama; setiap anggota yang diundang mendapat baris sendiri dengan jawaban yang disalin atau diselaraskan dengan submission leader. |
status_confirmation_member |
Pada setiap form_answer anggota (bukan leader), nilai status_confirmation_member awalnya false / 0 sampai anggota menerima atau memvalidasi datanya di halaman khusus member. Leader (submission utama) memiliki status konfirmasi yang sesuai aturan bisnis (biasanya true sejak tersimpan). |
| Notifikasi undangan | Sistem mengirim notifikasi baru (email dan/atau saluran lain yang disepakati teknis) kepada anggota ketika leader menambahkan mereka ke tim — isi pesan menjelaskan bahwa data pendaftaran perlu ditinjau dan disetujui. |
| Rute & halaman member | Route dan halaman Inertia/Vue baru di area portal member untuk: melihat data form_answer terkait undangan, mengedit bagian yang diizinkan (is_append), dan menyelesaikan validasi / konfirmasi sehingga status_confirmation_member menjadi true. |
Catatan integrasi: Alur review admin (review_status), email konfirmasi registrasi, dan QR harus konsisten dengan aturan “anggota hanya dianggap ‘siap’ untuk langkah berikutnya setelah konfirmasi” bila produk mewajibkan hal itu (detail disepakati di milestone terkait).
§4.2 menjelaskan mode tim secara umum. §4.3 (pendaftaran bundle) memperjelas mekanisme satu pendaftaran = banyak peserta dengan group_token, duplikasi field bertingkat (duplicatable), dan konfirmasi undangan anggota sebelum submission dianggap layak direview admin — tanpa mengubah prinsip satu form_answer per peserta untuk absensi dan QR.
Sistem form dinamis awalnya mengutamakan relasi 1 submission = 1 peserta. Untuk jenis event yang membutuhkan 1 pendaftaran = banyak peserta (grup/tim dipimpin satu ketua), produk menyediakan pendaftaran bundle dengan batasan:
- Tidak mengubah arsitektur utama form dinamis, penyimpanan jawaban JSON pada
form_answers, alur approval per submission, dan alur absensi + QR (tetap per barisform_answer). - Setiap peserta (leader maupun anggota) tetap memiliki sendiri-sendiri
form_answer, QR absensi sendiri, dan approval sendiri; yang menghubungkan mereka adalahgroup_token.
| Konsep | Penjelasan |
|---|---|
Satu peserta = satu form_answer |
Meskipun mendaftar bersama dalam satu bundle, setiap orang mempunyai baris form_answer sendiri agar absensi, QR, approval, dan ekspor tetap per individu. |
group_token |
Kunci relasi logis antar submission dalam bundle yang sama (nilai unik, disarankan 8 karakter alfanumerik acak — disepakati di implementasi). |
| Absensi & QR | Tetap mengacu pada submission individu; tidak ada perubahan konsep utama alur scan. |
registration_modepada form (mis. disimpan diforms.metadataatau kolom eksplisit — disepakati migrasi): nilaisingle(individu) ataubundle(pendaftaran kelompok).max_team_sizepada form: jumlah maksimum anggota (termasuk atau tidak termasuk leader — disepakati eksplisit di implementasi dan didokumentasikan di UI admin) dalam satu bundle.- Metadata field —
duplicatablepadaform_fields.metadata(bukan konfigurasi global di tabelformssemata): contoh{"duplicatable": true}. Field yangduplicatable: truediulang / ditambahkan di UI untuk setiap anggota tambahan setelah leader mengisi data utama; field tanpa flag tersebut dianggap data grup / induk (hanya sekali).- Perilaku
is_append(§4.2) tetap bisa dipakai bersamaan untuk batasan suntingan anggota saat konfirmasi, selama tidak konflik dengan desain teknis.
- Perilaku
- Admin membuat form, menyetel
registration_mode = bundle,max_team_size, dan menandai fieldduplicatablelewat metadata field di builder. - Leader mengisi data utama, memakai aksi “Tambah peserta”; sistem menampilkan blok field yang
duplicatable: trueuntuk setiap slot anggota. - Leader mengisi data per anggota (nama, email wajib akun sistem, dsb.). Validasi: email harus terdaftar di sistem, jumlah anggota tidak melebihi
max_team_size(sesuai aturan yang disepakati apakah leader dihitung atau tidak). - Saat submit: dalam transaksi terkontrol, sistem membuat satu
form_answeruntuk leader dan satuform_answerper anggota, menetapkangroup_tokenyang sama, peranregistration_role(leader|member), status konfirmasi anggota, mengirim email undangan, dan field lain seperti pada §4.3.5.
| Field | Tujuan |
|---|---|
group_token |
Menghubungkan submission dalam satu bundle (unik per grup; panjang/format disepakati). |
registration_role |
leader atau member. |
member_confirmation_status |
pending, accepted, rejected, expired — untuk anggota yang diundang; leader mengikuti aturan bisnis yang disepakati. |
invited_email |
Email undangan (terpisah dari blob JSON answers untuk audit dan konsistensi). |
member_confirmed_at |
Waktu anggota menyelesaikan konfirmasi (menerima). |
invitation_expired_at |
Batas waktu undangan aktif; kedaluwarsa tidak memakai scheduler — dicek saat anggota membuka tautan konfirmasi. |
Catatan: implementasi dapat menyesuaikan penamaan dengan kolom §4.2 yang sudah ada (mis. menyelaraskan status_confirmation_member dengan member_confirmation_status) melalui satu sumber kebenaran di migrasi — disepakati di M4c pada milestone.md.
- Sistem mengirim email undangan berisi tautan verifikasi/konfirmasi.
- Saat anggota membuka tautan, sistem mengecek
invitation_expired_at: jika lewat waktu, perbarui status keexpired, tolak akses (mis. HTTP 403) tanpa cron. - Anggota hanya dapat melihat dan mengubah data miliknya (sesuai izin field), lalu menerima atau menolak undangan.
- Menerima:
member_confirmation_status = accepted,member_confirmed_at = now()(set zona waktu aplikasi). - Menolak:
member_confirmation_status = rejected(data dapat disembunyikan dari alur aktif sesuai kebijakan produk).
Admin hanya meninjau / menyetujui / menolak submission yang member_confirmation_status == accepted (untuk baris anggota; leader mengikuti aturan yang sama atau disederhanakan sesuai keputusan tim). Hal ini menjaga konsistensi dengan alur approval yang ada tanpa mengganti pola utama review_status per form_answer.
Setelah fitur ini tersedia, sistem mendukung:
- Pendaftaran individu dan bundle (bergantung
registration_mode). - Undangan & konfirmasi anggota dengan kedaluwarsa berbasis cek saat akses.
- Validasi anggota (email terdaftar, batas ukuran tim).
- Absensi & QR per individu tanpa mengubah inti alur scan.
- Duplikasi field dinamis lewat metadata
duplicatable. - Approval per peserta tetap pada model yang ada.
- Email broadcast: pengingat event mendatang, pemberitahuan perubahan jadwal/lokasi/deskripsi.
- Pelaporan lanjutan (filter lanjutan, agregasi, unduhan format tambahan).
- Optimasi skala dan observabilitas (metrik, rate limit, dsb.) sesuai kebutuhan operasional.
| Aspek | Requirement |
|---|---|
| Keamanan | Autentikasi sesi/Laravel; pembatasan route per peran; validasi server-side untuk semua input form dinamis; upload file dibatasi tipe dan ukuran. |
| Performa | Pengiriman email tidak menghalangi response HTTP utama (queue). |
| Audit email | Riwayat pengiriman disimpan di email_logs (status, error, sent_at, korelasi ke submission/event/user). |
| Audit absensi | Rekaman scan (admin, waktu) menjadi sasaran akuntabilitas; persistensi mengikuti desain migrasi ketika tabel absensi ditambahkan. |
| Kompatibilitas | Flow scan QR dapat berjalan pada browser umum di desktop/mobile dengan akses kamera (dengan izin pengguna). |
| Kualitas kode | Formatter PHP (Laravel Pint, PSR-12) dan Prettier untuk front-end sesuai konfigurasi proyek. |
Deviasi dari Project Brief PDF bagian Blade/Livewire: UI aplikasi utama dibangun dengan Vue + Inertia, bukan Livewire/Volt sebagai lapisan UI utama.
- Laravel 12, PHP 8.2+.
- Endpoint/halaman melalui Inertia (response
Inertia::render) dan validasi Form Request / controller sesuai pola proyek. - Basis data: MySQL / MariaDB; Redis untuk cache/antrean sesuai konfigurasi.
- Laravel Queue untuk pengiriman email.
- Pustaka QR Code:
endroid/qr-code(sesuaicomposer.json) untuk generate gambar / payload QR. - Web server: sesuai deployment tim (mis. Frankenphp/Octane seperti yang tercatat di README proyek).
- Vue 3 (Composition API,
<script setup>) dan Inertia.js untuk navigasi dan state halaman. - Tailwind CSS dan komponen UI mengikuti konvensi repositori (mis. pola Shadcn-Vue di
resources/js/components/ui— lihat front-end rules). - Bukan Livewire/Volt untuk fitur-fitur di atas; migrasi dari modul Livewire yang masih ada adalah bagian dari pekerjaan berkelanjutan (lihat milestone.md).
- Filament (paket ada di
composer.json) dapat dipakai untuk panel admin terpisah atau dikurangi seiring konsolidasi UI di Vue — keputusan eksplisit disarankan di milestone M0 agar tidak ada duplikasi UX.
- Node.js & npm untuk build front-end (Vite).
- Docker untuk lingkungan tim (folder
docker/). - Dokumentasi kode: PHPDoc dan Markdown sesuai kebiasaan proyek.
Entitas utama mengikuti migrasi aktual di database/migrations/ (Laravel). Nama tabel di bawah ini menggantikan ringkasan brief yang sebelumnya memakai penamaan event_form_* / jadwal terpisah — skema repositori memakai forms / form_fields / form_answers, tanpa tabel event_schedules atau event_attendances pada migrasi saat ini (fitur absensi QR tetap pada rencana produk; payload QR mengacu ID submission / form_answers).
| Entitas / tabel | Peran |
|---|---|
users |
Akun pengguna (UUID); google_id, github_id untuk OAuth; peran Admin/Member lewat Spatie Laravel Permission (permissions, roles, pivot terkait). |
events |
Metadata event: tanggal, lokasi, kuota, registered_count, banner, harga, status, kategori & sesi (tipe teks panjang setelah migrasi perluasan kolom), jendela pendaftaran; soft delete. |
forms |
Form pendaftaran terikat event_id; judul, deskripsi, visible_for (JSON), closed_at, banner_url (longText, nullable), banner_caption (nullable), metadata JSON (nullable) untuk registration_mode (single | bundle, dan varian legacy mode tim), max_team_size / team_size, dan konfigurasi terkait; soft delete. |
form_fields |
Definisi field per form_id: input_type (enum/string sesuai driver: input, selectInput, textarea, datePicker, fileUpload, radio, checkbox), label, nama internal, metadata JSON (opsi, validasi, duplicatable untuk bundle, dsb.), is_append (boolean: field boleh disunting anggota saat validasi undangan), order; soft delete. |
form_answers |
Satu baris submission per peserta; jawaban answers JSON; foreign key ke users dan forms; untuk tim/bundle: relasi antar baris lewat group_token, registration_role, member_confirmation_status, invited_email, member_confirmed_at, invitation_expired_at (dan/atau kolom §4.2 seperti leader_form_answer_id / status_confirmation_member yang diselaraskan di migrasi); constraint unik (user_id, form_id) dapat memerlukan peninjauan khusus untuk bundle — disepakati di implementasi agar satu akun tidak bentrok antar peran dalam satu form. Satu submit bundle menciptakan banyak baris dalam satu transaksi. |
email_logs |
Audit pengiriman email: form_answer_id (nullable, nullOnDelete), event_id, user_id, recipient_email, status, error_message, sent_at; indeks pada form_answer_id dan (event_id, status). |
Infra Laravel (bukan domain bisnis inti, tetapi ada di migrasi): jobs, job_batches, failed_jobs, cache, cache_locks, sessions, password_reset_tokens.
- Alur Admin: buat event → bangun form → buka pendaftaran → lihat peserta → scan QR → lihat statistik kehadiran berjalan tanpa error kritikal pada jalur utama.
- Alur Member: daftar → isi form dinamis → terima email dengan QR → hadir dan tercatat absensi; mode tim: anggota yang diundang membuka halaman konfirmasi, menyesuaikan data (field
is_append), lalu memvalidasi sebelum mengikuti alur berikutnya; mode bundle (§4.3): leader menambah anggota dengan fieldduplicatable, sistem membuat banyakform_answerdengangroup_tokensama; anggota menyelesaikan konfirmasi undangan sebelum submission memenuhi syarat review admin. - Kebijakan akses: Member tidak mengakses panel admin; Admin tidak mengubah data orang lain tanpa alur yang disengaja (sesuai desain permission).
- Dokumentasi operasional minimal: cara set SMTP, queue worker, dan menjalankan build front-end (mengacu README dan instalasi).
- Aplikasi mobile native (hanya web yang responsif).
- Pembayaran / gateway (kecuali ditambahkan di PRD revisi).
- Integrasi kalender pihak ketiga (Google Calendar, dsb.) kecuali ditambahkan kemudian.
- Milestone & fase pengiriman
- README proyek
- Skema basis data: folder migrasi Laravel
../database/migrations/ - Project Brief D-Form v2 (PDF)