// "Tổng quan" cho user — overview KPI cá nhân + recent contracts. Khác Inbox // (chờ duyệt) — Dashboard cho user nhìn nhanh tất cả HĐ liên quan. import { useQuery } from '@tanstack/react-query' import { useNavigate } from 'react-router-dom' import { FileText, CheckCircle2, AlertTriangle, Pencil, Clock, Inbox, Coins } from 'lucide-react' import { PageHeader } from '@/components/PageHeader' import { PhaseBadge } from '@/components/PhaseBadge' import { SlaTimer } from '@/components/SlaTimer' import { EmptyState } from '@/components/EmptyState' import { Button } from '@/components/ui/Button' import { useAuth } from '@/contexts/AuthContext' import { api } from '@/lib/api' import type { Paged } from '@/types/master' import type { ContractListItem } from '@/types/contracts' import { ContractTypeLabel } from '@/types/forms' type MyDashboard = { draftsInProgress: number pendingMyApproval: number dueSoon: number overdue: number draftsTotalValue: number } const fmtMoney = (v: number) => { if (v >= 1_000_000_000) return (v / 1_000_000_000).toFixed(1) + ' tỷ' if (v >= 1_000_000) return (v / 1_000_000).toFixed(1) + ' tr' return v.toLocaleString('vi-VN') } function StatCard({ icon: Icon, label, value, hint, tone = 'default', onClick, }: { icon: React.ComponentType<{ className?: string }> label: string value: React.ReactNode hint?: string tone?: 'default' | 'warn' | 'good' | 'danger' onClick?: () => void }) { const toneClass = tone === 'warn' ? 'text-amber-600 bg-amber-50' : tone === 'good' ? 'text-emerald-600 bg-emerald-50' : tone === 'danger' ? 'text-red-600 bg-red-50' : 'text-brand-600 bg-brand-50' return ( {label} {value} {hint && {hint}} ) } export function UserDashboardPage() { const navigate = useNavigate() const { user } = useAuth() const stats = useQuery({ queryKey: ['my-dashboard'], queryFn: async () => (await api.get('/reports/my-dashboard')).data, }) const recent = useQuery({ queryKey: ['my-contracts-recent'], queryFn: async () => (await api.get>('/contracts', { params: { page: 1, pageSize: 5 } })).data, }) const s = stats.data return ( Của tôi navigate('/my-contracts')} /> navigate('/inbox')} /> navigate('/inbox')} /> navigate('/inbox')} /> navigate('/my-contracts')} /> HĐ gần đây navigate('/my-contracts')}> Xem tất cả {recent.isLoading && ( {Array.from({ length: 3 }).map((_, i) => ( ))} )} {!recent.isLoading && (recent.data?.items?.length ?? 0) === 0 && ( navigate('/contracts/new')}> Tạo HĐ mới } /> )} {recent.data?.items?.map(c => ( navigate(`/my-contracts?id=${c.id}`)} className="flex w-full items-center justify-between px-4 py-3 text-left transition hover:bg-slate-50" > {c.tenHopDong ?? '(chưa đặt tên)'} {c.maHopDong ?? '—'} · {ContractTypeLabel[c.type] ?? '—'} · {c.supplierName} {fmtMoney(c.giaTri)} ))} ) }