[CLAUDE] FE-Admin: KHKK man nhap — header hang muc du an + doi nut them vao bang (FE-only, 0-mig)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m59s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m59s
Anh gui anh man nhap KHKK/2026/008, 3 diem khoanh do -> lam 2, bo 1:
(1) "Lam header duoi day": o HANG MUC (DU AN) doi tu muc 1 xuong lam header
cua muc 2. No la goi viec ma CA BANG thuoc ve, dung dau bang thi dung vai
hon la nam lan giua cac o meta.
- DAT NGOAI ternary lines.length===0: phieu chua co NCC trung thau van phai
biet dang lap ke hoach cho goi viec nao (man rong nhin van gon nen khong
ai keu — dung ca "vang-mat trong giong on").
(2) "+ them hang muc o day": khoi "Them hang muc cho:" doi TU DUOI BANG vao
thanh dong cuoi tbody, ngay tren dong TONG. colSpan=4 (bang da len 4 cot
tu dau luot S177). Panel nhap addFor VAN o ngoai bang — form nhieu o, nhet
vao <table> thi vo luoi table-fixed dang giu 3 cot so thang hang.
Da doi chieu khong nhan doi nut (3 hit "Them hang muc cho:" = 1 nut moi +
1 chu thich + 1 header panel, khac viec).
(3) "nut edit": anh chot BO ("a vay thoi khoi"). Man nhap da sua duoc TRUC TIEP
tren dong (dropdown + o so + o ghi chu, tu luu khi roi o) nen nut nay se la
duong sua thu hai song song. O trong o cot 4 la do dong den tu phieu Duyet
NCC (peReferenceAmount != 0) nen nut xoa bi chan CO CHU DICH tu S175 —
khong phai thieu nut.
GIU ma du an lap tren tung dong (anh: "de sau nay nguoi ta de hinh dung hang
muc to no chua cac hang muc con nao") — dung thiet ke chot dau luot S177.
Diff CHI 2 file FE, 0 file backend => .NET test khong the bi anh huong, bo qua
vong test do (khai ro). 2 app build sach · lint 0 keu file nay · SHA IDENTICAL.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@ -452,12 +452,10 @@ export function KhkkDetailContent({
|
||||
<Field label="Mã kế hoạch" value={plan.maKeHoach ?? '—'} mono />
|
||||
<Field label="Dự án" value={plan.projectName ?? '—'} />
|
||||
<Field label="Người soạn" value={plan.drafterFullName ?? '—'} />
|
||||
{/* [S175 — anh: "hạng mục của Dự án, Duyệt NCC ấy"] Đây là HẠNG MỤC CÔNG VIỆC của
|
||||
dự án, lấy từ phiếu Duyệt NCC nguồn (`PE.WorkItemId`).
|
||||
🔴 KHÁC hẳn `line.tenHangMuc` — cái đó là danh-mục SP-002 dùng phân loại HỢP
|
||||
ĐỒNG (86 dòng, chọn ở mục 2). Hai thứ trùng tên tiếng Việt nhưng khác trục;
|
||||
lượt trước lead bày nhầm cái SP-002 ở đây nên anh bảo "Bỏ đi". */}
|
||||
<Field label="Hạng mục (dự án)" value={plan.peWorkItemName ?? '—'} />
|
||||
{/* 🧊 [S177 — anh: "Làm header dưới đây"] Ô **Hạng mục (dự án)** ĐÃ DỜI xuống làm
|
||||
header của mục 2 (xem ngay dưới). Nó là gói việc mà CẢ BẢNG hạng mục thuộc về,
|
||||
nên đứng đầu bảng thì đúng vai hơn là nằm lẫn giữa các ô meta ở đây.
|
||||
🔸 Chú thích S175 cũ ("xếp NGAY DƯỚI Dự án ở cột PHẢI") hết hiệu lực theo. */}
|
||||
<Field label="Ngày tạo" value={formatDateTime(plan.createdAt)} />
|
||||
</div>
|
||||
|
||||
@ -489,6 +487,19 @@ export function KhkkDetailContent({
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
{/* [S177 — anh: "Làm header dưới đây"] HẠNG MỤC (DỰ ÁN) = gói việc mà CẢ bảng dưới
|
||||
thuộc về, dời từ mục 1 xuống đây làm header của mục 2.
|
||||
🔴 ĐẶT NGOÀI ternary `lines.length === 0`: phiếu chưa có NCC trúng thầu thì vẫn
|
||||
phải biết mình đang lập kế hoạch cho gói việc nào — nhét vào nhánh có-dòng là
|
||||
đúng ca "vắng-mặt trông giống ổn" (màn rỗng nhìn vẫn gọn nên không ai kêu). */}
|
||||
<div className="mb-3 flex flex-wrap items-baseline gap-x-2 gap-y-0.5 border-b border-slate-200 pb-2">
|
||||
<span className="label-eyebrow text-slate-500">Hạng mục (dự án)</span>
|
||||
{plan.peWorkItemCode && (
|
||||
<span className="font-mono text-[11px] font-medium text-slate-500">{plan.peWorkItemCode}</span>
|
||||
)}
|
||||
<span className="text-sm font-semibold text-slate-800">{plan.peWorkItemName ?? '—'}</span>
|
||||
</div>
|
||||
|
||||
{plan.lines.length === 0 ? (
|
||||
<div className="text-sm italic text-slate-400">Phiếu nguồn chưa có NCC trúng thầu.</div>
|
||||
) : (
|
||||
@ -608,6 +619,33 @@ export function KhkkDetailContent({
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{/* [S177 — anh: "+ thêm hạng mục ở đây"] Nút thêm DỜI TỪ DƯỚI BẢNG vào
|
||||
thành dòng cuối của `tbody` — ngay sau dòng dữ liệu cuối, TRÊN dòng TỔNG.
|
||||
🔴 `colSpan={4}` (không phải 3): bảng đã lên 4 cột từ đầu lượt S177.
|
||||
🔸 Panel nhập (`addFor`) VẪN Ở NGOÀI bảng — nó là form nhiều ô, nhét vào
|
||||
trong `<table>` thì vỡ lưới `table-fixed` đang giữ 3 cột số thẳng hàng. */}
|
||||
{isDraft && (
|
||||
<tr>
|
||||
<td colSpan={4} className="px-3 py-2">
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
<span className="text-[11px] text-slate-500">Thêm hạng mục cho:</span>
|
||||
{[...new Map(plan.lines.map(l => [l.supplierId, l])).values()].map((l) => (
|
||||
<button
|
||||
key={l.supplierId}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setAddFor({ supplierId: l.supplierId, supplierName: l.supplierName })
|
||||
setAddCatalogId(''); setAddAmount(''); setAddNote('')
|
||||
}}
|
||||
className="inline-flex items-center gap-1 rounded border border-brand-300 px-2 py-1 text-[11px] font-medium text-brand-700 transition hover:bg-brand-50"
|
||||
>
|
||||
<Plus className="h-3 w-3" /> {l.supplierName ?? '(không rõ NCC)'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
{/* [S175 — anh: "chọn hạng mục tiền → thành tổng"] TỔNG = Σ giá các dòng.
|
||||
🔴 KHÔNG có cột "giá tham chiếu" / "giá duyệt" nữa — anh chốt "ko cần so
|
||||
@ -628,29 +666,11 @@ export function KhkkDetailContent({
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* [S175 — anh chốt] THÊM HẠNG MỤC cho 1 NCC. 1 NCC được N hạng mục: máy đã cho
|
||||
phép từ Mig 71 (unique 3 cột `Plan × Supplier × CatalogEntry`), lượt này chỉ
|
||||
bung UI — đúng khuôn "máy rộng, UI hẹp" anh chốt S164.
|
||||
🔴 CHỈ liệt kê NCC ĐÃ CÓ trên phiếu: NCC đến từ winner của phiếu Duyệt NCC;
|
||||
đẻ NCC mới ở đây là ký với nhà thầu chưa ai chọn (BE cũng chặn, 409). */}
|
||||
{isDraft && (
|
||||
<div className="mt-3 flex flex-wrap items-center gap-1.5">
|
||||
<span className="text-[11px] text-slate-500">Thêm hạng mục cho:</span>
|
||||
{[...new Map(plan.lines.map(l => [l.supplierId, l])).values()].map((l) => (
|
||||
<button
|
||||
key={l.supplierId}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setAddFor({ supplierId: l.supplierId, supplierName: l.supplierName })
|
||||
setAddCatalogId(''); setAddAmount(''); setAddNote('')
|
||||
}}
|
||||
className="inline-flex items-center gap-1 rounded border border-brand-300 px-2 py-1 text-[11px] font-medium text-brand-700 transition hover:bg-brand-50"
|
||||
>
|
||||
<Plus className="h-3 w-3" /> {l.supplierName ?? '(không rõ NCC)'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{/* 🧊 [S177 — anh: "+ thêm hạng mục ở đây"] Khối "Thêm hạng mục cho:" ĐÃ DỜI vào
|
||||
trong `tbody` (dòng cuối, trên TỔNG). Lý lẽ S175 vẫn đúng và đi theo lên trên:
|
||||
1 NCC được N hạng mục từ Mig 71, và CHỈ liệt kê NCC ĐÃ CÓ trên phiếu — đẻ NCC
|
||||
mới ở đây là ký với nhà thầu chưa ai chọn (BE cũng chặn, 409).
|
||||
🔴 Giữ 1 dòng vết vì đây là chỗ người sau sẽ tìm khi hỏi "nút thêm đâu rồi". */}
|
||||
|
||||
{addFor && (
|
||||
<div className="mt-2 rounded-md border border-brand-200 bg-brand-50/50 p-3">
|
||||
|
||||
@ -452,12 +452,10 @@ export function KhkkDetailContent({
|
||||
<Field label="Mã kế hoạch" value={plan.maKeHoach ?? '—'} mono />
|
||||
<Field label="Dự án" value={plan.projectName ?? '—'} />
|
||||
<Field label="Người soạn" value={plan.drafterFullName ?? '—'} />
|
||||
{/* [S175 — anh: "hạng mục của Dự án, Duyệt NCC ấy"] Đây là HẠNG MỤC CÔNG VIỆC của
|
||||
dự án, lấy từ phiếu Duyệt NCC nguồn (`PE.WorkItemId`).
|
||||
🔴 KHÁC hẳn `line.tenHangMuc` — cái đó là danh-mục SP-002 dùng phân loại HỢP
|
||||
ĐỒNG (86 dòng, chọn ở mục 2). Hai thứ trùng tên tiếng Việt nhưng khác trục;
|
||||
lượt trước lead bày nhầm cái SP-002 ở đây nên anh bảo "Bỏ đi". */}
|
||||
<Field label="Hạng mục (dự án)" value={plan.peWorkItemName ?? '—'} />
|
||||
{/* 🧊 [S177 — anh: "Làm header dưới đây"] Ô **Hạng mục (dự án)** ĐÃ DỜI xuống làm
|
||||
header của mục 2 (xem ngay dưới). Nó là gói việc mà CẢ BẢNG hạng mục thuộc về,
|
||||
nên đứng đầu bảng thì đúng vai hơn là nằm lẫn giữa các ô meta ở đây.
|
||||
🔸 Chú thích S175 cũ ("xếp NGAY DƯỚI Dự án ở cột PHẢI") hết hiệu lực theo. */}
|
||||
<Field label="Ngày tạo" value={formatDateTime(plan.createdAt)} />
|
||||
</div>
|
||||
|
||||
@ -489,6 +487,19 @@ export function KhkkDetailContent({
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
{/* [S177 — anh: "Làm header dưới đây"] HẠNG MỤC (DỰ ÁN) = gói việc mà CẢ bảng dưới
|
||||
thuộc về, dời từ mục 1 xuống đây làm header của mục 2.
|
||||
🔴 ĐẶT NGOÀI ternary `lines.length === 0`: phiếu chưa có NCC trúng thầu thì vẫn
|
||||
phải biết mình đang lập kế hoạch cho gói việc nào — nhét vào nhánh có-dòng là
|
||||
đúng ca "vắng-mặt trông giống ổn" (màn rỗng nhìn vẫn gọn nên không ai kêu). */}
|
||||
<div className="mb-3 flex flex-wrap items-baseline gap-x-2 gap-y-0.5 border-b border-slate-200 pb-2">
|
||||
<span className="label-eyebrow text-slate-500">Hạng mục (dự án)</span>
|
||||
{plan.peWorkItemCode && (
|
||||
<span className="font-mono text-[11px] font-medium text-slate-500">{plan.peWorkItemCode}</span>
|
||||
)}
|
||||
<span className="text-sm font-semibold text-slate-800">{plan.peWorkItemName ?? '—'}</span>
|
||||
</div>
|
||||
|
||||
{plan.lines.length === 0 ? (
|
||||
<div className="text-sm italic text-slate-400">Phiếu nguồn chưa có NCC trúng thầu.</div>
|
||||
) : (
|
||||
@ -608,6 +619,33 @@ export function KhkkDetailContent({
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{/* [S177 — anh: "+ thêm hạng mục ở đây"] Nút thêm DỜI TỪ DƯỚI BẢNG vào
|
||||
thành dòng cuối của `tbody` — ngay sau dòng dữ liệu cuối, TRÊN dòng TỔNG.
|
||||
🔴 `colSpan={4}` (không phải 3): bảng đã lên 4 cột từ đầu lượt S177.
|
||||
🔸 Panel nhập (`addFor`) VẪN Ở NGOÀI bảng — nó là form nhiều ô, nhét vào
|
||||
trong `<table>` thì vỡ lưới `table-fixed` đang giữ 3 cột số thẳng hàng. */}
|
||||
{isDraft && (
|
||||
<tr>
|
||||
<td colSpan={4} className="px-3 py-2">
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
<span className="text-[11px] text-slate-500">Thêm hạng mục cho:</span>
|
||||
{[...new Map(plan.lines.map(l => [l.supplierId, l])).values()].map((l) => (
|
||||
<button
|
||||
key={l.supplierId}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setAddFor({ supplierId: l.supplierId, supplierName: l.supplierName })
|
||||
setAddCatalogId(''); setAddAmount(''); setAddNote('')
|
||||
}}
|
||||
className="inline-flex items-center gap-1 rounded border border-brand-300 px-2 py-1 text-[11px] font-medium text-brand-700 transition hover:bg-brand-50"
|
||||
>
|
||||
<Plus className="h-3 w-3" /> {l.supplierName ?? '(không rõ NCC)'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
{/* [S175 — anh: "chọn hạng mục tiền → thành tổng"] TỔNG = Σ giá các dòng.
|
||||
🔴 KHÔNG có cột "giá tham chiếu" / "giá duyệt" nữa — anh chốt "ko cần so
|
||||
@ -628,29 +666,11 @@ export function KhkkDetailContent({
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* [S175 — anh chốt] THÊM HẠNG MỤC cho 1 NCC. 1 NCC được N hạng mục: máy đã cho
|
||||
phép từ Mig 71 (unique 3 cột `Plan × Supplier × CatalogEntry`), lượt này chỉ
|
||||
bung UI — đúng khuôn "máy rộng, UI hẹp" anh chốt S164.
|
||||
🔴 CHỈ liệt kê NCC ĐÃ CÓ trên phiếu: NCC đến từ winner của phiếu Duyệt NCC;
|
||||
đẻ NCC mới ở đây là ký với nhà thầu chưa ai chọn (BE cũng chặn, 409). */}
|
||||
{isDraft && (
|
||||
<div className="mt-3 flex flex-wrap items-center gap-1.5">
|
||||
<span className="text-[11px] text-slate-500">Thêm hạng mục cho:</span>
|
||||
{[...new Map(plan.lines.map(l => [l.supplierId, l])).values()].map((l) => (
|
||||
<button
|
||||
key={l.supplierId}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setAddFor({ supplierId: l.supplierId, supplierName: l.supplierName })
|
||||
setAddCatalogId(''); setAddAmount(''); setAddNote('')
|
||||
}}
|
||||
className="inline-flex items-center gap-1 rounded border border-brand-300 px-2 py-1 text-[11px] font-medium text-brand-700 transition hover:bg-brand-50"
|
||||
>
|
||||
<Plus className="h-3 w-3" /> {l.supplierName ?? '(không rõ NCC)'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{/* 🧊 [S177 — anh: "+ thêm hạng mục ở đây"] Khối "Thêm hạng mục cho:" ĐÃ DỜI vào
|
||||
trong `tbody` (dòng cuối, trên TỔNG). Lý lẽ S175 vẫn đúng và đi theo lên trên:
|
||||
1 NCC được N hạng mục từ Mig 71, và CHỈ liệt kê NCC ĐÃ CÓ trên phiếu — đẻ NCC
|
||||
mới ở đây là ký với nhà thầu chưa ai chọn (BE cũng chặn, 409).
|
||||
🔴 Giữ 1 dòng vết vì đây là chỗ người sau sẽ tìm khi hỏi "nút thêm đâu rồi". */}
|
||||
|
||||
{addFor && (
|
||||
<div className="mt-2 rounded-md border border-brand-200 bg-brand-50/50 p-3">
|
||||
|
||||
Reference in New Issue
Block a user