mirror of
https://github.com/grafana/grafana.git
synced 2025-09-23 01:43:36 +08:00
124 lines
3.8 KiB
TypeScript
124 lines
3.8 KiB
TypeScript
import { GrafanaTheme2 } from '@grafana/data';
|
|
import { useStyles2 } from '@grafana/ui';
|
|
import { Alert } from 'app/types/unified-alerting';
|
|
import { css, cx } from '@emotion/css';
|
|
import React, { FC, Fragment, useMemo, useState } from 'react';
|
|
import { getAlertTableStyles } from '../../styles/table';
|
|
import { alertInstanceKey } from '../../utils/rules';
|
|
import { AlertLabels } from '../AlertLabels';
|
|
import { CollapseToggle } from '../CollapseToggle';
|
|
import { AlertInstanceDetails } from './AlertInstanceDetails';
|
|
import { AlertStateTag } from './AlertStateTag';
|
|
|
|
type AlertWithKey = Alert & { key: string };
|
|
|
|
interface Props {
|
|
instances: Alert[];
|
|
}
|
|
|
|
export const AlertInstancesTable: FC<Props> = ({ instances }) => {
|
|
const styles = useStyles2(getStyles);
|
|
const tableStyles = useStyles2(getAlertTableStyles);
|
|
|
|
const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
|
|
|
|
// add key & sort instance. API returns instances in random order, different every time.
|
|
const sortedInstances = useMemo(
|
|
(): AlertWithKey[] =>
|
|
instances
|
|
.map((instance) => ({
|
|
...instance,
|
|
key: alertInstanceKey(instance),
|
|
}))
|
|
.sort((a, b) => a.key.localeCompare(b.key)),
|
|
[instances]
|
|
);
|
|
|
|
const toggleExpandedState = (ruleKey: string) =>
|
|
setExpandedKeys(
|
|
expandedKeys.includes(ruleKey) ? expandedKeys.filter((key) => key !== ruleKey) : [...expandedKeys, ruleKey]
|
|
);
|
|
|
|
return (
|
|
<table className={cx(tableStyles.table, styles.table)}>
|
|
<colgroup>
|
|
<col className={styles.colExpand} />
|
|
<col className={styles.colState} />
|
|
<col />
|
|
<col />
|
|
</colgroup>
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
<th>State</th>
|
|
<th>Labels</th>
|
|
<th>Created</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{sortedInstances.map(({ key, ...instance }, idx) => {
|
|
const isExpanded = expandedKeys.includes(key);
|
|
|
|
// don't allow expanding if there's nothing to show
|
|
const isExpandable = instance.value || !!Object.keys(instance.annotations ?? {}).length;
|
|
return (
|
|
<Fragment key={key}>
|
|
<tr className={idx % 2 === 0 ? tableStyles.evenRow : undefined}>
|
|
<td>
|
|
{isExpandable && (
|
|
<CollapseToggle
|
|
isCollapsed={!isExpanded}
|
|
onToggle={() => toggleExpandedState(key)}
|
|
data-testid="alert-collapse-toggle"
|
|
/>
|
|
)}
|
|
</td>
|
|
<td>
|
|
<AlertStateTag state={instance.state} />
|
|
</td>
|
|
<td className={styles.labelsCell}>
|
|
<AlertLabels labels={instance.labels} />
|
|
</td>
|
|
<td className={styles.createdCell}>
|
|
{instance.activeAt.startsWith('0001') ? '-' : instance.activeAt.substr(0, 19).replace('T', ' ')}
|
|
</td>
|
|
</tr>
|
|
{isExpanded && isExpandable && (
|
|
<tr className={idx % 2 === 0 ? tableStyles.evenRow : undefined}>
|
|
<td></td>
|
|
<td colSpan={3}>
|
|
<AlertInstanceDetails instance={instance} />
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</Fragment>
|
|
);
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
);
|
|
};
|
|
|
|
export const getStyles = (theme: GrafanaTheme2) => ({
|
|
colExpand: css`
|
|
width: 36px;
|
|
`,
|
|
colState: css`
|
|
width: 110px;
|
|
`,
|
|
labelsCell: css`
|
|
padding-top: ${theme.spacing(0.5)} !important;
|
|
padding-bottom: ${theme.spacing(0.5)} !important;
|
|
`,
|
|
createdCell: css`
|
|
white-space: nowrap;
|
|
`,
|
|
table: css`
|
|
td {
|
|
vertical-align: top;
|
|
padding-top: ${theme.spacing(1)};
|
|
padding-bottom: ${theme.spacing(1)};
|
|
}
|
|
`,
|
|
});
|