+ {/* 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.
+
+ {/* 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') && (
+
+ ← Đóng
+
+ )}
@@ -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 && (
+ {
+ if (confirm(`Xóa phiếu "${evaluation.tenGoiThau}"? Phiếu sẽ ẩn khỏi danh sách (soft-delete, không xóa hoàn toàn trong DB).`)) {
+ onDelete()
+ }
+ }}
+ className="gap-1.5 text-xs"
+ >
+ Xóa phiếu
+
+ )}
+
+ ✓ Các thay đổi đã tự động lưu khi chỉnh sửa từng phần.
+
-
- Lưu (đóng)
+ {
+ qc.invalidateQueries({ queryKey: ['pe-detail', evaluation.id] })
+ qc.invalidateQueries({ queryKey: ['pe-list'] })
+ toast.success('Đã lưu — sync server.')
+ }}
+ className="text-xs"
+ >
+ Lưu
{
@@ -926,7 +949,11 @@ function SuppliersTab({ ev, readOnly = false }: { ev: PeDetailBundle; readOnly?:
// User 2026-05-07: NCC đã được chọn (winner) → KHÔNG cho
// sửa/xóa (tránh thay đổi NCC đã chốt). Chỉ hiển thị
// checkmark active state.
+ // User 2026-05-07 (B11+): NCC đã có hạng mục báo giá (quotes
+ // entered in Section 4) → KHÔNG cho xóa (tránh mất báo giá đã nhập).
const isWinner = ev.selectedSupplierId === s.supplierId
+ const hasQuotes = ev.details.some(d => d.quotes.some(q => q.purchaseEvaluationSupplierId === s.id))
+ const canDelete = !isWinner && !hasQuotes
return (
+ {/* 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}