72 lines
5.0 KiB
Markdown
72 lines
5.0 KiB
Markdown
# sub-d2-fe-2 — ĐỢT 2 FRONTEND (nút Xóa màn duyệt + menu + màn "Đã xóa")
|
||
|
||
- **role:** implementer-frontend · **run:** `2026-07-27-S155-pe-delete-approver` · **đợt:** 2
|
||
- **scope:** `fe-admin/src/**` + `fe-user/src/**` ONLY. KHÔNG chạm `src/Backend/**`, KHÔNG chạm `tests/`.
|
||
- **ghi TRONG LÚC LÀM** (flush từng phần, tự `wc -c` sau mỗi flush).
|
||
|
||
---
|
||
|
||
## 0. Nền đã ĐO (trước khi sửa) — số thật, không phải trí nhớ
|
||
|
||
| Đo | Kết quả |
|
||
|---|---|
|
||
| `PeWorkflowPanel.tsx` md5 ×2 app | `02671be6334110028c55fe82f5e70dee` (IDENTICAL, 942 dòng) |
|
||
| `PurchaseEvaluationsListPage.tsx` md5 ×2 app | `99e38e167d2f745ea7628174f1d2d6bf` (IDENTICAL — **bonus**, spec không nói) |
|
||
| `usePermission.ts` md5 ×2 app | `2b210b753e8fe2704407c787cb82779d` (IDENTICAL) |
|
||
| `Layout.tsx` | **DIVERGE** admin `ca59f56c…` / user `cab674f4…` (đúng như thiết kế — 2 app khác staticMap) |
|
||
| `lib/menuKeys.ts` | DIVERGE đúng 2 dòng (`Off_AttendanceReport` admin-only) |
|
||
| `git status` lúc bắt đầu | sạch (chỉ `.session-counter.json`) ⇒ **lane BE chưa land gì** |
|
||
|
||
## 1. 🔴 PHỤ THUỘC LIÊN-LANE — FE gọi gì, BE phải có gì
|
||
|
||
Hợp-đồng `run.md:139-154` chốt 3 route + 2 menu-key. FE bám ĐÚNG. **NHƯNG** hợp-đồng
|
||
**không** đặt tên cho 2 thứ FE bắt buộc phải có — khai ở đây để lead verify, KHÔNG tự chế lung tung:
|
||
|
||
### (D1) Cờ F6 per-NGƯỜI trong bundle chi tiết phiếu
|
||
- FE đọc: `evaluation.currentLevelOptions.allowApproverDelete`
|
||
- BE PHẢI thêm: `bool AllowApproverDelete` làm **member positional thứ 9** của
|
||
`ApprovalWorkflowOptionsDto` (`src/Backend/…/PurchaseEvaluations/Dtos/PurchaseEvaluationDtos.cs:123-131`)
|
||
+ gán từ `curLevel.AllowApproverDelete` tại `PurchaseEvaluationFeatures.cs:1088-1096`.
|
||
- Vì sao chỗ này mà không phải chỗ khác: `currentLevelOptions` là kênh **DUY NHẤT** per-NGƯỜI —
|
||
nó resolve bằng `l.ApproverUserId == currentUser.UserId` (`PurchaseEvaluationFeatures.cs:1083-1085`).
|
||
Kênh còn lại `approvalFlow.steps[].levels[].allowApproverFinalize` là **OR-of-N per-CẤP**
|
||
(`:1182 g.Any(...)`) — dùng nó là vi phạm B2 (`run.md:159` per-ROW).
|
||
- **Nếu BE quên:** field về `undefined` → gate `=== true` false → **nút KHÔNG BAO GIỜ HIỆN**,
|
||
build 2 bên vẫn xanh. Đúng dạng silent-drop đợt 1. ⇒ lead phải grep xác nhận.
|
||
|
||
### (D2) Chỗ chứa "lý do xóa" trong request
|
||
- Hợp-đồng chỉ ghi `DELETE /api/purchase-evaluations/{id}/by-approver`, **không** nói payload.
|
||
- FE gửi **CẢ HAI** đường cho tương thích mọi kiểu binding BE:
|
||
`api.delete(url, { params: { reason }, data: { reason } })`
|
||
→ BE bind `[FromQuery] string? reason` ✅ · bind `[FromBody] {Reason}` ✅ · không nhận gì ✅ (bị bỏ qua).
|
||
- Chủ ý: thà thừa 1 query-param còn hơn mất vết audit vì lệch tên. Ghi rõ để reviewer không tưởng là cẩu thả.
|
||
|
||
### (D3) DTO list "Đã xóa"
|
||
- FE khai `PeDeletedListItem = PeListItem + { deletedAt?, deletedBy?, deletedByName? }` — **tất cả optional**,
|
||
render phòng thủ (`—` khi thiếu). BE trả thêm/thiếu đều không vỡ. Tên bám `AuditableEntity`
|
||
(`src/Backend/SolutionErp.Domain/Common/AuditableEntity.cs:5-7`: `IsDeleted`/`DeletedAt`/`DeletedBy`).
|
||
|
||
## 2. 🚩 FLAG — mục "vá `WfView` cho fe-admin" TÔI KHÔNG LÀM (tiền-đề của lệnh sai)
|
||
|
||
Lệnh: *"vá lệch sẵn có: `fe-admin:107` THIẾU `WfView` so với `fe-user:120`"*.
|
||
Đo trước khi làm thì tiền-đề **không đứng**:
|
||
|
||
| Bằng chứng | fe-user | fe-admin |
|
||
|---|---|---|
|
||
| Page `pages/pe/WorkflowMatrixViewPage.tsx` | CÓ (323 dòng) | **KHÔNG CÓ** |
|
||
| Route `/purchase-evaluations/workflow-matrix` | `App.tsx:60` | **KHÔNG CÓ** (`App.tsx:78-81` chỉ 4 route PE) |
|
||
| Types `types/approvalWorkflowV2.ts` | CÓ | **KHÔNG CÓ** (admin để type inline trong `ApprovalWorkflowsV2Page.tsx`) |
|
||
| Catch-all | — | `App.tsx:107-114` render *"Trang này chưa được build"* |
|
||
|
||
⇒ Thêm `WfView` vào regex admin **một mình** = đẻ link chết dẫn tới "Trang này chưa được build".
|
||
Đó là **regression nhìn thấy được**, tệ hơn trạng-thái-hiện-tại (leaf bị drop im lặng).
|
||
Sửa cho đúng cần **3 file mới bên admin** (page + types + route) = ngoài phạm vi đợt 2, và là
|
||
lựa-chọn UX (admin nên thấy matrix read-only hay Designer sửa được?) ⇒ **thuộc quyền owner/lead**.
|
||
|
||
**3 đường cho lead chọn (đều ≤ 1 lượt):**
|
||
- **(a)** mirror hẳn: copy `WorkflowMatrixViewPage.tsx` + `types/approvalWorkflowV2.ts` sang admin + route + regex.
|
||
- **(b)** rẻ nhất: admin map `Pe_{code}_WfView` → `/system/approval-workflows-v2/{code}` (Designer đã sống, `Layout.tsx:130-136`).
|
||
- **(c)** giữ nguyên: coi lệch này là CỐ Ý (admin có Designer rồi, không cần bản read-only) → ghi 1 dòng comment cho người sau khỏi "vá" lại.
|
||
|
||
Tôi để nguyên trạng (đường **c** *de facto*) + không chèn comment tránh đụng byte thừa. Chờ lead.
|