# 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 `jobs` table) - [ ] 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 ```bash 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 penuh - [ ] `StoreProgramRequest` + `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, download - [ ] `QrCodeService` — 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, export - [ ] `ParticipantImport` service — 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, externalRegister - [ ] `AttendanceService` — 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` — CRUD - [ ] `QuestionController` — CRUD, reorder - [ ] `ProgramQuestionnaireController` — 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 generate - [ ] `CertificateService` — image overlay (Intervention Image), PDF wrap (DOMPDF) - [ ] Font auto-scale berdasarkan panjang nama - [ ] Preview template dalam admin - [ ] Test generate dengan nama sample - [ ] `GenerateCertificateJob` — queue job - [ ] `AttendanceCheckController` — public semak kehadiran - [ ] `CertificateController` — 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 job - [ ] `BlastCertificateLinkJob` — mass email untuk semua peserta hadir - [ ] Admin trigger: "Email semua peserta" button - [ ] `email_logs` rekod 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_logs` rekod 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 ```env 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 ```bash # 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 |