diff --git a/ui/src/pages/Alerts/RuleEditor/ReviewStep.tsx b/ui/src/pages/Alerts/RuleEditor/ReviewStep.tsx index d2febd96..6330de33 100644 --- a/ui/src/pages/Alerts/RuleEditor/ReviewStep.tsx +++ b/ui/src/pages/Alerts/RuleEditor/ReviewStep.tsx @@ -1,5 +1,7 @@ -import { useMemo } from 'react'; -import { Alert, Toggle } from '@cameleer/design-system'; +import { useMemo, useState } from 'react'; +import { Alert, Button, Toggle } from '@cameleer/design-system'; +import { useRenderPreview } from '../../../api/queries/alertRules'; +import { describeApiError } from '../../../api/errors'; import { toRequest, type FormState } from './form-state'; /** @@ -24,12 +26,36 @@ export function computeSaveBlockReason(form: FormState): string | null { export function ReviewStep({ form, setForm, + ruleId, }: { form: FormState; setForm?: (f: FormState) => void; + /** + * Present only in edit mode. When absent the notification-preview button is + * hidden, because the backend `/render-preview` endpoint is id-bound and + * has no stateless variant — rendering against an unsaved draft would + * require a new endpoint and is explicitly out of scope here. + */ + ruleId?: string; }) { const req = toRequest(form); const saveBlockReason = useMemo(() => computeSaveBlockReason(form), [form]); + + const previewMutation = useRenderPreview(); + const [preview, setPreview] = useState<{ title: string; message: string } | null>(null); + const [previewError, setPreviewError] = useState(null); + + const onPreview = async () => { + setPreviewError(null); + try { + const res = await previewMutation.mutateAsync({ id: ruleId!, req: {} }); + setPreview({ title: res.title ?? '', message: res.message ?? '' }); + } catch (e) { + setPreview(null); + setPreviewError(describeApiError(e)); + } + }; + return (
{saveBlockReason && ( @@ -69,6 +95,57 @@ export function ReviewStep({ />
)} + + {ruleId && ( +
+
+ +
+ {previewError && ( + + {previewError} + + )} + {preview && ( +
+
+ Rendered notification preview +
+
+ {preview.title || (empty title)} +
+
+                {preview.message || '(empty message)'}
+              
+
+ )} +
+ )} +
Raw request JSON
     ) : (
-      
+      
     );
 
   return (