pmtool / src /hooks /asset-management /useAssets.ts
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
7.86 kB
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { assetApi } from '@/services/assetApi';
import { Asset, AssetCreateRequest, AssetUpdateRequest } from '@/types/asset-management';
import { toast } from '@/lib/custom-toast';
// Query keys for asset-related queries
export const assetQueryKeys = {
all: ['assets'] as const,
lists: () => [...assetQueryKeys.all, 'list'] as const,
list: (filters?: any) => [...assetQueryKeys.lists(), { filters }] as const,
details: () => [...assetQueryKeys.all, 'detail'] as const,
detail: (id: number) => [...assetQueryKeys.details(), id] as const,
};
/**
* Hook for fetching all assets
*/
export const useAssets = () => {
return useQuery({
queryKey: assetQueryKeys.lists(),
queryFn: () => assetApi.getAll(),
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
});
};
/**
* Hook for fetching a single asset by ID
*/
export const useAsset = (id: number) => {
return useQuery({
queryKey: assetQueryKeys.detail(id),
queryFn: () => assetApi.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 creating a new asset
*/
export const useCreateAsset = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (asset: AssetCreateRequest) => assetApi.create(asset),
onSuccess: (newAsset) => {
// Invalidate and refetch assets list
queryClient.invalidateQueries({ queryKey: assetQueryKeys.lists() });
// Optionally add the new asset to the cache
queryClient.setQueryData(assetQueryKeys.detail(newAsset.assetID), newAsset);
toast.success('Asset created successfully', {
description: `Asset ${newAsset.brandModel} (${newAsset.serialNumber}) has been created.`
});
},
onError: (error) => {
console.error('Create asset error:', error);
const errorMessage = error instanceof Error ? error.message : 'Failed to create asset';
toast.error('Failed to create asset', {
description: errorMessage,
action: {
label: 'Retry',
onClick: () => {
// The retry will be handled by the form submission
}
}
});
},
retry: (failureCount, error) => {
// Don't retry on validation errors (4xx status codes)
if (error instanceof Error && error.message.includes('Validation failed')) {
return false;
}
return failureCount < 2;
},
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
};
/**
* Hook for updating an existing asset
*/
export const useUpdateAsset = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, asset }: { id: number; asset: AssetUpdateRequest }) =>
assetApi.update(id, asset),
onMutate: async ({ id, asset }) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({ queryKey: assetQueryKeys.detail(id) });
// Snapshot the previous value
const previousAsset = queryClient.getQueryData(assetQueryKeys.detail(id));
// Optimistically update to the new value
queryClient.setQueryData(assetQueryKeys.detail(id), asset);
// Return a context object with the snapshotted value
return { previousAsset, id };
},
onError: (error, variables, context) => {
// If the mutation fails, use the context returned from onMutate to roll back
if (context?.previousAsset) {
queryClient.setQueryData(assetQueryKeys.detail(context.id), context.previousAsset);
}
console.error('Update asset error:', error);
const errorMessage = error instanceof Error ? error.message : 'Failed to update asset';
toast.error('Failed to update asset', {
description: errorMessage,
action: {
label: 'Retry',
onClick: () => {
// The retry will be handled by the form submission
}
}
});
},
onSuccess: (updatedAsset, { id }) => {
// Update the asset in the cache
queryClient.setQueryData(assetQueryKeys.detail(id), updatedAsset);
// Invalidate assets list to ensure consistency
queryClient.invalidateQueries({ queryKey: assetQueryKeys.lists() });
toast.success('Asset updated successfully', {
description: `Asset ${updatedAsset.brandModel} (${updatedAsset.serialNumber}) has been updated.`
});
},
retry: (failureCount, error) => {
// Don't retry on validation errors (4xx status codes)
if (error instanceof Error && error.message.includes('Validation failed')) {
return false;
}
return failureCount < 2;
},
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
};
/**
* Hook for deleting an asset
*/
export const useDeleteAsset = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: number) => assetApi.delete(id),
onMutate: async (id) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({ queryKey: assetQueryKeys.detail(id) });
await queryClient.cancelQueries({ queryKey: assetQueryKeys.lists() });
// Snapshot the previous values
const previousAsset = queryClient.getQueryData(assetQueryKeys.detail(id));
const previousAssets = queryClient.getQueryData(assetQueryKeys.lists());
// Optimistically remove the asset from the list
if (previousAssets) {
queryClient.setQueryData(
assetQueryKeys.lists(),
(previousAssets as Asset[]).filter(asset => asset.assetID !== id)
);
}
// Return a context object with the snapshotted values
return { previousAsset, previousAssets, id };
},
onError: (error, id, context) => {
// If the mutation fails, use the context to roll back
if (context?.previousAssets) {
queryClient.setQueryData(assetQueryKeys.lists(), context.previousAssets);
}
if (context?.previousAsset) {
queryClient.setQueryData(assetQueryKeys.detail(id), context.previousAsset);
}
console.error('Delete asset error:', error);
const errorMessage = error instanceof Error ? error.message : 'Failed to delete asset';
toast.error('Failed to delete asset', {
description: errorMessage,
action: {
label: 'Retry',
onClick: () => {
// The retry will be handled by calling the delete function again
}
}
});
},
onSuccess: (_, id, context) => {
// Remove the asset from the cache
queryClient.removeQueries({ queryKey: assetQueryKeys.detail(id) });
// Invalidate assets list to ensure consistency
queryClient.invalidateQueries({ queryKey: assetQueryKeys.lists() });
const assetInfo = context?.previousAsset as Asset;
toast.success('Asset deleted successfully', {
description: assetInfo ? `Asset ${assetInfo.brandModel} (${assetInfo.serialNumber}) has been deleted.` : undefined
});
},
retry: (failureCount, error) => {
// Don't retry on validation errors or not found errors
if (error instanceof Error && (
error.message.includes('not found') ||
error.message.includes('Validation failed')
)) {
return false;
}
return failureCount < 2;
},
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
};