diff --git a/src/components/ClaimDrawer/MatchedRemitCard.test.tsx b/src/components/ClaimDrawer/MatchedRemitCard.test.tsx
index bf72a1d..886463e 100644
--- a/src/components/ClaimDrawer/MatchedRemitCard.test.tsx
+++ b/src/components/ClaimDrawer/MatchedRemitCard.test.tsx
@@ -138,6 +138,61 @@ describe("MatchedRemitCard", () => {
unmount();
});
+ it("test_full_match_badge_renders_when_matchedLines_equals_totalLines", () => {
+ const { container, unmount } = renderIntoContainer(
+
+ );
+ 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(
+
+ );
+ 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(
+
+ );
+ expect(
+ container.querySelector('[data-testid="matched-remit-line-counts"]')
+ ).toBeNull();
+ unmount();
+ });
+
it("test_view_remittance_link_sets_window_location_href", () => {
// 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.
diff --git a/src/components/ClaimDrawer/MatchedRemitCard.tsx b/src/components/ClaimDrawer/MatchedRemitCard.tsx
index f725393..9cb7974 100644
--- a/src/components/ClaimDrawer/MatchedRemitCard.tsx
+++ b/src/components/ClaimDrawer/MatchedRemitCard.tsx
@@ -38,6 +38,12 @@ export function MatchedRemitCard({ matchedRemittance }: MatchedRemitCardProps) {
if (matchedRemittance === null) return null;
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 = () => {
window.location.href = `/remittances?id=${id}`;
@@ -109,6 +115,18 @@ export function MatchedRemitCard({ matchedRemittance }: MatchedRemitCardProps) {
+
+ {hasLineCounts ? (
+
+ lines: {matchedLines}/{totalLines} matched
+ {!allMatched ? ` (${totalLines! - matchedLines!} unmatched)` : null}
+
+ ) : null}
);
}
diff --git a/src/types/index.ts b/src/types/index.ts
index 7ae5af6..d3b6011 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -537,6 +537,13 @@ export interface ClaimDetailMatchedRemittance {
/** Mirrors the same `"received" | "reconciled"` mapping as the list endpoint. */
status: string;
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 {