mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-28 11:26:58 -05:00
Introduce statistical anomaly detection for metrics via edge function, hooks, and UI components. Adds detection algorithms (z-score, moving average, rate of change), anomaly storage, auto-alerts, and dashboard rendering of detected anomalies with run-once trigger and scheduling guidance.
102 lines
2.7 KiB
TypeScript
102 lines
2.7 KiB
TypeScript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { supabase } from '@/lib/supabaseClient';
|
|
import { queryKeys } from '@/lib/queryKeys';
|
|
import { toast } from 'sonner';
|
|
|
|
export interface AnomalyDetection {
|
|
id: string;
|
|
metric_name: string;
|
|
metric_category: string;
|
|
anomaly_type: 'spike' | 'drop' | 'trend_change' | 'outlier' | 'pattern_break';
|
|
severity: 'critical' | 'high' | 'medium' | 'low';
|
|
baseline_value: number;
|
|
anomaly_value: number;
|
|
deviation_score: number;
|
|
confidence_score: number;
|
|
detection_algorithm: string;
|
|
time_window_start: string;
|
|
time_window_end: string;
|
|
detected_at: string;
|
|
alert_created: boolean;
|
|
alert_id?: string;
|
|
alert_message?: string;
|
|
alert_resolved_at?: string;
|
|
}
|
|
|
|
export function useAnomalyDetections() {
|
|
return useQuery({
|
|
queryKey: queryKeys.monitoring.anomalyDetections(),
|
|
queryFn: async () => {
|
|
const { data, error } = await supabase
|
|
.from('recent_anomalies_view')
|
|
.select('*')
|
|
.order('detected_at', { ascending: false })
|
|
.limit(50);
|
|
|
|
if (error) throw error;
|
|
return (data || []) as AnomalyDetection[];
|
|
},
|
|
staleTime: 30000,
|
|
refetchInterval: 60000,
|
|
});
|
|
}
|
|
|
|
export function useRunAnomalyDetection() {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async () => {
|
|
const { data, error } = await supabase.functions.invoke('detect-anomalies', {
|
|
method: 'POST',
|
|
});
|
|
|
|
if (error) throw error;
|
|
return data;
|
|
},
|
|
onSuccess: (data) => {
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.monitoring.anomalyDetections() });
|
|
queryClient.invalidateQueries({ queryKey: queryKeys.monitoring.groupedAlerts() });
|
|
|
|
if (data.anomalies_detected > 0) {
|
|
toast.success(`Detected ${data.anomalies_detected} anomalies`);
|
|
} else {
|
|
toast.info('No anomalies detected');
|
|
}
|
|
},
|
|
onError: (error) => {
|
|
console.error('Failed to run anomaly detection:', error);
|
|
toast.error('Failed to run anomaly detection');
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRecordMetric() {
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
metricName,
|
|
metricCategory,
|
|
metricValue,
|
|
metadata,
|
|
}: {
|
|
metricName: string;
|
|
metricCategory: string;
|
|
metricValue: number;
|
|
metadata?: any;
|
|
}) => {
|
|
const { error } = await supabase
|
|
.from('metric_time_series')
|
|
.insert({
|
|
metric_name: metricName,
|
|
metric_category: metricCategory,
|
|
metric_value: metricValue,
|
|
metadata,
|
|
});
|
|
|
|
if (error) throw error;
|
|
},
|
|
onError: (error) => {
|
|
console.error('Failed to record metric:', error);
|
|
},
|
|
});
|
|
}
|