144 lines
5.7 KiB
Markdown
144 lines
5.7 KiB
Markdown
# 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: 150–200 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.)**
|