# Progress Log ## 2026-05-28 - Phase 0: Project Inspection and Planning Status: Completed Completed: - Inspected workspace. - Confirmed workspace is currently empty. - Confirmed no git repository is initialized. - Confirmed no existing Laravel application is present. - Confirmed no `AGENTS.md` project instruction file is present. - Checked local tooling: - PHP 8.5.1 available. - Composer 2.9.4 available. - Node 25.1.0 available. - `npm.cmd` 11.14.1 available. - `npm` PowerShell shim blocked by execution policy. - MySQL CLI not available in PATH. - Created Phase 0 planning documentation. Changed files: - `docs/00-project-brief.md` - `docs/01-development-plan.md` - `docs/02-architecture.md` - `docs/03-database-design.md` - `docs/04-rbac-permission-matrix.md` - `docs/05-workflow-design.md` - `docs/06-ui-ux-plan.md` - `docs/07-testing-plan.md` - `docs/08-deployment-notes.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Pending items: - Initialize or create Laravel project in Phase 1. - Confirm exact Laravel version during install. - Confirm MySQL server credentials and connectivity. - Verify package compatibility during Composer/npm installation. - Initialize git repository if desired. Next step: - Proceed to Phase 1 only after approval: Laravel setup, authentication scaffolding, Bootstrap 5 base layout, roles, permissions, and base dashboards. Manual testing for this phase: - Review documentation files for completeness. - Confirm assumptions in `docs/decision-log.md`. ## 2026-05-28 - Phase 0 Review Before Implementation Status: Completed Completed: - Reviewed all Phase 0 documentation files against the original requirement. - Confirmed the required documentation set is present. - Clarified Phase 1 package baseline in the development plan. - Clarified `election_settings.is_registration_open` in database and workflow design. - Recorded optional Telescope and DOMPDF decisions. Changed files: - `docs/01-development-plan.md` - `docs/03-database-design.md` - `docs/05-workflow-design.md` - `docs/decision-log.md` - `docs/progress-log.md` Pending items: - Proceed with Phase 1 implementation. Next step: - Scaffold Laravel, install packages, configure authentication, Bootstrap layout, roles/permissions, and base dashboards. ## 2026-05-28 - Phase 1: Laravel Setup and Authentication Status: Completed Completed: - Scaffolded Laravel application. - Installed Laravel framework 13.12.0. - Installed Laravel Breeze authentication scaffolding. - Disabled generic public account registration route. - Installed and configured Bootstrap 5 layout through Vite. - Installed Bootstrap Icons, jQuery, SweetAlert2, Tom Select, Flatpickr, and DataTables Bootstrap packages. - Installed `spatie/laravel-permission` and published migrations/config. - Installed `spatie/laravel-activitylog` and published migrations/config. - Installed `simplesoftwareio/simple-qrcode`. - Attempted `maatwebsite/excel`; Composer rejected it for PHP 8.5.1/Laravel 13 compatibility. - Installed `openspout/openspout` as compatible spreadsheet export foundation. - Installed Larastan/PHPStan. - Added role middleware aliases. - Added User `HasRoles` support. - Added role and permission seeder. - Added default Admin seeding through `config/prn.php`. - Added role-based dashboard routes and base dashboard UI. - Updated auth/profile/welcome views to Bootstrap. - Updated `.env.example` for MySQL. - Set application timezone to `Asia/Kuala_Lumpur`. - Kept local `.env` on SQLite because MySQL server credentials are not yet available. - Added tests for disabled registration and dashboard role protection. Changed files: - Laravel application scaffold files. - `app/Http/Controllers/DashboardController.php` - `app/Models/User.php` - `bootstrap/app.php` - `config/activitylog.php` - `config/permission.php` - `config/prn.php` - `config/app.php` - `database/migrations/*permission*` - `database/migrations/*activity_log*` - `database/seeders/DatabaseSeeder.php` - `database/seeders/RolePermissionSeeder.php` - `resources/css/app.css` - `resources/js/app.js` - `resources/views/auth/*.blade.php` - `resources/views/dashboards/index.blade.php` - `resources/views/layouts/*.blade.php` - `resources/views/profile/edit.blade.php` - `resources/views/welcome.blade.php` - `routes/auth.php` - `routes/web.php` - `tests/Feature/Auth/RegistrationTest.php` - `tests/Feature/DashboardAccessTest.php` - `.env.example` - `.gitignore` - `composer.json` - `composer.lock` - `package.json` - `package-lock.json` - `phpstan.neon` - `docs/*` Verification: - `php artisan migrate --seed` completed after seeder correction. - `php artisan db:seed` completed. - `npm.cmd run build` passed. - `php artisan route:list` passed. - `php artisan test` passed: 27 tests, 64 assertions. - `vendor\bin\pint.bat --test` passed. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. Pending items: - Confirm MySQL database credentials and switch local `.env` to MySQL when available. - Implement Phase 2 core domain migrations/models/seeders. - Replace OpenSpout with `maatwebsite/excel` only if a compatible release becomes available or runtime PHP target changes. Manual testing: - Visit `/`. - Log in as seeded Admin: - email: `admin@prn.local` - password: `password` - Confirm `/dashboard` redirects Admin to `/admin/dashboard`. - Confirm `/register` is not available. - Resize browser to confirm mobile navbar behavior. Next step: - Phase 2: Core database and seeders for election hierarchy, positions, quotas, applications, assignments, and operational records. ## 2026-05-28 - Phase 2: Core Database and Seeders Status: Completed Completed: - Added core election-domain migration. - Added `user_profiles`, election hierarchy, position/quota, application/document/history, assignment/history, representative, wheelchair, attendance, finance, export, and note tables. - Added domain models and Eloquent relationships. - Added baseline factories for election hierarchy, positions, applications, and assignments. - Added `PositionSeeder`. - Added `SampleElectionSeeder`. - Updated `DatabaseSeeder` to seed roles, positions, Admin user, and sample election data. - Seeded sample election `PRN2026`. - Seeded one Bahagian Pilihanraya, one Daerah Mengundi, one Pusat Mengundi, and three Saluran Mengundi. - Seeded 9 default positions and 17 sample quotas. - Seeded sample Admin Kewangan, PPM, and KTM users. - Seeded sample dual-role assignment where one user is both PPM and KTM. - Seeded sample wheelchair allocation. - Added feature test for core domain seeder output and dual-role assignment. - Confirmed MySQL connection through Laravel/PDO. - Shortened long composite index names for MySQL compatibility. Changed files: - `database/migrations/2026_05_28_030000_create_phase2_core_domain_tables.php` - `app/Models/*.php` - `database/factories/*.php` - `database/seeders/DatabaseSeeder.php` - `database/seeders/PositionSeeder.php` - `database/seeders/SampleElectionSeeder.php` - `tests/Feature/CoreDomainSeederTest.php` - `docs/00-project-brief.md` - `docs/01-development-plan.md` - `docs/03-database-design.md` - `docs/07-testing-plan.md` - `docs/08-deployment-notes.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Verification: - `php artisan migrate:fresh --seed` passed on MySQL database `prn2026`. - `php artisan test` passed: 28 tests, 80 assertions. - `vendor\bin\pint.bat --test` passed. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. Manual testing: - Log in as `admin@prn.local` / `password`. - Confirm base dashboard loads after fresh seed. - Optional sample accounts: - `kewangan@prn.local` / `password` - `ppm@prn.local` / `password` - `ktm@prn.local` / `password` - Confirm `/dashboard` redirects based on each user's role. Pending items: - Phase 3 Admin setup module UI and CRUD. - Implement policy-level ownership checks when domain UI routes are added. - Add business-rule services for vacancy and registration period calculations in later phases. Next step: - Phase 3: Admin setup module for CRUD Bahagian, Daerah, Pusat, Saluran, Jawatan/quota setup, PPM assignment, and QR generation. ## 2026-05-29 - Phase 3: Admin Setup Module Status: Completed Completed: - Added Admin setup routes under `/admin/setup`. - Added setup overview page. - Added CRUD controllers, Form Requests, and Blade views for Bahagian Pilihanraya, Daerah Mengundi, Pusat Mengundi, Saluran Mengundi, Jawatan, and Kekosongan Jawatan. - Added Pusat Mengundi detail page with saluran list, QR registration link, QR preview, and PPM assignment form. - Added QR generation service using public UUID registration URLs. - Added Admin-protected QR SVG serving route. - Added public QR placeholder route `/pohon/{pusatMengundi:public_uuid}`. - Added PPM assignment service using `staff_assignments`, assignment history, RBAC role assignment, and activity logging. - Added quota validation to prevent assigning a Saluran Mengundi from a different Pusat Mengundi. - Added public Blade layout for QR-facing pages. - Linked Admin dashboard action to Election setup. - Added feature tests for Admin setup module behavior. Changed files: - `app/Http/Controllers/Admin/Setup/AdminSetupController.php` - `app/Http/Controllers/Admin/Setup/BahagianPilihanrayaController.php` - `app/Http/Controllers/Admin/Setup/DaerahMengundiController.php` - `app/Http/Controllers/Admin/Setup/PositionController.php` - `app/Http/Controllers/Admin/Setup/PositionQuotaController.php` - `app/Http/Controllers/Admin/Setup/PusatMengundiController.php` - `app/Http/Controllers/Admin/Setup/SaluranMengundiController.php` - `app/Http/Controllers/DashboardController.php` - `app/Http/Controllers/PublicApplicationPlaceholderController.php` - `app/Http/Requests/Admin/Setup/*.php` - `app/Services/Admin/PpmAssignmentService.php` - `app/Services/Admin/PusatQrCodeService.php` - `resources/views/admin/setup/**/*.blade.php` - `resources/views/dashboards/index.blade.php` - `resources/views/layouts/public.blade.php` - `resources/views/public/application-placeholder.blade.php` - `routes/web.php` - `tests/Feature/AdminSetupTest.php` - `docs/01-development-plan.md` - `docs/05-workflow-design.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Verification: - `php artisan route:list --except-vendor` passed and shows 65 routes. - `php artisan test --filter=AdminSetupTest` passed: 4 tests, 24 assertions. - `php artisan test` passed: 32 tests, 104 assertions. - `vendor\bin\pint.bat --test` passed. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. - `npm.cmd run build` passed. Manual testing: - Log in as `admin@prn.local` / `password`. - Open `/admin/setup`. - Create or edit Bahagian, Daerah, Pusat, Saluran, Jawatan, and Kuota records. - Open a Pusat Mengundi detail page, generate QR, and assign PPM. - Open the generated `/pohon/{uuid}` public link and confirm it shows the intended Pusat Mengundi. - Log in as `ktm@prn.local` / `password` and confirm `/admin/setup` is forbidden. Pending items: - Full public application form and document upload in Phase 4. - Registration-period enforcement for public and KTM-created applications in Phase 4 and Phase 6. - Representative management for Police, KKM, and JKM in the later Admin management phase. - Rich filters for very large setup lists if operational data volume requires them. Next step: - Phase 4: Public application module using QR link, registration-period rules, document uploads, KTM selection for KP/KPDP, and duplicate IC blocking. ## 2026-05-29 - Phase 4: Public Application Module Status: Completed Completed: - Replaced QR placeholder flow with public application form on `/pohon/{pusatMengundi:public_uuid}`. - Added public success page using application public UUID. - Added `RegistrationPeriodService` for registration open/closed checks. - Added `KtmVacancyService` for KP/KPDP KTM selection and vacancy calculation. - Added `PublicApplicationSubmissionService` for transactional application creation, private document storage, status history, bank verification initialization, and activity logging. - Added `StorePublicApplicationRequest` with validation for applicant fields, IC normalization, required documents, public roles, duplicate IC, KTM-created blocking rule, and KTM vacancy recheck. - Added responsive public Blade form for applicant details, role selection, KTM selection, bank data, and document upload. - Added closed-registration page. - Stored IC document and bank statement metadata in `application_documents`. - Created pending `bank_verifications` row for public submissions. - Added Phase 4 feature tests and unit tests. Changed files: - `app/Http/Controllers/PublicApplicationController.php` - `app/Http/Requests/Public/StorePublicApplicationRequest.php` - `app/Services/RegistrationPeriodService.php` - `app/Services/Public/KtmVacancyService.php` - `app/Services/Public/PublicApplicationSubmissionService.php` - `resources/views/layouts/public.blade.php` - `resources/views/public/applications/create.blade.php` - `resources/views/public/applications/closed.blade.php` - `resources/views/public/applications/success.blade.php` - `routes/web.php` - `tests/Feature/PublicApplicationTest.php` - `tests/Unit/KtmVacancyServiceTest.php` - `tests/Unit/RegistrationPeriodServiceTest.php` - `docs/01-development-plan.md` - `docs/03-database-design.md` - `docs/05-workflow-design.md` - `docs/07-testing-plan.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Verification: - `php artisan route:list --except-vendor` passed and shows 67 routes. - `php artisan test --filter=PublicApplicationTest` passed: 6 tests, 27 assertions. - `php artisan test` passed: 41 tests, 137 assertions. - `vendor\bin\pint.bat --test` passed. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. - `npm.cmd run build` passed. Manual testing: - Open a generated `/pohon/{uuid}` QR link. - Submit a KTM or PAPM application with IC document and bank statement. - Submit KP with an available KTM selection. - Try submitting without IC document or bank statement and confirm validation blocks it. - Set registration end date to a past date and confirm public form shows closed-registration message. - Create a `created_by_ktm` application for an IC and confirm public self-registration with the same IC is blocked. Pending items: - Phase 5 PPM review, approval/rejection, role change, and assignment of submitted public applications. - Phase 6 KTM-created application deletion flow and email notification. - Authorized document viewing/downloading for PPM/Admin/Admin Kewangan. - Public application status portal if later required. Next step: - Phase 5: PPM review and assignment module. ## 2026-05-29 - Phase 5: PPM Review and Assignment Module Status: Completed Completed: - Added PPM application review routes under `/ppm/applications`. - Added PPM-scoped application list and detail screens. - Added `ApplicationPolicy` and `PpmScopeService` for record-level PPM access control. - Added `PpmApplicationReviewService` for transactional approve/reject behavior. - Added `AssignmentVacancyService` for Pusat/Saluran quota checks. - Extended `KtmVacancyService` to ignore the current application during PPM approval vacancy checks. - Added PPM approval form allowing role change before approval. - Implemented KTM assignment to available Saluran Mengundi. - Implemented KP/KPDP assignment to active KTM with remaining vacancy. - Implemented PAPM assignment at Pusat Mengundi level. - Implemented rejection with required reason. - Added authorized private document download for PPM-owned applications. - Added PPM dashboard metrics and link to the review queue. - Added representative display sections for Police, KKM, and JKM records. - Added `AuthorizesRequests` to the base controller for policy authorization. - Added Phase 5 feature tests. Changed files: - `app/Http/Controllers/Controller.php` - `app/Http/Controllers/DashboardController.php` - `app/Http/Controllers/Ppm/ApplicationReviewController.php` - `app/Http/Requests/Ppm/ApproveApplicationRequest.php` - `app/Http/Requests/Ppm/RejectApplicationRequest.php` - `app/Models/PusatMengundi.php` - `app/Policies/ApplicationPolicy.php` - `app/Services/Ppm/AssignmentVacancyService.php` - `app/Services/Ppm/PpmApplicationReviewService.php` - `app/Services/Ppm/PpmScopeService.php` - `app/Services/Public/KtmVacancyService.php` - `resources/views/ppm/applications/index.blade.php` - `resources/views/ppm/applications/show.blade.php` - `routes/web.php` - `tests/Feature/PpmApplicationReviewTest.php` - `docs/01-development-plan.md` - `docs/04-rbac-permission-matrix.md` - `docs/05-workflow-design.md` - `docs/07-testing-plan.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Verification: - `php artisan route:list --except-vendor` passed and shows 72 routes. - `php artisan test --filter=PpmApplicationReviewTest` passed: 6 tests, 24 assertions. - `php artisan test` passed: 47 tests, 161 assertions. - `vendor\bin\pint.bat --test` passed. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. - `npm.cmd run build` passed. Manual testing: - Log in as `ppm@prn.local` / `password`. - Open `/ppm/applications`. - Submit a public application through QR first, then confirm it appears in the PPM queue. - Open the application detail page and download uploaded documents. - Approve a KTM application into an available Saluran. - Approve a KP/KPDP application under an active KTM. - Change an applicant role before approval and confirm assignment is created. - Reject an application with a reason. - Confirm a PPM cannot access an application outside their assigned Pusat Mengundi. Pending items: - Phase 6 KTM flow for KP registration/approval, deletion of KTM-created records, and email notification. - Admin/Admin Kewangan document viewing routes. - Admin global management and post-registration notes in Phase 7. - Attendance module in Phase 10. Next step: - Phase 6: KTM flow for assigned Saluran, KP registration, KP approval, KTM-created deletion, and email notification. ## 2026-05-29 - Phase 6: KTM Flow Status: Completed Completed: - Added KTM application routes under `/ktm/applications`. - Added `KtmScopeService` for active KTM assignment ownership checks. - Added `KtmApplicationService` for KTM KP registration, KP approval, and KTM-created deletion. - Added queued Markdown Mailable `KtmCreatedApplicantMail`. - Added KTM team list showing assigned Saluran, active KP team members, and KP vacancy. - Added KTM KP registration form. - Added KTM application detail page with approve/delete actions. - Enforced KTM registration period on KP registration and approval. - Enforced KP-only creation and approval for KTM. - Enforced own-team-only approval/deletion. - Implemented `source = created_by_ktm` for KTM-created records. - Implemented delete flow that sets `deleted_by_ktm`, records status history, and soft deletes the KTM-created application. - Updated KTM dashboard with live metrics and navigation links. - Added Phase 6 feature tests. Changed files: - `app/Http/Controllers/DashboardController.php` - `app/Http/Controllers/Ktm/TeamController.php` - `app/Http/Requests/Ktm/RegisterKpApplicantRequest.php` - `app/Mail/KtmCreatedApplicantMail.php` - `app/Services/Ktm/KtmApplicationService.php` - `app/Services/Ktm/KtmScopeService.php` - `app/Services/Public/KtmVacancyService.php` - `resources/views/emails/ktm/created-applicant.blade.php` - `resources/views/ktm/applications/create.blade.php` - `resources/views/ktm/applications/index.blade.php` - `resources/views/ktm/applications/show.blade.php` - `routes/web.php` - `tests/Feature/KtmFlowTest.php` - `docs/01-development-plan.md` - `docs/04-rbac-permission-matrix.md` - `docs/05-workflow-design.md` - `docs/07-testing-plan.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Verification: - `php artisan route:list --except-vendor` passed and shows 78 routes. - `php artisan test --filter=KtmFlowTest` passed: 6 tests, 25 assertions. - `php artisan test` passed: 53 tests, 186 assertions. - `vendor\bin\pint.bat --test` passed. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. - `npm.cmd run build` passed. Manual testing: - Log in as `ktm@prn.local` / `password`. - Open `/ktm/applications`. - Confirm assigned Saluran and KP vacancy are shown. - Register a KP and confirm email is queued/sent according to local mail configuration. - Approve a KP under own team and confirm active assignment is created. - Try approving KP from another KTM team and confirm it is blocked. - Try approving a non-KP application and confirm it is blocked. - Set registration end date to the past and confirm KTM registration/approval is blocked. - Delete an unassigned KTM-created applicant and confirm public self-registration with the same IC is no longer blocked. Pending items: - Phase 7 Admin global management/manual entry and post-registration notes. - Admin/Admin Kewangan document viewing routes. - Finance verification module in Phase 8. - Attendance module in Phase 10. Next step: - Phase 7: Admin management and manual entry. ## 2026-05-29 - Phase 7: Admin Management and Manual Entry Status: Completed Completed: - Added Admin management routes under `/admin/management`. - Added Admin management overview dashboard. - Added global application list with filters. - Added Admin manual application entry with `source = admin_manual`. - Added Admin application edit screen. - Added direct assignment from Admin application edit screen. - Added global staff assignment list and assignment edit screen. - Added post-registration catatan enforcement through `AdminPostCloseNoteService`. - Added `system_notes` records for accepted post-closing Admin changes. - Added assignment/application history and activity log entries for Admin changes. - Added Police, KKM, and JKM representative entry/deletion screen. - Updated Admin dashboard with live counts and management module links. - Added Phase 7 feature tests. Changed files: - `app/Http/Controllers/Admin/Management/AdminManagementController.php` - `app/Http/Controllers/Admin/Management/ManagedApplicationController.php` - `app/Http/Controllers/Admin/Management/RepresentativeController.php` - `app/Http/Controllers/Admin/Management/StaffAssignmentController.php` - `app/Http/Controllers/DashboardController.php` - `app/Http/Requests/Admin/Management/*.php` - `app/Services/Admin/Management/*.php` - `resources/views/admin/management/**/*.blade.php` - `routes/web.php` - `tests/Feature/AdminManagementTest.php` - `docs/01-development-plan.md` - `docs/04-rbac-permission-matrix.md` - `docs/05-workflow-design.md` - `docs/07-testing-plan.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Verification: - `php artisan route:list --except-vendor` passed and shows 91 routes. - `php artisan test --filter=AdminManagementTest` passed: 5 tests, 18 assertions. - `php artisan test` passed: 58 tests, 204 assertions. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. - `vendor\bin\pint.bat --test` passed. - `npm.cmd run build` passed. Manual testing: - Log in as `admin@prn.local` / `password`. - Open `/admin/management`. - Create a manual application. - Edit a manual or public application. - Direct assign an application to a Pusat, Saluran, or KTM team. - Edit an existing staff assignment. - Create Police, KKM, and JKM representative records. - Close the registration period for the election and confirm Admin changes require catatan. - Log in as `kewangan@prn.local` / `password` and confirm `/admin/management/assignments` is forbidden. Pending items: - Admin document viewing route for IC/bank statement remains for the finance/document module. - Excel export implementation remains in later finance/export phases. - More granular representative edit forms can be added if operational users need inline correction instead of delete/recreate. Next step: - Phase 8: Admin Kewangan module for bank verification, finance filters, and finance export. ## 2026-05-29 - Phase 8: Admin Kewangan Status: Completed Completed: - Added finance bank verification routes under `/kewangan/bank`. - Added Admin Kewangan list of approved/assigned staff bank records. - Added filters for status, Pusat Mengundi, role, missing bank statement, missing account number, and keyword. - Added bank verification detail screen. - Added finance status update with finance note, verified by, and verified at. - Added activity log for finance verification changes. - Added sensitive display masking helper. - Added finance XLSX export using OpenSpout. - Added export log creation for finance exports. - Updated Admin Kewangan dashboard with live counts and module links. - Added Phase 8 feature tests. Changed files: - `app/Exports/FinanceVerificationExport.php` - `app/Http/Controllers/DashboardController.php` - `app/Http/Controllers/Kewangan/BankVerificationController.php` - `app/Http/Requests/Kewangan/UpdateBankVerificationRequest.php` - `app/Services/Finance/BankVerificationService.php` - `app/Services/Finance/FinanceExportService.php` - `app/Services/Finance/FinanceVerificationQuery.php` - `app/Support/SensitiveData.php` - `resources/views/kewangan/bank/index.blade.php` - `resources/views/kewangan/bank/show.blade.php` - `routes/web.php` - `tests/Feature/FinanceModuleTest.php` - `docs/01-development-plan.md` - `docs/04-rbac-permission-matrix.md` - `docs/05-workflow-design.md` - `docs/07-testing-plan.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Verification: - `php artisan route:list --except-vendor` passed and shows 95 routes. - `php artisan test --filter=FinanceModuleTest` passed: 5 tests, 18 assertions. - `php artisan test` passed: 63 tests, 222 assertions. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. - `vendor\bin\pint.bat --test` passed. - `npm.cmd run build` passed. Manual testing: - Log in as `kewangan@prn.local` / `password`. - Open `/kewangan/bank`. - Filter records by Pusat, role, status, missing bank statement, and missing account number. - Open a bank verification record. - Mark it verified, rejected, or requires correction with a finance note. - Export the finance list and confirm a file downloads. - Confirm `/admin/management/assignments` is forbidden for Admin Kewangan. Pending items: - Admin/Admin Kewangan authorized document download for bank statement remains to be added when document review UX is finalized. - Additional finance export formats can be added if official template is provided. Next step: - Phase 9: Wheelchair management. ## 2026-05-29 - Phase 9: Wheelchair Management Status: Completed Completed: - Added Admin wheelchair management routes under `/admin/management/wheelchairs`. - Added allocation upsert per election and Pusat Mengundi. - Added wheelchair management service for allocation and transaction rules. - Added taken transaction recording with quantity, time, person taking, and notes. - Added return transaction recording with quantity, time, condition, and notes. - Added quantity guards for allocation, available balance, and outstanding return balance. - Added activity logging for allocation and transaction changes. - Added wheelchair list and detail screens. - Updated Admin management dashboard and navigation. - Added Phase 9 feature tests. Changed files: - `app/Http/Controllers/Admin/Management/AdminManagementController.php` - `app/Http/Controllers/Admin/Management/WheelchairController.php` - `app/Http/Requests/Admin/Management/StoreWheelchairTransactionRequest.php` - `app/Http/Requests/Admin/Management/UpsertWheelchairAllocationRequest.php` - `app/Services/Admin/Management/WheelchairManagementService.php` - `resources/views/admin/management/_nav.blade.php` - `resources/views/admin/management/index.blade.php` - `resources/views/admin/management/wheelchairs/index.blade.php` - `resources/views/admin/management/wheelchairs/show.blade.php` - `routes/web.php` - `tests/Feature/WheelchairManagementTest.php` - `docs/01-development-plan.md` - `docs/04-rbac-permission-matrix.md` - `docs/05-workflow-design.md` - `docs/07-testing-plan.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Verification: - `php artisan route:list --except-vendor` passed and shows 100 routes. - `php artisan test --filter=WheelchairManagementTest` passed: 6 tests, 16 assertions. - `php artisan test` passed: 69 tests, 238 assertions. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. - `vendor\bin\pint.bat --test` passed. - `npm.cmd run build` passed. Manual testing: - Log in as `admin@prn.local` / `password`. - Open `/admin/management/wheelchairs`. - Create or update a wheelchair allocation for a Pusat Mengundi. - Open the allocation detail screen. - Record wheelchair taken transaction. - Record wheelchair returned transaction with condition `baik` or `rosak`. - Try taking more than available and confirm validation blocks it. - Try returning more than outstanding and confirm validation blocks it. - Try lowering allocation below outstanding and confirm validation blocks it. - Log in as `kewangan@prn.local` / `password` and confirm wheelchair management is forbidden. Pending items: - Phase 10 attendance module. - Optional wheelchair export/report if later required. Next step: - Phase 10: Attendance module. ## 2026-05-29 - Phase 10: Attendance Module Status: Completed Completed: - Added PPM attendance routes under `/ppm/attendance`. - Added PPM attendance list for assigned Pusat Mengundi. - Added PPM attendance recording screen for active staff under own Pusat Mengundi. - Added attendance activation guard using `election_settings.is_attendance_active`. - Added bulk attendance recording service. - Added activity log entries for attendance updates. - Added Admin attendance routes under `/admin/attendance`. - Added Admin attendance dashboard by Pusat Mengundi. - Added Admin attendance detail view with staff, role, Saluran, status, check-in time, notes, and recorded by. - Added attendance summary service with totals and role-level summary. - Added attendance detail XLSX export using OpenSpout. - Added export log creation for attendance exports. - Updated Admin and PPM dashboard links/metrics. - Added Phase 10 feature and unit tests. Changed files: - `app/Exports/AttendanceDetailExport.php` - `app/Http/Controllers/Admin/AttendanceController.php` - `app/Http/Controllers/DashboardController.php` - `app/Http/Controllers/Ppm/AttendanceController.php` - `app/Http/Requests/Ppm/RecordAttendanceRequest.php` - `app/Services/Attendance/AttendanceExportService.php` - `app/Services/Attendance/AttendanceRecordingService.php` - `app/Services/Attendance/AttendanceSummaryService.php` - `resources/views/admin/attendance/index.blade.php` - `resources/views/admin/attendance/show.blade.php` - `resources/views/ppm/attendance/index.blade.php` - `resources/views/ppm/attendance/show.blade.php` - `routes/web.php` - `tests/Feature/AttendanceModuleTest.php` - `tests/Unit/AttendanceSummaryServiceTest.php` - `docs/01-development-plan.md` - `docs/04-rbac-permission-matrix.md` - `docs/05-workflow-design.md` - `docs/07-testing-plan.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Verification: - `php artisan route:list --except-vendor` passed and shows 106 routes. - `php artisan test --filter=Attendance` passed: 7 tests, 21 assertions. - `php artisan test` passed: 76 tests, 259 assertions. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. - `vendor\bin\pint.bat --test` passed. - `npm.cmd run build` passed. Manual testing: - Activate attendance for the election in database/settings. - Log in as `ppm@prn.local` / `password`. - Open `/ppm/attendance`. - Record present/absent/not recorded statuses for staff under own Pusat Mengundi. - Confirm PPM cannot access or record another Pusat Mengundi. - Log in as `admin@prn.local` / `password`. - Open `/admin/attendance`. - View Pusat Mengundi summary and detail. - Export attendance detail and confirm file downloads. - Log in as `kewangan@prn.local` / `password` and confirm Admin attendance dashboard is forbidden. Pending items: - Admin settings UI to toggle `is_attendance_active` if operational users need UI control. - Official attendance report template can replace the current export layout when provided. Next step: - Phase 11: Testing, polish, security review, performance review, and final hardening. ## 2026-05-29 - Phase 11: Testing, Polish, and Hardening Status: Completed Completed: - Reviewed authorization, private document access, sensitive data masking, exports, and quality gates. - Added Admin document download route for private application documents. - Added Admin Kewangan bank statement download route. - Restricted finance document downloads to `bank_statement` only. - Added activity logging for Admin and finance document downloads. - Added secure document links to Admin application edit and finance bank detail screens. - Added feature tests for Admin/Admin Kewangan/unauthorized document access. - Added unit test for sensitive data masking. - Updated deployment notes with final quality gate commands and document storage hardening notes. - Updated Phase 11 documentation and release notes. Changed files: - `app/Http/Controllers/DocumentDownloadController.php` - `app/Http/Controllers/Admin/Management/ManagedApplicationController.php` - `resources/views/admin/management/applications/edit.blade.php` - `resources/views/kewangan/bank/show.blade.php` - `routes/web.php` - `tests/Feature/DocumentDownloadAuthorizationTest.php` - `tests/Unit/SensitiveDataTest.php` - `docs/01-development-plan.md` - `docs/07-testing-plan.md` - `docs/08-deployment-notes.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Verification: - `php artisan test --filter=DocumentDownloadAuthorizationTest` passed: 3 tests, 4 assertions. - `php artisan test --filter=SensitiveDataTest` passed: 1 test, 4 assertions. - `php artisan route:list --except-vendor` passed and shows 108 routes. - `php artisan test` passed: 80 tests, 267 assertions. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` passed. - `vendor\bin\pint.bat --test` passed. - `npm.cmd run build` passed. Manual testing: - Log in as `admin@prn.local` / `password`. - Open an application with uploaded documents and download IC/bank documents. - Log in as `kewangan@prn.local` / `password`. - Open bank verification detail and download bank statement. - Confirm Admin Kewangan cannot download IC document through finance document route. - Run end-to-end smoke test from QR application to attendance export. Pending items: - Replace current exports with official report templates if/when provided. - Add Admin UI toggle for attendance activation if operational users need it outside database/settings maintenance. Next step: - Production readiness review with real environment credentials, mail transport, queue worker, HTTPS, backup plan, and user acceptance testing. ## 2026-05-31 - Penambahbaikan daftar Pusat Mengundi ### Selesai - Membuang keperluan nama Saluran daripada borang dan validasi setup Saluran. - Menambah sepuluh input bilangan operasi pada borang daftar Pusat Mengundi. - Menjana Saluran bernombor serta kuota Pusat dan Saluran secara automatik semasa daftar Pusat. - Menukar istilah Calon Simpanan kepada Calon Tambahan dengan migrasi data. - Menambah ujian ciri untuk penciptaan Pusat, Saluran tanpa nama dan kuota operasi. ### Fail berubah - `app/Services/Admin/PusatOperationalSetupService.php` - `app/Http/Controllers/Admin/Setup/PusatMengundiController.php` - `app/Http/Requests/Admin/Setup/UpsertPusatMengundiRequest.php` - `app/Http/Requests/Admin/Setup/UpsertSaluranMengundiRequest.php` - `resources/views/admin/setup/pusat/_form.blade.php` - `resources/views/admin/setup/saluran/_form.blade.php` - `database/migrations/2026_05_31_000000_rename_calon_simpanan_position_to_calon_tambahan.php` - `database/seeders/PositionSeeder.php` - `database/seeders/SampleElectionSeeder.php` - `tests/Feature/Admin/PusatOperationalSetupTest.php` ### Semakan manual - Buka `/admin/setup/pusat/create`, pastikan sepuluh medan bilangan operasi dipaparkan. - Daftar Pusat baharu dan pastikan Saluran bernombor dijana tanpa meminta nama Saluran. ### Seterusnya - Jalankan migrasi, ujian automatik dan semakan statik selepas shell runner tersedia. ## 2026-06-02 - Perancangan Fasa 12–15: Pembetulan dan Penambahbaikan Status: Selesai (perancangan) Selesai: - Menjalankan semakan kod menyeluruh ke atas keseluruhan sistem selepas Phase 11 dan penambahbaikan 2026-05-31. - Mengenal pasti lapan isu meliputi keselamatan, prestasi, pengalaman pengguna, dan pengurusan data. - Menyusun isu-isu tersebut kepada empat fasa penambahbaikan berprioritikan (Fasa 12–15). - Mendokumentasikan plan penuh dalam `docs/09-improvement-plan.md`. - Mengemas kini `docs/01-development-plan.md` dengan ringkasan Fasa 12–15. Fail berubah: - `docs/09-improvement-plan.md` (baharu) - `docs/01-development-plan.md` - `docs/progress-log.md` - `docs/changelog.md` Isu yang dikenal pasti: - 12-A: Tiada UI toggle untuk `is_attendance_active` dan `is_registration_open_override` — memerlukan edit database terus dalam produksi. - 12-B: Semakan IC duplikasi mungkin tidak mengecualikan soft-deleted records secara eksplisit dan konsisten. - 12-C: `AdminPostCloseNoteService` boleh dipintas jika controller baharu lupa memanggilnya. - 12-D: `SampleElectionSeeder` mungkin masih mengandungi rujukan kepada `CALON_SIMPANAN` selepas migration rename Phase 11. - 12-E: Semakan jenis dokumen pada path download tidak sepenuhnya konsisten antara peranan. - 13-A: Composite indexes belum wujud pada `applications`, `staff_assignments`, `bank_verifications` — boleh perlahan apabila data besar. - 14-A: Tiada halaman status permohonan untuk pemohon awam — mereka perlu hubungi Admin untuk semak status. - 15-A: Fail XLSX eksport sensitif disimpan tanpa had masa — polisi purge automatik diperlukan. Seterusnya: - Mulakan Fasa 12 dengan 12-A (Admin Settings UI) kerana ia paling mendesak untuk kebolehgunaan dalam persekitaran produksi. ## 2026-06-02 - Fasa 12-A: Admin Settings UI dan 12-D: Pembetulan Seeder Status: Selesai Selesai: - Tambah halaman Admin Settings di `/admin/setup/settings/{election}` untuk toggle `is_attendance_active` dan `is_registration_open_override` tanpa perlu edit database. - Tambah `ElectionSettingsController`, `ElectionSettingsService`, `UpdateElectionSettingsRequest`. - Tambah view `admin/setup/settings/edit.blade.php` dengan toggle radio untuk kedua-dua tetapan, badge status semasa, dan amaran override aktif. - Tambah pautan "Tetapan" (kuning) ke `admin/setup/_nav.blade.php` dan setup index actions. - Perubahan tetapan direkod dalam `activity_log` dengan nilai sebelum dan selepas. - Betulkan bug 12-D: `SampleElectionSeeder::seedQuotas()` masih query `CALON_SIMPANAN` — ditukar kepada `CALON_TAMBAHAN`. - Betulkan `AdminSetupTest`: tambah medan operasi yang diperlukan untuk pusat.store, dan tukar nombor saluran dari '1' ke '3' untuk elak konflik dengan saluran auto-jana. - Betulkan anotasi PHPDoc pada `UpdateElectionSettingsRequest::prepareForValidation()`. - Tambah anotasi `@var ElectionSetting` pada `ElectionSettingsService` untuk PHPStan. - Jalankan `pint` (auto-fix 2 fail) dan `phpstan` (0 error). Fail berubah: - `app/Http/Controllers/Admin/Setup/ElectionSettingsController.php` (baharu) - `app/Http/Requests/Admin/Setup/UpdateElectionSettingsRequest.php` (baharu) - `app/Services/Admin/ElectionSettingsService.php` (baharu) - `resources/views/admin/setup/settings/edit.blade.php` (baharu) - `resources/views/admin/setup/_nav.blade.php` - `resources/views/admin/setup/index.blade.php` - `routes/web.php` - `database/seeders/SampleElectionSeeder.php` - `tests/Feature/Admin/ElectionSettingsTest.php` (baharu) - `tests/Feature/AdminSetupTest.php` - `docs/04-rbac-permission-matrix.md` - `docs/05-workflow-design.md` - `docs/09-improvement-plan.md` - `docs/decision-log.md` - `docs/progress-log.md` - `docs/changelog.md` Semakan: - `php artisan route:list --except-vendor` — 110 routes (tambah 2). - `php artisan test` — 90 tests, 310 assertions (**selesai, semua lulus**). - `vendor\bin\pint.bat --test` — lulus. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` — lulus, 0 error. Seterusnya: - 12-B: Semak dan betulkan query IC duplikasi untuk pastikan soft-deleted records dikecualikan. - 12-C: Perkukuh enforcement nota catatan post-pendaftaran. - 12-E: Konsistensikan semakan jenis dokumen download. ## 2026-06-02 - Fasa 12-B: Pembetulan Semakan IC Soft-Deleted Status: Selesai Selesai: - Audit menyeluruh semua lokasi semakan IC duplikasi: `StorePublicApplicationRequest::validateDuplicateIc()`, `KtmApplicationService::registerKp()`, `KtmVacancyService::remainingVacancy()`, dan `AssignmentVacancyService`. - Disahkan bahawa kedua-dua query IC menggunakan SoftDeletes scope (mengecualikan `deleted_at IS NOT NULL`) DAN `whereNotIn('status', ['cancelled', 'deleted_by_ktm'])` — perlindungan dua lapis yang betul. - `cancelled` records: dikecualikan oleh `whereNotIn` sahaja (bukan soft-deleted). - `deleted_by_ktm` records: dikecualikan oleh kedua-dua SoftDeletes scope DAN `whereNotIn` (redundant safety layer). - Tambah comment eksplisit pada kedua-dua query IC untuk mendokumentasikan intent perlindungan dua lapis ini. - Tambah 2 ujian baharu dalam `PublicApplicationTest`: - `test_public_registration_is_unblocked_after_ktm_deletes_applicant`: KTM cipta rekod, KTM delete (soft-delete + status deleted_by_ktm), pemohon awam dengan IC yang sama boleh daftar semula. - `test_cancelled_application_does_not_block_new_registration`: Rekod `cancelled` (bukan soft-deleted) tidak menyekat pendaftaran IC yang sama. - `KtmVacancyService` dan `AssignmentVacancyService` disahkan betul — menggunakan whitelist status dan `StaffAssignment` queries tidak melibatkan soft-deleted `Application` records secara salah. Fail berubah: - `app/Http/Requests/Public/StorePublicApplicationRequest.php` — tambah comment - `app/Services/Ktm/KtmApplicationService.php` — tambah comment - `tests/Feature/PublicApplicationTest.php` — tambah 2 ujian - `docs/09-improvement-plan.md` - `docs/progress-log.md` - `docs/changelog.md` Semakan: - `php artisan test` — 92 tests, 316 assertions (**semua lulus**). - `vendor\bin\pint.bat --test` — lulus. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` — lulus, 0 error. Seterusnya: - 12-C: Perkukuh enforcement nota catatan post-pendaftaran. - 12-E: Konsistensikan semakan jenis dokumen download. ## 2026-06-02 - Fasa 12-C: Perkukuh Enforcement Nota Catatan Post-Pendaftaran Status: Selesai Selesai: - Audit semua 6 service methods yang mengubah rekod domain selepas pendaftaran tutup — kesemua sudah memanggil `AdminPostCloseNoteService::requireIfClosed()` dengan betul. - Masalah: enforcement bergantung pada konvensyen — controller baharu boleh lupa panggil service tersebut. - Tambah middleware `RequireAdminPostCloseNote` sebagai safety net automatik pada route group `admin/management`. - Middleware menyemak `election_id` dari request body (untuk create routes) atau active election (untuk edit/delete routes). Jika pendaftaran ditutup dan `note` kosong, redirect balik dengan ralat validasi. - Daftarkan alias `admin.post_close_note` dalam `bootstrap/app.php`. - Apply middleware ke `['auth', 'role:Admin', 'admin.post_close_note']` dalam `routes/web.php`. - Tambah 5 ujian baharu dalam `AdminManagementTest`: - `test_admin_create_application_after_registration_closed_requires_catatan` - `test_admin_update_assignment_after_registration_closed_requires_catatan` - `test_admin_create_representative_after_registration_closed_requires_catatan` - `test_admin_delete_representative_after_registration_closed_requires_catatan` - `test_middleware_does_not_block_when_registration_is_open` Fail berubah: - `app/Http/Middleware/RequireAdminPostCloseNote.php` (baharu) - `bootstrap/app.php` — alias middleware baharu - `routes/web.php` — middleware pada route group management - `tests/Feature/AdminManagementTest.php` — tambah 5 ujian - `docs/decision-log.md` - `docs/09-improvement-plan.md` - `docs/progress-log.md` - `docs/changelog.md` Semakan: - `php artisan test` — 97 tests, 337 assertions (**semua lulus**). - `vendor\bin\pint.bat --test` — lulus. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` — lulus, 0 error. Seterusnya: - 12-E: Konsistensikan semakan jenis dokumen download. ## 2026-06-02 - Fasa 13-A: Composite Indexes Pangkalan Data Status: Selesai Selesai: - Audit index sedia ada berbanding query kritikal dalam `KtmVacancyService`, `AssignmentVacancyService`, `ManagedApplicationController`, dan `DashboardController`. - Dapatan: dua index yang disenaraikan dalam plan sudah wujud — `(election_id, ic_number)` dan `(election_id, pusat_mengundi_id, status)` pada `applications`, serta dua index komposit pada `staff_assignments`. - `bank_verifications` tiada kolum `election_id` — index `(election_id, status)` yang diancangkan dalam plan tidak relevan dan tidak ditambah. - Tiga index baharu yang benar-benar diperlukan ditambah melalui migration `2026_06_02_000000_add_performance_indexes.php`: - `applications.app_election_status_idx` — `(election_id, status)` untuk dashboard count queries - `applications.app_ktm_position_idx` — `(selected_ktm_assignment_id, requested_position_id)` untuk `KtmVacancyService` reserved count - `staff_assignments.sa_reports_to_pos_status_idx` — `(reports_to_assignment_id, position_id, status)` untuk `KtmVacancyService` assigned count - Dokumentasi index lengkap ditambah dalam `docs/03-database-design.md`. - Arahan verifikasi index dengan `SHOW INDEX` dan `EXPLAIN` ditambah dalam `docs/08-deployment-notes.md`. Fail berubah: - `database/migrations/2026_06_02_000000_add_performance_indexes.php` (baharu) - `docs/03-database-design.md` - `docs/08-deployment-notes.md` - `docs/decision-log.md` - `docs/09-improvement-plan.md` - `docs/progress-log.md` - `docs/changelog.md` Semakan: - `php artisan migrate` — lulus, 2 migration dijalankan. - `php artisan test` — 97 tests, 337 assertions (**semua lulus**). - `vendor\bin\pint.bat --test` — lulus. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` — lulus, 0 error. Seterusnya: - 12-E: Konsistensikan semakan jenis dokumen download. - Fasa 14: Portal status permohonan pemohon awam. - Fasa 15: Purge automatik fail eksport sensitif. ## 2026-06-02 - Fasa 12-E: Konsistensikan Semakan Jenis Dokumen Download Status: Selesai Selesai: - Audit tiga path download dokumen — PPM, Admin, Finance (Admin Kewangan). - Dapatan: - PPM: tiada type check eksplisit, tiada activity log, ownership failure pakai 404 (patut 403). - Admin: tiada type check eksplisit (senyap allow semua jenis). - Finance: sudah betul — explicit `bank_statement` check, log activity, pakai 403. - Tambah `ApplicationDocument::TYPE_IC_DOCUMENT`, `TYPE_BANK_STATEMENT`, `ALLOWED_FOR_ADMIN`, `ALLOWED_FOR_PPM`, `ALLOWED_FOR_FINANCE` sebagai constants dalam model — satu tempat yang autoriti untuk semua rules. - Kemaskini `DocumentDownloadController::admin()` guna `ALLOWED_FOR_ADMIN` — jenis baharu perlu didaftarkan secara eksplisit untuk Admin dapat akses. - Kemaskini `DocumentDownloadController::finance()` guna `ALLOWED_FOR_FINANCE` — logik sama, kini guna constant. - Kemaskini `ApplicationReviewController::downloadDocument()`: - Tambah `ALLOWED_FOR_PPM` type check → 403 jika jenis tidak dibenarkan. - Tukar ownership check dari 404 → 403 (tidak mendedahkan kewujudan ID dokumen). - Tambah activity log `document_downloaded_by_ppm`. - Tambah 9 ujian baharu dalam `DocumentDownloadAuthorizationTest` meliputi semua tiga path dan kes utama: - Admin: ic_document ✓, bank_statement ✓, peranan tidak dibenarkan ✗. - Finance: bank_statement ✓, ic_document ✗. - PPM: ic_document (pusat sendiri) ✓, bank_statement (pusat sendiri) ✓, dokumen pusat lain ✗, activity log disimpan. Fail berubah: - `app/Models/ApplicationDocument.php` — tambah type constants dan allowlist - `app/Http/Controllers/DocumentDownloadController.php` — explicit type checks via constants - `app/Http/Controllers/Ppm/ApplicationReviewController.php` — type check, 403, activity log - `tests/Feature/DocumentDownloadAuthorizationTest.php` — tulis semula dengan 9 ujian komprehensif - `docs/04-rbac-permission-matrix.md` - `docs/decision-log.md` - `docs/09-improvement-plan.md` - `docs/progress-log.md` - `docs/changelog.md` Semakan: - `php artisan test` — 103 tests, 342 assertions (**semua lulus**). - `vendor\bin\pint.bat --test` — lulus. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` — lulus, 0 error. Seterusnya: - Fasa 14: Portal status permohonan pemohon awam. - Fasa 15: Purge automatik fail eksport sensitif. ## 2026-06-02 - Fasa 14-A: Portal Status Permohonan Pemohon Awam Status: Selesai Selesai: - Tambah route `GET /permohonan/{publicUuid}/status` tanpa route model binding untuk elak 404 mendedahkan kewujudan UUID. - Tambah `PublicApplicationController::status()` yang mengendalikan semua kes dengan selamat: - UUID tidak wujud → 200 dengan mesej generik. - Rekod soft-deleted → 200 dengan mesej generik. - Status `deleted_by_ktm` → 200 dengan mesej generik (tidak dedahkan status dalaman). - Rekod aktif → 200 dengan data terhad. - Tambah `SensitiveData::maskName()` — tunjuk 3 aksara pertama sahaja. - Cipta view `public/applications/status.blade.php` memaparkan: nama disamarkan, jawatan dipohon, jawatan diluluskan (jika berbeza), pusat mengundi, badge status dengan warna, sebab penolakan (jika ditolak), nombor rujukan UUID. - Kemaskini `success.blade.php` dengan panel simpan pautan status dan butang salin URL ke clipboard. - Tambah 9 ujian baharu dalam `PublicApplicationStatusTest`: - submitted, assigned, rejected (dengan sebab), UUID tidak dikenali, deleted_by_ktm, soft-deleted — semuanya. - Nama penuh tidak dipaparkan, IC tidak dipaparkan. - Halaman boleh dicapai tanpa log masuk. - Success page paparkan pautan status. Fail berubah: - `app/Http/Controllers/PublicApplicationController.php` — tambah `status()` - `app/Support/SensitiveData.php` — tambah `maskName()` - `resources/views/public/applications/status.blade.php` (baharu) - `resources/views/public/applications/success.blade.php` — tambah panel pautan status - `routes/web.php` — route baharu - `tests/Feature/PublicApplicationStatusTest.php` (baharu) - `docs/05-workflow-design.md` - `docs/06-ui-ux-plan.md` - `docs/decision-log.md` - `docs/09-improvement-plan.md` - `docs/progress-log.md` - `docs/changelog.md` Semakan: - `php artisan test` — 112 tests, 364 assertions (**semua lulus**). - `vendor\bin\pint.bat --test` — lulus. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` — lulus, 0 error. Seterusnya: - Fasa 15: Purge automatik fail eksport sensitif. ## 2026-06-02 - Fasa 15-A: Purge Automatik Fail Eksport Sensitif Status: Selesai Selesai: - Tambah kolum `purged_at` (nullable timestamp) dan `purged_by_user_id` (nullable FK) pada `export_logs` melalui migration `2026_06_02_000001_add_purged_at_to_export_logs.php`. - Kemaskini model `ExportLog` dengan cast `purged_at` dan relationship `purgedBy()`. - Cipta Artisan command `ExportPurgeCommand` (`php artisan exports:purge --days=30`): - Query `export_logs` di mana `created_at < now - days` DAN `purged_at IS NULL`. - Padam fail dari storage disk yang berkaitan. - Rekod yang filenya sudah tiada di disk tetap ditandakan `purged_at` supaya tidak diproses semula. - Log aktiviti `export_file_purged` untuk setiap rekod. - Lapor bilangan fail dipadamkan dan rekod tiada fail. - Daftarkan command sebagai scheduled task harian pada `00:00` dalam `routes/console.php` (`--days=30`, withoutOverlapping, runInBackground). - Tambah 7 ujian baharu dalam `ExportPurgeCommandTest`: - Fail lama dipadamkan, `purged_at` ditetapkan. - Fail dalam tempoh simpanan tidak disentuh. - Rekod tanpa fail (`missing`) tetap ditandakan `purged_at`. - Rekod yang sudah `purged_at` tidak diproses semula. - `--days` custom berfungsi. - Activity log direkod. - `--days=0` gagal dengan exit code bukan 0. Fail berubah: - `database/migrations/2026_06_02_000001_add_purged_at_to_export_logs.php` (baharu) - `app/Models/ExportLog.php` — tambah cast + relationship - `app/Console/Commands/ExportPurgeCommand.php` (baharu) - `routes/console.php` — scheduled task - `tests/Feature/ExportPurgeCommandTest.php` (baharu) - `docs/03-database-design.md` - `docs/08-deployment-notes.md` - `docs/decision-log.md` - `docs/09-improvement-plan.md` - `docs/progress-log.md` - `docs/changelog.md` Semakan: - `php artisan migrate` — lulus. - `php artisan test` — 119 tests, 388 assertions (**semua lulus**). - `vendor\bin\pint.bat --test` — lulus. - `vendor\bin\phpstan.bat analyse --memory-limit=1G` — lulus, 0 error. Semua fasa selesai (12-A hingga 15-A). Sistem sedia untuk UAT.