chore: initial Laravel 13 project setup for eCert MBIP
- 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>
This commit is contained in:
434
docs/execution-plan.md
Normal file
434
docs/execution-plan.md
Normal file
@@ -0,0 +1,434 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user