feat(sp7): wire LineReconciliationTab into ClaimDrawer
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
// @vitest-environment happy-dom
|
||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { cleanup, renderHook, waitFor } from "@testing-library/react";
|
||||
import { useLineReconciliation } from "./useLineReconciliation";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
describe("useLineReconciliation", () => {
|
||||
it("fetches the line reconciliation data on mount", async () => {
|
||||
const fixture = {
|
||||
claimId: "CLM-1",
|
||||
summary: {
|
||||
billedTotal: "100",
|
||||
paidTotal: "80",
|
||||
adjustmentTotal: "20",
|
||||
matchedLines: 1,
|
||||
totalLines: 1,
|
||||
},
|
||||
lines: [],
|
||||
};
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => fixture,
|
||||
})
|
||||
);
|
||||
const { result } = renderHook(() => useLineReconciliation("CLM-1"));
|
||||
await waitFor(() => expect(result.current.loading).toBe(false));
|
||||
expect(result.current.data).toEqual(fixture);
|
||||
});
|
||||
|
||||
it("returns null when claimId is null", () => {
|
||||
const { result } = renderHook(() => useLineReconciliation(null));
|
||||
expect(result.current.data).toBeNull();
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
it("exposes an error when fetch fails", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue({
|
||||
ok: false,
|
||||
status: 500,
|
||||
json: async () => ({}),
|
||||
})
|
||||
);
|
||||
const { result } = renderHook(() => useLineReconciliation("CLM-1"));
|
||||
await waitFor(() => expect(result.current.error).not.toBeNull());
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import type { LineReconciliationResponse } from "@/types";
|
||||
|
||||
/**
|
||||
* Data hook for the ClaimDrawer's Line Reconciliation tab.
|
||||
*
|
||||
* Calls GET /api/claims/{claimId}/line-reconciliation on mount and
|
||||
* when claimId changes. When claimId is null, returns a no-data state.
|
||||
*/
|
||||
export function useLineReconciliation(claimId: string | null) {
|
||||
const [data, setData] = useState<LineReconciliationResponse | null>(null);
|
||||
const [loading, setLoading] = useState<boolean>(claimId !== null);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (claimId === null) {
|
||||
setData(null);
|
||||
setLoading(false);
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL ?? "";
|
||||
fetch(`${baseUrl}/api/claims/${claimId}/line-reconciliation`)
|
||||
.then(async (r) => {
|
||||
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
||||
return (await r.json()) as LineReconciliationResponse;
|
||||
})
|
||||
.then((json) => {
|
||||
if (cancelled) return;
|
||||
setData(json);
|
||||
setError(null);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (cancelled) return;
|
||||
setError(e as Error);
|
||||
})
|
||||
.finally(() => {
|
||||
if (cancelled) return;
|
||||
setLoading(false);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [claimId]);
|
||||
|
||||
return { data, loading, error };
|
||||
}
|
||||
Reference in New Issue
Block a user