feat(frontend): refactor Remittances page to useRemittances + primitives

This commit is contained in:
Tyler
2026-06-19 19:44:28 -06:00
parent 70e7745912
commit deca199951
+107 -47
View File
@@ -1,3 +1,4 @@
import { useState } from "react";
import { import {
Table, Table,
TableBody, TableBody,
@@ -7,13 +8,37 @@ import {
TableRow, TableRow,
} from "@/components/ui/table"; } from "@/components/ui/table";
import { RemitStatusBadge } from "@/components/StatusBadge"; import { RemitStatusBadge } from "@/components/StatusBadge";
import { useAppStore } from "@/store"; 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 { useRemittances } from "@/hooks/useRemittances";
import { fmt } from "@/lib/format"; import { fmt } from "@/lib/format";
import { cn } from "@/lib/utils";
import type { RemittanceStatus } from "@/types";
const PAGE_SIZE = 25;
const STATUS_OPTIONS: FilterChipOption[] = [
{ value: "received", label: "Received" },
{ value: "posted", label: "Posted" },
{ value: "reconciled", label: "Reconciled" },
];
export function Remittances() { export function Remittances() {
const remits = useAppStore((s) => s.remittances); const [page, setPage] = useState(1);
const [status, setStatus] = useState<RemittanceStatus | null>(null);
const total = remits.reduce( const { data, isLoading, isError, error, refetch } = useRemittances({
sort: "receivedDate",
order: "desc",
limit: PAGE_SIZE,
offset: (page - 1) * PAGE_SIZE,
});
const items = data?.items ?? [];
const total = items.reduce(
(acc, r) => ({ (acc, r) => ({
paid: acc.paid + r.paidAmount, paid: acc.paid + r.paidAmount,
adjustments: acc.adjustments + r.adjustmentAmount, adjustments: acc.adjustments + r.adjustmentAmount,
@@ -34,12 +59,20 @@ export function Remittances() {
</p> </p>
</header> </header>
{isError ? (
<ErrorState
message="Couldn't load remittances from the backend."
detail={error instanceof Error ? error.message : String(error)}
onRetry={() => refetch()}
/>
) : null}
<div className="grid gap-4 grid-cols-2 lg:grid-cols-3"> <div className="grid gap-4 grid-cols-2 lg:grid-cols-3">
<div className="surface rounded-xl p-5"> <div className="surface rounded-xl p-5">
<div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted-foreground"> <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted-foreground">
Remits Remits
</div> </div>
<div className="display text-[28px] leading-none mt-3">{fmt.num(remits.length)}</div> <div className="display text-[28px] leading-none mt-3">{fmt.num(data?.total ?? 0)}</div>
</div> </div>
<div className="surface rounded-xl p-5"> <div className="surface rounded-xl p-5">
<div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted-foreground"> <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted-foreground">
@@ -55,51 +88,78 @@ export function Remittances() {
</div> </div>
</div> </div>
<div className="surface rounded-xl p-4">
<FilterChips
options={STATUS_OPTIONS}
value={status}
onChange={(v) => {
setStatus((v as RemittanceStatus | null) ?? null);
setPage(1);
}}
eyebrow="Status"
/>
</div>
<div className="surface rounded-xl overflow-hidden"> <div className="surface rounded-xl overflow-hidden">
<Table> {isLoading ? (
<TableHeader> <div className="p-4 space-y-2">
<TableRow> {Array.from({ length: 5 }).map((_, i) => (
<TableHead>Remit</TableHead> <Skeleton key={i} variant="row" />
<TableHead>Claim</TableHead> ))}
<TableHead>Payer</TableHead> </div>
<TableHead className="text-right">Paid</TableHead> ) : items.length === 0 ? (
<TableHead className="text-right">Adjustment</TableHead> <EmptyState
<TableHead>Status</TableHead> eyebrow="Remittances · awaiting first 835"
<TableHead>Received</TableHead> message="Drop an 835 file on the Upload page to populate this list."
</TableRow> />
</TableHeader> ) : (
<TableBody> <>
{remits.length === 0 ? ( <Table>
<TableRow> <TableHeader>
<TableCell colSpan={7} className="text-center py-12 text-muted-foreground"> <TableRow>
No remittances yet. <TableHead>Remit</TableHead>
</TableCell> <TableHead>Claim</TableHead>
</TableRow> <TableHead>Payer</TableHead>
) : ( <TableHead className="text-right">Paid</TableHead>
remits.map((r) => ( <TableHead className="text-right">Adjustment</TableHead>
<TableRow key={r.id}> <TableHead>Status</TableHead>
<TableCell className="display num text-[13px]">{r.id}</TableCell> <TableHead>Received</TableHead>
<TableCell className="display num text-[13px] text-muted-foreground">
{r.claimId}
</TableCell>
<TableCell>{r.payerName}</TableCell>
<TableCell className="text-right display num">
{fmt.usdPrecise(r.paidAmount)}
</TableCell>
<TableCell className="text-right display num text-muted-foreground">
{fmt.usdPrecise(r.adjustmentAmount)}
</TableCell>
<TableCell>
<RemitStatusBadge status={r.status} />
</TableCell>
<TableCell className="text-muted-foreground num text-[13px]">
{fmt.dateShort(r.receivedDate)}
</TableCell>
</TableRow> </TableRow>
)) </TableHeader>
)} <TableBody>
</TableBody> {items.map((r) => (
</Table> <TableRow key={r.id} className={cn("animate-row-flash")}>
<TableCell className="display num text-[13px]">{r.id}</TableCell>
<TableCell className="display num text-[13px] text-muted-foreground">
{r.claimId}
</TableCell>
<TableCell>{r.payerName}</TableCell>
<TableCell className="text-right display num">
{fmt.usdPrecise(r.paidAmount)}
</TableCell>
<TableCell className="text-right display num text-muted-foreground">
{fmt.usdPrecise(r.adjustmentAmount)}
</TableCell>
<TableCell>
<RemitStatusBadge status={r.status} />
</TableCell>
<TableCell className="text-muted-foreground num text-[13px]">
{fmt.dateShort(r.receivedDate)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className="px-4 pb-4">
<Pagination
page={page}
pageSize={PAGE_SIZE}
total={data?.total ?? 0}
onPageChange={setPage}
/>
</div>
</>
)}
</div> </div>
</div> </div>
); );