feat(drill): AckDrawer with header + segment status list
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
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();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user