From e03314e2e735394ef54e202bc80924a98fb4d84b Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Mon, 11 May 2026 10:41:48 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20FE-PE:=20NCC=20table=201=20c?= =?UTF-8?q?=E1=BB=99t=20"S=E1=BB=91=20ti=E1=BB=81n"=20+=20QuoteDialog=201?= =?UTF-8?q?=20input=20=C4=91=C6=A1n=20gi=E1=BA=A3n=20h=C3=B3a?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User Session 20 turn 3: "Tạm thời chỉ cần nhập số tiền vào là đc, không cần 3 cột có VAT / ko VAT / tổng. 2 cột kia ẩn đi, chỉ 1 cột nhập tiền duy nhất." FE-only mirror fe-admin + fe-user: NCC inline table HangMucCard — bỏ 2 th + 2 td: Trước: NCC | Liên hệ | Điều khoản TT | File báo giá | ĐG chưa VAT | ĐG có VAT | Thành tiền | Action Sau: NCC | Liên hệ | Điều khoản TT | File báo giá | Số tiền | Action QuoteDialog — đơn giản hóa form: Trước: 3 input (Đơn giá chưa VAT / ĐG có VAT / Thành tiền auto-calc) + Ghi chú + display khoiLuong info Sau: 1 input "Số tiền" (autoFocus) — map thẳng vào thanhTien field Schema BE giữ nguyên (bgVat / chuaVat / note vẫn POST): - Row mới: bgVat=0, chuaVat=0, note='' - Existing: giữ giá trị cũ Bỏ prop khoiLuong (không dùng — không còn auto-calc thanhTien = chuaVat × khoiLuong) Bỏ updateAndRecalc helper KHÔNG đụng schema BE — endpoint POST /purchase-evaluations/{id}/quotes giữ nguyên payload shape, chỉ FE rút gọn input mặt người dùng nhập. Verify: - npm run build × fe-admin pass - npm run build × fe-user pass Co-Authored-By: Claude Opus 4.7 (1M context) --- fe-admin/src/components/pe/PeDetailTabs.tsx | 67 ++++++++------------- fe-user/src/components/pe/PeDetailTabs.tsx | 67 ++++++++------------- 2 files changed, 48 insertions(+), 86 deletions(-) diff --git a/fe-admin/src/components/pe/PeDetailTabs.tsx b/fe-admin/src/components/pe/PeDetailTabs.tsx index fe96286..4af4413 100644 --- a/fe-admin/src/components/pe/PeDetailTabs.tsx +++ b/fe-admin/src/components/pe/PeDetailTabs.tsx @@ -1334,9 +1334,7 @@ function HangMucCard({ Liên hệ Điều khoản TT File báo giá - ĐG chưa VAT - ĐG có VAT - Thành tiền + Số tiền {!readOnly && } @@ -1374,18 +1372,6 @@ function HangMucCard({ readOnly={readOnly} /> - - {q ? fmtMoney(q.chuaVat) : } - - - {q ? fmtMoney(q.bgVat) : } - {q ? fmtMoney(q.thanhTien) : } @@ -1457,7 +1443,6 @@ function HangMucCard({ supplierRowId={quoteEdit.supplier.id} supplierName={quoteEdit.supplier.supplierName} itemName={detail.noiDung} - khoiLuong={detail.khoiLuongThiCong || detail.khoiLuongNganSach} existing={quoteEdit.existing} onClose={() => setQuoteEdit(null)} /> @@ -1526,49 +1511,43 @@ function DetailDialog({ evaluationId, row, onClose }: { evaluationId: string; ro } function QuoteDialog({ - evaluationId, detailId, supplierRowId, supplierName, itemName, khoiLuong, existing, onClose, + evaluationId, detailId, supplierRowId, supplierName, itemName, existing, onClose, }: { evaluationId: string detailId: string supplierRowId: string supplierName: string itemName: string - khoiLuong: number existing: PeQuote | null onClose: () => void }) { const qc = useQueryClient() - // User 2026-05-07: Bỏ `isSelected` checkbox per-quote (consolidate winner - // selection ở Section 2.a NccSelectorRow). BE vẫn nhận isSelected nhưng FE - // luôn gửi `false` (existing.isSelected nếu có để giữ nguyên trạng thái cũ). + // Session 20 turn 3: user yêu cầu "tạm thời chỉ cần nhập số tiền, không + // cần 3 cột có VAT / không VAT / tổng". UI chỉ 1 input thanhTien; bgVat / + // chuaVat / note vẫn gửi BE giữ schema (default 0 / empty cho row mới, + // giữ giá trị cũ nếu existing). const [form, setForm] = useState({ - bgVat: existing?.bgVat ?? 0, - chuaVat: existing?.chuaVat ?? 0, thanhTien: existing?.thanhTien ?? 0, - note: existing?.note ?? '', }) - const updateAndRecalc = (patch: Partial) => { - const next = { ...form, ...patch } - next.thanhTien = Number(next.chuaVat) * khoiLuong - setForm(next) - } - const mut = useMutation({ mutationFn: async () => api.post(`/purchase-evaluations/${evaluationId}/quotes`, { purchaseEvaluationDetailId: detailId, purchaseEvaluationSupplierId: supplierRowId, - ...form, - isSelected: existing?.isSelected ?? false, // giữ nguyên trạng thái cũ, không expose UI + bgVat: existing?.bgVat ?? 0, + chuaVat: existing?.chuaVat ?? 0, + thanhTien: form.thanhTien, + note: existing?.note ?? '', + isSelected: existing?.isSelected ?? false, }), - onSuccess: () => { toast.success('Đã lưu báo giá.'); qc.invalidateQueries({ queryKey: ['pe-detail', evaluationId] }); onClose() }, + onSuccess: () => { toast.success('Đã lưu số tiền.'); qc.invalidateQueries({ queryKey: ['pe-detail', evaluationId] }); onClose() }, onError: e => toast.error(getErrorMessage(e)), }) const del = useMutation({ mutationFn: async () => existing ? api.delete(`/purchase-evaluations/${evaluationId}/quotes/${existing.id}`) : Promise.resolve(), - onSuccess: () => { toast.success('Đã xóa báo giá.'); qc.invalidateQueries({ queryKey: ['pe-detail', evaluationId] }); onClose() }, + onSuccess: () => { toast.success('Đã xóa.'); qc.invalidateQueries({ queryKey: ['pe-detail', evaluationId] }); onClose() }, onError: e => toast.error(getErrorMessage(e)), }) @@ -1585,25 +1564,27 @@ function QuoteDialog({ } > - {/* Loading overlay khi save có delay (user 2026-05-07) */}
{isSaving && (
- {mut.isPending ? 'Đang lưu báo giá…' : 'Đang xóa…'} + {mut.isPending ? 'Đang lưu…' : 'Đang xóa…'}
)} -

Hạng mục: {itemName} · KL {khoiLuong}

-
-
updateAndRecalc({ chuaVat: Number(e.target.value) })} />
-
setForm({ ...form, bgVat: Number(e.target.value) })} />
-
setForm({ ...form, thanhTien: Number(e.target.value) })} />
+

Hạng mục: {itemName}

+
+ + setForm({ thanhTien: Number(e.target.value) })} + autoFocus + />
-
setForm({ ...form, note: e.target.value })} />
) diff --git a/fe-user/src/components/pe/PeDetailTabs.tsx b/fe-user/src/components/pe/PeDetailTabs.tsx index fe96286..4af4413 100644 --- a/fe-user/src/components/pe/PeDetailTabs.tsx +++ b/fe-user/src/components/pe/PeDetailTabs.tsx @@ -1334,9 +1334,7 @@ function HangMucCard({ Liên hệ Điều khoản TT File báo giá - ĐG chưa VAT - ĐG có VAT - Thành tiền + Số tiền {!readOnly && } @@ -1374,18 +1372,6 @@ function HangMucCard({ readOnly={readOnly} /> - - {q ? fmtMoney(q.chuaVat) : } - - - {q ? fmtMoney(q.bgVat) : } - {q ? fmtMoney(q.thanhTien) : } @@ -1457,7 +1443,6 @@ function HangMucCard({ supplierRowId={quoteEdit.supplier.id} supplierName={quoteEdit.supplier.supplierName} itemName={detail.noiDung} - khoiLuong={detail.khoiLuongThiCong || detail.khoiLuongNganSach} existing={quoteEdit.existing} onClose={() => setQuoteEdit(null)} /> @@ -1526,49 +1511,43 @@ function DetailDialog({ evaluationId, row, onClose }: { evaluationId: string; ro } function QuoteDialog({ - evaluationId, detailId, supplierRowId, supplierName, itemName, khoiLuong, existing, onClose, + evaluationId, detailId, supplierRowId, supplierName, itemName, existing, onClose, }: { evaluationId: string detailId: string supplierRowId: string supplierName: string itemName: string - khoiLuong: number existing: PeQuote | null onClose: () => void }) { const qc = useQueryClient() - // User 2026-05-07: Bỏ `isSelected` checkbox per-quote (consolidate winner - // selection ở Section 2.a NccSelectorRow). BE vẫn nhận isSelected nhưng FE - // luôn gửi `false` (existing.isSelected nếu có để giữ nguyên trạng thái cũ). + // Session 20 turn 3: user yêu cầu "tạm thời chỉ cần nhập số tiền, không + // cần 3 cột có VAT / không VAT / tổng". UI chỉ 1 input thanhTien; bgVat / + // chuaVat / note vẫn gửi BE giữ schema (default 0 / empty cho row mới, + // giữ giá trị cũ nếu existing). const [form, setForm] = useState({ - bgVat: existing?.bgVat ?? 0, - chuaVat: existing?.chuaVat ?? 0, thanhTien: existing?.thanhTien ?? 0, - note: existing?.note ?? '', }) - const updateAndRecalc = (patch: Partial) => { - const next = { ...form, ...patch } - next.thanhTien = Number(next.chuaVat) * khoiLuong - setForm(next) - } - const mut = useMutation({ mutationFn: async () => api.post(`/purchase-evaluations/${evaluationId}/quotes`, { purchaseEvaluationDetailId: detailId, purchaseEvaluationSupplierId: supplierRowId, - ...form, - isSelected: existing?.isSelected ?? false, // giữ nguyên trạng thái cũ, không expose UI + bgVat: existing?.bgVat ?? 0, + chuaVat: existing?.chuaVat ?? 0, + thanhTien: form.thanhTien, + note: existing?.note ?? '', + isSelected: existing?.isSelected ?? false, }), - onSuccess: () => { toast.success('Đã lưu báo giá.'); qc.invalidateQueries({ queryKey: ['pe-detail', evaluationId] }); onClose() }, + onSuccess: () => { toast.success('Đã lưu số tiền.'); qc.invalidateQueries({ queryKey: ['pe-detail', evaluationId] }); onClose() }, onError: e => toast.error(getErrorMessage(e)), }) const del = useMutation({ mutationFn: async () => existing ? api.delete(`/purchase-evaluations/${evaluationId}/quotes/${existing.id}`) : Promise.resolve(), - onSuccess: () => { toast.success('Đã xóa báo giá.'); qc.invalidateQueries({ queryKey: ['pe-detail', evaluationId] }); onClose() }, + onSuccess: () => { toast.success('Đã xóa.'); qc.invalidateQueries({ queryKey: ['pe-detail', evaluationId] }); onClose() }, onError: e => toast.error(getErrorMessage(e)), }) @@ -1585,25 +1564,27 @@ function QuoteDialog({ } > - {/* Loading overlay khi save có delay (user 2026-05-07) */}
{isSaving && (
- {mut.isPending ? 'Đang lưu báo giá…' : 'Đang xóa…'} + {mut.isPending ? 'Đang lưu…' : 'Đang xóa…'}
)} -

Hạng mục: {itemName} · KL {khoiLuong}

-
-
updateAndRecalc({ chuaVat: Number(e.target.value) })} />
-
setForm({ ...form, bgVat: Number(e.target.value) })} />
-
setForm({ ...form, thanhTien: Number(e.target.value) })} />
+

Hạng mục: {itemName}

+
+ + setForm({ thanhTien: Number(e.target.value) })} + autoFocus + />
-
setForm({ ...form, note: e.target.value })} />
)