mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
168 lines
4.7 KiB
TypeScript
168 lines
4.7 KiB
TypeScript
import {Fragment} from 'react';
|
|
import styled from '@emotion/styled';
|
|
import type {Location} from 'history';
|
|
|
|
import type {PanelTableProps} from 'sentry/components/panels/panelTable';
|
|
import PanelTable, {PanelTableHeader} from 'sentry/components/panels/panelTable';
|
|
import {Tooltip} from 'sentry/components/tooltip';
|
|
import Truncate from 'sentry/components/truncate';
|
|
import {space} from 'sentry/styles/space';
|
|
import type {Organization} from 'sentry/types';
|
|
import type {TableData, TableDataRow} from 'sentry/utils/discover/discoverQuery';
|
|
import type {MetaType} from 'sentry/utils/discover/eventView';
|
|
import type EventView from 'sentry/utils/discover/eventView';
|
|
import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
|
|
import {fieldAlignment} from 'sentry/utils/discover/fields';
|
|
import useOrganization from 'sentry/utils/useOrganization';
|
|
import useProjects from 'sentry/utils/useProjects';
|
|
import {TransactionLink} from 'sentry/views/discover/table/tableView';
|
|
import TopResultsIndicator from 'sentry/views/discover/table/topResultsIndicator';
|
|
import {
|
|
decodeColumnOrder,
|
|
getTargetForTransactionSummaryLink,
|
|
} from 'sentry/views/discover/utils';
|
|
|
|
type Props = {
|
|
eventView: EventView;
|
|
fieldAliases: string[];
|
|
fields: string[];
|
|
loading: boolean;
|
|
location: Location;
|
|
title: string;
|
|
className?: string;
|
|
data?: TableData['data'];
|
|
fieldHeaderMap?: Record<string, string>;
|
|
getCustomFieldRenderer?: (
|
|
field: string,
|
|
meta: MetaType,
|
|
organization?: Organization
|
|
) => ReturnType<typeof getFieldRenderer> | null;
|
|
loader?: PanelTableProps['loader'];
|
|
metadata?: TableData['meta'];
|
|
stickyHeaders?: boolean;
|
|
topResultsIndicators?: number;
|
|
};
|
|
|
|
function SimpleTableChart({
|
|
className,
|
|
loading,
|
|
eventView,
|
|
fields,
|
|
metadata,
|
|
data,
|
|
title,
|
|
fieldHeaderMap,
|
|
stickyHeaders,
|
|
getCustomFieldRenderer,
|
|
topResultsIndicators,
|
|
location,
|
|
fieldAliases,
|
|
loader,
|
|
}: Props) {
|
|
const organization = useOrganization();
|
|
const {projects} = useProjects();
|
|
function renderRow(
|
|
index: number,
|
|
row: TableDataRow,
|
|
tableMeta: NonNullable<TableData['meta']>,
|
|
columns: ReturnType<typeof decodeColumnOrder>
|
|
) {
|
|
return columns.map((column, columnIndex) => {
|
|
const fieldRenderer =
|
|
getCustomFieldRenderer?.(column.key, tableMeta, organization) ??
|
|
getFieldRenderer(column.key, tableMeta);
|
|
|
|
const unit = tableMeta.units?.[column.key];
|
|
let cell = fieldRenderer(row, {organization, location, eventView, unit});
|
|
|
|
if (column.key === 'transaction' && row.transaction) {
|
|
cell = (
|
|
<TransactionLink
|
|
to={getTargetForTransactionSummaryLink(
|
|
row,
|
|
organization,
|
|
projects,
|
|
eventView,
|
|
location
|
|
)}
|
|
>
|
|
{cell}
|
|
</TransactionLink>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<TableCell key={`${index}-${columnIndex}:${column.name}`}>
|
|
{topResultsIndicators && columnIndex === 0 && (
|
|
<TopResultsIndicator count={topResultsIndicators} index={index} />
|
|
)}
|
|
{cell}
|
|
</TableCell>
|
|
);
|
|
});
|
|
}
|
|
|
|
const meta = metadata ?? {};
|
|
const columns = decodeColumnOrder(
|
|
fields.map((field, index) => ({field, alias: fieldAliases[index]}))
|
|
);
|
|
|
|
return (
|
|
<Fragment>
|
|
{title && <h4>{title}</h4>}
|
|
<StyledPanelTable
|
|
className={className}
|
|
isLoading={loading}
|
|
loader={loader}
|
|
headers={columns.map((column, index) => {
|
|
const align = fieldAlignment(column.name, column.type, meta);
|
|
const header =
|
|
column.column.alias || (fieldHeaderMap?.[column.key] ?? column.name);
|
|
return (
|
|
<HeadCell key={index} align={align}>
|
|
<Tooltip title={header}>
|
|
<StyledTruncate value={header} maxLength={30} expandable={false} />
|
|
</Tooltip>
|
|
</HeadCell>
|
|
);
|
|
})}
|
|
isEmpty={!data?.length}
|
|
stickyHeaders={stickyHeaders}
|
|
disablePadding
|
|
>
|
|
{data?.map((row, index) => renderRow(index, row, meta, columns))}
|
|
</StyledPanelTable>
|
|
</Fragment>
|
|
);
|
|
}
|
|
|
|
const StyledTruncate = styled(Truncate)`
|
|
white-space: nowrap;
|
|
`;
|
|
|
|
const StyledPanelTable = styled(PanelTable)`
|
|
border-radius: 0;
|
|
border-left: 0;
|
|
border-right: 0;
|
|
border-bottom: 0;
|
|
|
|
margin: 0;
|
|
${PanelTableHeader} {
|
|
height: min-content;
|
|
}
|
|
`;
|
|
|
|
type HeadCellProps = {
|
|
align: string | undefined;
|
|
};
|
|
const HeadCell = styled('div')<HeadCellProps>`
|
|
${(p: HeadCellProps) => (p.align ? `text-align: ${p.align};` : '')}
|
|
padding: ${space(1)} ${space(3)};
|
|
`;
|
|
|
|
export const TableCell = styled('div')`
|
|
padding: ${space(1)} ${space(3)};
|
|
`;
|
|
|
|
export default SimpleTableChart;
|