import {useCallback, useMemo} from 'react'; import styled from '@emotion/styled'; import Count from 'sentry/components/count'; import GridEditable, { COL_WIDTH_UNDEFINED, GridColumnOrder, } from 'sentry/components/gridEditable'; import PerformanceDuration from 'sentry/components/performanceDuration'; import {ArrayLinks} from 'sentry/components/profiling/arrayLinks'; import {t} from 'sentry/locale'; import {Project} from 'sentry/types'; import {SuspectFunction} from 'sentry/types/profiling/core'; import {Container, NumberContainer} from 'sentry/utils/discover/styles'; import {getShortEventId} from 'sentry/utils/events'; import {generateProfileFlamechartRouteWithQuery} from 'sentry/utils/profiling/routes'; import {renderTableHead} from 'sentry/utils/profiling/tableRenderer'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; interface FunctionsTableProps { error: string | null; functions: SuspectFunction[]; isLoading: boolean; project: Project; sort: string; } function FunctionsTable(props: FunctionsTableProps) { const location = useLocation(); const organization = useOrganization(); const sort = useMemo(() => { let column = props.sort; let order: 'asc' | 'desc' = 'asc' as const; if (props.sort.startsWith('-')) { column = props.sort.substring(1); order = 'desc' as const; } if (!SORTABLE_COLUMNS.has(column as any)) { column = 'p99'; } return { key: column as TableColumnKey, order, }; }, [props.sort]); const functions: TableDataRow[] = useMemo(() => { return props.functions.map(func => { const {worst, examples, ...rest} = func; const allExamples = examples.filter(example => example !== worst); allExamples.unshift(worst); return { ...rest, examples: allExamples.map(example => { const profileId = example.replaceAll('-', ''); return { value: getShortEventId(profileId), target: generateProfileFlamechartRouteWithQuery({ orgSlug: organization.slug, projectSlug: props.project.slug, profileId, query: { // specify the frame to focus, the flamegraph will switch // to the appropriate thread when these are specified frameName: func.name, framePackage: func.package, }, }), }; }), }; }); }, [organization.slug, props.project.slug, props.functions]); const generateSortLink = useCallback( (column: TableColumnKey) => { if (!SORTABLE_COLUMNS.has(column)) { return () => undefined; } const direction = sort.key !== column ? 'desc' : sort.order === 'desc' ? 'asc' : 'desc'; return () => ({ ...location, query: { ...location.query, functionsSort: `${direction === 'desc' ? '-' : ''}${column}`, }, }); }, [location, sort] ); return ( COLUMNS[key])} columnSortBy={[]} grid={{ renderHeadCell: renderTableHead({ currentSort: sort, rightAlignedColumns: RIGHT_ALIGNED_COLUMNS, sortableColumns: SORTABLE_COLUMNS, generateSortLink, }), renderBodyCell: renderFunctionsTableCell, }} location={location} /> ); } const RIGHT_ALIGNED_COLUMNS = new Set(['p75', 'p99', 'count']); const SORTABLE_COLUMNS = RIGHT_ALIGNED_COLUMNS; function renderFunctionsTableCell( column: TableColumn, dataRow: TableDataRow, rowIndex: number, columnIndex: number ) { return ( ); } interface ProfilingFunctionsTableCellProps { column: TableColumn; columnIndex: number; dataRow: TableDataRow; rowIndex: number; } const EmptyValueContainer = styled('span')` color: ${p => p.theme.gray300}; `; function ProfilingFunctionsTableCell({ column, dataRow, }: ProfilingFunctionsTableCellProps) { const value = dataRow[column.key]; switch (column.key) { case 'count': return ( ); case 'p75': case 'p99': return ( ); case 'examples': return ; case 'name': case 'package': const name = value || {t('Unknown')}; return {name}; default: return {value}; } } type TableColumnKey = keyof Omit; type TableDataRow = Record; type TableColumn = GridColumnOrder; const COLUMN_ORDER: TableColumnKey[] = [ 'name', 'package', 'count', 'p75', 'p99', 'examples', ]; const COLUMNS: Record = { name: { key: 'name', name: t('Name'), width: COL_WIDTH_UNDEFINED, }, package: { key: 'package', name: t('Package'), width: COL_WIDTH_UNDEFINED, }, path: { key: 'path', name: t('Path'), width: COL_WIDTH_UNDEFINED, }, p75: { key: 'p75', name: t('P75 Duration'), width: COL_WIDTH_UNDEFINED, }, p99: { key: 'p99', name: t('P99 Duration'), width: COL_WIDTH_UNDEFINED, }, count: { key: 'count', name: t('Count'), width: COL_WIDTH_UNDEFINED, }, examples: { key: 'examples', name: t('Example Profiles'), width: COL_WIDTH_UNDEFINED, }, }; export {FunctionsTable};