diff --git a/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts b/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts index 24e9925e97..30b7f5131d 100644 --- a/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts +++ b/grafana-plugin/src/components/AlertTemplates/CommonAlertTemplatesForm.config.ts @@ -16,6 +16,7 @@ export interface TemplateForEdit { additionalDescription?: string; }; isRoute?: boolean; + type?: 'html' | 'plain' | 'image' | 'boolean'; } export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = { @@ -23,11 +24,13 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = { displayName: 'Web title', name: TemplateOptions.WebTitle.key, description: '', + type: 'html', }, web_message_template: { displayName: 'Web message', name: TemplateOptions.WebMessage.key, description: '', + type: 'html', }, slack_title_template: { name: 'slack_title_template', @@ -37,21 +40,25 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = { chatOpsName: 'slack', data: 'Click "Acknowledge" and then "Unacknowledge" in Slack to trigger re-rendering.', }, + type: 'plain', }, sms_title_template: { name: TemplateOptions.SMS.key, displayName: 'Sms title', description: '', + type: 'plain', }, phone_call_title_template: { name: TemplateOptions.Phone.key, displayName: 'Phone call title', description: '', + type: 'plain', }, email_title_template: { name: TemplateOptions.EmailTitle.key, displayName: 'Email title', description: '', + type: 'plain', }, telegram_title_template: { name: TemplateOptions.TelegramTitle.key, @@ -60,6 +67,7 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = { additionalData: { chatOpsName: 'telegram', }, + type: 'plain', }, slack_message_template: { name: TemplateOptions.SlackMessage.key, @@ -69,11 +77,13 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = { chatOpsName: 'slack', data: 'Click "Acknowledge" and then "Unacknowledge" in Slack to trigger re-rendering.', }, + type: 'plain', }, email_message_template: { name: TemplateOptions.EmailMessage.key, displayName: 'Email message', description: '', + type: 'plain', }, telegram_message_template: { name: TemplateOptions.TelegramMessage.key, @@ -82,6 +92,7 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = { additionalData: { chatOpsName: 'telegram', }, + type: 'plain', }, slack_image_url_template: { name: TemplateOptions.SlackImage.key, @@ -91,11 +102,13 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = { chatOpsName: 'slack', data: 'Click "Acknowledge" and then "Unacknowledge" in Slack to trigger re-rendering.', }, + type: 'plain', }, web_image_url_template: { name: TemplateOptions.WebImage.key, displayName: 'Web image url', description: '', + type: 'image', }, telegram_image_url_template: { name: TemplateOptions.TelegramImage.key, @@ -104,28 +117,33 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = { additionalData: { chatOpsName: 'telegram', }, + type: 'image', }, grouping_id_template: { name: TemplateOptions.Grouping.key, displayName: 'Grouping', description: 'Reduce noise, minimize duplication with Alert Grouping, based on time, alert content, and even multiple features at the same time. Check the cheasheet to customize your template.', + type: 'plain', }, acknowledge_condition_template: { name: TemplateOptions.Autoacknowledge.key, displayName: 'Acknowledge condition', description: '', + type: 'boolean', }, resolve_condition_template: { name: TemplateOptions.Resolve.key, displayName: 'Resolve condition', description: 'When monitoring systems return to normal, they can send "resolve" alerts. OnCall can use these signals to resolve alert groups accordingly.', + type: 'boolean', }, source_link_template: { name: TemplateOptions.SourceLink.key, displayName: 'Source link', description: '', + type: 'plain', }, route_template: { name: TemplateOptions.Routing.key, @@ -137,5 +155,6 @@ export const commonTemplateForEdit: { [id: string]: TemplateForEdit } = { data: 'Selected Alert will be directed to this route', }, isRoute: true, + type: 'boolean', }, }; diff --git a/grafana-plugin/src/containers/IntegrationContainers/IntegrationCommonTemplatesList.config.ts b/grafana-plugin/src/containers/IntegrationContainers/IntegrationCommonTemplatesList.config.ts index 312227f327..1cadbe6896 100644 --- a/grafana-plugin/src/containers/IntegrationContainers/IntegrationCommonTemplatesList.config.ts +++ b/grafana-plugin/src/containers/IntegrationContainers/IntegrationCommonTemplatesList.config.ts @@ -4,6 +4,7 @@ interface TemplateToRender { name: string; label: string; height: string; + type: 'plain' | 'html' | 'image' | 'boolean'; labelTooltip?: string; } @@ -22,6 +23,7 @@ export const commonTemplatesToRender: TemplateBlock[] = [ labelTooltip: 'The Grouping Template is applied to every incoming alert payload after the Routing Template. It can be based on time, or alert content, or both. If the resulting grouping key matches an existing non-resolved alert group, the alert will be grouped accordingly. Otherwise, a new alert group will be created', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'plain', }, { name: 'resolve_condition_template', @@ -29,6 +31,7 @@ export const commonTemplatesToRender: TemplateBlock[] = [ labelTooltip: 'If Autoresolution Template is True, the alert will resolve its group as "resolved by source". If the group is already resolved, the alert will be added to that group', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'boolean', }, ], }, @@ -38,17 +41,20 @@ export const commonTemplatesToRender: TemplateBlock[] = [ { name: 'web_title_template', label: 'Title', - height: MONACO_INPUT_HEIGHT_TALL, + height: MONACO_INPUT_HEIGHT_SMALL, + type: 'html', }, { name: 'web_message_template', label: 'Message', height: MONACO_INPUT_HEIGHT_TALL, + type: 'html', }, { name: 'web_image_url_template', label: 'Image', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'image', }, ], }, @@ -59,11 +65,13 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'acknowledge_condition_template', label: 'Auto acknowledge', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'boolean', }, { name: 'source_link_template', label: 'Source link', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'plain', }, ], }, @@ -74,11 +82,13 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'phone_call_title_template', label: 'Phone Call', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'plain', }, { name: 'sms_title_template', label: 'SMS', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'plain', }, ], }, @@ -89,16 +99,19 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'slack_title_template', label: 'Title', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'plain', }, { name: 'slack_message_template', label: 'Message', height: MONACO_INPUT_HEIGHT_TALL, + type: 'plain', }, { name: 'slack_image_url_template', label: 'Image', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'image', }, ], }, @@ -109,16 +122,19 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'telegram_title_template', label: 'Title', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'plain', }, { name: 'telegram_message_template', label: 'Message', height: MONACO_INPUT_HEIGHT_TALL, + type: 'plain', }, { name: 'telegram_image_url_template', label: 'Image', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'image', }, ], }, @@ -129,8 +145,9 @@ export const commonTemplatesToRender: TemplateBlock[] = [ name: 'email_title_template', label: 'Title', height: MONACO_INPUT_HEIGHT_SMALL, + type: 'plain', }, - { name: 'email_message_template', label: 'Message', height: MONACO_INPUT_HEIGHT_TALL }, + { name: 'email_message_template', label: 'Message', height: MONACO_INPUT_HEIGHT_TALL, type: 'plain' }, ], }, ]; diff --git a/grafana-plugin/src/containers/IntegrationTemplate/IntegrationTemplate.tsx b/grafana-plugin/src/containers/IntegrationTemplate/IntegrationTemplate.tsx index 10af17b1a2..dc3292675b 100644 --- a/grafana-plugin/src/containers/IntegrationTemplate/IntegrationTemplate.tsx +++ b/grafana-plugin/src/containers/IntegrationTemplate/IntegrationTemplate.tsx @@ -244,6 +244,7 @@ interface ResultProps { payload?: JSON; error?: string; onSaveAndFollowLink?: (link: string) => void; + templateIsRoute?: boolean; } const Result = (props: ResultProps) => { @@ -270,6 +271,8 @@ const Result = (props: ResultProps) => { key={template.name} templateName={template.name} templateBody={templateBody} + templateType={template.type} + templateIsRoute={template.isRoute} alertReceiveChannelId={alertReceiveChannelId} payload={payload} /> diff --git a/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.module.css b/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.module.css index 41af313fae..ad1530f6c6 100644 --- a/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.module.css +++ b/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.module.css @@ -18,3 +18,7 @@ max-width: 100%; max-height: 100%; } + +.display-linebreak { + white-space: pre-line; +} diff --git a/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx b/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx index 756ed5a0d8..6253ad4c10 100644 --- a/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx +++ b/grafana-plugin/src/containers/TemplatePreview/TemplatePreview.tsx @@ -19,6 +19,8 @@ const cx = cn.bind(styles); interface TemplatePreviewProps { templateName: string; templateBody: string | null; + templateType?: 'plain' | 'html' | 'image' | 'boolean'; + templateIsRoute?: boolean; payload?: JSON; alertReceiveChannelId: AlertReceiveChannel['id']; onEditClick?: () => void; @@ -32,7 +34,8 @@ interface ConditionalResult { } const TemplatePreview = observer((props: TemplatePreviewProps) => { - const { templateName, templateBody, payload, alertReceiveChannelId, alertGroupId } = props; + const { templateName, templateBody, templateType, payload, alertReceiveChannelId, alertGroupId, templateIsRoute } = + props; const [result, setResult] = useState<{ preview: string | null } | undefined>(undefined); const [conditionalResult, setConditionalResult] = useState({}); @@ -49,7 +52,7 @@ const TemplatePreview = observer((props: TemplatePreviewProps) => { setResult(data); if (data?.preview === 'True') { setConditionalResult({ isResult: true, value: 'True' }); - } else if (templateName.includes('route') || templateName.includes('condition')) { + } else if (templateType === 'boolean') { setConditionalResult({ isResult: true, value: 'False' }); } else { setConditionalResult({ isResult: false, value: undefined }); @@ -67,7 +70,7 @@ const TemplatePreview = observer((props: TemplatePreviewProps) => { useEffect(handleTemplateBodyChange, [templateBody, payload]); const conditionalMessage = (success: boolean) => { - if (templateName.includes('route')) { + if (templateIsRoute) { return ( Selected alert will {!success && not} be matched with this route @@ -83,58 +86,87 @@ const TemplatePreview = observer((props: TemplatePreviewProps) => { } }; - return result ? ( - <> - {conditionalResult?.isResult ? ( - - {conditionalResult.value === 'True' ? ( - - - {conditionalResult.value} - - {conditionalMessage(conditionalResult.value === 'True')} - - ) : ( - - - -
- - {conditionalMessage(conditionalResult.value === 'True')} - - )} - - ) : ( - <> - {templateName.includes('image') ? ( -
- { - const target = e.target as HTMLImageElement; - target.alt = result.preview || 'No image found'; + function renderResult() { + switch (templateType) { + case 'html': { + return renderHtmlResult(); + } + case 'image': { + return renderImageResult(); + } + case 'boolean': { + return renderBooleanResult(); + } + case 'plain': { + return renderPlainResult(); + } + default: { + return renderPlainResult(); + } + } + } + function renderBooleanResult() { + return ( + + {conditionalResult.value === 'True' ? ( + + + {conditionalResult.value} + + {conditionalMessage(conditionalResult.value === 'True')} + + ) : ( + + + +
-
- ) : ( -
') || ''), - }} - /> - )} - - )} - - ) : ( - - ); + + {conditionalMessage(conditionalResult.value === 'True')} + + )} + + ); + } + function renderHtmlResult() { + return ( +
+ ); + } + function renderPlainResult() { + return ( +
+ ); + } + function renderImageResult() { + return ( +
+ { + const target = e.target as HTMLImageElement; + target.alt = result.preview || 'No image found'; + }} + /> +
+ ); + } + + return result ? <>{renderResult()} : ; }); export default TemplatePreview;