# 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.)**