SilenceTableRow.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import { css, cx } from '@emotion/css';
  2. import React, { FC, Fragment, useState } from 'react';
  3. import { useDispatch } from 'react-redux';
  4. import { dateMath, GrafanaTheme, intervalToAbbreviatedDurationString } from '@grafana/data';
  5. import { useStyles, Link } from '@grafana/ui';
  6. import { contextSrv } from 'app/core/services/context_srv';
  7. import { Silence, AlertmanagerAlert } from 'app/plugins/datasource/alertmanager/types';
  8. import { expireSilenceAction } from '../../state/actions';
  9. import { makeAMLink } from '../../utils/misc';
  10. import { CollapseToggle } from '../CollapseToggle';
  11. import { ActionButton } from '../rules/ActionButton';
  12. import { ActionIcon } from '../rules/ActionIcon';
  13. import { Matchers } from './Matchers';
  14. import { SilenceStateTag } from './SilenceStateTag';
  15. import SilencedAlertsTable from './SilencedAlertsTable';
  16. interface Props {
  17. className?: string;
  18. silence: Silence;
  19. silencedAlerts: AlertmanagerAlert[];
  20. alertManagerSourceName: string;
  21. }
  22. const SilenceTableRow: FC<Props> = ({ silence, className, silencedAlerts, alertManagerSourceName }) => {
  23. const [isCollapsed, setIsCollapsed] = useState<boolean>(true);
  24. const dispatch = useDispatch();
  25. const styles = useStyles(getStyles);
  26. const { status, matchers = [], startsAt, endsAt, comment, createdBy } = silence;
  27. const dateDisplayFormat = 'YYYY-MM-DD HH:mm';
  28. const startsAtDate = dateMath.parse(startsAt);
  29. const endsAtDate = dateMath.parse(endsAt);
  30. const duration = intervalToAbbreviatedDurationString({ start: new Date(startsAt), end: new Date(endsAt) });
  31. const handleExpireSilenceClick = () => {
  32. dispatch(expireSilenceAction(alertManagerSourceName, silence.id));
  33. };
  34. const detailsColspan = contextSrv.isEditor ? 4 : 3;
  35. return (
  36. <Fragment>
  37. <tr className={className} data-testid="silence-table-row">
  38. <td>
  39. <CollapseToggle isCollapsed={isCollapsed} onToggle={(value) => setIsCollapsed(value)} />
  40. </td>
  41. <td>
  42. <SilenceStateTag state={status.state} />
  43. </td>
  44. <td className={styles.matchersCell}>
  45. <Matchers matchers={matchers} />
  46. </td>
  47. <td data-testid="silenced-alerts">{silencedAlerts.length}</td>
  48. <td>
  49. {startsAtDate?.format(dateDisplayFormat)} {'-'}
  50. <br />
  51. {endsAtDate?.format(dateDisplayFormat)}
  52. </td>
  53. {contextSrv.isEditor && (
  54. <td className={styles.actionsCell}>
  55. {status.state === 'expired' ? (
  56. <Link href={makeAMLink(`/alerting/silence/${silence.id}/edit`, alertManagerSourceName)}>
  57. <ActionButton icon="sync">Recreate</ActionButton>
  58. </Link>
  59. ) : (
  60. <ActionButton icon="bell" onClick={handleExpireSilenceClick}>
  61. Unsilence
  62. </ActionButton>
  63. )}
  64. {status.state !== 'expired' && (
  65. <ActionIcon
  66. to={makeAMLink(`/alerting/silence/${silence.id}/edit`, alertManagerSourceName)}
  67. icon="pen"
  68. tooltip="edit"
  69. />
  70. )}
  71. </td>
  72. )}
  73. </tr>
  74. {!isCollapsed && (
  75. <>
  76. <tr className={className}>
  77. <td />
  78. <td>Comment</td>
  79. <td colSpan={detailsColspan}>{comment}</td>
  80. </tr>
  81. <tr className={className}>
  82. <td />
  83. <td>Schedule</td>
  84. <td colSpan={detailsColspan}>{`${startsAtDate?.format(dateDisplayFormat)} - ${endsAtDate?.format(
  85. dateDisplayFormat
  86. )}`}</td>
  87. </tr>
  88. <tr className={className}>
  89. <td />
  90. <td>Duration</td>
  91. <td colSpan={detailsColspan}>{duration}</td>
  92. </tr>
  93. <tr className={className}>
  94. <td />
  95. <td>Created by</td>
  96. <td colSpan={detailsColspan}>{createdBy}</td>
  97. </tr>
  98. {!!silencedAlerts.length && (
  99. <tr className={cx(className, styles.alertRulesCell)}>
  100. <td />
  101. <td>Affected alerts</td>
  102. <td colSpan={detailsColspan}>
  103. <SilencedAlertsTable silencedAlerts={silencedAlerts} />
  104. </td>
  105. </tr>
  106. )}
  107. </>
  108. )}
  109. </Fragment>
  110. );
  111. };
  112. const getStyles = (theme: GrafanaTheme) => ({
  113. matchersCell: css`
  114. & > * + * {
  115. margin-left: ${theme.spacing.xs};
  116. }
  117. `,
  118. actionsCell: css`
  119. text-align: right;
  120. width: 1%;
  121. white-space: nowrap;
  122. & > * + * {
  123. margin-left: ${theme.spacing.sm};
  124. }
  125. `,
  126. alertRulesCell: css`
  127. vertical-align: top;
  128. `,
  129. });
  130. export default SilenceTableRow;