feat(ui/alerts): SilenceRuleMenu — 1h/8h/24h/custom duration menu
Used by InboxPage row + bulk actions to silence an alert's underlying rule for a chosen preset window. 'Custom…' routes to /alerts/silences?ruleId=<id> (T13 adds the prefill wire). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
71
ui/src/pages/Alerts/SilenceRuleMenu.tsx
Normal file
71
ui/src/pages/Alerts/SilenceRuleMenu.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { BellOff } from 'lucide-react';
|
||||||
|
import { useNavigate } from 'react-router';
|
||||||
|
import { Button, Dropdown, useToast } from '@cameleer/design-system';
|
||||||
|
import type { DropdownItem } from '@cameleer/design-system';
|
||||||
|
import { useCreateSilence } from '../../api/queries/alertSilences';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
ruleId: string;
|
||||||
|
ruleTitle?: string;
|
||||||
|
onDone?: () => void;
|
||||||
|
variant?: 'row' | 'bulk';
|
||||||
|
}
|
||||||
|
|
||||||
|
const PRESETS: Array<{ label: string; hours: number }> = [
|
||||||
|
{ label: '1 hour', hours: 1 },
|
||||||
|
{ label: '8 hours', hours: 8 },
|
||||||
|
{ label: '24 hours', hours: 24 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function SilenceRuleMenu({ ruleId, ruleTitle, onDone, variant = 'row' }: Props) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { toast } = useToast();
|
||||||
|
const createSilence = useCreateSilence();
|
||||||
|
|
||||||
|
const handlePreset = (hours: number) => async () => {
|
||||||
|
const now = new Date();
|
||||||
|
const reason = ruleTitle
|
||||||
|
? `Silenced from inbox (${ruleTitle})`
|
||||||
|
: 'Silenced from inbox';
|
||||||
|
try {
|
||||||
|
await createSilence.mutateAsync({
|
||||||
|
matcher: { ruleId },
|
||||||
|
reason,
|
||||||
|
startsAt: now.toISOString(),
|
||||||
|
endsAt: new Date(now.getTime() + hours * 3_600_000).toISOString(),
|
||||||
|
});
|
||||||
|
toast({ title: `Silenced for ${hours}h`, variant: 'success' });
|
||||||
|
onDone?.();
|
||||||
|
} catch (e) {
|
||||||
|
toast({ title: 'Silence failed', description: String(e), variant: 'error' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCustom = () => {
|
||||||
|
navigate(`/alerts/silences?ruleId=${encodeURIComponent(ruleId)}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const items: DropdownItem[] = [
|
||||||
|
...PRESETS.map(({ label, hours }) => ({
|
||||||
|
label,
|
||||||
|
disabled: createSilence.isPending,
|
||||||
|
onClick: handlePreset(hours),
|
||||||
|
})),
|
||||||
|
{ divider: true, label: '' },
|
||||||
|
{ label: 'Custom…', onClick: handleCustom },
|
||||||
|
];
|
||||||
|
|
||||||
|
const buttonLabel = variant === 'bulk' ? 'Silence rules' : 'Silence rule…';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
trigger={
|
||||||
|
<Button variant="secondary" size="sm">
|
||||||
|
<BellOff size={14} style={{ marginRight: 4 }} />
|
||||||
|
{buttonLabel}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
items={items}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user