import React, { FC } from 'react'; import { uniqueId } from 'lodash'; import { AlertState, dateTimeFormat, GrafanaTheme } from '@grafana/data'; import { Alert, LoadingPlaceholder, useStyles } from '@grafana/ui'; import { css } from '@emotion/css'; import { StateHistoryItem, StateHistoryItemData } from 'app/types/unified-alerting'; import { DynamicTable, DynamicTableColumnProps, DynamicTableItemProps } from '../DynamicTable'; import { AlertStateTag } from './AlertStateTag'; import { useManagedAlertStateHistory } from '../../hooks/useManagedAlertStateHistory'; import { AlertLabel } from '../AlertLabel'; import { GrafanaAlertState, PromAlertingRuleState } from 'app/types/unified-alerting-dto'; type StateHistoryRowItem = { id: string; state: PromAlertingRuleState | GrafanaAlertState | AlertState; text?: string; data?: StateHistoryItemData; timestamp?: number; }; type StateHistoryRow = DynamicTableItemProps; interface RuleStateHistoryProps { alertId: string; } const StateHistory: FC = ({ alertId }) => { const { loading, error, result = [] } = useManagedAlertStateHistory(alertId); if (loading && !error) { return ; } if (error && !loading) { return {error.message}; } const columns: Array> = [ { id: 'state', label: 'State', size: 'max-content', renderCell: renderStateCell }, { id: 'value', label: '', size: 'auto', renderCell: renderValueCell }, { id: 'timestamp', label: 'Time', size: 'max-content', renderCell: renderTimestampCell }, ]; const items: StateHistoryRow[] = result .reduce((acc: StateHistoryRowItem[], item, index) => { acc.push({ id: String(item.id), state: item.newState, text: item.text, data: item.data, timestamp: item.updated, }); // if the preceding state is not the same, create a separate state entry – this likely means the state was reset if (!hasMatchingPrecedingState(index, result)) { acc.push({ id: uniqueId(), state: item.prevState }); } return acc; }, []) .map((historyItem) => ({ id: historyItem.id, data: historyItem, })); return ; }; function renderValueCell(item: StateHistoryRow) { const matches = item.data.data?.evalMatches ?? []; return ( <> {item.data.text} {matches.map((match) => ( ))} ); } function renderStateCell(item: StateHistoryRow) { return ; } function renderTimestampCell(item: StateHistoryRow) { return (
{item.data.timestamp && {dateTimeFormat(item.data.timestamp)}}
); } const LabelsWrapper: FC<{}> = ({ children }) => { const { wrapper } = useStyles(getStyles); return
{children}
; }; const TimestampStyle = css` display: flex; align-items: flex-end; flex-direction: column; `; const getStyles = (theme: GrafanaTheme) => ({ wrapper: css` & > * { margin-right: ${theme.spacing.xs}; } `, }); // this function will figure out if a given historyItem has a preceding historyItem where the states match - in other words // the newState of the previous historyItem is the same as the prevState of the current historyItem function hasMatchingPrecedingState(index: number, items: StateHistoryItem[]): boolean { const currentHistoryItem = items[index]; const previousHistoryItem = items[index + 1]; if (!previousHistoryItem) { return false; } return previousHistoryItem.newState === currentHistoryItem.prevState; } export { StateHistory };