From 169459e66f16dd114ca16ab9d201f3afce48f531 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Mon, 11 May 2026 10:53:30 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20FE-PE:=20NCC=20cell=20button=20visua?= =?UTF-8?q?l=20+=20H=E1=BA=A1ng=20m=E1=BB=A5c=20header=20g=E1=BB=99p=201?= =?UTF-8?q?=20=C3=B4=20Ng=C3=A2n=20s=C3=A1ch=20+=20DetailDialog=20r=C3=BAt?= =?UTF-8?q?=20g=E1=BB=8Dn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User Session 20 turn 5: 2 yêu cầu UX rõ ràng chỗ nhập tiền. FE-only mirror fe-admin + fe-user. 1. NCC grid cell "Số tiền" → button visual rõ ràng cho user biết là chỗ nhập: - Trước: trông như text cell (chỉ hover bg → user không biết click được) - Sau: + ) : ( +
+ {q ? `${fmtMoney(q.thanhTien)} đ` : } +
)} - title={!readOnly ? 'Click để nhập / sửa số tiền' : undefined} - > - {q ? fmtMoney(q.thanhTien) : } {!readOnly && ( @@ -1519,14 +1525,18 @@ function HangMucCard({ function DetailDialog({ evaluationId, row, onClose }: { evaluationId: string; row: PeDetailRow | null; onClose: () => void }) { const qc = useQueryClient() + // Session 20 turn 5: user yêu cầu rút gọn — chỉ Tên hạng mục + Số tiền + // ngân sách (VND format) + Ghi chú. Các field schema khác (groupCode/ + // groupName/itemCode/donViTinh/khoiLuongs/donGia) giữ default cho BE + // schema backward compat — KHÔNG expose UI cho user. const [form, setForm] = useState({ - groupCode: row?.groupCode ?? 'A.I', - groupName: row?.groupName ?? '', + groupCode: row?.groupCode ?? '01', + groupName: row?.groupName ?? 'Hạng mục chính', itemCode: row?.itemCode ?? '', noiDung: row?.noiDung ?? '', - donViTinh: row?.donViTinh ?? '', - khoiLuongNganSach: row?.khoiLuongNganSach ?? 0, - khoiLuongThiCong: row?.khoiLuongThiCong ?? 0, + donViTinh: row?.donViTinh ?? 'gói', + khoiLuongNganSach: row?.khoiLuongNganSach ?? 1, + khoiLuongThiCong: row?.khoiLuongThiCong ?? 1, donGiaNganSach: row?.donGiaNganSach ?? 0, thanhTienNganSach: row?.thanhTienNganSach ?? 0, ghiChu: row?.ghiChu ?? '', @@ -1540,11 +1550,10 @@ function DetailDialog({ evaluationId, row, onClose }: { evaluationId: string; ro onError: e => toast.error(getErrorMessage(e)), }) - const updateAndRecalc = (patch: Partial) => { - const next = { ...form, ...patch } - // Auto-compute ThanhTien = KL ngân sách × ĐG ngân sách - next.thanhTienNganSach = Number(next.khoiLuongNganSach) * Number(next.donGiaNganSach) - setForm(next) + // Sync ngân sách: user nhập "Số tiền ngân sách" → set cả donGia + thanhTien + // (KL = 1 ngầm). BE giữ schema 3 field. + const setBudgetAmount = (n: number) => { + setForm({ ...form, donGiaNganSach: n, thanhTienNganSach: n }) } return ( @@ -1552,24 +1561,38 @@ function DetailDialog({ evaluationId, row, onClose }: { evaluationId: string; ro open onClose={onClose} title={(row ? 'Sửa' : 'Thêm') + ' hạng mục'} - size="lg" footer={<> } >
-
-
setForm({ ...form, groupCode: e.target.value })} />
-
setForm({ ...form, groupName: e.target.value })} placeholder="Bê tông / Phụ gia..." />
-
setForm({ ...form, itemCode: e.target.value })} />
-
setForm({ ...form, noiDung: e.target.value })} />
-
setForm({ ...form, donViTinh: e.target.value })} />
-
updateAndRecalc({ khoiLuongNganSach: Number(e.target.value) })} />
-
setForm({ ...form, khoiLuongThiCong: Number(e.target.value) })} />
-
updateAndRecalc({ donGiaNganSach: Number(e.target.value) })} />
-
setForm({ ...form, thanhTienNganSach: Number(e.target.value) })} />
-
setForm({ ...form, ghiChu: e.target.value })} />
+
+ + setForm({ ...form, noiDung: e.target.value })} + placeholder="vd Cung cấp bê tông M250" + /> +
+
+ +
+ setBudgetAmount(parseVnd(e.target.value))} + placeholder="0" + className="pr-12 font-mono text-right" + /> + đ +
+

VND — nhập số, tự format dấu chấm ngàn (vd 1.000.000)

+
+
+ + setForm({ ...form, ghiChu: e.target.value })} />
diff --git a/fe-user/src/components/pe/PeDetailTabs.tsx b/fe-user/src/components/pe/PeDetailTabs.tsx index c853b99..5096a2d 100644 --- a/fe-user/src/components/pe/PeDetailTabs.tsx +++ b/fe-user/src/components/pe/PeDetailTabs.tsx @@ -1324,16 +1324,11 @@ function HangMucCard({
-
KL
-
{detail.khoiLuongNganSach}
-
-
-
ĐG ngân sách
-
{fmtMoney(detail.donGiaNganSach)}
-
-
-
Thành tiền NS
-
{fmtMoney(detail.thanhTienNganSach)}
+
Số tiền ngân sách
+
+ {fmtMoney(detail.thanhTienNganSach)} + đ +
{showBudgetCol && bgValue != null && (
@@ -1409,7 +1404,6 @@ function HangMucCard({ const hasQuotes = ev.details.some(dd => dd.quotes.some(qq => qq.purchaseEvaluationSupplierId === s.id)) const canDelete = !isWinner && !hasQuotes const openQuote = () => setQuoteEdit({ supplier: s, existing: q }) - const cellHover = !readOnly && 'cursor-pointer hover:bg-brand-50' return ( @@ -1438,16 +1432,28 @@ function HangMucCard({ readOnly={readOnly} /> - + {!readOnly ? ( + + ) : ( +
+ {q ? `${fmtMoney(q.thanhTien)} đ` : } +
)} - title={!readOnly ? 'Click để nhập / sửa số tiền' : undefined} - > - {q ? fmtMoney(q.thanhTien) : } {!readOnly && ( @@ -1519,14 +1525,18 @@ function HangMucCard({ function DetailDialog({ evaluationId, row, onClose }: { evaluationId: string; row: PeDetailRow | null; onClose: () => void }) { const qc = useQueryClient() + // Session 20 turn 5: user yêu cầu rút gọn — chỉ Tên hạng mục + Số tiền + // ngân sách (VND format) + Ghi chú. Các field schema khác (groupCode/ + // groupName/itemCode/donViTinh/khoiLuongs/donGia) giữ default cho BE + // schema backward compat — KHÔNG expose UI cho user. const [form, setForm] = useState({ - groupCode: row?.groupCode ?? 'A.I', - groupName: row?.groupName ?? '', + groupCode: row?.groupCode ?? '01', + groupName: row?.groupName ?? 'Hạng mục chính', itemCode: row?.itemCode ?? '', noiDung: row?.noiDung ?? '', - donViTinh: row?.donViTinh ?? '', - khoiLuongNganSach: row?.khoiLuongNganSach ?? 0, - khoiLuongThiCong: row?.khoiLuongThiCong ?? 0, + donViTinh: row?.donViTinh ?? 'gói', + khoiLuongNganSach: row?.khoiLuongNganSach ?? 1, + khoiLuongThiCong: row?.khoiLuongThiCong ?? 1, donGiaNganSach: row?.donGiaNganSach ?? 0, thanhTienNganSach: row?.thanhTienNganSach ?? 0, ghiChu: row?.ghiChu ?? '', @@ -1540,11 +1550,10 @@ function DetailDialog({ evaluationId, row, onClose }: { evaluationId: string; ro onError: e => toast.error(getErrorMessage(e)), }) - const updateAndRecalc = (patch: Partial) => { - const next = { ...form, ...patch } - // Auto-compute ThanhTien = KL ngân sách × ĐG ngân sách - next.thanhTienNganSach = Number(next.khoiLuongNganSach) * Number(next.donGiaNganSach) - setForm(next) + // Sync ngân sách: user nhập "Số tiền ngân sách" → set cả donGia + thanhTien + // (KL = 1 ngầm). BE giữ schema 3 field. + const setBudgetAmount = (n: number) => { + setForm({ ...form, donGiaNganSach: n, thanhTienNganSach: n }) } return ( @@ -1552,24 +1561,38 @@ function DetailDialog({ evaluationId, row, onClose }: { evaluationId: string; ro open onClose={onClose} title={(row ? 'Sửa' : 'Thêm') + ' hạng mục'} - size="lg" footer={<> } >
-
-
setForm({ ...form, groupCode: e.target.value })} />
-
setForm({ ...form, groupName: e.target.value })} placeholder="Bê tông / Phụ gia..." />
-
setForm({ ...form, itemCode: e.target.value })} />
-
setForm({ ...form, noiDung: e.target.value })} />
-
setForm({ ...form, donViTinh: e.target.value })} />
-
updateAndRecalc({ khoiLuongNganSach: Number(e.target.value) })} />
-
setForm({ ...form, khoiLuongThiCong: Number(e.target.value) })} />
-
updateAndRecalc({ donGiaNganSach: Number(e.target.value) })} />
-
setForm({ ...form, thanhTienNganSach: Number(e.target.value) })} />
-
setForm({ ...form, ghiChu: e.target.value })} />
+
+ + setForm({ ...form, noiDung: e.target.value })} + placeholder="vd Cung cấp bê tông M250" + /> +
+
+ +
+ setBudgetAmount(parseVnd(e.target.value))} + placeholder="0" + className="pr-12 font-mono text-right" + /> + đ +
+

VND — nhập số, tự format dấu chấm ngàn (vd 1.000.000)

+
+
+ + setForm({ ...form, ghiChu: e.target.value })} />