64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
import { useQuery } from "@tanstack/react-query";
|
|
import { api, ApiError } from "@/lib/api";
|
|
import type { ClaimDetail } from "@/types";
|
|
|
|
/**
|
|
* Per-claim detail drawer query (SP4).
|
|
*
|
|
* Returns `{ data, isLoading, isError, error, refetch }`:
|
|
* - `id === null` (drawer closed): the query is disabled and the hook
|
|
* short-circuits to the empty drawer state `{ data: null, isLoading:
|
|
* false, isError: false, error: null }`. `refetch` is a no-op since no
|
|
* fetch is in flight.
|
|
* - `id` is set: fetches `GET /api/claims/{id}` via `api.getClaimDetail`.
|
|
* The response is cached for 30 s so j/k navigation across claims
|
|
* reuses prior fetches within the drawer session (spec §3.4).
|
|
* - On 404: `error` is the `ApiError(status=404)` thrown by the client.
|
|
* We disable retries on 404 so the drawer can render the not-found
|
|
* state immediately rather than masking it with three back-to-back
|
|
* retries.
|
|
*/
|
|
export function useClaimDetail(id: string | null): {
|
|
data: ClaimDetail | null;
|
|
isLoading: boolean;
|
|
isError: boolean;
|
|
error: Error | null;
|
|
refetch: () => void;
|
|
} {
|
|
const q = useQuery<ClaimDetail>({
|
|
queryKey: ["claim-detail", id],
|
|
queryFn: () => api.getClaimDetail(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,
|
|
// `q.error` is typed as `Error | null` (TanStack Query v5's default
|
|
// TError is `Error`); at runtime the api layer throws `ApiError`,
|
|
// which is a subclass of `Error`, so the cast is safe.
|
|
error: q.error,
|
|
// TanStack's refetch returns a Promise; the plan's signature wants
|
|
// a plain `() => void` so consumers can fire-and-forget.
|
|
refetch: () => {
|
|
void q.refetch();
|
|
},
|
|
};
|
|
}
|