2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00
2026-06-24 18:30:00 +08:00

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.

# 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 #1Hamper #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

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

Description
No description provided
Readme 219 KiB
Languages
PHP 50.2%
Blade 44.6%
CSS 2.5%
Shell 1.6%
Dockerfile 0.9%
Other 0.2%