Spaces:
Sleeping
Sleeping
| 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'); | |
| }, | |
| }); | |
| }; |