- PHP 42%
- JavaScript 37.6%
- CSS 13.6%
- Shell 6.8%
| data | ||
| public | ||
| src | ||
| tests | ||
| .gitignore | ||
| PRD.md | ||
| README.md | ||
| seed.php | ||
Undian Parallax
Aplikasi undian (giveaway/raffle) PHP native + SQLite dengan latar parallax. Ringan dan portabel: cukup copy folder, tanpa instalasi database server.
Fitur
- Halaman Undian (default) dengan efek parallax dan animasi rol + ledakan saat menarik pemenang. Hanya menampilkan nomor undian dan nama lengkap pemenang.
- Pendaftaran peserta manual (nomor undian, nama, alias/sebutan, data identitas & domisili).
- Dasbor Admin dengan sub-tab: Total Peserta / Belum Menang / Sudah Menang, plus pencarian dan pagination.
- Alias / sebutan: diisi manual atau dibuat otomatis bila kosong. Hanya nomor undian yang wajib unik; alias, nomor identitas, dan data lain boleh sama.
- Banner undian: unggah gambar berukuran tepat 1200 × 400 px (JPG/PNG/WEBP/GIF, maks 5 MB) untuk ditampilkan di panggung undian.
- Judul aplikasi: ubah judul/brand dari Dasbor Admin.
- Teks footer: ubah teks footer halaman undian dari Dasbor Admin (kosongkan untuk menyembunyikan).
- Durasi undian: atur lama pengacakan 10–30 detik dari Dasbor Admin (default 20 detik), lengkap dengan hitungan mundur di panggung undian.
- Eliminasi pemenang: status yang membatalkan kemenangan dan mengeluarkan peserta dari undian; data peserta tetap tersimpan.
- Hapus peserta: menghapus data peserta yang salah input (riwayat kemenangannya ikut terhapus).
- Detail peserta: tombol untuk melihat seluruh data peserta (termasuk hadiah bila sudah menang).
- Zona Bahaya: reset (truncate) seluruh data peserta beserta riwayat pemenang event ini.
- Import CSV (Excel: Save As CSV).
- Undian diproses server-side secara acak dan transaksional.
- Efek suara:
drums.mp3diputar selama pengacakan sesuai durasi undian (10–30 detik; melompat ke detik 35 satu detik sebelum selesai), lalutepuktangan.mp33 detik setelah nama pemenang muncul. - Kembang api: animasi fireworks.js setelah undian selesai, berlangsung selama audio tepuk tangan diputar, bersamaan dengan tepuk tangan (dimatikan bila pengguna memilih reduce motion).
Tech Stack
- Frontend: HTML5, CSS3, JavaScript vanilla + parallax.js dan fireworks.js, keduanya disimpan lokal di
public/assets/(tanpa CDN, bisa jalan offline). - Backend: PHP native (diuji pada PHP 7.4), ekstensi
pdo_sqlite. - Database: SQLite (
data/database.sqlite, dibuat otomatis).
Struktur
undian/
├── public/ <- Document root
│ ├── index.php <- Halaman Undian (Basic Auth)
│ ├── admin.php <- Halaman Admin (Basic Auth)
│ ├── assets/{style.css,app.js,parallax.min.js,fireworks.js,particles.gif,drums.mp3,tepuktangan.mp3}
│ ├── api/ <- Endpoint REST
│ │ ├── event.php GET info event + banner
│ │ ├── participants.php GET daftar peserta (paginated) + detail + statistik
│ │ ├── register.php POST daftar peserta
│ │ ├── draw.php POST tarik pemenang
│ │ ├── eliminate.php POST eliminasi (batalkan menang) / aktifkan
│ │ ├── delete.php POST hapus peserta
│ │ ├── reset.php POST reset data peserta
│ │ ├── banner.php POST unggah banner
│ │ └── import.php POST import CSV
│ └── uploads/ <- Gambar banner (.htaccess menolak skrip)
├── src/config.php PDO SQLite, auto-migrasi, helper, CORS
├── data/ database.sqlite + .htaccess (di luar web root)
├── seed.php isi data dummy
├── tests/smoke.sh smoke test end-to-end
└── PRD.md
Menjalankan
Document root harus diarahkan ke folder public/.
Development cepat:
php -d upload_max_filesize=200M -d post_max_size=201M -d max_input_time=600 -S localhost:8000 -t public
Buka http://localhost:8000. Database dan tabel dibuat otomatis saat request pertama.
Import CSV besar butuh batas upload PHP yang lebih tinggi.
public/.user.inisudah menyetelnya untuk PHP-FPM/CGI. Dev serverphp -Stidak membaca.user.ini, jadi pakai perintah-ddi atas (atauphp.ini). Apache mod_php: setphp_value upload_max_filesize 200Mdanphp_value post_max_size 201Mdi vhost/.htaccess. Nginx:client_max_body_size 201m;.
index.php— halaman undian (default). Dilindungi Basic Auth yang sama dengan admin, jadi browser cukup login sekali.admin.php— halaman admin (pendaftaran, banner, import, dasbor). Dilindungi Basic Auth; default useradmin, passwordadmin123(ubahADMIN_USER/ADMIN_PASSdisrc/config.php).
Produksi (Apache/Nginx): set document root ke public/. Pastikan folder data/ tidak dapat diakses publik.
Data dummy
php seed.php 1000 # membuat 1000 peserta dummy (default 25)
Uji
bash tests/smoke.sh # register -> list -> draw -> habis
REST API
| Method | Endpoint | Fungsi |
|---|---|---|
| GET | /api/event.php?event_id=1 |
Info event + banner |
| POST | /api/event.php |
Ubah judul aplikasi / durasi undian / footer (JSON: app_title, draw_duration 10–30, footer_text) |
| GET | /api/participants.php?event_id=1&page=1&filter=all&q= |
Daftar peserta per halaman (10 baris). filter: all/wait/win; q: cari nomor undian, alias, nama, HP, kota, hadiah. Hanya kolom tabel yang dikirim. |
| GET | /api/participants.php?id=123&event_id=1 |
Detail lengkap satu peserta + hadiah bila menang (dipakai modal Detail) |
| GET | /api/participants.php?roll=1&event_id=1 |
Sampel maksimal 30 peserta yang belum menang (id, no_unik, nama) untuk animasi rol halaman undian |
| POST | /api/register.php |
Daftar peserta (JSON) |
| POST | /api/draw.php |
Tarik pemenang (JSON: hadiah; respons berisi id, no_unik, nama) |
| POST | /api/eliminate.php |
Eliminasi pemenang / aktifkan peserta (JSON: participant_id, eliminated 0/1) |
| POST | /api/delete.php |
Hapus peserta (JSON: participant_id) |
| POST | /api/reset.php |
Reset seluruh peserta + pemenang event |
| POST | /api/banner.php |
Unggah banner (multipart: image) |
| POST | /api/import.php |
Import peserta (multipart: file CSV) |
Format Import CSV
Baris pertama berisi nama kolom (boleh tidak berurutan; delimiter , atau ;).
| Kolom | Wajib | Keterangan |
|---|---|---|
no_unik |
Ya | Nomor undian, unik per event. Kosong = dibuat otomatis. |
nama |
Ya | Nama lengkap peserta. |
alias |
Tidak | Sebutan; dibuat otomatis bila kosong. Boleh sama. |
no_hp |
Tidak | Nomor kontak. |
identitas_jenis |
Tidak | KTP / SIM / Paspor / Lainnya. |
identitas_no |
Tidak | Nomor identitas. Boleh sama. |
alamat_domisili |
Tidak | Alamat lengkap. |
negara |
Tidak | Negara asal. |
provinsi |
Tidak | Provinsi. |
kota |
Tidak | Kota / Kabupaten. |
Contoh:
no_unik,nama,alias,no_hp,identitas_jenis,identitas_no,alamat_domisili,negara,provinsi,kota
001,Budi Santoso,Elang Cepat,08123456789,KTP,3201012345670001,Jl. Merdeka No. 1,Indonesia,Jawa Barat,Bandung
002,Siti Aminah,,08129876543,Paspor,A1234567,,Indonesia,DKI Jakarta,Jakarta
no_unik wajib unik per event; baris dengan nomor undian duplikat dilewati dan dilaporkan, baris lain tetap diproses. Kolom lain (alias, nomor identitas, dll.) boleh duplikat.
Berkas maksimal 200 MB dan seluruh baris diimpor (tanpa batas jumlah baris). File dibaca per baris, jadi tidak perlu menaikkan memory_limit. Untuk file besar, naikkan juga batas waktu request di web server (mis. fastcgi_read_timeout) bila impor memakan lebih dari satu menit.
Keamanan
- Semua query memakai PDO prepared statements.
- File
data/database.sqlitediletakkan di luar document root dan diproteksi.htaccess. - Unggahan banner divalidasi berdasarkan isi file (JPG/PNG/WEBP/GIF, maks 5 MB) dan folder
uploads/menolak eksekusi skrip. - CORS dapat dibatasi lewat
ALLOWED_ORIGINSdisrc/config.php(default: same-origin).
Catatan
- Halaman
admin.phpdilindungi Basic Auth (ADMIN_USER/ADMIN_PASSdisrc/config.php). Endpoint API masih terbuka, jadi proteksi API juga diperlukan sebelum dipakai publik. - Import hanya mendukung CSV. Untuk Excel, simpan sebagai CSV. Dukungan
.xlsxlangsung memerlukan library tambahan. - Nomor undian unik per event dijamin oleh constraint
UNIQUE(event_id, no_unik).