first
This commit is contained in:
143
README.md
Normal file
143
README.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# 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.)**
|
||||
Reference in New Issue
Block a user