feat(sp7): MatchedRemitCard — line-count badge

This commit is contained in:
Tyler
2026-06-20 19:47:11 -06:00
parent 00cdd068fc
commit b538c0939b
3 changed files with 80 additions and 0 deletions
@@ -138,6 +138,61 @@ describe("MatchedRemitCard", () => {
unmount(); unmount();
}); });
it("test_full_match_badge_renders_when_matchedLines_equals_totalLines", () => {
const { container, unmount } = renderIntoContainer(
<MatchedRemitCard
matchedRemittance={makeMatchedRemittance({
matchedLines: 4,
totalLines: 4,
})}
/>
);
const badge = container.querySelector(
'[data-testid="matched-remit-line-counts"]'
);
expect(badge).not.toBeNull();
expect(badge?.textContent ?? "").toMatch(/4\/4 matched/);
expect(badge?.getAttribute("data-all-matched")).toBe("true");
unmount();
});
it("test_partial_match_badge_renders_unmatched_count", () => {
const { container, unmount } = renderIntoContainer(
<MatchedRemitCard
matchedRemittance={makeMatchedRemittance({
matchedLines: 3,
totalLines: 4,
})}
/>
);
const badge = container.querySelector(
'[data-testid="matched-remit-line-counts"]'
);
expect(badge).not.toBeNull();
expect(badge?.textContent ?? "").toMatch(/3\/4 matched/);
expect(badge?.textContent ?? "").toMatch(/1 unmatched/);
expect(badge?.getAttribute("data-all-matched")).toBe("false");
unmount();
});
it("test_badge_omitted_when_line_counts_undefined", () => {
// Backward compatibility: pre-SP7 claim-detail responses don't
// include matchedLines / totalLines. The card should still render
// without the badge.
const { container, unmount } = renderIntoContainer(
<MatchedRemitCard
matchedRemittance={makeMatchedRemittance({
matchedLines: undefined,
totalLines: undefined,
})}
/>
);
expect(
container.querySelector('[data-testid="matched-remit-line-counts"]')
).toBeNull();
unmount();
});
it("test_view_remittance_link_sets_window_location_href", () => { it("test_view_remittance_link_sets_window_location_href", () => {
// Spy on the `href` setter so the click doesn't actually navigate // Spy on the `href` setter so the click doesn't actually navigate
// away in the test runner, and so we can assert what was assigned. // away in the test runner, and so we can assert what was assigned.
@@ -38,6 +38,12 @@ export function MatchedRemitCard({ matchedRemittance }: MatchedRemitCardProps) {
if (matchedRemittance === null) return null; if (matchedRemittance === null) return null;
const { id, totalPaid, status, receivedAt } = matchedRemittance; const { id, totalPaid, status, receivedAt } = matchedRemittance;
// SP7: per-line counts may be missing on pre-SP7 claim-detail responses.
const matchedLines = matchedRemittance.matchedLines;
const totalLines = matchedRemittance.totalLines;
const hasLineCounts =
typeof matchedLines === "number" && typeof totalLines === "number";
const allMatched = hasLineCounts && matchedLines === totalLines;
const handleViewRemittance = () => { const handleViewRemittance = () => {
window.location.href = `/remittances?id=${id}`; window.location.href = `/remittances?id=${id}`;
@@ -109,6 +115,18 @@ export function MatchedRemitCard({ matchedRemittance }: MatchedRemitCardProps) {
</Button> </Button>
</div> </div>
</div> </div>
{hasLineCounts ? (
<span
data-testid="matched-remit-line-counts"
data-all-matched={allMatched ? "true" : "false"}
className="font-mono text-xs uppercase tracking-wider"
style={{ color: allMatched ? "var(--tt-amber)" : "var(--tt-oxblood)" }}
>
lines: {matchedLines}/{totalLines} matched
{!allMatched ? ` (${totalLines! - matchedLines!} unmatched)` : null}
</span>
) : null}
</section> </section>
); );
} }
+7
View File
@@ -537,6 +537,13 @@ export interface ClaimDetailMatchedRemittance {
/** Mirrors the same `"received" | "reconciled"` mapping as the list endpoint. */ /** Mirrors the same `"received" | "reconciled"` mapping as the list endpoint. */
status: string; status: string;
receivedAt: string; // ISO Z receivedAt: string; // ISO Z
/**
* SP7: per-line counts for the badge. Optional for backward
* compatibility — older claim-detail responses (pre-SP7) won't have
* these fields. The card omits the badge when undefined.
*/
matchedLines?: number;
totalLines?: number;
} }
export interface ClaimDetailStateHistoryEvent { export interface ClaimDetailStateHistoryEvent {