mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 18:11:13 -05:00
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
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { AlertCircle, AlertTriangle, Info, ChevronDown, ChevronUp, Clock, Zap, RefreshCw } from 'lucide-react';
|
import { AlertCircle, AlertTriangle, Info, ChevronDown, ChevronUp, Clock, Zap, RefreshCw, Loader2 } from 'lucide-react';
|
||||||
import { formatDistanceToNow } from 'date-fns';
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
import type { GroupedAlert } from '@/hooks/admin/useGroupedAlerts';
|
import type { GroupedAlert } from '@/hooks/admin/useGroupedAlerts';
|
||||||
import { useResolveAlertGroup, useSnoozeAlertGroup } from '@/hooks/admin/useAlertGroupActions';
|
import { useResolveAlertGroup, useSnoozeAlertGroup } from '@/hooks/admin/useAlertGroupActions';
|
||||||
@@ -211,7 +211,14 @@ export function GroupedAlertsPanel({ alerts, isLoading }: GroupedAlertsPanelProp
|
|||||||
onClick={() => handleResolveGroup(alert)}
|
onClick={() => handleResolveGroup(alert)}
|
||||||
disabled={resolveGroup.isPending}
|
disabled={resolveGroup.isPending}
|
||||||
>
|
>
|
||||||
Resolve All
|
{resolveGroup.isPending ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||||
|
Resolving...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'Resolve All'
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import { queryKeys } from '@/lib/queryKeys';
|
import { queryKeys } from '@/lib/queryKeys';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import { breadcrumb } from '@/lib/errorBreadcrumbs';
|
||||||
import type { GroupedAlert } from './useGroupedAlerts';
|
import type { GroupedAlert } from './useGroupedAlerts';
|
||||||
|
|
||||||
export function useResolveAlertGroup() {
|
export function useResolveAlertGroup() {
|
||||||
@@ -16,13 +17,33 @@ export function useResolveAlertGroup() {
|
|||||||
source: 'system' | 'rate_limit';
|
source: 'system' | 'rate_limit';
|
||||||
}) => {
|
}) => {
|
||||||
const table = source === 'system' ? 'system_alerts' : 'rate_limit_alerts';
|
const table = source === 'system' ? 'system_alerts' : 'rate_limit_alerts';
|
||||||
const { error } = await supabase
|
|
||||||
|
// Log breadcrumb for debugging
|
||||||
|
breadcrumb.userAction(`resolve-alerts`, 'AlertGroupActions', {
|
||||||
|
alertIds,
|
||||||
|
source,
|
||||||
|
count: alertIds.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data, error } = await supabase
|
||||||
.from(table)
|
.from(table)
|
||||||
.update({ resolved_at: new Date().toISOString() })
|
.update({ resolved_at: new Date().toISOString() })
|
||||||
.in('id', alertIds);
|
.in('id', alertIds)
|
||||||
|
.select();
|
||||||
|
|
||||||
if (error) throw error;
|
if (error) {
|
||||||
return { count: alertIds.length };
|
// 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 }) => {
|
onMutate: async ({ alertIds }) => {
|
||||||
// Cancel any outgoing refetches
|
// Cancel any outgoing refetches
|
||||||
@@ -60,7 +81,7 @@ export function useResolveAlertGroup() {
|
|||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
toast.success(`Resolved ${data.count} alert${data.count > 1 ? 's' : ''}`);
|
toast.success(`Resolved ${data.count} alert${data.count > 1 ? 's' : ''}`);
|
||||||
},
|
},
|
||||||
onError: (error, variables, context) => {
|
onError: (error: Error, variables, context) => {
|
||||||
// Rollback on error
|
// Rollback on error
|
||||||
if (context?.previousData) {
|
if (context?.previousData) {
|
||||||
queryClient.setQueryData(
|
queryClient.setQueryData(
|
||||||
@@ -68,8 +89,19 @@ export function useResolveAlertGroup() {
|
|||||||
context.previousData
|
context.previousData
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
toast.error('Failed to resolve alerts');
|
|
||||||
console.error('Error resolving alert group:', error);
|
// 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: () => {
|
onSettled: () => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
-- Add UPDATE policy for rate_limit_alerts to allow moderators to resolve alerts
|
||||||
|
CREATE POLICY "Moderators can resolve rate limit alerts"
|
||||||
|
ON public.rate_limit_alerts
|
||||||
|
FOR UPDATE
|
||||||
|
TO authenticated
|
||||||
|
USING (
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM public.user_roles
|
||||||
|
WHERE user_id = auth.uid()
|
||||||
|
AND role IN ('admin', 'moderator', 'superuser')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
WITH CHECK (
|
||||||
|
-- Only allow updating resolved_at field
|
||||||
|
resolved_at IS NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Update system_alerts policy to allow moderators to resolve alerts
|
||||||
|
DROP POLICY IF EXISTS "Admins can manage system alerts" ON public.system_alerts;
|
||||||
|
|
||||||
|
CREATE POLICY "Staff can manage system alerts"
|
||||||
|
ON public.system_alerts
|
||||||
|
FOR UPDATE
|
||||||
|
TO authenticated
|
||||||
|
USING (
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM public.user_roles
|
||||||
|
WHERE user_id = auth.uid()
|
||||||
|
AND role IN ('admin', 'superuser', 'moderator')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
WITH CHECK (
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM public.user_roles
|
||||||
|
WHERE user_id = auth.uid()
|
||||||
|
AND role IN ('admin', 'superuser', 'moderator')
|
||||||
|
)
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user