From 1c4a98c0da377e8962b05627c3a7955311edb767 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Wed, 22 Apr 2026 09:09:13 +0200 Subject: [PATCH] =?UTF-8?q?ui(alerts):=20Silences=20page=20adopts=20Rules?= =?UTF-8?q?=20UX=20=E2=80=94=20top-right=20button=20+=20modal=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: the Silences page rendered an always-visible 4-field form strip above the list, taking room even when the environment had zero silences. Inconsistent with Rules, which puts a "New rule" action in the page header and reserves the content area for either the list or an empty state. After: header mirrors Rules — title + subtitle on the left, a "New silence" primary button on the right. The create form moved into a Modal opened by that button (and by the empty-state's "Create silence" action). `?ruleId=` deep links still work: the param is read on mount, prefills the Rule ID field, and auto-opens the modal — preserving the InboxPage "Silence rule… → Custom…" flow. Dropped: unused `sectionStyles` import. Co-Authored-By: Claude Opus 4.7 (1M context) --- ui/src/pages/Alerts/SilencesPage.tsx | 93 +++++++++++++++++----------- 1 file changed, 56 insertions(+), 37 deletions(-) diff --git a/ui/src/pages/Alerts/SilencesPage.tsx b/ui/src/pages/Alerts/SilencesPage.tsx index dd7572fc..5c85da65 100644 --- a/ui/src/pages/Alerts/SilencesPage.tsx +++ b/ui/src/pages/Alerts/SilencesPage.tsx @@ -3,7 +3,7 @@ import { useSearchParams } from 'react-router'; import { BellOff } from 'lucide-react'; import { Button, FormField, Input, useToast, DataTable, - EmptyState, ConfirmDialog, MonoText, + EmptyState, ConfirmDialog, MonoText, Modal, } from '@cameleer/design-system'; import type { Column } from '@cameleer/design-system'; import { PageLoader } from '../../components/PageLoader'; @@ -14,7 +14,6 @@ import { type AlertSilenceResponse, } from '../../api/queries/alertSilences'; import { describeApiError } from '../../api/errors'; -import sectionStyles from '../../styles/section-card.module.css'; import tableStyles from '../../styles/table-section.module.css'; import css from './alerts-page.module.css'; @@ -29,13 +28,29 @@ export default function SilencesPage() { const [matcherAppSlug, setMatcherAppSlug] = useState(''); const [hours, setHours] = useState(1); const [pendingEnd, setPendingEnd] = useState(null); + const [createOpen, setCreateOpen] = useState(false); const [searchParams] = useSearchParams(); useEffect(() => { const r = searchParams.get('ruleId'); - if (r) setMatcherRuleId(r); + if (r) { + setMatcherRuleId(r); + setCreateOpen(true); + } }, [searchParams]); + const resetForm = () => { + setReason(''); + setMatcherRuleId(''); + setMatcherAppSlug(''); + setHours(1); + }; + + const closeModal = () => { + setCreateOpen(false); + resetForm(); + }; + if (isLoading) return ; if (error) return
Failed to load silences: {describeApiError(error)}
; @@ -58,10 +73,7 @@ export default function SilencesPage() { startsAt: now.toISOString(), endsAt: endsAt.toISOString(), }); - setReason(''); - setMatcherRuleId(''); - setMatcherAppSlug(''); - setHours(1); + closeModal(); toast({ title: 'Silence created', variant: 'success' }); } catch (e) { toast({ title: 'Create failed', description: describeApiError(e), variant: 'error' }); @@ -109,17 +121,38 @@ export default function SilencesPage() { : `${rows.length} active silence${rows.length === 1 ? '' : 's'}`} +
+ +
-
-
+ {rows.length === 0 ? ( + } + title="No silences" + description="Nothing is currently silenced in this environment." + action={ + + } + /> + ) : ( +
+ + columns={columns} + data={rows.map((s) => ({ ...s, id: s.id ?? '' }))} + flush + fillHeight + /> +
+ )} + + +
setMatcherRuleId(e.target.value)} /> @@ -141,28 +174,14 @@ export default function SilencesPage() { 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 - /> -
- )} +