import { useState } from 'react'; import { BellOff } from 'lucide-react'; import { Button, FormField, Input, useToast, DataTable, EmptyState, ConfirmDialog, MonoText, } from '@cameleer/design-system'; import type { Column } from '@cameleer/design-system'; import { PageLoader } from '../../components/PageLoader'; import { useAlertSilences, useCreateSilence, useDeleteSilence, type AlertSilenceResponse, } from '../../api/queries/alertSilences'; import sectionStyles from '../../styles/section-card.module.css'; import tableStyles from '../../styles/table-section.module.css'; import css from './alerts-page.module.css'; export default function SilencesPage() { const { data, isLoading, error } = useAlertSilences(); const create = useCreateSilence(); const remove = useDeleteSilence(); const { toast } = useToast(); const [reason, setReason] = useState(''); const [matcherRuleId, setMatcherRuleId] = useState(''); const [matcherAppSlug, setMatcherAppSlug] = useState(''); const [hours, setHours] = useState(1); const [pendingEnd, setPendingEnd] = useState(null); if (isLoading) return ; if (error) return
Failed to load silences: {String(error)}
; const rows = data ?? []; const onCreate = async () => { const now = new Date(); const endsAt = new Date(now.getTime() + hours * 3600_000); const matcher: Record = {}; if (matcherRuleId) matcher.ruleId = matcherRuleId; if (matcherAppSlug) matcher.appSlug = matcherAppSlug; if (Object.keys(matcher).length === 0) { toast({ title: 'Silence needs at least one matcher field', variant: 'error' }); return; } try { await create.mutateAsync({ matcher, reason: reason || undefined, startsAt: now.toISOString(), endsAt: endsAt.toISOString(), }); setReason(''); setMatcherRuleId(''); setMatcherAppSlug(''); setHours(1); toast({ title: 'Silence created', variant: 'success' }); } catch (e) { toast({ title: 'Create failed', description: String(e), variant: 'error' }); } }; const confirmEnd = async () => { if (!pendingEnd) return; try { await remove.mutateAsync(pendingEnd.id!); toast({ title: 'Silence removed', variant: 'success' }); } catch (e) { toast({ title: 'Remove failed', description: String(e), variant: 'error' }); } finally { setPendingEnd(null); } }; const columns: Column[] = [ { key: 'matcher', header: 'Matcher', render: (_, s) => {JSON.stringify(s.matcher)}, }, { key: 'reason', header: 'Reason', render: (_, s) => s.reason ?? '—' }, { key: 'startsAt', header: 'Starts', width: '200px' }, { key: 'endsAt', header: 'Ends', width: '200px' }, { key: 'actions', header: '', width: '90px', render: (_, s) => ( ), }, ]; return (

Alert silences

{rows.length === 0 ? 'Nothing silenced right now' : `${rows.length} active silence${rows.length === 1 ? '' : 's'}`}
setMatcherRuleId(e.target.value)} /> setMatcherAppSlug(e.target.value)} /> setHours(Number(e.target.value))} /> setReason(e.target.value)} placeholder="Maintenance window" />
{rows.length === 0 ? ( } title="No silences" description="Nothing is currently silenced in this environment." /> ) : (
columns={columns} data={rows.map((s) => ({ ...s, id: s.id ?? '' }))} flush fillHeight />
)} setPendingEnd(null)} onConfirm={confirmEnd} title="End silence?" message="End this silence early? Affected rules will resume firing." confirmText="End silence" variant="warning" loading={remove.isPending} />
); }