The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/alerting/unified/components/rules/RulesTable.tsx

172 lines
5.0 KiB

import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2 } from '@grafana/ui';
import React, { FC, useMemo } from 'react';
import { css, cx } from '@emotion/css';
import { RuleDetails } from './RuleDetails';
import { isCloudRulesSource } from '../../utils/datasource';
import { useHasRuler } from '../../hooks/useHasRuler';
import { CombinedRule } from 'app/types/unified-alerting';
import { Annotation } from '../../utils/constants';
import { RuleState } from './RuleState';
import { RuleHealth } from './RuleHealth';
import { DynamicTable, DynamicTableColumnProps, DynamicTableItemProps } from '../DynamicTable';
import { DynamicTableWithGuidelines } from '../DynamicTableWithGuidelines';
type RuleTableColumnProps = DynamicTableColumnProps<CombinedRule>;
type RuleTableItemProps = DynamicTableItemProps<CombinedRule>;
interface Props {
rules: CombinedRule[];
showGuidelines?: boolean;
showGroupColumn?: boolean;
showSummaryColumn?: boolean;
emptyMessage?: string;
className?: string;
}
export const RulesTable: FC<Props> = ({
rules,
className,
showGuidelines = false,
emptyMessage = 'No rules found.',
showGroupColumn = false,
showSummaryColumn = false,
}) => {
const styles = useStyles2(getStyles);
const wrapperClass = cx(styles.wrapper, className, { [styles.wrapperMargin]: showGuidelines });
const items = useMemo((): RuleTableItemProps[] => {
const seenKeys: string[] = [];
return rules.map((rule, ruleIdx) => {
let key = JSON.stringify([rule.promRule?.type, rule.labels, rule.query, rule.name, rule.annotations]);
if (seenKeys.includes(key)) {
key += `-${ruleIdx}`;
}
seenKeys.push(key);
return {
id: key,
data: rule,
};
});
}, [rules]);
const columns = useColumns(showSummaryColumn, showGroupColumn);
if (!rules.length) {
return <div className={cx(wrapperClass, styles.emptyMessage)}>{emptyMessage}</div>;
}
const TableComponent = showGuidelines ? DynamicTableWithGuidelines : DynamicTable;
return (
<div className={wrapperClass} data-testid="rules-table">
<TableComponent
cols={columns}
isExpandable={true}
items={items}
renderExpandedContent={({ data: rule }) => <RuleDetails rule={rule} />}
/>
</div>
);
};
export const getStyles = (theme: GrafanaTheme2) => ({
wrapperMargin: css`
${theme.breakpoints.up('md')} {
margin-left: 36px;
}
`,
emptyMessage: css`
padding: ${theme.spacing(1)};
`,
wrapper: css`
width: auto;
background-color: ${theme.colors.background.secondary};
border-radius: ${theme.shape.borderRadius()};
`,
table: css`
width: 100%;
border-radius: ${theme.shape.borderRadius()};
border: solid 1px ${theme.colors.border.weak};
background-color: ${theme.colors.background.secondary};
th {
padding: ${theme.spacing(1)};
}
td + td {
padding: ${theme.spacing(0, 1)};
}
tr {
height: 38px;
}
`,
evenRow: css`
background-color: ${theme.colors.background.primary};
`,
state: css`
width: 110px;
`,
});
function useColumns(showSummaryColumn: boolean, showGroupColumn: boolean) {
const hasRuler = useHasRuler();
return useMemo((): RuleTableColumnProps[] => {
const columns: RuleTableColumnProps[] = [
{
id: 'state',
label: 'State',
// eslint-disable-next-line react/display-name
renderCell: ({ data: rule }) => {
const { namespace } = rule;
const { rulesSource } = namespace;
const { promRule, rulerRule } = rule;
const isDeleting = !!(hasRuler(rulesSource) && promRule && !rulerRule);
const isCreating = !!(hasRuler(rulesSource) && rulerRule && !promRule);
return <RuleState rule={rule} isDeleting={isDeleting} isCreating={isCreating} />;
},
size: '165px',
},
{
id: 'name',
label: 'Name',
// eslint-disable-next-line react/display-name
renderCell: ({ data: rule }) => rule.name,
size: 5,
},
{
id: 'health',
label: 'Health',
// eslint-disable-next-line react/display-name
renderCell: ({ data: { promRule } }) => (promRule ? <RuleHealth rule={promRule} /> : null),
size: '75px',
},
];
if (showSummaryColumn) {
columns.push({
id: 'summary',
label: 'Summary',
// eslint-disable-next-line react/display-name
renderCell: ({ data: rule }) => rule.annotations[Annotation.summary] ?? '',
size: 5,
});
}
if (showGroupColumn) {
columns.push({
id: 'group',
label: 'Group',
// eslint-disable-next-line react/display-name
renderCell: ({ data: rule }) => {
const { namespace, group } = rule;
const { rulesSource } = namespace;
return isCloudRulesSource(rulesSource) ? `${namespace.name} > ${group.name}` : namespace.name;
},
size: 5,
});
}
return columns;
}, [hasRuler, showSummaryColumn, showGroupColumn]);
}