feat(sp7): MatchedRemitCard — line-count badge
This commit is contained in:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user