// Workspace 2-panel cho leaf "Thao tác" Pe_*_Create (Type A=DuyetNcc / B= // DuyetNccPhuongAn). Pattern mirror HĐ Thầu phụ ContractCreatePage: // Panel 1 (320px): list pure picker (read-only, không edit/delete) + sticky // "+ Thêm mới" bottom button (Q1 user 2026-05-07). // Panel 2 (1fr): empty state · mode=new · else // (5 section + Section 5 // Ý kiến 4PB DISABLED — Q5: nhập ở leaf "Duyệt"). // // URL: /purchase-evaluations/workspace?type={1|2}[&id=...][&mode=new][&q=][&phase=] // Workflow Panel + Approvals + History KHÔNG render ở workspace (Q1 — chỉ // hiện ở leaf Danh sách + Duyệt vẫn 3-panel). import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useNavigate, useSearchParams } from 'react-router-dom' import { toast } from 'sonner' import { ClipboardCheck } from 'lucide-react' import { EmptyState } from '@/components/EmptyState' import { PeDetailTabs } from '@/components/pe/PeDetailTabs' import { PeListPanel } from '@/components/pe/PeListPanel' import { PeWorkspaceCreateView } from '@/components/pe/PeWorkspaceCreateView' import { api } from '@/lib/api' import { getErrorMessage } from '@/lib/apiError' import { PurchaseEvaluationType, PurchaseEvaluationTypeLabel, type PeDetailBundle, } from '@/types/purchaseEvaluation' export function PurchaseEvaluationWorkspacePage() { const navigate = useNavigate() const qc = useQueryClient() const [sp, setSp] = useSearchParams() const typeFilter = sp.get('type') ? Number(sp.get('type')) : PurchaseEvaluationType.DuyetNcc const search = sp.get('q') ?? '' const phase = sp.get('phase') ?? '' const selectedId = sp.get('id') const mode = sp.get('mode') // 'new' | null const autoEditHeader = sp.get('editHeader') === '1' const detail = useQuery({ queryKey: ['pe-detail', selectedId], queryFn: async () => (await api.get(`/purchase-evaluations/${selectedId}`)).data, enabled: !!selectedId, }) const del = useMutation({ mutationFn: async (id: string) => api.delete(`/purchase-evaluations/${id}`), onSuccess: () => { toast.success('Đã xóa phiếu.') setParams({ id: null }) qc.invalidateQueries({ queryKey: ['pe-list'] }) }, onError: e => toast.error(getErrorMessage(e)), }) function setParams(updates: Record) { const next = new URLSearchParams(sp) for (const [k, v] of Object.entries(updates)) { if (v == null || v === '') next.delete(k) else next.set(k, v) } // Search input gõ liên tục → replace (không spam history); pick/mode → push const replace = Object.keys(updates).length === 1 && updates.q !== undefined setSp(next, { replace }) } const headerTitle = `${PurchaseEvaluationTypeLabel[typeFilter]} — Thao tác` return (

{headerTitle}

Workspace 2-panel — Workflow + Duyệt ở menu “Duyệt”.
{/* Panel 1: List pure picker + sticky create + pencil edit hover */} setParams({ id, mode: null, editHeader: null })} onSearchChange={q => setParams({ q })} onPhaseChange={p => setParams({ phase: p })} showCreateButton onCreate={() => setParams({ mode: 'new', id: null, editHeader: null })} onEditClick={id => setParams({ id, mode: null, editHeader: '1' })} /> {/* Panel 2: Empty | Header form | Detail tabs (workspace mode) */}
{/* Empty: chưa pick + chưa create */} {!selectedId && mode !== 'new' && ( )} {/* Mode "new": sectioned create view (5 sections, 3-5 locked tới khi save) */} {mode === 'new' && ( setParams({ id: newId, mode: null, type: String(t) })} onCancel={() => setParams({ mode: null })} /> )} {/* Mode "edit": detail tabs (workspace = no workflow + Section 5 disabled) */} {selectedId && detail.isLoading && (
Đang tải…
)} {selectedId && detail.data && ( setParams({ id: null, editHeader: null })} onDelete={() => del.mutate(detail.data!.id)} mode="workspace" autoEditHeader={autoEditHeader} /> )}
{/* Mobile fallback: nếu không lg, redirect về detail page */} {selectedId && (
{/* Quick UX: tap row khi mobile sẽ navigate fullpage detail */}
)}
) }