AddAlertManagerModal.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { css, cx } from '@emotion/css';
  2. import React, { FC, useMemo } from 'react';
  3. import { GrafanaTheme2 } from '@grafana/data';
  4. import { Button, Field, FieldArray, Form, Icon, Input, Modal, useStyles2 } from '@grafana/ui';
  5. import { AlertmanagerUrl } from 'app/plugins/datasource/alertmanager/types';
  6. interface Props {
  7. onClose: () => void;
  8. alertmanagers: AlertmanagerUrl[];
  9. onChangeAlertmanagerConfig: (alertmanagers: string[]) => void;
  10. }
  11. export const AddAlertManagerModal: FC<Props> = ({ alertmanagers, onChangeAlertmanagerConfig, onClose }) => {
  12. const styles = useStyles2(getStyles);
  13. const defaultValues: Record<string, AlertmanagerUrl[]> = useMemo(
  14. () => ({
  15. alertmanagers: alertmanagers,
  16. }),
  17. [alertmanagers]
  18. );
  19. const modalTitle = (
  20. <div className={styles.modalTitle}>
  21. <Icon name="bell" className={styles.modalIcon} />
  22. <h3>Add Alertmanager</h3>
  23. </div>
  24. );
  25. const onSubmit = (values: Record<string, AlertmanagerUrl[]>) => {
  26. onChangeAlertmanagerConfig(values.alertmanagers.map((am) => cleanAlertmanagerUrl(am.url)));
  27. onClose();
  28. };
  29. return (
  30. <Modal title={modalTitle} isOpen={true} onDismiss={onClose} className={styles.modal}>
  31. <div className={styles.description}>
  32. We use a service discovery method to find existing Alertmanagers for a given URL.
  33. </div>
  34. <Form onSubmit={onSubmit} defaultValues={defaultValues}>
  35. {({ register, control, errors }) => (
  36. <div>
  37. <FieldArray control={control} name="alertmanagers">
  38. {({ fields, append, remove }) => (
  39. <div className={styles.fieldArray}>
  40. <div className={styles.bold}>Source url</div>
  41. <div className={styles.muted}>
  42. Authentication can be done via URL (e.g. user:password@myalertmanager.com) and only the Alertmanager
  43. v2 API is supported. The suffix is added internally, there is no need to specify it.
  44. </div>
  45. {fields.map((field, index) => {
  46. return (
  47. <Field
  48. invalid={!!errors?.alertmanagers?.[index]}
  49. error="Field is required"
  50. key={`${field.id}-${index}`}
  51. >
  52. <Input
  53. className={styles.input}
  54. defaultValue={field.url}
  55. {...register(`alertmanagers.${index}.url`, { required: true })}
  56. placeholder="http://localhost:9093"
  57. addonAfter={
  58. <Button
  59. aria-label="Remove alertmanager"
  60. type="button"
  61. onClick={() => remove(index)}
  62. variant="destructive"
  63. className={styles.destroyInputRow}
  64. >
  65. <Icon name="trash-alt" />
  66. </Button>
  67. }
  68. />
  69. </Field>
  70. );
  71. })}
  72. <Button type="button" variant="secondary" onClick={() => append({ url: '' })}>
  73. Add URL
  74. </Button>
  75. </div>
  76. )}
  77. </FieldArray>
  78. <div>
  79. <Button type="submit" onSubmit={() => onSubmit}>
  80. Add Alertmanagers
  81. </Button>
  82. </div>
  83. </div>
  84. )}
  85. </Form>
  86. </Modal>
  87. );
  88. };
  89. function cleanAlertmanagerUrl(url: string): string {
  90. return url.replace(/\/$/, '').replace(/\/api\/v[1|2]\/alerts/i, '');
  91. }
  92. const getStyles = (theme: GrafanaTheme2) => {
  93. const muted = css`
  94. color: ${theme.colors.text.secondary};
  95. `;
  96. return {
  97. description: cx(
  98. css`
  99. margin-bottom: ${theme.spacing(2)};
  100. `,
  101. muted
  102. ),
  103. muted: muted,
  104. bold: css`
  105. font-weight: ${theme.typography.fontWeightBold};
  106. `,
  107. modal: css``,
  108. modalIcon: cx(
  109. muted,
  110. css`
  111. margin-right: ${theme.spacing(1)};
  112. `
  113. ),
  114. modalTitle: css`
  115. display: flex;
  116. `,
  117. input: css`
  118. margin-bottom: ${theme.spacing(1)};
  119. margin-right: ${theme.spacing(1)};
  120. `,
  121. inputRow: css`
  122. display: flex;
  123. `,
  124. destroyInputRow: css`
  125. padding: ${theme.spacing(1)};
  126. `,
  127. fieldArray: css`
  128. margin-bottom: ${theme.spacing(4)};
  129. `,
  130. };
  131. };