feat(sprint-5): Phase 2 — React Query API client layer
- @tanstack/react-query with QueryClientProvider in providers/index.tsx - Typed api-client.ts fetch wrapper with ApiError class + apiDownload - Service modules: members, distributions, stock, reports, dashboard, portal, staff - Offline banner component (onlineManager subscription) - API error boundary with retry button - Loading skeleton components (card, table, chart, form, dashboard) - i18n for error/loading states (de/en)
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
|
||||
import type { Batch, BatchSummary, Strain } from "@/types/api"
|
||||
|
||||
import { apiClient } from "@/lib/api-client"
|
||||
|
||||
// --- Types ---
|
||||
|
||||
export interface BatchesPage {
|
||||
content: Batch[]
|
||||
totalElements: number
|
||||
totalPages: number
|
||||
number: number
|
||||
size: number
|
||||
}
|
||||
|
||||
export interface CreateBatchRequest {
|
||||
strainName: string
|
||||
thcPercent: number
|
||||
cbdPercent: number
|
||||
totalGrams: number
|
||||
supplier: string
|
||||
harvestDate: string
|
||||
notes?: string
|
||||
}
|
||||
|
||||
// --- Query Hooks ---
|
||||
|
||||
export function useBatchesQuery(params?: {
|
||||
page?: number
|
||||
size?: number
|
||||
status?: string
|
||||
}) {
|
||||
return useQuery({
|
||||
queryKey: ["batches", params],
|
||||
queryFn: () =>
|
||||
apiClient<BatchesPage>("/batches", {
|
||||
params: {
|
||||
page: params?.page,
|
||||
size: params?.size ?? 20,
|
||||
status: params?.status || undefined,
|
||||
},
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
export function useBatchQuery(id: string) {
|
||||
return useQuery({
|
||||
queryKey: ["batches", id],
|
||||
queryFn: () => apiClient<Batch>(`/batches/${id}`),
|
||||
enabled: !!id,
|
||||
})
|
||||
}
|
||||
|
||||
export function useStrainsQuery() {
|
||||
return useQuery({
|
||||
queryKey: ["strains"],
|
||||
queryFn: () => apiClient<Strain[]>("/strains"),
|
||||
staleTime: 5 * 60 * 1000, // strains rarely change — 5 min stale
|
||||
})
|
||||
}
|
||||
|
||||
export function useStockSummaryQuery() {
|
||||
return useQuery({
|
||||
queryKey: ["batches", "summary"],
|
||||
queryFn: () => apiClient<BatchSummary[]>("/batches/summary"),
|
||||
})
|
||||
}
|
||||
|
||||
// --- Mutation Hooks ---
|
||||
|
||||
export function useCreateBatchMutation() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateBatchRequest) =>
|
||||
apiClient<Batch>("/batches", { method: "POST", body: data }),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["batches"] })
|
||||
queryClient.invalidateQueries({ queryKey: ["dashboard"] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useRecallBatchMutation() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (id: string) =>
|
||||
apiClient<Batch>(`/batches/${id}/recall`, { method: "POST" }),
|
||||
onSuccess: (_data, id) => {
|
||||
queryClient.invalidateQueries({ queryKey: ["batches"] })
|
||||
queryClient.invalidateQueries({ queryKey: ["batches", id] })
|
||||
queryClient.invalidateQueries({ queryKey: ["dashboard"] })
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user