[CLAUDE] PurchaseEvaluation: form tạo phiếu — ẩn mục 3/4/5 + auto-chọn quy trình duyệt (anh Kiệt FDC UAT R1)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m14s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m14s
D1 — ẩn mục 3 (NCC tham gia) / 4 (Hạng mục + Báo giá) / 5 (Ý kiến) + dòng 2a placeholder khỏi form TẠO (chỉ là "Lưu phiếu trước", gây rối) — mở khóa đầy đủ ở Detail tabs sau khi tạo. D5 — auto-chọn quy trình duyệt khi danh sách chỉ có 1 lựa chọn (hết nhầm "chưa chọn quy trình"; prod PE type-1 có đúng 1 workflow IsUserSelectable). Gỡ FormRow/LockedHint/Lock unused (TS6133). FE 2 app SHA-identical. Both build PASS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -6,10 +6,9 @@
|
|||||||
//
|
//
|
||||||
// Pattern user 2026-05-07: "Thêm mới list ra hết trường dữ liệu giống chỉnh
|
// Pattern user 2026-05-07: "Thêm mới list ra hết trường dữ liệu giống chỉnh
|
||||||
// sửa nhưng trống, mở rộng từng phần. Save header xong mới cho nhập chi tiết."
|
// sửa nhưng trống, mở rộng từng phần. Save header xong mới cho nhập chi tiết."
|
||||||
import { useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import { Lock } from 'lucide-react'
|
|
||||||
import { Button } from '@/components/ui/Button'
|
import { Button } from '@/components/ui/Button'
|
||||||
import { Input } from '@/components/ui/Input'
|
import { Input } from '@/components/ui/Input'
|
||||||
import { Label } from '@/components/ui/Label'
|
import { Label } from '@/components/ui/Label'
|
||||||
@ -103,6 +102,15 @@ export function PeWorkspaceCreateView({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// [D5 anh Kiệt FDC] Auto-chọn quy trình khi danh sách chỉ có 1 lựa chọn → hết nhầm
|
||||||
|
// "chưa chọn quy trình" (chỉ set khi user chưa tự chọn, giữ lựa chọn thủ công nếu có).
|
||||||
|
useEffect(() => {
|
||||||
|
const list = approvalWorkflows.data
|
||||||
|
if (list && list.length === 1 && !form.approvalWorkflowId) {
|
||||||
|
setForm(f => ({ ...f, approvalWorkflowId: list[0].id }))
|
||||||
|
}
|
||||||
|
}, [approvalWorkflows.data, form.approvalWorkflowId])
|
||||||
|
|
||||||
const budgetPayload = {
|
const budgetPayload = {
|
||||||
budgetPeriodAmount: form.budgetPeriodAmount > 0 ? form.budgetPeriodAmount : null,
|
budgetPeriodAmount: form.budgetPeriodAmount > 0 ? form.budgetPeriodAmount : null,
|
||||||
}
|
}
|
||||||
@ -242,11 +250,6 @@ export function PeWorkspaceCreateView({
|
|||||||
{/* Section 2 — Chọn NCC/TP (chỉ Ngân sách editable, còn lại sẽ unlock sau create) */}
|
{/* Section 2 — Chọn NCC/TP (chỉ Ngân sách editable, còn lại sẽ unlock sau create) */}
|
||||||
<Section title="2. Chọn NCC / TP">
|
<Section title="2. Chọn NCC / TP">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<FormRow
|
|
||||||
label="a. NCC / TP được chọn"
|
|
||||||
value={<span className="text-slate-400">— (sau khi thêm NCC tham gia + chốt winner)</span>}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* [S61 Mig 50] b. Ngân sách kỳ này — ô đơn thay picker Budget cũ +
|
{/* [S61 Mig 50] b. Ngân sách kỳ này — ô đơn thay picker Budget cũ +
|
||||||
toggle nhập tay (module Budget xóa hẳn). Số phân bổ cho RIÊNG
|
toggle nhập tay (module Budget xóa hẳn). Số phân bổ cho RIÊNG
|
||||||
phiếu này (row 3 bảng "Tổng hợp ngân sách trình ký"). */}
|
phiếu này (row 3 bảng "Tổng hợp ngân sách trình ký"). */}
|
||||||
@ -290,22 +293,9 @@ export function PeWorkspaceCreateView({
|
|||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
{/* Section 3 — NCC tham gia (locked) */}
|
{/* [D1 anh Kiệt FDC] Mục 3 (NCC tham gia) / 4 (Hạng mục + Báo giá) / 5 (Ý kiến) ẨN
|
||||||
<Section title="3. NCC / TP tham gia (0)">
|
khỏi form TẠO — chỉ là placeholder "Lưu phiếu trước", gây rối. Mở khóa đầy đủ
|
||||||
<LockedHint text="Lưu phiếu trước → thêm NCC + nhập thông tin liên hệ + chốt so sánh ở Detail tabs." />
|
ở Detail tabs sau khi tạo phiếu. */}
|
||||||
</Section>
|
|
||||||
|
|
||||||
{/* Section 4 — Hạng mục + Báo giá (locked) */}
|
|
||||||
<Section title="4. Hạng mục + Báo giá (0)">
|
|
||||||
<LockedHint text="Lưu phiếu trước → thêm hạng mục + nhập báo giá ma trận NCC × Item ở Detail tabs." />
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
{/* Section 5 — Ý kiến 4 phòng ban (locked + amber hint per Q5 user) */}
|
|
||||||
<Section title="5. Ý kiến 4 phòng ban (sign-off)">
|
|
||||||
<div className="rounded border border-amber-200 bg-amber-50 px-3 py-2 text-[12px] text-amber-800">
|
|
||||||
Ý kiến + chữ ký nhập khi duyệt phiếu — vào menu “Duyệt” để ký.
|
|
||||||
</div>
|
|
||||||
</Section>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action bar */}
|
{/* Action bar */}
|
||||||
@ -334,21 +324,3 @@ function Section({ title, children }: { title: string; children: React.ReactNode
|
|||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormRow({ label, value }: { label: string; value: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<div className="flex items-baseline gap-3 text-sm">
|
|
||||||
<span className="w-44 shrink-0 text-[12px] text-slate-500">{label}</span>
|
|
||||||
<div className="min-w-0 flex-1 text-slate-800">{value}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function LockedHint({ text }: { text: string }) {
|
|
||||||
return (
|
|
||||||
<div className="inline-flex items-center gap-1.5 rounded border border-dashed border-slate-300 bg-slate-50 px-3 py-1.5 text-[12px] text-slate-500">
|
|
||||||
<Lock className="h-3 w-3" />
|
|
||||||
{text}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -6,10 +6,9 @@
|
|||||||
//
|
//
|
||||||
// Pattern user 2026-05-07: "Thêm mới list ra hết trường dữ liệu giống chỉnh
|
// Pattern user 2026-05-07: "Thêm mới list ra hết trường dữ liệu giống chỉnh
|
||||||
// sửa nhưng trống, mở rộng từng phần. Save header xong mới cho nhập chi tiết."
|
// sửa nhưng trống, mở rộng từng phần. Save header xong mới cho nhập chi tiết."
|
||||||
import { useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import { Lock } from 'lucide-react'
|
|
||||||
import { Button } from '@/components/ui/Button'
|
import { Button } from '@/components/ui/Button'
|
||||||
import { Input } from '@/components/ui/Input'
|
import { Input } from '@/components/ui/Input'
|
||||||
import { Label } from '@/components/ui/Label'
|
import { Label } from '@/components/ui/Label'
|
||||||
@ -103,6 +102,15 @@ export function PeWorkspaceCreateView({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// [D5 anh Kiệt FDC] Auto-chọn quy trình khi danh sách chỉ có 1 lựa chọn → hết nhầm
|
||||||
|
// "chưa chọn quy trình" (chỉ set khi user chưa tự chọn, giữ lựa chọn thủ công nếu có).
|
||||||
|
useEffect(() => {
|
||||||
|
const list = approvalWorkflows.data
|
||||||
|
if (list && list.length === 1 && !form.approvalWorkflowId) {
|
||||||
|
setForm(f => ({ ...f, approvalWorkflowId: list[0].id }))
|
||||||
|
}
|
||||||
|
}, [approvalWorkflows.data, form.approvalWorkflowId])
|
||||||
|
|
||||||
const budgetPayload = {
|
const budgetPayload = {
|
||||||
budgetPeriodAmount: form.budgetPeriodAmount > 0 ? form.budgetPeriodAmount : null,
|
budgetPeriodAmount: form.budgetPeriodAmount > 0 ? form.budgetPeriodAmount : null,
|
||||||
}
|
}
|
||||||
@ -242,11 +250,6 @@ export function PeWorkspaceCreateView({
|
|||||||
{/* Section 2 — Chọn NCC/TP (chỉ Ngân sách editable, còn lại sẽ unlock sau create) */}
|
{/* Section 2 — Chọn NCC/TP (chỉ Ngân sách editable, còn lại sẽ unlock sau create) */}
|
||||||
<Section title="2. Chọn NCC / TP">
|
<Section title="2. Chọn NCC / TP">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<FormRow
|
|
||||||
label="a. NCC / TP được chọn"
|
|
||||||
value={<span className="text-slate-400">— (sau khi thêm NCC tham gia + chốt winner)</span>}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* [S61 Mig 50] b. Ngân sách kỳ này — ô đơn thay picker Budget cũ +
|
{/* [S61 Mig 50] b. Ngân sách kỳ này — ô đơn thay picker Budget cũ +
|
||||||
toggle nhập tay (module Budget xóa hẳn). Số phân bổ cho RIÊNG
|
toggle nhập tay (module Budget xóa hẳn). Số phân bổ cho RIÊNG
|
||||||
phiếu này (row 3 bảng "Tổng hợp ngân sách trình ký"). */}
|
phiếu này (row 3 bảng "Tổng hợp ngân sách trình ký"). */}
|
||||||
@ -290,22 +293,9 @@ export function PeWorkspaceCreateView({
|
|||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
{/* Section 3 — NCC tham gia (locked) */}
|
{/* [D1 anh Kiệt FDC] Mục 3 (NCC tham gia) / 4 (Hạng mục + Báo giá) / 5 (Ý kiến) ẨN
|
||||||
<Section title="3. NCC / TP tham gia (0)">
|
khỏi form TẠO — chỉ là placeholder "Lưu phiếu trước", gây rối. Mở khóa đầy đủ
|
||||||
<LockedHint text="Lưu phiếu trước → thêm NCC + nhập thông tin liên hệ + chốt so sánh ở Detail tabs." />
|
ở Detail tabs sau khi tạo phiếu. */}
|
||||||
</Section>
|
|
||||||
|
|
||||||
{/* Section 4 — Hạng mục + Báo giá (locked) */}
|
|
||||||
<Section title="4. Hạng mục + Báo giá (0)">
|
|
||||||
<LockedHint text="Lưu phiếu trước → thêm hạng mục + nhập báo giá ma trận NCC × Item ở Detail tabs." />
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
{/* Section 5 — Ý kiến 4 phòng ban (locked + amber hint per Q5 user) */}
|
|
||||||
<Section title="5. Ý kiến 4 phòng ban (sign-off)">
|
|
||||||
<div className="rounded border border-amber-200 bg-amber-50 px-3 py-2 text-[12px] text-amber-800">
|
|
||||||
Ý kiến + chữ ký nhập khi duyệt phiếu — vào menu “Duyệt” để ký.
|
|
||||||
</div>
|
|
||||||
</Section>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action bar */}
|
{/* Action bar */}
|
||||||
@ -334,21 +324,3 @@ function Section({ title, children }: { title: string; children: React.ReactNode
|
|||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormRow({ label, value }: { label: string; value: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<div className="flex items-baseline gap-3 text-sm">
|
|
||||||
<span className="w-44 shrink-0 text-[12px] text-slate-500">{label}</span>
|
|
||||||
<div className="min-w-0 flex-1 text-slate-800">{value}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function LockedHint({ text }: { text: string }) {
|
|
||||||
return (
|
|
||||||
<div className="inline-flex items-center gap-1.5 rounded border border-dashed border-slate-300 bg-slate-50 px-3 py-1.5 text-[12px] text-slate-500">
|
|
||||||
<Lock className="h-3 w-3" />
|
|
||||||
{text}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user