diff --git a/ui/src/pages/Alerts/RulesListPage.tsx b/ui/src/pages/Alerts/RulesListPage.tsx index ee769393..cdd2bd6a 100644 --- a/ui/src/pages/Alerts/RulesListPage.tsx +++ b/ui/src/pages/Alerts/RulesListPage.tsx @@ -1,3 +1,115 @@ +import { Link, useNavigate } from 'react-router'; +import { Button, SectionHeader, Toggle, useToast, Badge } from '@cameleer/design-system'; +import { PageLoader } from '../../components/PageLoader'; +import { SeverityBadge } from '../../components/SeverityBadge'; +import { + useAlertRules, + useDeleteAlertRule, + useSetAlertRuleEnabled, + type AlertRuleResponse, +} from '../../api/queries/alertRules'; +import { useEnvironments } from '../../api/queries/admin/environments'; +import { useSelectedEnv } from '../../api/queries/alertMeta'; +import sectionStyles from '../../styles/section-card.module.css'; + export default function RulesListPage() { - return
RulesListPage — coming soon
; + const navigate = useNavigate(); + const env = useSelectedEnv(); + const { data: rules, isLoading, error } = useAlertRules(); + const { data: envs } = useEnvironments(); + const setEnabled = useSetAlertRuleEnabled(); + const deleteRule = useDeleteAlertRule(); + const { toast } = useToast(); + + if (isLoading) return ; + if (error) return
Failed to load rules: {String(error)}
; + + const rows = rules ?? []; + const otherEnvs = (envs ?? []).filter((e) => e.slug !== env); + + const onToggle = async (r: AlertRuleResponse) => { + try { + await setEnabled.mutateAsync({ id: r.id, enabled: !r.enabled }); + toast({ title: r.enabled ? 'Disabled' : 'Enabled', description: r.name, variant: 'success' }); + } catch (e) { + toast({ title: 'Toggle failed', description: String(e), variant: 'error' }); + } + }; + + const onDelete = async (r: AlertRuleResponse) => { + if (!confirm(`Delete rule "${r.name}"? Fired alerts are preserved via rule_snapshot.`)) return; + try { + await deleteRule.mutateAsync(r.id); + toast({ title: 'Deleted', description: r.name, variant: 'success' }); + } catch (e) { + toast({ title: 'Delete failed', description: String(e), variant: 'error' }); + } + }; + + const onPromote = (r: AlertRuleResponse, targetEnvSlug: string) => { + navigate(`/alerts/rules/new?promoteFrom=${env}&ruleId=${r.id}&targetEnv=${targetEnvSlug}`); + }; + + return ( +
+
+ Alert rules + + + +
+
+ {rows.length === 0 ? ( +

No rules yet. Create one to start evaluating alerts for this environment.

+ ) : ( + + + + + + + + + + + + + {rows.map((r) => ( + + + + + + + + + ))} + +
NameKindSeverityEnabledTargets
{r.name} + onToggle(r)} + disabled={setEnabled.isPending} + /> + {r.targets.length} + {otherEnvs.length > 0 && ( + + )} + +
+ )} +
+
+ ); }