import type {ReactNode} from 'react'; import styled from '@emotion/styled'; import type {Location} from 'history'; import Count from 'sentry/components/count'; import type {GridColumnOrder} from 'sentry/components/gridEditable'; import GridEditable, {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable'; import SortLink from 'sentry/components/gridEditable/sortLink'; import {IconArrow} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types'; import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers'; import type {ColumnType} from 'sentry/utils/discover/fields'; import {fieldAlignment} from 'sentry/utils/discover/fields'; import type {AnomalyInfo} from 'sentry/utils/performance/anomalies/anomaliesQuery'; type Props = { isLoading: boolean; location: Location; organization: Organization; anomalies?: AnomalyInfo[]; }; const transformRow = (anom: AnomalyInfo): TableDataRowWithExtras => { return { anomaly: `#${anom.id}`, confidence: anom.confidence, timestamp: new Date(anom.start), timeInterval: anom.end - anom.start, expected: anom.expected, received: anom.received, }; }; export default function AnomaliesTable(props: Props) { const {location, organization, isLoading, anomalies} = props; const data: TableDataRowWithExtras[] = anomalies?.map(transformRow) || []; return ( ); } function renderHeadCell(column: TableColumn, _index: number): ReactNode { const align = fieldAlignment(column.key, COLUMN_TYPE[column.key]); return ( undefined} /> ); } function renderBodyCellWithMeta(location: Location, organization: Organization) { return function ( column: TableColumn, dataRow: TableDataRowWithExtras ): React.ReactNode { const fieldRenderer = getFieldRenderer(column.key, COLUMN_TYPE); if (column.key === 'confidence') { return ( {dataRow.confidence === 'low' ? ( {t('Low Confidence')} ) : ( {t('High Confidence')} )} ); } if (column.key === 'expected') { return ( ); } if (column.key === 'received') { return ( dataRow.expected ? 'up' : 'down'} /> ); } return fieldRenderer(dataRow, {location, organization}); }; } const NumberCell = styled('div')` display: flex; justify-content: flex-end; align-items: center; gap: ${space(0.5)}; `; const LowConfidence = styled('div')` color: ${p => p.theme.yellow400}; `; const HighConfidence = styled('div')` color: ${p => p.theme.red400}; `; const ConfidenceCell = styled('div')` text-align: left; justify-self: flex-end; flex-grow: 1; `; type TableColumnKey = | 'anomaly' | 'confidence' | 'timeInterval' | 'timestamp' | 'expected' | 'received'; type TableColumn = GridColumnOrder; type TableDataRow = Record; type TableDataRowWithExtras = TableDataRow & {}; const COLUMNS: Record = { anomaly: { key: 'anomaly', name: t('Anomaly'), width: COL_WIDTH_UNDEFINED, }, confidence: { key: 'confidence', name: t('Confidence'), width: COL_WIDTH_UNDEFINED, }, timeInterval: { key: 'timeInterval', name: t('Time Interval'), width: COL_WIDTH_UNDEFINED, }, timestamp: { key: 'timestamp', name: t('Timestamp'), width: COL_WIDTH_UNDEFINED, }, expected: { key: 'expected', name: t('Expected'), width: COL_WIDTH_UNDEFINED, }, received: { key: 'received', name: t('Received'), width: COL_WIDTH_UNDEFINED, }, }; const COLUMN_TYPE: Record = { anomaly: 'string', confidence: 'string', timeInterval: 'duration', timestamp: 'date', expected: 'number', received: 'number', };