import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import type { AssetComponentCreate, AssetCreate, AssetUpdate, CategoryCreate, CategoryUpdate, InvitationCreate, LocationCreate, LocationUpdate, TeamCreate, UserUpdate, } from '@siop/shared'; import { api } from './client'; /** Hooks R1 — chaque appel passe par le client typé (règle d'or). */ 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; } export function useLocations() { return useQuery({ queryKey: ['locations'], queryFn: async () => (await unwrap(await api.GET('/locations'))).locations, }); } export function useAssets() { return useQuery({ queryKey: ['assets'], queryFn: async () => (await unwrap(await api.GET('/assets'))).assets, }); } /** Options minimales (signalement) — accessibles à tout rôle authentifié. */ export function useAssetOptions() { return useQuery({ queryKey: ['asset-options'], queryFn: async () => (await unwrap(await api.GET('/assets/options'))).options, }); } export function useAsset(id: string | undefined) { return useQuery({ queryKey: ['assets', id], enabled: !!id, queryFn: async () => unwrap(await api.GET('/assets/{id}', { params: { path: { id: id! } } })), }); } export function useCategories() { return useQuery({ queryKey: ['categories'], queryFn: async () => (await unwrap(await api.GET('/categories'))).categories, }); } export function useTeams() { return useQuery({ queryKey: ['teams'], queryFn: async () => (await unwrap(await api.GET('/teams'))).teams, }); } export function useUsers() { return useQuery({ queryKey: ['users'], queryFn: async () => (await unwrap(await api.GET('/users'))).users, }); } export function useRoles() { return useQuery({ queryKey: ['roles'], queryFn: async () => (await unwrap(await api.GET('/roles'))).roles, }); } function useInvalidate(...keys: string[]) { const queryClient = useQueryClient(); return () => Promise.all(keys.map((key) => queryClient.invalidateQueries({ queryKey: [key] }))); } export function useCreateLocation() { const invalidate = useInvalidate('locations'); return useMutation({ mutationFn: async (body: LocationCreate) => unwrap(await api.POST('/locations', { body })), onSuccess: invalidate, }); } export function useUpdateLocation(id: string) { const invalidate = useInvalidate('locations', 'partners'); return useMutation({ mutationFn: async (body: LocationUpdate) => unwrap(await api.PATCH('/locations/{id}', { params: { path: { id } }, body })), onSuccess: invalidate, }); } export function useCreateAsset() { const invalidate = useInvalidate('assets', 'locations'); return useMutation({ mutationFn: async (body: AssetCreate) => unwrap(await api.POST('/assets', { body })), onSuccess: invalidate, }); } export function useUpdateAsset(id: string) { const invalidate = useInvalidate('assets'); return useMutation({ mutationFn: async (body: AssetUpdate) => unwrap(await api.PATCH('/assets/{id}', { params: { path: { id } }, body })), onSuccess: invalidate, }); } export function useAddComponent(assetId: string) { const invalidate = useInvalidate('assets'); return useMutation({ mutationFn: async (body: AssetComponentCreate) => unwrap( await api.POST('/assets/{id}/components', { params: { path: { id: assetId } }, body, }), ), onSuccess: invalidate, }); } export function useRemoveComponent(assetId: string) { const invalidate = useInvalidate('assets'); return useMutation({ mutationFn: async (componentId: string) => { const res = await api.DELETE('/assets/{id}/components/{componentId}', { params: { path: { id: assetId, componentId } }, }); if (res.response.status >= 400) throw new Error('Suppression impossible'); }, onSuccess: invalidate, }); } export function useCreateCategory() { const invalidate = useInvalidate('categories'); return useMutation({ mutationFn: async (body: CategoryCreate) => unwrap(await api.POST('/categories', { body })), onSuccess: invalidate, }); } export function useUpdateCategory() { const invalidate = useInvalidate('categories'); return useMutation({ mutationFn: async ({ id, ...body }: CategoryUpdate & { id: string }) => unwrap(await api.PATCH('/categories/{id}', { params: { path: { id } }, body })), onSuccess: invalidate, }); } export function useCreateTeam() { const invalidate = useInvalidate('teams', 'users'); return useMutation({ mutationFn: async (body: TeamCreate) => unwrap(await api.POST('/teams', { body })), onSuccess: invalidate, }); } export function useInviteUser() { const invalidate = useInvalidate('users'); return useMutation({ mutationFn: async (body: InvitationCreate) => unwrap(await api.POST('/users/invitations', { body })), onSuccess: invalidate, }); } export function useResendInvitation() { const invalidate = useInvalidate('users'); return useMutation({ mutationFn: async (userId: string) => unwrap( await api.POST('/users/{id}/invitation', { params: { path: { id: userId } } }), ), onSuccess: invalidate, }); } export function useUpdateUser() { const invalidate = useInvalidate('users', 'teams'); return useMutation({ mutationFn: async ({ id, ...body }: UserUpdate & { id: string }) => unwrap(await api.PATCH('/users/{id}', { params: { path: { id } }, body })), onSuccess: invalidate, }); }