// 3-panel "Danh sách HĐ" — Panel 1 (list compact, click chọn) | Panel 2 // (detail content) | Panel 3 (workflow + lịch sử duyệt). Selected HĐ giữ qua // URL `?id=` để bookmarkable + back/forward navigation work. // // Mobile fallback (< lg): hiển thị Panel 1 list, click row → fullpage // /contracts/:id (giữ flow cũ, không cố nhồi 3 panel vào màn hình hẹp). import { useMemo } from 'react' import { useQuery } from '@tanstack/react-query' import { useNavigate, useSearchParams } from 'react-router-dom' import { FileText, Plus, Search, X } from 'lucide-react' import { ContractDetailContent } from '@/components/contracts/ContractDetailContent' import { WorkflowHistoryPanel } from '@/components/contracts/WorkflowHistoryPanel' import { PhaseBadge } from '@/components/PhaseBadge' import { SlaTimer } from '@/components/SlaTimer' import { EmptyState } from '@/components/EmptyState' import { Button } from '@/components/ui/Button' import { Input } from '@/components/ui/Input' import { api } from '@/lib/api' import { cn } from '@/lib/cn' import type { Paged } from '@/types/master' import type { ContractDetail, ContractListItem } from '@/types/contracts' import { ContractTypeLabel } from '@/types/forms' const fmtMoney = (v: number) => v.toLocaleString('vi-VN') export function MyContractsPage() { const navigate = useNavigate() const [searchParams, setSearchParams] = useSearchParams() const typeFilter = searchParams.get('type') ? Number(searchParams.get('type')) : null const selectedId = searchParams.get('id') const search = searchParams.get('q') ?? '' const list = useQuery({ queryKey: ['my-contracts', typeFilter], queryFn: async () => (await api.get>('/contracts', { params: { page: 1, pageSize: 100 } })).data, }) const detail = useQuery({ queryKey: ['contract', selectedId], queryFn: async () => (await api.get(`/contracts/${selectedId}`)).data, enabled: !!selectedId, }) const rows = useMemo(() => { let items = list.data?.items ?? [] if (typeFilter != null) items = items.filter(c => c.type === typeFilter) if (search.trim()) { const q = search.toLowerCase() items = items.filter(c => (c.maHopDong ?? '').toLowerCase().includes(q) || (c.tenHopDong ?? '').toLowerCase().includes(q) || (c.supplierName ?? '').toLowerCase().includes(q), ) } return items }, [list.data, typeFilter, search]) function selectContract(id: string) { // Desktop ≥ lg: cập nhật URL để render Panel 2/3 cạnh List. // Mobile: Panel 2/3 hidden → navigate fullpage /contracts/:id (UX khả dụng). if (typeof window !== 'undefined' && window.matchMedia('(min-width: 1024px)').matches) { const next = new URLSearchParams(searchParams) next.set('id', id) setSearchParams(next, { replace: false }) } else { navigate(`/contracts/${id}`) } } function clearSelection() { const next = new URLSearchParams(searchParams) next.delete('id') setSearchParams(next, { replace: false }) } function updateSearch(value: string) { const next = new URLSearchParams(searchParams) if (value) next.set('q', value) else next.delete('q') setSearchParams(next, { replace: true }) } const typeLabel = typeFilter != null ? ContractTypeLabel[typeFilter] : null return (
{/* Compact page header — chiếm ít chiều cao để 3 panel có max chỗ */}

HĐ của tôi {typeLabel && · {typeLabel}}

{rows.length}
{/* 3-panel grid — flex-1 để fill phần còn lại của viewport */}
{/* Panel 1 — List */} {/* Panel 2 — Detail content */}
{!selectedId && ( )} {selectedId && detail.isLoading && (
Đang tải HĐ…
)} {selectedId && detail.data && ( )}
{/* Panel 3 — Workflow + history */}
) }