diff --git a/src/components/inbox/Lane.test.tsx b/src/components/inbox/Lane.test.tsx new file mode 100644 index 0000000..db6dee6 --- /dev/null +++ b/src/components/inbox/Lane.test.tsx @@ -0,0 +1,58 @@ +// @vitest-environment happy-dom +import { afterEach, describe, expect, it, vi } from "vitest"; +import { cleanup, render, screen, fireEvent } from "@testing-library/react"; +import { Lane } from "./Lane"; +import type { InboxClaimRow } from "@/lib/inbox-api"; + +afterEach(() => cleanup()); + +const sampleRows: InboxClaimRow[] = [ + { + id: "CLP-1", + kind: "claim", + patient_control_number: "PCN-1", + charge_amount: 100, + payer_id: "PAYER-A", + provider_npi: "1234567890", + state: "rejected", + rejection_reason: "R69", + rejected_at: "2026-06-20T09:00:00Z", + service_date_from: null, + score: null, + score_tier: null, + score_breakdown: null, + }, +]; + +describe("Lane", () => { + it("renders the lane name and row count", () => { + const { container } = render( + {}} />, + ); + expect(container.textContent).toContain("REJECTED"); + expect(container.textContent).toContain("1"); + }); + + it("renders an empty state when no rows", () => { + const { container } = render( + {}} />, + ); + expect(container.textContent).toMatch(/nothing here/i); + }); + + it("supports multi-select via row checkbox toggle", () => { + const onSelectionChange = vi.fn(); + render( + {}} + onSelectionChange={onSelectionChange} + />, + ); + const cb = screen.getByRole("checkbox"); + fireEvent.click(cb); + expect(onSelectionChange).toHaveBeenCalled(); + }); +}); diff --git a/src/components/inbox/Lane.tsx b/src/components/inbox/Lane.tsx new file mode 100644 index 0000000..9e22dfb --- /dev/null +++ b/src/components/inbox/Lane.tsx @@ -0,0 +1,101 @@ +import { useState } from "react"; +import { InboxRow, type Accent } from "./InboxRow"; +import type { InboxClaimRow, InboxCandidateRow } from "@/lib/inbox-api"; + +export type LaneRow = InboxClaimRow | InboxCandidateRow; + +export function Lane({ + name, + accent, + rows, + onRowClick, + onSelectionChange, +}: { + name: string; + accent: Accent; + rows: LaneRow[]; + onRowClick: (row: LaneRow) => void; + onSelectionChange?: (ids: string[]) => void; +}) { + const [selected, setSelected] = useState>(new Set()); + + function rowId(row: LaneRow): string { + return row.kind === "remit" ? (row as InboxCandidateRow).payer_claim_control_number : row.id; + } + + function toggle(id: string) { + const next = new Set(selected); + if (next.has(id)) next.delete(id); + else next.add(id); + setSelected(next); + onSelectionChange?.(Array.from(next)); + } + + return ( +
+
+

+ {name} +

+ + {rows.length} + +
+ + {rows.length === 0 ? ( +

+ Nothing here. +

+ ) : ( +
+ {rows.map((row) => { + const id = rowId(row); + return ( +
+ +
+ onRowClick(row)} /> +
+
+ ); + })} +
+ )} +
+ ); +}