Files
thrilltrack-explorer/src/hooks/admin/useAlertGroupActions.ts
gpt-engineer-app[bot] 2093560f64 Connect to Lovable Cloud
Implement monitoring and alert-resolve UI improvements:
- Enhance useAlertGroupActions with robust error handling and breadcrumb logging
- Add loading state visuals to GroupedAlertsPanel and Resolve All button
- Integrate loading indicator (Loader2) for better user feedback during resolves
2025-11-11 13:21:58 +00:00

143 lines
4.3 KiB
TypeScript

import { useMutation, useQueryClient } from '@tanstack/react-query';
import { supabase } from '@/lib/supabaseClient';
import { queryKeys } from '@/lib/queryKeys';
import { toast } from 'sonner';
import { breadcrumb } from '@/lib/errorBreadcrumbs';
import type { GroupedAlert } from './useGroupedAlerts';
export function useResolveAlertGroup() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
alertIds,
source
}: {
alertIds: string[];
source: 'system' | 'rate_limit';
}) => {
const table = source === 'system' ? 'system_alerts' : 'rate_limit_alerts';
// Log breadcrumb for debugging
breadcrumb.userAction(`resolve-alerts`, 'AlertGroupActions', {
alertIds,
source,
count: alertIds.length,
});
const { data, error } = await supabase
.from(table)
.update({ resolved_at: new Date().toISOString() })
.in('id', alertIds)
.select();
if (error) {
// Enhanced error handling with specific messages
if (error.code === '42501') {
throw new Error('Permission denied. You do not have access to resolve these alerts.');
} else if (error.code === 'PGRST116') {
throw new Error('No alerts found to resolve. They may have already been resolved.');
} else {
console.error('Supabase error details:', error);
throw new Error(`Failed to resolve alerts: ${error.message}`);
}
}
return { count: alertIds.length, updatedAlerts: data };
},
onMutate: async ({ alertIds }) => {
// Cancel any outgoing refetches
await queryClient.cancelQueries({
queryKey: queryKeys.monitoring.groupedAlerts()
});
const previousData = queryClient.getQueryData(
queryKeys.monitoring.groupedAlerts()
);
// Optimistically update to the new value
queryClient.setQueryData(
queryKeys.monitoring.groupedAlerts(),
(old: GroupedAlert[] | undefined) => {
if (!old) return old;
return old.map(alert => {
const hasMatchingIds = alert.alert_ids.some(id =>
alertIds.includes(id)
);
if (hasMatchingIds) {
return {
...alert,
unresolved_count: 0,
has_resolved: true,
};
}
return alert;
});
}
);
return { previousData };
},
onSuccess: (data) => {
toast.success(`Resolved ${data.count} alert${data.count > 1 ? 's' : ''}`);
},
onError: (error: Error, variables, context) => {
// Rollback on error
if (context?.previousData) {
queryClient.setQueryData(
queryKeys.monitoring.groupedAlerts(),
context.previousData
);
}
// Show detailed error message
toast.error(error.message || 'Failed to resolve alerts', {
description: 'Please try again or contact support if the issue persists.',
duration: 5000,
});
// Log to error tracking system
breadcrumb.apiCall('resolve-alerts', 'POST', 500);
console.error('Error resolving alert group:', error, {
alertIds: variables.alertIds,
source: variables.source,
});
},
onSettled: () => {
queryClient.invalidateQueries({
queryKey: queryKeys.monitoring.groupedAlerts()
});
queryClient.invalidateQueries({
queryKey: queryKeys.monitoring.combinedAlerts()
});
},
});
}
export function useSnoozeAlertGroup() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
groupKey,
duration
}: {
groupKey: string;
duration: number;
}) => {
const snoozedAlerts = JSON.parse(
localStorage.getItem('snoozed_alerts') || '{}'
);
snoozedAlerts[groupKey] = Date.now() + duration;
localStorage.setItem('snoozed_alerts', JSON.stringify(snoozedAlerts));
return { groupKey, until: snoozedAlerts[groupKey] };
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: queryKeys.monitoring.groupedAlerts()
});
toast.success('Alert group snoozed');
},
});
}