Files
2024-02-15 17:01:50 +00:00

185 lines
4.5 KiB
TypeScript

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 (
<GridEditable
isLoading={isLoading}
data={data}
columnOrder={Object.values(COLUMNS)}
columnSortBy={[]}
grid={{
renderHeadCell,
renderBodyCell: renderBodyCellWithMeta(location, organization),
}}
location={location}
/>
);
}
function renderHeadCell(column: TableColumn, _index: number): ReactNode {
const align = fieldAlignment(column.key, COLUMN_TYPE[column.key]);
return (
<SortLink
title={column.name}
align={align}
direction={undefined}
canSort={false}
generateSortLink={() => 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 (
<ConfidenceCell>
{dataRow.confidence === 'low' ? (
<LowConfidence>{t('Low Confidence')}</LowConfidence>
) : (
<HighConfidence>{t('High Confidence')}</HighConfidence>
)}
</ConfidenceCell>
);
}
if (column.key === 'expected') {
return (
<NumberCell>
<Count value={dataRow.expected} />
</NumberCell>
);
}
if (column.key === 'received') {
return (
<NumberCell>
<Count value={dataRow.received} />
<IconArrow
size="sm"
direction={dataRow.received > dataRow.expected ? 'up' : 'down'}
/>
</NumberCell>
);
}
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<TableColumnKey>;
type TableDataRow = Record<TableColumnKey, any>;
type TableDataRowWithExtras = TableDataRow & {};
const COLUMNS: Record<TableColumnKey, TableColumn> = {
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<TableColumnKey, ColumnType> = {
anomaly: 'string',
confidence: 'string',
timeInterval: 'duration',
timestamp: 'date',
expected: 'number',
received: 'number',
};