[CLAUDE] FE-User+FE-Admin: cay 4 folder GD duoi tung goi thau (Duyet NCC / Ke hoach HD / Duyet HD / HD cung) x2 app + 5 fix review F-1/3/4/6/7
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m50s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m50s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@ -26,6 +26,7 @@ import { PageHeader } from '@/components/ui/PageHeader'
|
||||
import { Button } from '@/components/ui/Button'
|
||||
import { Input } from '@/components/ui/Input'
|
||||
import { Textarea } from '@/components/ui/Textarea'
|
||||
import { PipelineTreePanel } from '@/components/pipeline/PipelineTreePanel'
|
||||
import { api, TOKEN_KEY } from '@/lib/api'
|
||||
import { getErrorMessage } from '@/lib/apiError'
|
||||
import { cn } from '@/lib/cn'
|
||||
@ -110,6 +111,12 @@ export function HardCopiesPage() {
|
||||
Có file scan là xem như <span className="font-semibold">xong</span>.
|
||||
</div>
|
||||
|
||||
{/* [S162] Cây toàn trình bám trái (owner chốt AskUser 30-07: folder GĐ áp
|
||||
cả các trang giai đoạn khác) — folder "Hợp đồng cứng" mở sẵn ở đây. */}
|
||||
<div className="grid gap-5 lg:grid-cols-[19rem_minmax(0,1fr)] xl:grid-cols-[21rem_minmax(0,1fr)]">
|
||||
<PipelineTreePanel currentStage={4} />
|
||||
|
||||
<div className="min-w-0 space-y-5">
|
||||
<div
|
||||
className="card-accent flex items-center gap-3 px-4 py-3"
|
||||
style={{ ['--accent' as string]: 'var(--color-teal-500)' }}
|
||||
@ -130,6 +137,8 @@ export function HardCopiesPage() {
|
||||
className="card-accent overflow-hidden"
|
||||
style={{ ['--accent' as string]: 'var(--color-teal-500)' }}
|
||||
>
|
||||
{/* [S162] Cột trái ăn ~19rem ⇒ bảng cuộn ngang trong thẻ, không tràn lưới. */}
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="border-b border-slate-200 bg-slate-50/70">
|
||||
<tr>
|
||||
@ -217,6 +226,7 @@ export function HardCopiesPage() {
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{totalPages > 1 && (
|
||||
<div className="flex items-center justify-between border-t border-slate-200 px-4 py-2.5 text-sm">
|
||||
<div className="text-slate-500">
|
||||
@ -238,6 +248,8 @@ export function HardCopiesPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -276,6 +288,7 @@ function HardCopyPanel({ contractId }: { contractId: string }) {
|
||||
setNote('')
|
||||
qc.invalidateQueries({ queryKey: ['contract', contractId] })
|
||||
qc.invalidateQueries({ queryKey: ['hard-copies'] }) // badge cột "Bản cứng" lật
|
||||
qc.invalidateQueries({ queryKey: ['pipeline-contract-index'] }) // [F-7 S162] badge GĐ4 trên cây lật cùng
|
||||
toast.success('Đã lưu bản cứng')
|
||||
},
|
||||
onError: (err) => toast.error(`Tải lên lỗi: ${getErrorMessage(err)}`),
|
||||
|
||||
@ -21,6 +21,7 @@ import { PageHeader } from '@/components/ui/PageHeader'
|
||||
import { KpiCard } from '@/components/ui/KpiCard'
|
||||
import { Button } from '@/components/ui/Button'
|
||||
import { Input } from '@/components/ui/Input'
|
||||
import { PipelineTreePanel } from '@/components/pipeline/PipelineTreePanel'
|
||||
import { api } from '@/lib/api'
|
||||
import { cn } from '@/lib/cn'
|
||||
import {
|
||||
@ -131,8 +132,15 @@ export function KhkkListPage() {
|
||||
}
|
||||
/>
|
||||
|
||||
{/* [S162] Cây toàn trình bám trái + nội dung cũ giữ nguyên bên phải (owner
|
||||
chốt AskUser 30-07: "áp cả các trang GĐ khác"). `minmax(0,1fr)` để bảng
|
||||
7 cột không thổi vỡ lưới ở 1366; `<lg` cây xếp trên và tự thu gọn. */}
|
||||
<div className="grid gap-5 lg:grid-cols-[19rem_minmax(0,1fr)] xl:grid-cols-[21rem_minmax(0,1fr)]">
|
||||
<PipelineTreePanel currentStage={2} />
|
||||
|
||||
<div className="min-w-0 space-y-5">
|
||||
{!deletedView && (
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-6">
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 xl:grid-cols-6">
|
||||
{phaseCards.map((c) => (
|
||||
<KpiCard
|
||||
key={c.value ?? 'all'}
|
||||
@ -164,6 +172,9 @@ export function KhkkListPage() {
|
||||
</div>
|
||||
|
||||
<div className="card-accent overflow-hidden" style={{ ['--accent' as string]: 'var(--color-brand-500)' }}>
|
||||
{/* [S162] Cột trái ăn ~19rem ⇒ bảng 7 cột cuộn ngang trong thẻ thay vì
|
||||
tràn ra ngoài lưới trên laptop 1366. */}
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="border-b border-slate-200 bg-slate-50/70">
|
||||
<tr>
|
||||
@ -244,6 +255,7 @@ export function KhkkListPage() {
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{totalPages > 1 && (
|
||||
<div className="flex items-center justify-between border-t border-slate-200 px-4 py-2.5 text-sm">
|
||||
<div className="text-slate-500">
|
||||
@ -260,6 +272,8 @@ export function KhkkListPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -29,6 +29,8 @@ import { PeDetailTabs } from '@/components/pe/PeDetailTabs'
|
||||
import { PeUrgentChips } from '@/components/pe/PeUrgentChips'
|
||||
import { PeFinalizeChip } from '@/components/pe/PeFinalizeChip'
|
||||
import { PeWorkflowPanel } from '@/components/pe/PeWorkflowPanel'
|
||||
import { PipelineStageFolders } from '@/components/pipeline/PipelineStageFolders'
|
||||
import { usePipelineStages } from '@/hooks/usePipelineStages'
|
||||
|
||||
export function PurchaseEvaluationsListPage() {
|
||||
const qc = useQueryClient()
|
||||
@ -47,6 +49,14 @@ export function PurchaseEvaluationsListPage() {
|
||||
const selectedId = sp.get('id')
|
||||
const isExpand = sp.get('expand') === '1' // S79 — overlay mở rộng
|
||||
|
||||
// [S162] Nguồn 4 folder giai đoạn dưới từng gói thầu (GĐ2 KHKK / GĐ3 HĐ /
|
||||
// GĐ4 HĐ cứng). Hook tự gate quyền TRƯỚC khi fetch — không quyền thì không
|
||||
// gọi API, folder hiện dạng khoá.
|
||||
const { buildStages } = usePipelineStages()
|
||||
// Đang lọc (tìm kiếm / trạng thái / "Chờ tôi" / "Đã xóa") ⇒ chỉ hiện folder
|
||||
// CÓ nội dung khớp, tránh đổ 4 folder rỗng dưới mọi gói thầu.
|
||||
const filterActive = search.trim() !== '' || phase !== '' || pendingMe || deletedView
|
||||
|
||||
// Mig 23 — list quy trình duyệt V2 cho dropdown filter (filter theo type screen)
|
||||
const approvalWorkflows = useQuery({
|
||||
queryKey: ['approval-workflows-v2-filter', typeFilter],
|
||||
@ -507,7 +517,12 @@ export function PurchaseEvaluationsListPage() {
|
||||
<span className={cn('flex-1 truncate text-[12px]', wg.workItemId ? 'font-medium text-slate-700' : 'italic text-slate-400')}>{wg.workItemName}</span>
|
||||
<span className="rounded bg-slate-100 px-1.5 py-0.5 text-[10px] text-slate-600">{wg.items.length}</span>
|
||||
</summary>
|
||||
<ul className="ml-3 divide-y divide-slate-100 border-l border-slate-200">
|
||||
{/* [S162] 4 folder giai đoạn dưới GÓI THẦU (owner chốt AskUser 30-07).
|
||||
Khối <li> phiếu bên dưới GIỮ NGUYÊN từng byte — kể cả thụt lề — để
|
||||
diff chứng minh không đụng behavior chọn phiếu / chip GẤP / nút
|
||||
"Xem mở rộng"; nó chỉ được RE-PARENT vào folder GĐ1 "Duyệt NCC". */}
|
||||
<PipelineStageFolders className="ml-3 border-l border-slate-200" currentStage={1} hideEmpty={filterActive} stages={buildStages(wg.items, { stage1Content: (
|
||||
<ul className="divide-y divide-slate-100">
|
||||
{wg.items.map(p => (
|
||||
<li key={p.id}>
|
||||
{/* S79 — dòng phiếu: vùng bấm chính (chọn → inline) + nút
|
||||
@ -607,6 +622,7 @@ export function PurchaseEvaluationsListPage() {
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) })} />
|
||||
</details>
|
||||
)
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user