import { useQuery, useMutation, useQueryClient, keepPreviousData } from '@tanstack/react-query'; import api from './client'; import { Ticket, Category, CTIType, Item, User, AuditLog, Comment } from '../types'; // ── Keys ───────────────────────────────────────────────────────────────────── export const qk = { tickets: (filters?: Record) => ['tickets', filters ?? {}] as const, ticket: (id: string) => ['ticket', id] as const, ticketAudit: (id: string) => ['ticket', id, 'audit'] as const, categories: () => ['cti', 'categories'] as const, types: (categoryId?: string) => ['cti', 'types', categoryId ?? null] as const, items: (typeId?: string) => ['cti', 'items', typeId ?? null] as const, users: () => ['users'] as const, }; // ── Tickets ────────────────────────────────────────────────────────────────── export function useTickets(params: Record = {}) { // Strip undefined values for a stable key const clean: Record = {}; Object.entries(params).forEach(([k, v]) => { if (v !== undefined && v !== '' && v !== null) clean[k] = v; }); return useQuery({ queryKey: qk.tickets(clean), queryFn: async () => { const res = await api.get('/tickets', { params: clean }); return res.data; }, placeholderData: keepPreviousData, }); } export function useTicket(id: string | undefined) { return useQuery({ queryKey: qk.ticket(id ?? ''), queryFn: async () => (await api.get(`/tickets/${id}`)).data, enabled: !!id, }); } export function useTicketAudit(id: string | undefined, enabled = true) { return useQuery({ queryKey: qk.ticketAudit(id ?? ''), queryFn: async () => (await api.get(`/tickets/${id}/audit`)).data, enabled: !!id && enabled, }); } export function useCreateTicket() { const qc = useQueryClient(); return useMutation({ mutationFn: async (data: Record) => (await api.post('/tickets', data)).data, onSuccess: () => { qc.invalidateQueries({ queryKey: ['tickets'] }); }, }); } export function useUpdateTicket() { const qc = useQueryClient(); return useMutation({ mutationFn: async ({ id, data }: { id: string; data: Record }) => (await api.patch(`/tickets/${id}`, data)).data, onSuccess: (ticket) => { qc.setQueryData(qk.ticket(ticket.displayId), ticket); qc.invalidateQueries({ queryKey: ['tickets'] }); qc.invalidateQueries({ queryKey: ['ticket', ticket.displayId, 'audit'] }); }, }); } export function useDeleteTicket() { const qc = useQueryClient(); return useMutation({ mutationFn: async (id: string) => { await api.delete(`/tickets/${id}`); }, onSuccess: () => { qc.invalidateQueries({ queryKey: ['tickets'] }); }, }); } // ── Comments ───────────────────────────────────────────────────────────────── export function useAddComment(ticketId: string | undefined) { const qc = useQueryClient(); return useMutation({ mutationFn: async (body: string) => (await api.post(`/tickets/${ticketId}/comments`, { body })).data, onSuccess: () => { if (ticketId) { qc.invalidateQueries({ queryKey: qk.ticket(ticketId) }); qc.invalidateQueries({ queryKey: qk.ticketAudit(ticketId) }); } }, }); } export function useDeleteComment(ticketId: string | undefined) { const qc = useQueryClient(); return useMutation({ mutationFn: async (commentId: string) => { await api.delete(`/tickets/${ticketId}/comments/${commentId}`); }, onSuccess: () => { if (ticketId) { qc.invalidateQueries({ queryKey: qk.ticket(ticketId) }); qc.invalidateQueries({ queryKey: qk.ticketAudit(ticketId) }); } }, }); } // ── CTI ────────────────────────────────────────────────────────────────────── export function useCategories() { return useQuery({ queryKey: qk.categories(), queryFn: async () => (await api.get('/cti/categories')).data, staleTime: 5 * 60 * 1000, }); } export function useTypes(categoryId?: string) { return useQuery({ queryKey: qk.types(categoryId), queryFn: async () => (await api.get('/cti/types', { params: categoryId ? { categoryId } : {} })).data, enabled: !!categoryId, staleTime: 5 * 60 * 1000, }); } export function useItems(typeId?: string) { return useQuery({ queryKey: qk.items(typeId), queryFn: async () => (await api.get('/cti/items', { params: typeId ? { typeId } : {} })).data, enabled: !!typeId, staleTime: 5 * 60 * 1000, }); } function useCtiMutation(fn: (vars: TVars) => Promise) { const qc = useQueryClient(); return useMutation({ mutationFn: fn, onSuccess: () => qc.invalidateQueries({ queryKey: ['cti'] }), }); } export function useCreateCategory() { return useCtiMutation(async (data: { name: string }) => (await api.post('/cti/categories', data)).data, ); } export function useUpdateCategory() { return useCtiMutation(async ({ id, name }: { id: string; name: string }) => (await api.put(`/cti/categories/${id}`, { name })).data, ); } export function useDeleteCategory() { return useCtiMutation(async (id: string) => { await api.delete(`/cti/categories/${id}`); }); } export function useCreateType() { return useCtiMutation(async (data: { name: string; categoryId: string }) => (await api.post('/cti/types', data)).data, ); } export function useUpdateType() { return useCtiMutation(async ({ id, name }: { id: string; name: string }) => (await api.put(`/cti/types/${id}`, { name })).data, ); } export function useDeleteType() { return useCtiMutation(async (id: string) => { await api.delete(`/cti/types/${id}`); }); } export function useCreateItem() { return useCtiMutation(async (data: { name: string; typeId: string }) => (await api.post('/cti/items', data)).data, ); } export function useUpdateItem() { return useCtiMutation(async ({ id, name }: { id: string; name: string }) => (await api.put(`/cti/items/${id}`, { name })).data, ); } export function useDeleteItem() { return useCtiMutation(async (id: string) => { await api.delete(`/cti/items/${id}`); }); } // ── Users ──────────────────────────────────────────────────────────────────── export function useUsers() { return useQuery({ queryKey: qk.users(), queryFn: async () => (await api.get('/users')).data, staleTime: 60 * 1000, }); } export function useCreateUser() { const qc = useQueryClient(); return useMutation({ mutationFn: async (data: Record) => (await api.post('/users', data)).data, onSuccess: () => qc.invalidateQueries({ queryKey: qk.users() }), }); } export function useUpdateUser() { const qc = useQueryClient(); return useMutation({ mutationFn: async ({ id, data }: { id: string; data: Record }) => (await api.patch(`/users/${id}`, data)).data, onSuccess: () => qc.invalidateQueries({ queryKey: qk.users() }), }); } export function useDeleteUser() { const qc = useQueryClient(); return useMutation({ mutationFn: async (id: string) => { await api.delete(`/users/${id}`); }, onSuccess: () => qc.invalidateQueries({ queryKey: qk.users() }), }); }