Files
cabutan-bertuah-koipb/README.md
2026-06-24 18:30:00 +08:00

144 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Sistem Kehadiran & Cabutan Bertuah MAT — KOIPB
Aplikasi web **Laravel 12 + MySQL** untuk Mesyuarat Agung Tahunan (MAT) Koperasi Iskandar Puteri Berhad (KOIPB): rekod kehadiran anggota di kaunter dan jalankan cabutan bertuah (wheel of fortune) di skrin projektor.
Jangkaan kehadiran: 150200 orang.
---
## Teknologi
| Lapisan | Pilihan | Sebab |
|---|---|---|
| Framework | Laravel 12 (PHP 8.5) | Stabil, lengkap |
| Pangkalan data | MySQL 8 | Seperti diminta |
| UI | Blade + Bootstrap 5 (CDN) | Praktikal, responsive, tiada langkah build |
| Interaksi | jQuery + SweetAlert2 | Live search, alert kemas |
| Roda + confetti | Canvas asli + `canvas-confetti` | Animasi smooth, projector-friendly |
| Peranan/akses | `spatie/laravel-permission` | Role-based standard industri |
| Import/Export | `openspout/openspout` (CSV/XLSX) | Serasi PHP 8.5 (maatwebsite/excel TIDAK serasi PHP 8.5) |
| PDF laporan | `barryvdh/laravel-dompdf` | Eksport PDF mudah |
> **Nota penting:** `maatwebsite/excel` tidak boleh dipasang pada PHP 8.5 (bergantung pada phpspreadsheet 1.x yang dihadkan kepada PHP < 8.5). Sebab itu sistem guna **openspout** terus untuk baca/tulis CSV & XLSX.
---
## Setup Local
Prasyarat: PHP 8.5, Composer, MySQL berjalan.
```bash
# 1. Pasang dependency
composer install
# 2. Fail .env sudah disediakan. Pastikan tetapan DB betul:
# DB_DATABASE=koipb_mat DB_USERNAME=root DB_PASSWORD=1234
# Cipta database jika belum ada:
# CREATE DATABASE koipb_mat CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 3. Jana app key (jika .env belum ada APP_KEY)
php artisan key:generate
# 4. Migrate + seed (200 anggota, 21 item hadiah, 3 user)
php artisan migrate:fresh --seed
# 5. Jalankan
php artisan serve
# Buka http://127.0.0.1:8000
```
**Logo:** letakkan logo sebenar di `public/images/logo-koipb.jpg` (kini ada placeholder).
---
## Login Default
Semua kata laluan: **`password`**
| Peranan | E-mel | Akses |
|---|---|---|
| Admin | `admin@koipb.test` | Semua modul |
| Petugas Kaunter | `kaunter@koipb.test` | Kaunter kehadiran + senarai kehadiran |
| Petugas Cabutan / Urusetia | `cabutan@koipb.test` | Skrin cabutan + laporan |
---
## Route Penting
| URL | Modul | Akses |
|---|---|---|
| `/login` | Log masuk | Semua |
| `/dashboard` | Statistik event | Semua login |
| `/kaunter` | Kaunter kehadiran (live search) | Admin, Kaunter |
| `/kehadiran` | Senarai kehadiran + tapis + export | Admin, Kaunter |
| `/cabutan` | **Skrin cabutan (roda)** — projector | Admin, Cabutan |
| `/laporan` | Laporan + export Excel/PDF | Admin, Cabutan |
| `/admin/anggota` | Urus + import anggota | Admin |
| `/admin/hadiah` | Urus + import hadiah | Admin |
| `/admin/tetapan` | Tetapan sesi + reset event | Admin |
| `/admin/audit` | Audit trail | Admin |
Endpoint AJAX cabutan: `POST /cabutan/spin`, `/cabutan/confirm`, `/cabutan/batal`; `GET /cabutan/pool`.
---
## Cara Import CSV / XLSX
Admin → **Anggota / Hadiah → Import**. Format CSV atau XLSX. Baris pertama = nama lajur (header dinormalkan: huruf kecil + underscore). Templat contoh boleh dimuat turun dari skrin import (atau `public/samples/`).
**Anggota** — lajur: `no_anggota, no_pekerja, no_kp, nama, jabatan, bahagian, telefon, status_aktif`
- `nama` wajib; `no_kp` & `no_pekerja` unik; `status_aktif` default = aktif.
- Duplicate (dalam fail atau dalam DB) dilangkau — ringkasan **berjaya / duplicate / gagal** dipaparkan & disimpan dalam log import.
**Hadiah** — lajur: `kod_hadiah, nama_hadiah, kategori, nilai_anggaran, susunan_cabutan, kuantiti`
- Jika `kuantiti > 1`, sistem jana item berasingan (cth `Hamper #1``Hamper #5`).
---
## Cara Demo Cabutan (end-to-end)
1. Log masuk **admin** → import/seed anggota & hadiah (seeder sudah sediakan).
2. Log masuk **kaunter** (`/kaunter`) → cari nama/no KP/no pekerja → **Rekod Hadir**.
- Hanya anggota berdaftar boleh direkod; bukan anggota → mesej amaran.
- Double attendance dihalang (paras DB + UI).
3. Log masuk **cabutan** (`/cabutan`) di projektor:
- Pilih hadiah ikut susunan → **SPIN** (roda berpusing pada peserta hadir).
- Calon pemenang dipaparkan di panel kanan.
- **Confirm Pemenang** → panel hijau/emas + confetti + "TAHNIAH!".
- **Batal / Tiada Di Dewan** → panel merah; hadiah jadi *perlu cabut semula*.
- **Cabut Semula** → spin sekali lagi untuk hadiah sama (attempt bertambah).
4. **Laporan** (`/laporan`) → pemenang, hadiah belum dicabut, cabutan dibatalkan, kehadiran → export Excel/PDF.
---
## Rules / Jaminan Sistem
- Hanya anggota **hadir** masuk pool cabutan.
- Pemenang **disahkan** dikeluarkan dari pool — tidak boleh menang lagi.
- Cabutan **dibatalkan** boleh dicabut semula; setting default kekalkan peserta batal dalam pool.
- **Race condition / double-confirm** dihalang di paras DB melalui unique index (`confirmed_member_id`, `confirmed_prize_id`) + transaction `lockForUpdate`.
- **Double attendance** dihalang oleh unique constraint `attendance_records.member_id`.
- No KP **dimask** di UI biasa (cth `850101-01-****`).
- Semua tindakan penting (login, hadir, spin, confirm, batal, import, reset) direkod dalam **audit trail**.
---
## Skema Database
`users`, `roles`/`permissions` (spatie), `members`, `attendance_records`, `prizes`,
`draw_sessions`, `draw_results`, `import_logs`, `audit_logs`.
Index utama: `members.no_kp`, `members.no_pekerja`, `members.nama`,
`attendance_records.member_id` (unique), `prizes.draw_order`,
`draw_results.(prize_id|member_id|status)`, unique `confirmed_member_id` & `confirmed_prize_id`.
---
## Ujian
```bash
php artisan test
```
Liputan: kehadiran tidak duplicate, hanya hadir boleh masuk cabutan, pemenang disahkan tidak boleh menang lagi (paras servis + DB), cabutan batal boleh redraw, akses ikut peranan, import duplicate/kuantiti. **(18 ujian, semua lulus.)**