- Laravel 13.9 + PHP 8.5 + MySQL - Bootstrap 5.3 + jQuery 3.7 + Chart.js (replacing Alpine/Tailwind) - Packages: intervention/image, dompdf, simple-qrcode, league/csv, laravel/breeze, laravel/boost - 17 database migrations: users, programs, qr_codes, participants, attendances, certificates, questionnaires, email_logs, audit_logs - 13 Eloquent models with full relationships - Admin layout (Bootstrap 5 sidebar) + public layout (mobile-first) - Rate limiters: checkin (60/min), certificate (30/min) - Admin seeder: admin@mbip.gov.my - Storage directories + symlink configured Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
13 KiB
13 KiB
eCert MBIP — Execution Plan (Phased)
Pre-requisites (Sebelum Fasa 1)
- Sahkan "Laravel Boost" — package apa yang dimaksudkan
- Sahkan MySQL server credentials (host, port, db name, user, password)
- Sahkan SMTP settings untuk email
- Sahkan storage path boleh write
- Sahkan Git SSH key berfungsi ke git.mbip.my
Fasa 1: Foundation & Authentication
Anggaran: 1-2 jam
Tasks
- Install Laravel 12 via
composer create-project - Setup
.env(DB, queue, mail, app URL) - Install Laravel Breeze (Blade stack)
- Install Composer packages (intervention/image, simple-qrcode, dompdf, league/csv)
- Buat semua 15 migrations
- Buat admin layout Blade (sidebar, navbar) dengan Bootstrap 5
- Buat public layout Blade (mobile-first) dengan Bootstrap 5
- Setup Queue (database driver → create
jobstable) - Setup Storage symlink
- Buat Seeder: admin user
- Commit:
chore: initial Laravel project setup - Commit:
feat: admin authentication and layout
Files Utama
app/Http/Controllers/Admin/DashboardController.php
resources/views/layouts/admin.blade.php
resources/views/layouts/public.blade.php
resources/views/admin/dashboard.blade.php
database/migrations/* (15 migration files)
database/seeders/AdminSeeder.php
Commands Selepas Fasa 1
php artisan migrate
php artisan db:seed --class=AdminSeeder
php artisan storage:link
php artisan queue:table && php artisan migrate
Manual Test
- Login admin berjaya
- Dashboard papar (walaupun kosong)
- Logout berjaya
Fasa 2: Program Management
Anggaran: 2-3 jam
Tasks
ProgramController— CRUD penuhStoreProgramRequest+UpdateProgramRequest— validation- Program index view (table + badge status)
- Program create/edit form
- Program show view (tab: details, participants, qr, template, questionnaire, stats)
- Status management (draft → published → closed)
- Protect delete jika ada kehadiran
- Commit:
feat: program management
Files Utama
app/Http/Controllers/Admin/ProgramController.php
app/Http/Requests/Admin/StoreProgramRequest.php
app/Http/Requests/Admin/UpdateProgramRequest.php
app/Models/Program.php
resources/views/admin/programs/*
Manual Test
- Tambah program
- Edit program
- Tukar status
- Cuba padam program yang ada kehadiran (mesti gagal)
Fasa 3: QR Code Generation
Anggaran: 1-2 jam
Tasks
QrCodeController— generate, show, downloadQrCodeService— generate token, buat QR image, simpan storage- QR code preview dalam admin
- Download QR sebagai PNG
- Commit:
feat: qr code generation
Files Utama
app/Http/Controllers/Admin/QrCodeController.php
app/Services/QrCodeService.php
app/Models/ProgramQrCode.php
resources/views/admin/programs/qr.blade.php
Manual Test
- Generate QR code untuk program
- QR code papar dalam admin
- Download QR sebagai PNG berjaya
- Scan QR code → bawa ke /p/{token} (walaupun page belum siap)
Fasa 4: Participant Management & CSV Import
Anggaran: 2-3 jam
Tasks
ParticipantController— add manual, import, list, exportParticipantImportservice — parse CSV, validate, bulk insert- Import summary: berjaya, duplicate, gagal
- Export CSV senarai peserta
- Commit:
feat: participant management
Files Utama
app/Http/Controllers/Admin/ParticipantController.php
app/Services/ParticipantImportService.php
app/Models/Participant.php
app/Models/ProgramParticipant.php
resources/views/admin/programs/participants/*
CSV Template Headers
name,no_kp,email,phone,agency
Manual Test
- Tambah peserta manual
- Import CSV (normal)
- Import CSV dengan duplicate (summary papar betul)
- Import CSV dengan row kosong/invalid
- Export CSV peserta
Fasa 5: Public Check-in Flow
Anggaran: 3-4 jam
Tasks
CheckinController— show, staffCheckin, externalRegisterAttendanceService— rekod kehadiran, cegah duplicate- Public check-in page (mobile-first, Bootstrap 5)
- Staff check-in form + validation
- Walk-in registration form + validation
- Status page selepas check-in
- Rate limiting pada routes
- Commit:
feat: participant registration and attendance
Files Utama
app/Http/Controllers/Public/CheckinController.php
app/Services/AttendanceService.php
resources/views/public/checkin/*
Manual Test
- Scan QR → buka page check-in
- Staff check-in dengan no_kp betul
- Staff check-in dengan no_kp salah
- Staff check-in yang sudah hadir (mesej duplicate)
- Walk-in daftar baru
- Walk-in dengan no_kp sama → error duplicate
- Test rate limit (cuba submit banyak kali)
Fasa 6: Questionnaire Management
Anggaran: 3-4 jam
Tasks
QuestionnaireSetController— CRUDQuestionController— CRUD, reorderProgramQuestionnaireController— attach, confirm, detach- Public questionnaire form (semua jenis soalan)
- Submit response + answers
- Semak sudah jawab (cegah double submit)
- Commit:
feat: questionnaire management
Files Utama
app/Http/Controllers/Admin/QuestionnaireSetController.php
app/Http/Controllers/Admin/QuestionController.php
app/Http/Controllers/Admin/ProgramQuestionnaireController.php
app/Http/Controllers/Public/QuestionnaireController.php
app/Models/QuestionnaireSet.php
app/Models/QuestionnaireQuestion.php
app/Models/QuestionnaireResponse.php
app/Models/QuestionnaireAnswer.php
resources/views/admin/questionnaires/*
resources/views/public/questionnaire/*
Manual Test
- Cipta questionnaire set
- Tambah pelbagai jenis soalan
- Attach questionnaire ke program
- Confirm questionnaire
- Peserta boleh akses questionnaire (selepas check-in)
- Submit soalselidik berjaya
- Cuba submit semula → error (sudah jawab)
Fasa 7: Certificate Template & Generation
Anggaran: 4-5 jam
Tasks
CertificateTemplateController— upload, config, preview, test generateCertificateService— image overlay (Intervention Image), PDF wrap (DOMPDF)- Font auto-scale berdasarkan panjang nama
- Preview template dalam admin
- Test generate dengan nama sample
GenerateCertificateJob— queue jobAttendanceCheckController— public semak kehadiranCertificateController— public, show, download gate- Certificate gate: soalselidik dijawab + masa download aktif
- Rekod downloaded_at dan download_count
- Commit:
feat: certificate template upload - Commit:
feat: certificate generation and download
Files Utama
app/Http/Controllers/Admin/CertificateTemplateController.php
app/Http/Controllers/Public/CertificateController.php
app/Http/Controllers/Public/AttendanceCheckController.php
app/Services/CertificateService.php
app/Jobs/GenerateCertificateJob.php
app/Models/Certificate.php
app/Models/CertificateTemplate.php
resources/views/admin/programs/template/*
resources/views/public/certificate/*
resources/fonts/ ← bundel NotoSans TTF
Manual Test
- Upload template imej
- Set koordinat nama + no_kp
- Preview template
- Test generate → papar hasil
- Peserta scan QR (masa download) → semak kehadiran
- Peserta download sijil (selepas soalselidik)
- Peserta cuba download sebelum jawab soalselidik → redirect
Fasa 8: Email & Queue
Anggaran: 2-3 jam
Tasks
CertificateReadyMail— Mailable class- Email template Blade (HTML + text)
SendCertificateEmailJob— queue jobBlastCertificateLinkJob— mass email untuk semua peserta hadir- Admin trigger: "Email semua peserta" button
email_logsrekod semua attempt- Commit:
feat: email certificate link
Files Utama
app/Mail/CertificateReadyMail.php
app/Jobs/SendCertificateEmailJob.php
app/Jobs/BlastCertificateLinkJob.php
resources/views/emails/certificate-ready.blade.php
resources/views/emails/certificate-ready.text.blade.php
Manual Test
- Trigger email untuk satu peserta
- Email diterima dengan link betul
- Link dalam email bawa ke certificate page
email_logsrekod status sent- Test email SMTP failure → status failed dalam email_logs
Fasa 9: Statistics Dashboard
Anggaran: 2-3 jam
Tasks
StatisticsController— dashboard + per-program- Dashboard cards (counts)
- Per-program breakdown (attendance by type, session, soalselidik)
- Chart.js integration (rating chart, attendance trend)
- Export CSV statistik
- Commit:
feat: admin statistics dashboard
Files Utama
app/Http/Controllers/Admin/StatisticsController.php
app/Http/Controllers/Admin/DashboardController.php (update)
resources/views/admin/dashboard.blade.php (update)
resources/views/admin/programs/statistics.blade.php
Manual Test
- Dashboard papar kiraan betul
- Per-program statistik betul
- Chart.js render tanpa error
- Export CSV berjaya
Fasa 10: Security Hardening & Audit Log
Anggaran: 1-2 jam
Tasks
AuditLogService— log admin actions- Hook audit log ke key events
- Review semua route ada proper validation
- Semak file upload hanya benarkan jpg/png
- Semak no_kp tidak expose dalam URL
- Rate limiting final review
- Commit:
feat: audit logging and security hardening
Fasa 11: Testing
Anggaran: 3-4 jam
Tasks
- Setup Pest
- Admin can create program
- QR token valid opens check-in page
- Staff pre-registered can check-in
- External participant can register and check-in
- Duplicate no_kp for same program rejected
- Participant cannot download certificate before questionnaire
- Participant can download certificate after questionnaire
- Admin can view basic statistics
- Commit:
test: core attendance and certificate flow
Environment Variables Checklist
APP_NAME="eCert MBIP"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ecert_mbip
DB_USERNAME=root
DB_PASSWORD=
QUEUE_CONNECTION=database
MAIL_MAILER=smtp
MAIL_HOST=
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@mbip.gov.my
MAIL_FROM_NAME="eCert MBIP"
FILESYSTEM_DISK=local
SESSION_DRIVER=database
SESSION_LIFETIME=120
Git Commit Sequence
# Fasa 1
git commit -m "chore: initial Laravel project setup"
git commit -m "feat: admin authentication and layout"
# Fasa 2
git commit -m "feat: program management"
# Fasa 3
git commit -m "feat: qr code generation"
# Fasa 4
git commit -m "feat: participant management and csv import"
# Fasa 5
git commit -m "feat: public check-in flow and attendance"
# Fasa 6
git commit -m "feat: questionnaire management"
# Fasa 7
git commit -m "feat: certificate template upload"
git commit -m "feat: certificate generation and download"
# Fasa 8
git commit -m "feat: email certificate link"
# Fasa 9
git commit -m "feat: admin statistics dashboard"
# Fasa 10
git commit -m "feat: audit logging and security hardening"
# Fasa 11
git commit -m "test: core attendance and certificate flow"
# Production push (HANYA apabila diarahkan)
# git push -u origin master
Risiko Teknikal
| # | Risiko | Kemungkinan | Impak | Mitigasi |
|---|---|---|---|---|
| 1 | GD font rendering — perlu TTF, tidak ada jika lupa bundel | Sederhana | Tinggi | Bundel Noto Sans TTF dalam resources/fonts/, test awal |
| 2 | Queue worker tidak berjalan di Windows production | Tinggi | Tinggi | Guna Task Scheduler Windows atau switch ke Linux server |
| 3 | CSV import gagal handle UTF-8 BOM (Excel export) | Tinggi | Sederhana | Detect dan strip BOM dalam import service |
| 4 | SMTP kerajaan — TLS/SSL config berbeza | Sederhana | Tinggi | Test awal dengan real SMTP, fallback ke log driver |
| 5 | Concurrent check-in race condition | Rendah | Sederhana | DB unique constraint + try-catch pada insert |
| 6 | PDF certificate kualiti rendah jika gambar template kecil | Sederhana | Sederhana | Minta admin upload template minimum 1240px lebar |
| 7 | "Laravel Boost" tidak jelas | — | Rendah | Perlu penjelasan sebelum install |
| 8 | Session tidak persistent untuk public (no login) | — | Sederhana | Guna certificate token + signed URL, bukan session |
| 9 | Storage permissions pada Windows production | Sederhana | Tinggi | Pastikan storage/ dan bootstrap/cache/ writable |
| 10 | PDPA — no_kp exposure dalam log/debug | Rendah | Tinggi | Disable query log production, redact no_kp dalam audit_logs |