104 lines
3.2 KiB
TypeScript
104 lines
3.2 KiB
TypeScript
import { useQuery } from "@tanstack/react-query";
|
|
import { api, ApiError } from "@/lib/api";
|
|
import type { Remittance } from "@/types";
|
|
|
|
/**
|
|
* UI-facing remittance detail shape returned by `GET /api/remittances/{id}`.
|
|
*
|
|
* The backend mapper (`cyclone.store.to_ui_remittance_with_adjustments`)
|
|
* returns the standard `Remittance` fields plus the labeled `adjustments`
|
|
* array. We extend the base type locally with the additional 835-derived
|
|
* fields the backend emits — `payerClaimControlNumber` and `totalCharge`
|
|
* (which aren't part of the v1 `Remittance` interface but ARE part of the
|
|
* backend's `/api/remittances/{id}` payload). All newly-added fields are
|
|
* optional so the component degrades gracefully when the backend hasn't
|
|
* populated them (e.g. earlier rows without those columns populated).
|
|
*
|
|
* `payer` and `payee` blocks are also optional — the current detail
|
|
* endpoint does not surface these yet, but the PartiesGrid renders them
|
|
* when present so a future backend version lights up the cards without a
|
|
* frontend change.
|
|
*/
|
|
export interface RemitDetail extends Remittance {
|
|
payerClaimControlNumber?: string;
|
|
totalCharge?: number;
|
|
paymentMethod?: string | null;
|
|
paymentDate?: string | null;
|
|
payer?: {
|
|
name?: string | null;
|
|
id?: string | null;
|
|
address?: {
|
|
line1?: string;
|
|
line2?: string | null;
|
|
city?: string;
|
|
state?: string;
|
|
zip?: string;
|
|
} | null;
|
|
} | null;
|
|
payee?: {
|
|
name?: string | null;
|
|
npi?: string | null;
|
|
address?: {
|
|
line1?: string;
|
|
line2?: string | null;
|
|
city?: string;
|
|
state?: string;
|
|
zip?: string;
|
|
} | null;
|
|
} | null;
|
|
}
|
|
|
|
/**
|
|
* Per-remittance detail drawer query (RemitDrawer).
|
|
*
|
|
* Mirror of `useClaimDetail` — same return shape, same retry semantics.
|
|
* Returns `{ data, isLoading, isError, error, refetch }`:
|
|
* - `id === null` (drawer closed): the query is disabled and the hook
|
|
* short-circuits to the empty drawer state so a closed drawer doesn't
|
|
* burn a network request or a TanStack cache slot.
|
|
* - `id` is set: fetches `GET /api/remittances/{id}` via
|
|
* `api.getRemittance`. Cached 30 s so j/k navigation across remits
|
|
* reuses prior fetches within the drawer session.
|
|
* - On 404: the hook's retry predicate short-circuits (no retries) so
|
|
* the drawer's not-found state appears immediately rather than being
|
|
* masked by three back-to-back retry attempts.
|
|
*/
|
|
export function useRemitDetail(id: string | null): {
|
|
data: RemitDetail | null;
|
|
isLoading: boolean;
|
|
isError: boolean;
|
|
error: Error | null;
|
|
refetch: () => void;
|
|
} {
|
|
const q = useQuery<RemitDetail>({
|
|
queryKey: ["remit-detail", id],
|
|
queryFn: () => api.getRemittance<RemitDetail>(id as string),
|
|
enabled: id !== null,
|
|
staleTime: 30 * 1000,
|
|
retry: (failureCount, error) => {
|
|
if (error instanceof ApiError && error.status === 404) return false;
|
|
return failureCount < 3;
|
|
},
|
|
});
|
|
|
|
if (id === null) {
|
|
return {
|
|
data: null,
|
|
isLoading: false,
|
|
isError: false,
|
|
error: null,
|
|
refetch: () => {},
|
|
};
|
|
}
|
|
|
|
return {
|
|
data: q.data ?? null,
|
|
isLoading: q.isLoading,
|
|
isError: q.isError,
|
|
error: q.error,
|
|
refetch: () => {
|
|
void q.refetch();
|
|
},
|
|
};
|
|
}
|