feat(frontend): add 6 query/mutation hooks (batches, claims, remits, providers, activity, parse)
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { api, type ListActivityParams, type PaginatedResponse } from "@/lib/api";
|
||||
import { useAppStore } from "@/store";
|
||||
import type { Activity } from "@/types";
|
||||
|
||||
/**
|
||||
* Lists activity. Polls every 30s when a backend is configured (so the
|
||||
* Activity Log page reflects new events without a manual refresh); in
|
||||
* sample-data mode it returns the in-memory store directly.
|
||||
*/
|
||||
export function useActivity(params: ListActivityParams = {}) {
|
||||
const fallback = useSyncExternalStore(
|
||||
(cb) => useAppStore.subscribe(cb),
|
||||
() => useAppStore.getState().activity,
|
||||
() => useAppStore.getState().activity
|
||||
);
|
||||
|
||||
const q = useQuery<PaginatedResponse<Activity>>({
|
||||
queryKey: ["activity", params],
|
||||
queryFn: () => api.listActivity<Activity>(params),
|
||||
enabled: api.isConfigured,
|
||||
refetchInterval: api.isConfigured ? 30_000 : false,
|
||||
});
|
||||
|
||||
if (!api.isConfigured) {
|
||||
return {
|
||||
data: {
|
||||
items: fallback,
|
||||
total: fallback.length,
|
||||
returned: fallback.length,
|
||||
has_more: false,
|
||||
} satisfies PaginatedResponse<Activity>,
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
refetch: () => Promise.resolve(),
|
||||
} as const;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { api, type BatchSummary } from "@/lib/api";
|
||||
|
||||
/**
|
||||
* Lists parsed batches. Batches are only available when a backend is
|
||||
* configured — when running in sample-data mode there's no persisted
|
||||
* history, so we leave the query disabled.
|
||||
*/
|
||||
export function useBatches(limit?: number) {
|
||||
return useQuery<BatchSummary[]>({
|
||||
queryKey: ["batches", limit ?? null],
|
||||
queryFn: () => api.listBatches(limit),
|
||||
enabled: api.isConfigured,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { api, type ListClaimsParams, type PaginatedResponse } from "@/lib/api";
|
||||
import { useAppStore } from "@/store";
|
||||
import type { Claim } from "@/types";
|
||||
|
||||
export const EMPTY_CLAIMS: PaginatedResponse<Claim> = {
|
||||
items: [],
|
||||
total: 0,
|
||||
returned: 0,
|
||||
has_more: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Lists claims. Falls back to the in-memory zustand store when no backend
|
||||
* is configured, applying the same `status` / `provider_npi` / `batch_id`
|
||||
* / pagination parameters the live endpoint would accept. Note: the UI
|
||||
* `Claim` shape doesn't carry a `batchId` yet, so the `batch_id` filter is
|
||||
* a no-op in fallback mode — it'll start working once the Claim type
|
||||
* gains that field.
|
||||
*/
|
||||
export function useClaims(params: ListClaimsParams) {
|
||||
const fallback = useSyncExternalStore(
|
||||
(cb) => useAppStore.subscribe(cb),
|
||||
() => useAppStore.getState().claims,
|
||||
() => useAppStore.getState().claims
|
||||
);
|
||||
|
||||
const q = useQuery<PaginatedResponse<Claim>>({
|
||||
queryKey: ["claims", params],
|
||||
queryFn: () => api.listClaims<Claim>(params),
|
||||
enabled: api.isConfigured,
|
||||
});
|
||||
|
||||
if (!api.isConfigured) {
|
||||
const offset = params.offset ?? 0;
|
||||
const limit = params.limit ?? 100;
|
||||
const filtered = fallback.filter((c) => {
|
||||
if (params.status && c.status !== params.status) return false;
|
||||
if (params.provider_npi && c.providerNpi !== params.provider_npi) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
const sliced = filtered.slice(offset, offset + limit);
|
||||
return {
|
||||
data: {
|
||||
items: sliced,
|
||||
total: filtered.length,
|
||||
returned: sliced.length,
|
||||
has_more: filtered.length > offset + limit,
|
||||
} satisfies PaginatedResponse<Claim>,
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
refetch: () => Promise.resolve(),
|
||||
} as const;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { api, type ParseOptions } from "@/lib/api";
|
||||
import type {
|
||||
BatchSummary as ParserBatchSummary,
|
||||
ParseResult837,
|
||||
ParseResult835,
|
||||
} from "@/types";
|
||||
|
||||
type ParseResult = ParseResult837 | ParseResult835 | ParserBatchSummary;
|
||||
|
||||
/**
|
||||
* Mutation that runs an EDI file through the parser. On success, invalidates
|
||||
* every list query that might have been affected by the new batch so pages
|
||||
* showing stale data re-fetch.
|
||||
*/
|
||||
export function useParse(kind: "837p" | "835") {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<ParseResult, Error, { file: File; options?: ParseOptions }>({
|
||||
mutationFn: (opts) => {
|
||||
const fn = kind === "837p" ? api.parse837 : api.parse835;
|
||||
return fn(opts.file, opts.options) as Promise<ParseResult>;
|
||||
},
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ["batches"] });
|
||||
qc.invalidateQueries({ queryKey: ["claims"] });
|
||||
qc.invalidateQueries({ queryKey: ["remittances"] });
|
||||
qc.invalidateQueries({ queryKey: ["providers"] });
|
||||
qc.invalidateQueries({ queryKey: ["activity"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { api, type ListProvidersParams, type PaginatedResponse } from "@/lib/api";
|
||||
import { useAppStore } from "@/store";
|
||||
import type { Provider } from "@/types";
|
||||
|
||||
/**
|
||||
* Lists providers. Falls back to the in-memory zustand store when no
|
||||
* backend is configured; the store carries the full provider directory
|
||||
* so no pagination is applied in fallback mode.
|
||||
*/
|
||||
export function useProviders(params: ListProvidersParams = {}) {
|
||||
const fallback = useSyncExternalStore(
|
||||
(cb) => useAppStore.subscribe(cb),
|
||||
() => useAppStore.getState().providers,
|
||||
() => useAppStore.getState().providers
|
||||
);
|
||||
|
||||
const q = useQuery<PaginatedResponse<Provider>>({
|
||||
queryKey: ["providers", params],
|
||||
queryFn: () => api.listProviders<Provider>(params),
|
||||
enabled: api.isConfigured,
|
||||
});
|
||||
|
||||
if (!api.isConfigured) {
|
||||
return {
|
||||
data: {
|
||||
items: fallback,
|
||||
total: fallback.length,
|
||||
returned: fallback.length,
|
||||
has_more: false,
|
||||
} satisfies PaginatedResponse<Provider>,
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
refetch: () => Promise.resolve(),
|
||||
} as const;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSyncExternalStore } from "react";
|
||||
import { api, type ListRemittancesParams, type PaginatedResponse } from "@/lib/api";
|
||||
import { useAppStore } from "@/store";
|
||||
import type { Remittance } from "@/types";
|
||||
|
||||
/**
|
||||
* Lists remittances. Falls back to the in-memory zustand store when no
|
||||
* backend is configured; the fallback honors `offset` / `limit` and
|
||||
* returns the full list otherwise.
|
||||
*/
|
||||
export function useRemittances(params: ListRemittancesParams) {
|
||||
const fallback = useSyncExternalStore(
|
||||
(cb) => useAppStore.subscribe(cb),
|
||||
() => useAppStore.getState().remittances,
|
||||
() => useAppStore.getState().remittances
|
||||
);
|
||||
|
||||
const q = useQuery<PaginatedResponse<Remittance>>({
|
||||
queryKey: ["remittances", params],
|
||||
queryFn: () => api.listRemittances<Remittance>(params),
|
||||
enabled: api.isConfigured,
|
||||
});
|
||||
|
||||
if (!api.isConfigured) {
|
||||
const offset = params.offset ?? 0;
|
||||
const limit = params.limit ?? 100;
|
||||
const sliced = fallback.slice(offset, offset + limit);
|
||||
return {
|
||||
data: {
|
||||
items: sliced,
|
||||
total: fallback.length,
|
||||
returned: sliced.length,
|
||||
has_more: fallback.length > offset + limit,
|
||||
} satisfies PaginatedResponse<Remittance>,
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
refetch: () => Promise.resolve(),
|
||||
} as const;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
Reference in New Issue
Block a user