# In-Plan Report Submit/Review Bug-Fix Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Fix the dead "ส่งให้กองแผนตรวจสอบ" button on `agency-report-form/list-all` (typeUrl 20) so it actually submits selected reports to the planning division, add a status badge + checkbox guard to that list, and lock the report form for the agency once it's submitted — mirroring the pattern already shipped for out-of-plan reports. **Architecture:** No new tables/endpoints/components. Three surgical edits to existing files: (1) one added boolean condition in `make-year-plant-form.component.ts`'s `save()` so typeUrl 20 stops being intercepted by the generic `isListAllMode()` branch and instead reaches its own already-correct `else if(this.typeUrl == 20)` block; (2) a `[disabled]` binding plus a new `reportStatusBadge()` helper and badge `` in the list-all checkbox column; (3) one added `if` block in `agency-report-form.component.ts`'s existing `typeUrl == 202` branch, copied from the identical pattern already present in the `typeUrl == 282` branch two branches below it. **Tech Stack:** Angular 17, Angular Material (`mat-checkbox`), RxJS (`tap`). ## Global Constraints - No backend (`rmutr-api`) changes — `AgencyReportService.updateStatusAgency()` (POST `update_status/1`) and `AgencyReportService.get()` already exist and already work (used today by the out-of-plan flow and by `check-project-report`). - typeUrl 12 and typeUrl 14 behavior in `make-year-plant-form.component.ts` must be **completely unaffected** — every change in this plan is gated so it only fires for `typeUrl === 20`. - Do not touch `check-project-report` (typeUrl 16) or any out-of-plan code (typeUrl 28x/29x) — both already work correctly per prior investigation and must not regress. - This repo has no meaningful unit-test culture for these Angular components. Per-task verification uses `ng build --configuration=production` (from `/Users/nut.looknut/Project/rmutr/rmutr-web`) to catch compile/template errors, plus a manual browser check via `npm start` for the final task. - Follow existing code conventions exactly: raw `Swal`/`this.swSV.confirmSave()`/`this.swSV.errText()` patterns already used in these files — don't introduce a new dialog library. --- ### Task 1: Fix the `save()` dispatch bug for typeUrl 20 **Files:** - Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plant-form.component.ts:2380` **Interfaces:** - Consumes: nothing new — `this.typeUrl` (existing component field), `this.isListAllMode()` (existing method, ts:159-161) - Produces: nothing new — this task only changes which existing branch of `save()` executes for typeUrl 20. The already-existing `else if(this.typeUrl == 20)` block (ts:2439-2508) becomes reachable; its `case 'add':` sub-branch (ts:2457-2478) is what will now run when the button is clicked from `list-all` (route has no `:id`, so `this.state` is always `'add'` there). - [ ] **Step 1: Change the guard condition** Find, in `make-year-plant-form.component.ts`: ```ts save(){ console.log(this.typeUrl) if(this.isListAllMode()){ ``` Change to: ```ts save(){ console.log(this.typeUrl) if(this.isListAllMode() && this.typeUrl !== 20){ ``` - [ ] **Step 2: Build to confirm no errors** Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && ng build --configuration=production` Expected: build succeeds with no new errors. - [ ] **Step 3: Commit** ```bash git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plant-form.component.ts git commit -m "fix: route typeUrl 20 list-all submit through the correct save() branch" ``` --- ### Task 2: Checkbox guard + status badge on the `list-all` report column **Files:** - Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plant-form.component.ts` (add helper method near `isListAllMode()`, ts:159-161) - Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plan-form.component.html:472-493` **Interfaces:** - Consumes: `item.value.agency_report.agency_report_uid` / `item.value.agency_report.status_id` (existing form data, already patched from backend — confirmed present at ts:1446, 1448 in the `agency_report` FormGroup builder) - Produces: `reportStatusBadge(status_id: number | null): {label: string, bg: string, color: string}` — a new public method on `MakeYearPlanFormComponent`/`MakeYearPlantFormComponent`, called only from the template in this task. No other task depends on it. - [ ] **Step 1: Add the `reportStatusBadge()` helper method** Find, in `make-year-plant-form.component.ts`: ```ts isListAllMode(): boolean { return this.state === 'add' && (this.typeUrl === 12 || this.typeUrl === 14 || this.typeUrl === 20) } ``` Change to: ```ts isListAllMode(): boolean { return this.state === 'add' && (this.typeUrl === 12 || this.typeUrl === 14 || this.typeUrl === 20) } reportStatusBadge(status_id: number | null): { label: string, bg: string, color: string } { switch (status_id) { case 1: return { label: 'รอตรวจสอบ', bg: '#fef3c7', color: '#92400e' } case 2: return { label: 'ส่งแก้ไข', bg: '#fee2e2', color: '#991b1b' } case 3: return { label: 'ตรวจสอบแล้ว', bg: '#dcfce7', color: '#166534' } default: return { label: 'ร่าง', bg: '#e5e7eb', color: '#374151' } } } ``` - [ ] **Step 2: Add `[disabled]` to the checkbox and the badge `` in the template** Find, in `make-year-plan-form.component.html`: ```html
แจ้งแล้ว {{getSentCount(item.value)}} ครั้ง
``` Change to: ```html
แจ้งแล้ว {{getSentCount(item.value)}} ครั้ง {{reportStatusBadge(item.value.agency_report?.status_id).label}}
``` - [ ] **Step 3: Build to confirm no errors** Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && ng build --configuration=production` Expected: build succeeds with no new errors. - [ ] **Step 4: Commit** ```bash git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plant-form.component.ts rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/make-year-plan-form/make-year-plan-form.component.html git commit -m "feat: add status badge and re-submit guard to list-all report checkboxes" ``` --- ### Task 3: Lock the in-plan report form once submitted **Files:** - Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/agency-report-form/agency-report-form.component.ts:469-508` **Interfaces:** - Consumes: `this.isLocked` (existing field, ts:180, already declared — no new field needed), `x.status_id` (existing `agency_report` field, already loaded by `AgencyReportSV.get()`) - Produces: nothing new — `isLocked` being `true` is already read by the existing template bindings at `agency-report-form.component.html:428` (locked banner) and `:430` (hides the "บันทึก" button), both unconditional on `typeUrl`, so they activate automatically for typeUrl `202`/`202163` once this task sets the flag. The `save_send()`/`save_pass()` buttons (gated by `typeUrl == 202161`) are untouched by this task and keep working because both methods call `getRawValue()`, which reads values from disabled controls. - [ ] **Step 1: Add the lock check to the `typeUrl == 202` branch** Find, in `agency-report-form.component.ts`: ```ts else if(this.typeUrl == 202 || this.typeUrl == 202161 || this.typeUrl == 202162 || this.typeUrl == 202163) { this.AgencyReportSV.get(this.uniquekey).pipe( tap((x:any)=>{ console.log(x) this.form.patchValue(x) this.change_year(x.budget_year_name_th) if (x.agency_report_items) { const formArray = this.form.get('agency_report_items') as FormArray x.agency_report_items.forEach((agency_report_item) => { formArray.push(this.agency_report_item_form(agency_report_item)) }); } if (x.agency_report_details) { x.agency_report_details.forEach((agency_report_detail) => { let form_agency_report_details = this.form.get('agency_report_details') as FormArray form_agency_report_details.push(this.agency_report_details_form(agency_report_detail)) }); const firstIssue = x.agency_report_details.find(d => d.type == 1 && d.text_1) if (firstIssue) this.change_budget_strategy_faculty_strategic_edit(firstIssue.text_1) } if (x.agency_report_popularities) { x.agency_report_popularities.forEach((p) => { const arr = this.form.get('agency_report_popularities') as FormArray arr.push(this.fb.group(p)) }) this.cdRef.detectChanges() } }), concatMap((x: any) => x.change_project_detail_uid ? this.ChangeProjectDetailSV.get(x.change_project_detail_uid) : of(null) ), tap((changeDetail: any) => { if (changeDetail?.project_name_th) { this.form.get('project_name_th').setValue(changeDetail.project_name_th) } Swal.close() }) ).subscribe() } ``` Change to: ```ts else if(this.typeUrl == 202 || this.typeUrl == 202161 || this.typeUrl == 202162 || this.typeUrl == 202163) { this.AgencyReportSV.get(this.uniquekey).pipe( tap((x:any)=>{ console.log(x) this.form.patchValue(x) this.change_year(x.budget_year_name_th) if (x.agency_report_items) { const formArray = this.form.get('agency_report_items') as FormArray x.agency_report_items.forEach((agency_report_item) => { formArray.push(this.agency_report_item_form(agency_report_item)) }); } if (x.agency_report_details) { x.agency_report_details.forEach((agency_report_detail) => { let form_agency_report_details = this.form.get('agency_report_details') as FormArray form_agency_report_details.push(this.agency_report_details_form(agency_report_detail)) }); const firstIssue = x.agency_report_details.find(d => d.type == 1 && d.text_1) if (firstIssue) this.change_budget_strategy_faculty_strategic_edit(firstIssue.text_1) } if (x.agency_report_popularities) { x.agency_report_popularities.forEach((p) => { const arr = this.form.get('agency_report_popularities') as FormArray arr.push(this.fb.group(p)) }) this.cdRef.detectChanges() } if (x.status_id === 1 || x.status_id === 3) { this.isLocked = true this.form.disable() } }), concatMap((x: any) => x.change_project_detail_uid ? this.ChangeProjectDetailSV.get(x.change_project_detail_uid) : of(null) ), tap((changeDetail: any) => { if (changeDetail?.project_name_th) { this.form.get('project_name_th').setValue(changeDetail.project_name_th) } Swal.close() }) ).subscribe() } ``` - [ ] **Step 2: Build to confirm no errors** Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && ng build --configuration=production` Expected: build succeeds with no new errors. - [ ] **Step 3: Commit** ```bash git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/agency-report-form/agency-report-form.component.ts git commit -m "feat: lock in-plan report form once submitted or reviewed" ``` --- ### Task 4: End-to-end manual verification **Files:** none (verification only) **Interfaces:** - Consumes: all of Tasks 1-3 - Produces: nothing — this is the final confirmation task for this plan - [ ] **Step 1: Start the dev server** Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && npm start` Expected: compiles, dev server serves on the configured local port. - [ ] **Step 2: Verify the list-all badge and checkbox guard** In the browser, log in and navigate to `app/agency-report-form/list-all` (typeUrl 20). Confirm: - Rows with a saved report but not yet submitted show a gray "ร่าง" badge and their checkbox is selectable. - Rows with no report at all show no badge and their checkbox is disabled. - [ ] **Step 3: Verify submit actually reaches the backend** Select 1-2 "ร่าง" rows → click "ส่งให้กองแผนตรวจสอบ" → confirm the dialog. Confirm: - No JS error in the browser console. - After the save completes and the list reloads, those rows' badges change to yellow "รอตรวจสอบ" and their checkboxes become disabled. - [ ] **Step 4: Verify the empty-selection error still fires** Deselect everything (or select only rows without a report) → click "ส่งให้กองแผนตรวจสอบ". Confirm the existing error message "กรุณาเลือกรายการที่ต้องการส่ง" appears and no request is sent. - [ ] **Step 5: Verify the report form locks for the agency once submitted** From `list-all`, open (แก้ไขรายงาน) one of the rows now in "รอตรวจสอบ" status. Confirm all fields are disabled and the "บันทึก" button is hidden, replaced by the "รายงานนี้ถูกล็อก ไม่สามารถแก้ไขได้" banner. - [ ] **Step 6: Verify `check-project-report` regression-free (planning division side)** Log in as (or switch to) a planning-division account and open `app/check-project-report`. Confirm the rows submitted in Step 3 appear under the "รอตรวจสอบ" tab. Click into one and click "ส่งแก้ไข". Confirm: - Status becomes 2 (ส่งแก้ไข). - Back on the agency's `list-all`, that row's badge is now red "ส่งแก้ไข" and its checkbox is selectable again. - Opening the report form for that row is now editable (not locked), since status_id is 2. - [ ] **Step 7: Verify "ตรวจสอบผ่าน" end state** Re-submit the same row from `list-all` (repeat Step 3). As the planning division, open it from `check-project-report`'s "รอตรวจสอบ" tab and click "ตรวจสอบผ่าน". Confirm status becomes 3, the row moves to the "ตรวจสอบแล้ว" tab, and on the agency's `list-all` the badge is now green "ตรวจสอบแล้ว" with the form permanently locked when reopened. - [ ] **Step 8: Regression check typeUrl 12/14 unaffected** If test access to a typeUrl 12 or 14 `list-all`-equivalent view is available, open it and confirm the existing save behavior (grouped `ChangeProjectSV.put()` per `change_project_uid`) is unchanged — no badge, no `[disabled]` guard, same button/label as before this plan.