import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import type { AssigneesUpdate, ChecklistPatch, MeterReadingCreate, PreventiveGenerate, ReportUpsert, RequestApprove, RequestCreate, RequestReject, TaskTemplateCreate, TaskTemplateUpdate, TransitionRequest, WorkOrderCreate, } from '@siop/shared'; import { api } from './client'; /** Hooks R2 — client typé, invalidations ciblées. */ async function unwrap(res: { data?: T; error?: unknown; response: Response }): Promise { if (res.error || res.data === undefined) { const message = (res.error as { message?: string } | undefined)?.message ?? `Le serveur a répondu ${res.response.status}`; throw new Error(Array.isArray(message) ? message.join(' — ') : message); } return res.data; } function useInvalidate(...keys: string[]) { const queryClient = useQueryClient(); return () => Promise.all(keys.map((key) => queryClient.invalidateQueries({ queryKey: [key] }))); } // ————— Ordres de travail ————— export function useWorkOrders(enabled = true) { return useQuery({ queryKey: ['work-orders'], enabled, queryFn: async () => (await unwrap(await api.GET('/work-orders'))).workOrders, }); } export function useWorkOrder(id: string | undefined) { return useQuery({ queryKey: ['work-orders', id], enabled: !!id, queryFn: async () => unwrap(await api.GET('/work-orders/{id}', { params: { path: { id: id! } } })), }); } export function useCreateWorkOrder() { const invalidate = useInvalidate('work-orders'); return useMutation({ mutationFn: async (body: WorkOrderCreate) => unwrap(await api.POST('/work-orders', { body })), onSuccess: invalidate, }); } export function useTransitionWorkOrder(id: string) { const invalidate = useInvalidate('work-orders', 'requests', 'preventive-status'); return useMutation({ mutationFn: async (body: TransitionRequest) => unwrap(await api.POST('/work-orders/{id}/transition', { params: { path: { id } }, body })), onSuccess: invalidate, }); } export function useCommentWorkOrder(id: string) { const invalidate = useInvalidate('work-orders'); return useMutation({ mutationFn: async (message: string) => unwrap( await api.POST('/work-orders/{id}/comments', { params: { path: { id } }, body: { message }, }), ), onSuccess: invalidate, }); } export function useSetAssignees(id: string) { const invalidate = useInvalidate('work-orders'); return useMutation({ mutationFn: async (body: AssigneesUpdate) => unwrap(await api.PUT('/work-orders/{id}/assignees', { params: { path: { id } }, body })), onSuccess: invalidate, }); } export function useUpsertReport(id: string) { const invalidate = useInvalidate('work-orders'); return useMutation({ mutationFn: async (body: ReportUpsert) => unwrap(await api.PUT('/work-orders/{id}/report', { params: { path: { id } }, body })), onSuccess: invalidate, }); } export function usePatchChecklist(id: string) { const invalidate = useInvalidate('work-orders'); return useMutation({ mutationFn: async ({ itemId, ...body }: ChecklistPatch & { itemId: string }) => unwrap( await api.PATCH('/work-orders/{id}/checklist/{itemId}', { params: { path: { id, itemId } }, body, }), ), onSuccess: invalidate, }); } // ————— Demandes ————— export function useRequests() { return useQuery({ queryKey: ['requests'], queryFn: async () => (await unwrap(await api.GET('/requests'))).requests, }); } export function useCreateRequest() { const invalidate = useInvalidate('requests'); return useMutation({ mutationFn: async (body: RequestCreate) => unwrap(await api.POST('/requests', { body })), onSuccess: invalidate, }); } export function useApproveRequest() { const invalidate = useInvalidate('requests', 'work-orders'); return useMutation({ mutationFn: async ({ id, ...body }: RequestApprove & { id: string }) => unwrap(await api.POST('/requests/{id}/approve', { params: { path: { id } }, body })), onSuccess: invalidate, }); } export function useRejectRequest() { const invalidate = useInvalidate('requests'); return useMutation({ mutationFn: async ({ id, ...body }: RequestReject & { id: string }) => unwrap(await api.POST('/requests/{id}/reject', { params: { path: { id } }, body })), onSuccess: invalidate, }); } // ————— Référentiels du bilan ————— export function useReferenceValues() { return useQuery({ queryKey: ['reference-values'], staleTime: 5 * 60_000, queryFn: async () => (await unwrap(await api.GET('/reference-values'))).referenceValues, }); } // ————— Préventif ————— export function useTaskTemplates() { return useQuery({ queryKey: ['task-templates'], queryFn: async () => (await unwrap(await api.GET('/preventive/templates'))).templates, }); } export function useCreateTaskTemplate() { const invalidate = useInvalidate('task-templates'); return useMutation({ mutationFn: async (body: TaskTemplateCreate) => unwrap(await api.POST('/preventive/templates', { body })), onSuccess: invalidate, }); } export function useUpdateTaskTemplate() { const invalidate = useInvalidate('task-templates'); return useMutation({ mutationFn: async ({ id, ...body }: TaskTemplateUpdate & { id: string }) => unwrap( await api.PATCH('/preventive/templates/{id}', { params: { path: { id } }, body }), ), onSuccess: invalidate, }); } export function usePreventiveStatus(enabled = true) { return useQuery({ queryKey: ['preventive-status'], enabled, queryFn: async () => unwrap(await api.GET('/preventive/status')), }); } export function useGeneratePreventive() { const invalidate = useInvalidate('preventive-status', 'work-orders'); return useMutation({ mutationFn: async (body: PreventiveGenerate) => unwrap(await api.POST('/preventive/generate', { body })), onSuccess: invalidate, }); } // ————— Compteurs ————— export function useMeters(assetId: string | undefined) { return useQuery({ queryKey: ['meters', assetId], enabled: !!assetId, queryFn: async () => (await unwrap(await api.GET('/assets/{id}/meters', { params: { path: { id: assetId! } } }))) .meters, }); } export function useAddMeterReading(assetId: string) { const invalidate = useInvalidate('meters'); return useMutation({ mutationFn: async (body: MeterReadingCreate) => unwrap( await api.POST('/assets/{id}/meter-readings', { params: { path: { id: assetId } }, body, }), ), onSuccess: invalidate, }); }