From 378c9939e616144a49565532be6b65106be45444 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Thu, 7 May 2026 16:37:04 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20FE-Admin+FE-User:=20PE=20detail=20po?= =?UTF-8?q?lish=20B12=20=E2=80=94=20L=C6=B0u=20(no=20close),=20X=C3=B3a=20?= =?UTF-8?q?phi=E1=BA=BFu,=20header=20bar=20simplify,=20NCC=20name=20col,?= =?UTF-8?q?=20no-delete=20c=C3=B3=20quotes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User feedback 2026-05-07 (annotation screenshot): 1. "Lưu" thay "Lưu (đóng)" — KHÔNG đóng workspace, chỉ toast + invalidate sync 2. Thêm nút "Xóa phiếu" bottom — CHỈ Bản nháp (DangSoanThao), KHÔNG xóa Trả lại (đã có lịch sử workflow). Soft-delete (AuditableEntity IsDeleted=true, không xóa hoàn toàn DB). 3. Bỏ nút "Sửa header" + "Đóng" + "Xóa" header bar workspace mode (chuyển xuống bottom action bar). Header bar chỉ còn nhóm display info + nút "Đóng" cho non-workspace view (Danh sách / Duyệt readOnly). 4. Section 4 column header NCC: dùng s.supplierName (master) thay vì displayName ?? supplierName (custom). displayName fallback sang title tooltip. 5. Section 3 row Xóa: nếu NCC đã có quotes (báo giá ở Section 4) → KHÔNG cho xóa (tránh mất báo giá). Hiển thị icon disabled + tooltip "xóa báo giá trước rồi mới xóa NCC". Implementation: ~ PeDetailTabs.tsx (× 2 app) - Header bar workspace mode actions: bỏ "Sửa header" Pencil button (có inline edit Section 1 + pencil hover Panel 1 thay thế), bỏ "Xóa" (chuyển xuống bottom). "Đóng" giữ chỉ cho readOnly + non-workspace view. - useNavigate import bỏ (chỉ dùng còn ở CreateContractDialog scope local). - Bottom action bar workspace + canEdit + !readOnly: * LEFT: "Xóa phiếu" red button (chỉ phase === DangSoanThao) + confirm dialog "soft-delete, không xóa hoàn toàn DB" + onDelete callback (existing DELETE /pe/:id endpoint, AuditableEntity IsDeleted=true). * CENTER: status text "✓ Các thay đổi đã tự động lưu khi chỉnh sửa..." * RIGHT: "Lưu" ghost button → invalidate ['pe-detail', id] + ['pe-list'] + toast "Đã lưu — sync server" (KHÔNG onBack — workspace stay open). * RIGHT: "Lưu & Gửi Duyệt →" giữ nguyên (POST transitions). - SuppliersTab row actions: hasQuotes computed (= ev.details.some(d => d.quotes.some(q => q.purchaseEvaluationSupplierId === s.id))). canDelete = !isWinner && !hasQuotes. Render Trash button enabled vs disabled span với tooltip "xóa báo giá trước". - ItemsTab matrix column header: {s.supplierName} (was {s.displayName ?? s.supplierName}). title attr giữ displayName tooltip. Verify: npm run build fe-admin + fe-user pass · 0 TS error · áp rule strict verify khi remove import + button/condition logic changes. UAT mode: skip dotnet test (FE-only), push ngay. Co-Authored-By: Claude Opus 4.7 (1M context) --- fe-admin/src/components/pe/PeDetailTabs.tsx | 119 +++++++++++++------- fe-user/src/components/pe/PeDetailTabs.tsx | 119 +++++++++++++------- 2 files changed, 156 insertions(+), 82 deletions(-) diff --git a/fe-admin/src/components/pe/PeDetailTabs.tsx b/fe-admin/src/components/pe/PeDetailTabs.tsx index f4fd7bc..1ccdd26 100644 --- a/fe-admin/src/components/pe/PeDetailTabs.tsx +++ b/fe-admin/src/components/pe/PeDetailTabs.tsx @@ -69,10 +69,10 @@ export function PeDetailTabs({ /** Auto open Section 1 InfoTab in edit mode khi mount — triggered từ pencil icon Panel 1 */ autoEditHeader?: boolean }) { - const navigate = useNavigate() const qc = useQueryClient() - // isDraft renamed → canEditPhase: bao gồm cả TraLai (per user 2026-05-07). - // Header bar action buttons (Sửa header / Xóa) hiện khi phase editable + !readOnly. + // canEditPhase: bao gồm cả TraLai (user 2026-05-07). Header bar action + // buttons "Sửa header" + "Xóa" + "Đóng" workspace mode đã chuyển xuống bottom + // action bar (B11+ user 2026-05-07). const canEditPhase = isEditablePhase(evaluation.phase) const opinionsReadOnly = readOnly || mode === 'workspace' @@ -137,19 +137,14 @@ export function PeDetailTabs({ {evaluation.drafterName && <>·Soạn: {evaluation.drafterName}} -
- {canEditPhase && !readOnly && ( - <> - - - - )} - -
+ {/* Header bar actions: User 2026-05-07 chốt bỏ "Sửa header" + "Xóa" + + "Đóng" (workspace mode actions chuyển xuống bottom action bar). Vẫn + giữ Đóng cho non-workspace view (Danh sách + Duyệt — readOnly). */} + {(readOnly || mode !== 'workspace') && ( +
+ +
+ )}
@@ -176,17 +171,45 @@ export function PeDetailTabs({
- {/* Action bar bottom — workspace mode + canEdit + !readOnly. 2 nút Lưu - (đóng workspace, các thay đổi đã auto-save inline) + Lưu & Gửi Duyệt - (POST /transitions → next phase, vào quy trình duyệt). User 2026-05-07. */} + {/* Action bar bottom — workspace mode + canEdit + !readOnly. 3 nút: + - Xóa phiếu (CHỈ Bản nháp, soft-delete BE) — bên trái red + - Lưu (toast confirm, KHÔNG đóng workspace) — chính giữa ghost + - Lưu & Gửi Duyệt → (POST /transitions → next phase) — bên phải brand + User 2026-05-07. */} {mode === 'workspace' && canEditPhase && !readOnly && (
-
- ✓ Các thay đổi đã tự động lưu khi chỉnh sửa từng phần. +
+ {/* Xóa phiếu — CHỈ DangSoanThao (bản nháp). TraLai không cho xóa + (đã có lịch sử workflow). Soft-delete qua DELETE /pe/:id endpoint + (AuditableEntity IsDeleted=true, không xóa hoàn toàn DB). */} + {evaluation.phase === PurchaseEvaluationPhase.DangSoanThao && ( + + )} + + ✓ Các thay đổi đã tự động lưu khi chỉnh sửa từng phần. +
- {!isWinner && ( - <> - - - + + )} + {canDelete ? ( + + ) : !isWinner && hasQuotes && ( + + + )}
@@ -1138,8 +1172,11 @@ function ItemsTab({ ev, readOnly = false }: { ev: PeDetailBundle; readOnly?: boo )} {ev.suppliers.map(s => ( - - {s.displayName ?? s.supplierName} + + {/* User 2026-05-07: dùng tên NCC (master) thay vì displayName + (custom name) để column header rõ ràng. displayName fallback + sang title tooltip nếu có. */} + {s.supplierName} ))} {!readOnly && } diff --git a/fe-user/src/components/pe/PeDetailTabs.tsx b/fe-user/src/components/pe/PeDetailTabs.tsx index f4fd7bc..1ccdd26 100644 --- a/fe-user/src/components/pe/PeDetailTabs.tsx +++ b/fe-user/src/components/pe/PeDetailTabs.tsx @@ -69,10 +69,10 @@ export function PeDetailTabs({ /** Auto open Section 1 InfoTab in edit mode khi mount — triggered từ pencil icon Panel 1 */ autoEditHeader?: boolean }) { - const navigate = useNavigate() const qc = useQueryClient() - // isDraft renamed → canEditPhase: bao gồm cả TraLai (per user 2026-05-07). - // Header bar action buttons (Sửa header / Xóa) hiện khi phase editable + !readOnly. + // canEditPhase: bao gồm cả TraLai (user 2026-05-07). Header bar action + // buttons "Sửa header" + "Xóa" + "Đóng" workspace mode đã chuyển xuống bottom + // action bar (B11+ user 2026-05-07). const canEditPhase = isEditablePhase(evaluation.phase) const opinionsReadOnly = readOnly || mode === 'workspace' @@ -137,19 +137,14 @@ export function PeDetailTabs({ {evaluation.drafterName && <>·Soạn: {evaluation.drafterName}}
-
- {canEditPhase && !readOnly && ( - <> - - - - )} - -
+ {/* Header bar actions: User 2026-05-07 chốt bỏ "Sửa header" + "Xóa" + + "Đóng" (workspace mode actions chuyển xuống bottom action bar). Vẫn + giữ Đóng cho non-workspace view (Danh sách + Duyệt — readOnly). */} + {(readOnly || mode !== 'workspace') && ( +
+ +
+ )}
@@ -176,17 +171,45 @@ export function PeDetailTabs({
- {/* Action bar bottom — workspace mode + canEdit + !readOnly. 2 nút Lưu - (đóng workspace, các thay đổi đã auto-save inline) + Lưu & Gửi Duyệt - (POST /transitions → next phase, vào quy trình duyệt). User 2026-05-07. */} + {/* Action bar bottom — workspace mode + canEdit + !readOnly. 3 nút: + - Xóa phiếu (CHỈ Bản nháp, soft-delete BE) — bên trái red + - Lưu (toast confirm, KHÔNG đóng workspace) — chính giữa ghost + - Lưu & Gửi Duyệt → (POST /transitions → next phase) — bên phải brand + User 2026-05-07. */} {mode === 'workspace' && canEditPhase && !readOnly && (
-
- ✓ Các thay đổi đã tự động lưu khi chỉnh sửa từng phần. +
+ {/* Xóa phiếu — CHỈ DangSoanThao (bản nháp). TraLai không cho xóa + (đã có lịch sử workflow). Soft-delete qua DELETE /pe/:id endpoint + (AuditableEntity IsDeleted=true, không xóa hoàn toàn DB). */} + {evaluation.phase === PurchaseEvaluationPhase.DangSoanThao && ( + + )} + + ✓ Các thay đổi đã tự động lưu khi chỉnh sửa từng phần. +
- {!isWinner && ( - <> - - - + + )} + {canDelete ? ( + + ) : !isWinner && hasQuotes && ( + + + )}
@@ -1138,8 +1172,11 @@ function ItemsTab({ ev, readOnly = false }: { ev: PeDetailBundle; readOnly?: boo )} {ev.suppliers.map(s => ( - - {s.displayName ?? s.supplierName} + + {/* User 2026-05-07: dùng tên NCC (master) thay vì displayName + (custom name) để column header rõ ràng. displayName fallback + sang title tooltip nếu có. */} + {s.supplierName} ))} {!readOnly && }