docs: add implementation plan for in-plan report submit/review bug fix
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
# 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 `<span>` 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 `<span>` in the template**
|
||||
|
||||
Find, in `make-year-plan-form.component.html`:
|
||||
```html
|
||||
<td class="td_class" [attr.rowspan]="cpPDetailsArray(i).length + cpADetailsArray(i).length" [hidden]="typeUrl != 20 && !(typeUrl == 12 && isListAllMode())">
|
||||
<div class="select-cell">
|
||||
<mat-checkbox
|
||||
(click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(item.value) : null"
|
||||
[checked]="selection.isSelected(item.value)"
|
||||
[aria-label]="checkboxLabel(item.value)">
|
||||
</mat-checkbox>
|
||||
<ng-container *ngIf="typeUrl == 12 && getSentCount(item.value) > 0">
|
||||
<span style="font-size:10px; background:#e65100; color:#fff; padding:2px 6px; border-radius:10px; display:block; text-align:center; margin-top:3px; white-space:nowrap;">แจ้งแล้ว {{getSentCount(item.value)}} ครั้ง</span>
|
||||
</ng-container>
|
||||
<button mat-icon-button *ngIf="typeUrl == 20 && item.value.agency_report?.agency_report_uid == null"
|
||||
(click)="addFormAgencyReport(item.value)"
|
||||
class="report-btn report-btn--add" title="เพิ่มรายงาน">
|
||||
<mat-icon>note_add</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button *ngIf="typeUrl == 20 && item.value.agency_report?.agency_report_uid != null"
|
||||
(click)="addFormAgencyReport(item.value)"
|
||||
class="report-btn report-btn--edit" title="แก้ไขรายงาน">
|
||||
<mat-icon>edit_document</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
```
|
||||
Change to:
|
||||
```html
|
||||
<td class="td_class" [attr.rowspan]="cpPDetailsArray(i).length + cpADetailsArray(i).length" [hidden]="typeUrl != 20 && !(typeUrl == 12 && isListAllMode())">
|
||||
<div class="select-cell">
|
||||
<mat-checkbox
|
||||
(click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(item.value) : null"
|
||||
[checked]="selection.isSelected(item.value)"
|
||||
[disabled]="typeUrl == 20 && (item.value.agency_report?.agency_report_uid == null || item.value.agency_report?.status_id === 1 || item.value.agency_report?.status_id === 3)"
|
||||
[aria-label]="checkboxLabel(item.value)">
|
||||
</mat-checkbox>
|
||||
<ng-container *ngIf="typeUrl == 12 && getSentCount(item.value) > 0">
|
||||
<span style="font-size:10px; background:#e65100; color:#fff; padding:2px 6px; border-radius:10px; display:block; text-align:center; margin-top:3px; white-space:nowrap;">แจ้งแล้ว {{getSentCount(item.value)}} ครั้ง</span>
|
||||
</ng-container>
|
||||
<button mat-icon-button *ngIf="typeUrl == 20 && item.value.agency_report?.agency_report_uid == null"
|
||||
(click)="addFormAgencyReport(item.value)"
|
||||
class="report-btn report-btn--add" title="เพิ่มรายงาน">
|
||||
<mat-icon>note_add</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button *ngIf="typeUrl == 20 && item.value.agency_report?.agency_report_uid != null"
|
||||
(click)="addFormAgencyReport(item.value)"
|
||||
class="report-btn report-btn--edit" title="แก้ไขรายงาน">
|
||||
<mat-icon>edit_document</mat-icon>
|
||||
</button>
|
||||
<span *ngIf="typeUrl == 20 && item.value.agency_report?.agency_report_uid != null"
|
||||
[style.background]="reportStatusBadge(item.value.agency_report?.status_id).bg"
|
||||
[style.color]="reportStatusBadge(item.value.agency_report?.status_id).color"
|
||||
style="display:inline-block;padding:2px 8px;border-radius:10px;font-size:10px;font-weight:600;margin-top:3px;white-space:nowrap;">
|
||||
{{reportStatusBadge(item.value.agency_report?.status_id).label}}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
```
|
||||
|
||||
- [ ] **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.
|
||||
Reference in New Issue
Block a user