feat(ui/alerts): hooks for bulk-ack, delete, bulk-delete, restore + acked/read filter params

- useAlerts gains acked/read filter params threaded into query + queryKey
- new mutations: useBulkAckAlerts, useDeleteAlert, useBulkDeleteAlerts, useRestoreAlert
- all cache-invalidate the alerts list and unread-count on success

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-21 19:00:18 +02:00
parent 207ae246af
commit be703eb71d

View File

@@ -11,6 +11,8 @@ type AlertSeverity = NonNullable<AlertDto['severity']>;
export interface AlertsFilter { export interface AlertsFilter {
state?: AlertState | AlertState[]; state?: AlertState | AlertState[];
severity?: AlertSeverity | AlertSeverity[]; severity?: AlertSeverity | AlertSeverity[];
acked?: boolean;
read?: boolean;
ruleId?: string; ruleId?: string;
limit?: number; limit?: number;
} }
@@ -47,7 +49,7 @@ export function useAlerts(filter: AlertsFilter = {}) {
const severityKey = severityArr ? [...severityArr].sort() : null; const severityKey = severityArr ? [...severityArr].sort() : null;
return useQuery({ return useQuery({
queryKey: ['alerts', env, 'list', fetchLimit, stateKey, severityKey], queryKey: ['alerts', env, 'list', fetchLimit, stateKey, severityKey, filter.acked ?? null, filter.read ?? null],
enabled: !!env, enabled: !!env,
refetchInterval: 30_000, refetchInterval: 30_000,
refetchIntervalInBackground: false, refetchIntervalInBackground: false,
@@ -56,6 +58,8 @@ export function useAlerts(filter: AlertsFilter = {}) {
const query: Record<string, unknown> = { limit: fetchLimit }; const query: Record<string, unknown> = { limit: fetchLimit };
if (stateArr && stateArr.length > 0) query.state = stateArr; if (stateArr && stateArr.length > 0) query.state = stateArr;
if (severityArr && severityArr.length > 0) query.severity = severityArr; if (severityArr && severityArr.length > 0) query.severity = severityArr;
if (filter.acked !== undefined) query.acked = filter.acked;
if (filter.read !== undefined) query.read = filter.read;
const { data, error } = await apiClient.GET( const { data, error } = await apiClient.GET(
'/environments/{envSlug}/alerts', '/environments/{envSlug}/alerts',
{ {
@@ -180,3 +184,80 @@ export function useBulkReadAlerts() {
}, },
}); });
} }
/** Acknowledge a batch of alert instances. */
export function useBulkAckAlerts() {
const env = useSelectedEnv();
const qc = useQueryClient();
return useMutation({
mutationFn: async (ids: string[]) => {
if (!env) throw new Error('no env');
const { error } = await apiClient.POST(
'/environments/{envSlug}/alerts/bulk-ack',
{ params: { path: { envSlug: env } }, body: { instanceIds: ids } } as any,
);
if (error) throw error;
},
onSuccess: () => qc.invalidateQueries({ queryKey: ['alerts', env] }),
});
}
/** Delete (soft) a single alert instance. */
export function useDeleteAlert() {
const env = useSelectedEnv();
const qc = useQueryClient();
return useMutation({
mutationFn: async (id: string) => {
if (!env) throw new Error('no env');
const { error } = await apiClient.DELETE(
'/environments/{envSlug}/alerts/{id}',
{ params: { path: { envSlug: env, id } } } as any,
);
if (error) throw error;
},
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['alerts', env] });
qc.invalidateQueries({ queryKey: ['alerts', env, 'unread-count'] });
},
});
}
/** Delete (soft) a batch of alert instances. */
export function useBulkDeleteAlerts() {
const env = useSelectedEnv();
const qc = useQueryClient();
return useMutation({
mutationFn: async (ids: string[]) => {
if (!env) throw new Error('no env');
const { error } = await apiClient.POST(
'/environments/{envSlug}/alerts/bulk-delete',
{ params: { path: { envSlug: env } }, body: { instanceIds: ids } } as any,
);
if (error) throw error;
},
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['alerts', env] });
qc.invalidateQueries({ queryKey: ['alerts', env, 'unread-count'] });
},
});
}
/** Restore a soft-deleted alert instance. */
export function useRestoreAlert() {
const env = useSelectedEnv();
const qc = useQueryClient();
return useMutation({
mutationFn: async (id: string) => {
if (!env) throw new Error('no env');
const { error } = await apiClient.POST(
'/environments/{envSlug}/alerts/{id}/restore',
{ params: { path: { envSlug: env, id } } } as any,
);
if (error) throw error;
},
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['alerts', env] });
qc.invalidateQueries({ queryKey: ['alerts', env, 'unread-count'] });
},
});
}