import { Fragment, useCallback, useMemo, useState } from "react"; import { ChevronDown, ChevronRight, Receipt } from "lucide-react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { RemitStatusBadge } from "@/components/StatusBadge"; 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 { KeyboardCheatsheet } from "@/components/KeyboardCheatsheet"; import { PageHeader } from "@/components/PageHeader"; import { TailStatusPill } from "@/components/TailStatusPill"; import { useRemittances } from "@/hooks/useRemittances"; import { useRowKeyboard } from "@/hooks/useRowKeyboard"; import { useTailStream } from "@/hooks/useTailStream"; import { useMergedTail } from "@/hooks/useMergedTail"; import { fmt } from "@/lib/format"; import { cn } from "@/lib/utils"; import type { CasAdjustment, Remittance, RemittanceStatus } from "@/types"; const PAGE_SIZE = 25; const STATUS_OPTIONS: FilterChipOption[] = [ { value: "received", label: "Received" }, { value: "posted", label: "Posted" }, { value: "reconciled", label: "Reconciled" }, ]; /** * One persisted CAS row, rendered as a "code — label" pair plus the * dollar amount. Lives inside the expanded detail row of a remit so * the operator can see exactly why the payer adjusted the claim. */ function AdjustmentRow({ adj }: { adj: CasAdjustment }) { return (
{adj.group}-{adj.reason} {adj.quantity !== null ? ( qty {adj.quantity} ) : null}
{adj.label}
{fmt.usdPrecise(adj.amount)}
); } export function Remittances() { const [page, setPage] = useState(1); const [status, setStatus] = useState(null); const [expanded, setExpanded] = useState>(() => new Set()); const [selectedIndex, setSelectedIndex] = useState(null); const [helpOpen, setHelpOpen] = useState(false); const { data, isLoading, isError, error, refetch, dataUpdatedAt } = useRemittances({ sort: "receivedDate", order: "desc", limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE, }); const tailFilterFn = useMemo( () => (r: Remittance) => { if (status && r.status !== status) return false; return true; }, [status], ); const { status: tailStatus, lastEventAt: tailLastEventAt, forceReconnect } = useTailStream("remittances"); const items = useMergedTail("remittances", data?.items ?? [], tailFilterFn); const total = items.reduce( (acc, r) => ({ paid: acc.paid + r.paidAmount, adjustments: acc.adjustments + r.adjustmentAmount, }), { paid: 0, adjustments: 0 } ); const toggleExpand = (id: string) => { setExpanded((prev) => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); }; const moveNext = useCallback(() => { setSelectedIndex((i) => { if (items.length === 0) return null; if (i === null) return 0; return (i + 1) % items.length; }); }, [items.length]); const movePrev = useCallback(() => { setSelectedIndex((i) => { if (items.length === 0) return null; if (i === null) return items.length - 1; return (i - 1 + items.length) % items.length; }); }, [items.length]); useRowKeyboard({ enabled: !helpOpen && items.length > 0, onNext: moveNext, onPrev: movePrev, onClose: () => setHelpOpen(false), onToggleHelp: () => setHelpOpen((v) => !v), }); return ( <> setHelpOpen(false)} />
835 remittances} subtitle="Electronic remittance advice from payers, matched to submitted claims." actions={ } /> {isError ? ( refetch()} /> ) : null}
Remits
{fmt.num(data?.total ?? 0)}
Total paid
{fmt.usd(total.paid)}
Adjustments
{fmt.usd(total.adjustments)}
{ setStatus((v as RemittanceStatus | null) ?? null); setPage(1); }} eyebrow="Status" />
{isLoading ? (
{Array.from({ length: 5 }).map((_, i) => ( ))}
) : items.length === 0 ? ( ) : ( <> Remit Claim Payer Paid Adjustment Status Received {items.map((r, idx) => { const isOpen = expanded.has(r.id); const hasAdjustments = !!r.adjustments && r.adjustments.length > 0; const isSelected = selectedIndex === idx; return ( hasAdjustments ? toggleExpand(r.id) : undefined } aria-expanded={hasAdjustments ? isOpen : undefined} style={{ cursor: hasAdjustments ? "pointer" : undefined }} > {hasAdjustments ? ( isOpen ? ( ) : ( ) ) : null} {r.id} {r.claimId} {r.payerName} {fmt.usdPrecise(r.paidAmount)} {r.adjustmentAmount > 0 ? fmt.usdPrecise(r.adjustmentAmount) : "—"} {fmt.dateShort(r.receivedDate)} {isOpen && hasAdjustments ? (
Adjustments ({r.adjustments!.length})
{r.adjustments!.map((adj, i) => ( ))}
) : null}
); })}
)}
); }