import { useMemo, useRef, useState } from "react"; import { Search, X } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { ClaimStatusBadge } from "@/components/StatusBadge"; import { NewClaimDialog } from "@/components/NewClaimDialog"; import { Skeleton } from "@/components/ui/skeleton"; import { EmptyState } from "@/components/ui/empty-state"; import { ErrorState } from "@/components/ui/error-state"; import { FilterChips, type FilterChipOption } from "@/components/ui/filter-chips"; import { Pagination } from "@/components/ui/pagination"; import { useClaims } from "@/hooks/useClaims"; import { useAppStore } from "@/store"; import { fmt } from "@/lib/format"; import type { ClaimStatus } from "@/types"; import { cn } from "@/lib/utils"; const ALL = "all" as const; const PAGE_SIZE = 25; const STATUS_OPTIONS: FilterChipOption[] = [ { value: "all", label: "All" }, { value: "submitted", label: "Submitted" }, { value: "accepted", label: "Accepted" }, { value: "denied", label: "Denied" }, { value: "paid", label: "Paid" }, { value: "pending", label: "Pending" }, ]; export function Claims() { const [status, setStatus] = useState(ALL); const [npi, setNpi] = useState(ALL); const [query, setQuery] = useState(""); const [page, setPage] = useState(1); const searchRef = useRef(null); const providers = useAppStore((s) => s.providers); const providerMap = useMemo( () => Object.fromEntries(providers.map((p) => [p.npi, p])), [providers] ); const params = { status: status === ALL ? undefined : status, provider_npi: npi === ALL ? undefined : npi, sort: "billedAmount", order: "desc" as const, limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE, }; const { data, isLoading, isError, error, refetch } = useClaims(params); const items = data?.items ?? []; const totals = useMemo( () => ({ billed: items.reduce((s, c) => s + c.billedAmount, 0), received: items.reduce((s, c) => s + c.receivedAmount, 0), }), [items] ); // Local search filter on the current page (cheap; no extra fetch). const visible = useMemo(() => { const q = query.trim().toLowerCase(); if (!q) return items; return items.filter( (c) => c.id.toLowerCase().includes(q) || c.patientName.toLowerCase().includes(q) || c.payerName.toLowerCase().includes(q) || c.cptCode.toLowerCase().includes(q) ); }, [items, query]); const filtered = status !== ALL || npi !== ALL; return (
Claims

All claims

{isError ? ( refetch()} /> ) : null}
setQuery(e.target.value)} className="pl-9 pr-16" /> {query ? ( ) : null}
{ setStatus((v as ClaimStatus | null) ?? ALL); setPage(1); }} eyebrow="Status" />
{fmt.num(data?.total ?? 0)} {" "} claims Billed{" "} {fmt.usd(totals.billed)} Received{" "} {fmt.usd(totals.received)}
{isLoading ? (
{Array.from({ length: 5 }).map((_, i) => ( ))}
) : items.length === 0 ? ( ) : ( <> Claim Patient Provider Payer Billed Received Status Submitted {visible.map((c) => { const provider = providerMap[c.providerNpi]; return (
{c.id}
CPT {c.cptCode}
{c.patientName}
{provider?.name ?? "Unknown"}
{c.providerNpi}
{c.payerName} {fmt.usd(c.billedAmount)} {c.receivedAmount > 0 ? fmt.usd(c.receivedAmount) : "—"} {fmt.dateShort(c.submissionDate)}
); })}
)}
); }