90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
import { useQuery } from "@tanstack/react-query";
|
|
import { api, ApiError } from "@/lib/api";
|
|
import type { Ack } from "@/types";
|
|
|
|
/**
|
|
* UI-facing ack detail shape returned by `GET /api/acks/{id}`.
|
|
*
|
|
* Extends the base `Ack` shape with the `raw_999_text` field that
|
|
* `api.getAck` populates on top of the canonical row. The download
|
|
* button inside `AckDrawer` reads this string to hand the user the
|
|
* regenerated X12 file.
|
|
*/
|
|
export interface AckDetail extends Ack {
|
|
/**
|
|
* Full regenerated 999 X12 text. The backend re-emits the parsed
|
|
* transaction set so a user can grab the original file from the
|
|
* drawer without a second round-trip to `/api/acks/{id}/raw`.
|
|
*/
|
|
raw_999_text?: string;
|
|
/**
|
|
* Raw JSON envelope captured by the parser (the same dict the
|
|
* parser wrote into `raw_json`). Optional so older rows without
|
|
* it still typecheck.
|
|
*/
|
|
rawJson?: unknown;
|
|
}
|
|
|
|
/**
|
|
* Per-ack detail drawer query (AckDrawer · SP21 Phase 5 Task 5.2).
|
|
*
|
|
* Twin of `useProviderDetail` and `useClaimDetail` — same return
|
|
* shape, same retry semantics, no in-memory fallback (the spec §5.2
|
|
* calls out that ACKs are backend-only; `useAcks` has no sample-data
|
|
* path so there's nothing to fall back on).
|
|
*
|
|
* Returns `{ data, isLoading, isError, error, refetch }`:
|
|
* - `ackId === 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.
|
|
* - `ackId` is set: fetches `GET /api/acks/{id}` via `api.getAck`.
|
|
* Cached 60 s — the underlying ack rows don't change after
|
|
* parse-time.
|
|
* - 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.
|
|
*
|
|
* The drawer accepts `ackId: string | null` (the URL keeps the id as
|
|
* a string for clean deep-link round-tripping) and does the
|
|
* `Number()` coercion here, matching how `useProviderDetail`
|
|
* (`string`) and `useRemitDetail` (`string`) already work.
|
|
*/
|
|
export function useAckDetail(ackId: string | null): {
|
|
data: AckDetail | null;
|
|
isLoading: boolean;
|
|
isError: boolean;
|
|
error: Error | null;
|
|
refetch: () => void;
|
|
} {
|
|
const q = useQuery<AckDetail>({
|
|
queryKey: ["ack-detail", ackId],
|
|
queryFn: () => api.getAck(Number(ackId)),
|
|
enabled: ackId !== null,
|
|
staleTime: 60 * 1000,
|
|
retry: (failureCount, error) => {
|
|
if (error instanceof ApiError && error.status === 404) return false;
|
|
return failureCount < 3;
|
|
},
|
|
});
|
|
|
|
if (ackId === 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();
|
|
},
|
|
};
|
|
}
|