import { useNavigate } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { Box, Typography, Card, CardContent, Grid, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Chip, Button, Skeleton, Alert, } from '@mui/material'; import AddCircleIcon from '@mui/icons-material/AddCircle'; import DescriptionIcon from '@mui/icons-material/Description'; import HourglassTopIcon from '@mui/icons-material/HourglassTop'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import SendIcon from '@mui/icons-material/Send'; import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; import Tooltip from '@mui/material/Tooltip'; import { proposalsApi, type ProposalListItem, type ProposalStats } from '../../lib/api/proposals'; import { QUERY_KEYS } from '../../constants/queryKeys'; import { STATUS_CHIP_STYLES } from '../../constants'; import { formatCurrency, formatDate, formatDateFull } from '../../lib/format'; import { useDocumentTitle } from '../../hooks/useDocumentTitle'; function KpiCard({ icon, label, value, subtitle }: { icon: React.ReactNode; label: string; value: string; subtitle?: string }) { return ( {icon} {label} {value} {subtitle && ( {subtitle} )} ); } export default function Dashboard() { useDocumentTitle('Dashboard'); const navigate = useNavigate(); const { data: recentData, isLoading: recentLoading, error: recentError } = useQuery<{ items: ProposalListItem[]; totalCount: number }>({ queryKey: [QUERY_KEYS.proposals, 'dashboard-recent'], queryFn: () => proposalsApi.getAll({ page: 1, pageSize: 5, mine: true }), }); // Fix: WEB-M8 — capture error state for stats to show error instead of misleading zeros const { data: stats, isLoading: statsLoading, isError: statsError, refetch: refetchStats } = useQuery({ queryKey: [QUERY_KEYS.proposals, 'stats'], queryFn: () => proposalsApi.getStats(), }); const proposals = recentData?.items ?? []; const total = stats?.totalCount ?? 0; const counts = { inReview: stats?.inReviewCount ?? 0, approved: stats?.approvedCount ?? 0, sent: stats?.sentCount ?? 0, }; return ( Dashboard {/* Fix: WEB-M8 — show error state instead of misleading zeros when stats fetch fails */} {statsError ? ( } action={ } > Failed to load dashboard statistics. Please try again. ) : ( <> {statsLoading ? ( ) : ( } label="Total Submitted" value={String(total)} subtitle="All time" /> )} {statsLoading ? ( ) : ( } label="In Review" value={String(counts.inReview)} subtitle="Awaiting pricing" /> )} {statsLoading ? ( ) : ( } label="Approved" value={String(counts.approved)} subtitle="Ready to send" /> )} {statsLoading ? ( ) : ( } label="Sent" value={String(counts.sent)} subtitle="Delivered to customer" /> )} )} Recent Proposals Proposal # Customer Category Status Bid Amount Submitted {recentLoading ? Array.from({ length: 5 }).map((_, i) => ( {Array.from({ length: 6 }).map((_, j) => ( ))} )) : proposals.map((row) => ( navigate(`/proposals/${row.id}`)} > {row.proposalNumber} {row.customerName} {row.serviceCategory} {formatCurrency(row.totalBidAmount)} {formatDate(row.submittedAt)} ))} {!recentLoading && recentError && ( Failed to load proposals. Please try again. )} {!recentLoading && !recentError && proposals.length === 0 && ( No proposals yet. Submit your first one! )}
); }