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 })} />
)