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}
);
})}
>
)}
>
);
}