import { useQuery } from '@tanstack/react-query' import { FileText, CheckCircle2, AlertTriangle, TrendingUp, Coins, Pencil, Clock, Inbox } from 'lucide-react' import { useNavigate } from 'react-router-dom' import { PageHeader } from '@/components/PageHeader' import { BarChart } from '@/components/BarChart' import { PhaseBadge } from '@/components/PhaseBadge' import { useAuth } from '@/contexts/AuthContext' import { api } from '@/lib/api' import { cn } from '@/lib/cn' import type { DashboardStats } from '@/types/reports' 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') } // Tone → tinted icon chip (brand-led, semantic accents for warn/good). const STAT_TONE: Record<'default' | 'warn' | 'good', { chip: string; icon: string; value: string }> = { default: { chip: 'bg-brand-50', icon: 'text-brand-600', value: 'text-slate-900' }, warn: { chip: 'bg-amber-50', icon: 'text-amber-600', value: 'text-amber-700' }, good: { chip: 'bg-emerald-50', icon: 'text-emerald-600', value: 'text-slate-900' }, } function StatCard({ icon: Icon, label, value, hint, tone = 'default' }: { icon: React.ComponentType<{ className?: string }> label: string value: React.ReactNode hint?: string tone?: 'default' | 'warn' | 'good' }) { const t = STAT_TONE[tone] return (
{label}
{value}
{hint &&
{hint}
}
) } // Small section title with a brand tick — gives each band a clear, quiet anchor. function SectionLabel({ children }: { children: React.ReactNode }) { return (

{children}

) } function MyDashboardRow() { const navigate = useNavigate() const { user } = useAuth() const q = useQuery({ queryKey: ['my-dashboard'], queryFn: async () => (await api.get('/reports/my-dashboard')).data, staleTime: 30_000, }) const d = q.data if (!d) return null // Admin thấy everything nhưng card "Tôi đang soạn thảo" + "Chờ tôi duyệt" // thường = 0 cho admin. Chỉ show row nếu có ít nhất 1 card có giá trị. const anyValue = d.draftsInProgress + d.pendingMyApproval + d.dueSoon + d.overdue > 0 if (!anyValue && user?.roles.includes('Admin')) return null return (
Của tôi
Sắp quá hạn (24h)
{d.dueSoon}
Đã quá hạn
{d.overdue}
) } export function DashboardPage() { const stats = useQuery({ queryKey: ['dashboard-stats'], queryFn: async () => (await api.get('/reports/dashboard')).data, staleTime: 60_000, }) if (stats.isLoading) { return (
{[1, 2, 3, 4, 5].map(i => (
))}
) } const d = stats.data if (!d) return null return (
Toàn hệ thống {/* KPI Cards */}
{/* By Phase */}

HĐ theo phase

{d.byPhase.length === 0 &&
Chưa có HĐ nào
} {d.byPhase .slice() .sort((a, b) => b.count - a.count) .map(p => { const total = d.byPhase.reduce((s, x) => s + x.count, 0) || 1 const pct = (p.count / total) * 100 return (
{p.count}
) })}
{/* Monthly value */}

Giá trị HĐ theo tháng · 12 tháng gần nhất

({ label: `${String(m.month).padStart(2, '0')}/${m.year}`, value: m.totalValue, sublabel: `${m.count} HĐ`, }))} formatValue={fmtMoney} />
{/* Top suppliers */}

Top NCC theo số HĐ

({ label: s.supplierName, value: s.count, sublabel: `Tổng ${fmtMoney(s.totalValue)} VND`, }))} />
{/* Top projects */}

Top dự án theo số HĐ

({ label: p.projectName, value: p.count, sublabel: `Tổng ${fmtMoney(p.totalValue)} VND`, }))} />
) }