import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { assetAssignmentApi } from '@/services/assetAssignmentApi'; import { AssetAssignment, AssetAssignmentCreateRequest, AssetAssignmentUpdateRequest } from '@/types/asset-management'; import { toast } from '@/lib/custom-toast'; import { assetQueryKeys } from './useAssets'; // Query keys for asset assignment-related queries export const assignmentQueryKeys = { all: ['assignments'] as const, lists: () => [...assignmentQueryKeys.all, 'list'] as const, list: (filters?: any) => [...assignmentQueryKeys.lists(), { filters }] as const, details: () => [...assignmentQueryKeys.all, 'detail'] as const, detail: (id: number) => [...assignmentQueryKeys.details(), id] as const, byAsset: (assetId: number) => [...assignmentQueryKeys.all, 'byAsset', assetId] as const, byEmployee: (employeeId: number) => [...assignmentQueryKeys.all, 'byEmployee', employeeId] as const, history: (assetId: number) => [...assignmentQueryKeys.all, 'history', assetId] as const, active: () => [...assignmentQueryKeys.all, 'active'] as const, }; /** * Hook for fetching all asset assignments */ export const useAssignments = () => { return useQuery({ queryKey: assignmentQueryKeys.lists(), queryFn: () => assetAssignmentApi.getAll(), staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 10 * 60 * 1000, // 10 minutes }); }; /** * Hook for fetching a single asset assignment by ID */ export const useAssignment = (id: number) => { return useQuery({ queryKey: assignmentQueryKeys.detail(id), queryFn: () => assetAssignmentApi.getById(id), staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 10 * 60 * 1000, // 10 minutes enabled: !!id && id > 0, // Only run query if ID is valid }); }; /** * Hook for fetching asset assignments by asset ID */ export const useAssignmentsByAsset = (assetId: number) => { return useQuery({ queryKey: assignmentQueryKeys.byAsset(assetId), queryFn: () => assetAssignmentApi.getByAssetId(assetId), staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 10 * 60 * 1000, // 10 minutes enabled: !!assetId && assetId > 0, // Only run query if asset ID is valid }); }; /** * Hook for fetching asset assignments by employee ID */ export const useAssignmentsByEmployee = (employeeId: number) => { return useQuery({ queryKey: assignmentQueryKeys.byEmployee(employeeId), queryFn: () => assetAssignmentApi.getByEmployeeId(employeeId), staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 10 * 60 * 1000, // 10 minutes enabled: !!employeeId && employeeId > 0, // Only run query if employee ID is valid }); }; /** * Hook for creating a new asset assignment */ export const useCreateAssignment = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (assignment: AssetAssignmentCreateRequest) => { // Ensure dates are properly formatted before sending to API const formattedAssignment = { ...assignment, assignedDate: assignment.assignedDate || new Date().toISOString(), returnedDate: assignment.returnedDate || null, }; return assetAssignmentApi.create(formattedAssignment); }, onSuccess: (newAssignment) => { // Invalidate and refetch assignment lists queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.lists() }); // Invalidate assignments for the specific asset queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.byAsset(newAssignment.assetID) }); // Invalidate assignments for the specific employee queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.byEmployee(newAssignment.employeeID) }); // Optionally add the new assignment to the cache queryClient.setQueryData( assignmentQueryKeys.detail(newAssignment.assignmentID), newAssignment ); // Invalidate the asset details to refresh related data queryClient.invalidateQueries({ queryKey: assetQueryKeys.detail(newAssignment.assetID) }); toast.success('Asset assignment created successfully'); }, onError: (error) => { console.error('Create assignment error:', error); toast.error('Failed to create asset assignment'); }, }); }; /** * Hook for updating an existing asset assignment */ export const useUpdateAssignment = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: ({ id, assignment }: { id: number; assignment: AssetAssignmentUpdateRequest }) => { // Ensure dates are properly formatted before sending to API const formattedAssignment = { ...assignment, assignedDate: assignment.assignedDate || new Date().toISOString(), returnedDate: assignment.returnedDate || null, }; return assetAssignmentApi.update(id, formattedAssignment); }, onMutate: async ({ id, assignment }) => { // Cancel any outgoing refetches await queryClient.cancelQueries({ queryKey: assignmentQueryKeys.detail(id) }); // Snapshot the previous value const previousAssignment = queryClient.getQueryData(assignmentQueryKeys.detail(id)); // Optimistically update to the new value queryClient.setQueryData(assignmentQueryKeys.detail(id), assignment); // Return a context object with the snapshotted value return { previousAssignment, id, assetId: assignment.assetID, employeeId: assignment.employeeID }; }, onError: (error, variables, context) => { // If the mutation fails, use the context returned from onMutate to roll back if (context?.previousAssignment) { queryClient.setQueryData( assignmentQueryKeys.detail(context.id), context.previousAssignment ); } console.error('Update assignment error:', error); toast.error('Failed to update asset assignment'); }, onSuccess: (updatedAssignment, { id }) => { // Update the assignment in the cache queryClient.setQueryData(assignmentQueryKeys.detail(id), updatedAssignment); // Invalidate assignment lists to ensure consistency queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.lists() }); // Invalidate assignments for the specific asset queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.byAsset(updatedAssignment.assetID) }); // Invalidate assignments for the specific employee queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.byEmployee(updatedAssignment.employeeID) }); // Invalidate the asset details to refresh related data queryClient.invalidateQueries({ queryKey: assetQueryKeys.detail(updatedAssignment.assetID) }); toast.success('Asset assignment updated successfully'); }, }); }; /** * Hook for deleting an asset assignment */ export const useDeleteAssignment = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (id: number) => assetAssignmentApi.delete(id), onMutate: async (id) => { // Cancel any outgoing refetches await queryClient.cancelQueries({ queryKey: assignmentQueryKeys.detail(id) }); await queryClient.cancelQueries({ queryKey: assignmentQueryKeys.lists() }); // Snapshot the previous values const previousAssignment = queryClient.getQueryData(assignmentQueryKeys.detail(id)); const previousAssignmentsList = queryClient.getQueryData(assignmentQueryKeys.lists()); // Get the asset and employee IDs from the assignment to invalidate related queries const assignment = previousAssignment as AssetAssignment; const assetId = assignment?.assetID; const employeeId = assignment?.employeeID; // Optimistically remove the assignment from the list if (previousAssignmentsList) { queryClient.setQueryData( assignmentQueryKeys.lists(), (previousAssignmentsList as AssetAssignment[]).filter( assignment => assignment.assignmentID !== id ) ); } // Optimistically remove from asset-specific list if we have the asset ID if (assetId) { const previousAssetAssignments = queryClient.getQueryData( assignmentQueryKeys.byAsset(assetId) ); if (previousAssetAssignments) { queryClient.setQueryData( assignmentQueryKeys.byAsset(assetId), (previousAssetAssignments as AssetAssignment[]).filter( assignment => assignment.assignmentID !== id ) ); } } // Optimistically remove from employee-specific list if we have the employee ID if (employeeId) { const previousEmployeeAssignments = queryClient.getQueryData( assignmentQueryKeys.byEmployee(employeeId) ); if (previousEmployeeAssignments) { queryClient.setQueryData( assignmentQueryKeys.byEmployee(employeeId), (previousEmployeeAssignments as AssetAssignment[]).filter( assignment => assignment.assignmentID !== id ) ); } } // Return a context object with the snapshotted values return { previousAssignment, previousAssignmentsList, id, assetId, employeeId }; }, onError: (error, id, context) => { // If the mutation fails, use the context to roll back if (context?.previousAssignmentsList) { queryClient.setQueryData(assignmentQueryKeys.lists(), context.previousAssignmentsList); } if (context?.previousAssignment) { queryClient.setQueryData(assignmentQueryKeys.detail(id), context.previousAssignment); } if (context?.assetId) { // Invalidate asset-specific assignments to refresh from server queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.byAsset(context.assetId) }); } if (context?.employeeId) { // Invalidate employee-specific assignments to refresh from server queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.byEmployee(context.employeeId) }); } console.error('Delete assignment error:', error); toast.error('Failed to delete asset assignment'); }, onSuccess: (_, id, context) => { // Remove the assignment from the cache queryClient.removeQueries({ queryKey: assignmentQueryKeys.detail(id) }); // Invalidate assignment lists to ensure consistency queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.lists() }); // Invalidate assignments for the specific asset if we have the asset ID if (context?.assetId) { queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.byAsset(context.assetId) }); // Invalidate the asset details to refresh related data queryClient.invalidateQueries({ queryKey: assetQueryKeys.detail(context.assetId) }); } // Invalidate assignments for the specific employee if we have the employee ID if (context?.employeeId) { queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.byEmployee(context.employeeId) }); } toast.success('Asset assignment deleted successfully'); }, }); }; /** * Hook for fetching assignment history by asset ID */ export const useAssignmentHistory = (assetId: number) => { return useQuery({ queryKey: assignmentQueryKeys.history(assetId), queryFn: () => assetAssignmentApi.getAssignmentHistory(assetId), staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 10 * 60 * 1000, // 10 minutes enabled: !!assetId && assetId > 0, // Only run query if asset ID is valid }); }; /** * Hook for fetching all active assignments */ export const useActiveAssignments = () => { return useQuery({ queryKey: assignmentQueryKeys.active(), queryFn: () => assetAssignmentApi.getActiveAssignments(), staleTime: 2 * 60 * 1000, // 2 minutes (shorter for active data) gcTime: 5 * 60 * 1000, // 5 minutes }); }; /** * Hook for bulk returning multiple assignments */ export const useBulkReturnAssignments = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: (request: { assignmentIds: number[]; returnedDate: string; returnReason?: string; remarks?: string; performedBy?: { employeeID: number; name: string; }; }) => assetAssignmentApi.bulkReturn(request), onSuccess: (result) => { // Invalidate all assignment-related queries to refresh data queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.all }); // Invalidate active assignments specifically queryClient.invalidateQueries({ queryKey: assignmentQueryKeys.active() }); // Invalidate asset queries to refresh asset status queryClient.invalidateQueries({ queryKey: assetQueryKeys.all }); // Show success/failure summary const { successful, failed } = result.summary; if (successful > 0 && failed === 0) { toast.success(`Successfully returned ${successful} assignment${successful > 1 ? 's' : ''}`); } else if (successful > 0 && failed > 0) { toast.warning(`Returned ${successful} assignment${successful > 1 ? 's' : ''}, ${failed} failed`); } else { toast.error(`Failed to return assignments: ${result.message}`); } }, onError: (error) => { console.error('Bulk return assignments error:', error); toast.error('Failed to return assignments'); }, }); };