profileTransactionsTable.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import {useMemo} from 'react';
  2. import Count from 'sentry/components/count';
  3. import DateTime from 'sentry/components/dateTime';
  4. import GridEditable, {
  5. COL_WIDTH_UNDEFINED,
  6. GridColumnOrder,
  7. } from 'sentry/components/gridEditable';
  8. import ProjectBadge from 'sentry/components/idBadge/projectBadge';
  9. import Link from 'sentry/components/links/link';
  10. import PerformanceDuration from 'sentry/components/performanceDuration';
  11. import {t} from 'sentry/locale';
  12. import {ProfileTransaction} from 'sentry/types/profiling/core';
  13. import {defined} from 'sentry/utils';
  14. import {Container, NumberContainer} from 'sentry/utils/discover/styles';
  15. import {generateProfileSummaryRouteWithQuery} from 'sentry/utils/profiling/routes';
  16. import {renderTableHead} from 'sentry/utils/profiling/tableRenderer';
  17. import {useLocation} from 'sentry/utils/useLocation';
  18. import useOrganization from 'sentry/utils/useOrganization';
  19. import useProjects from 'sentry/utils/useProjects';
  20. interface ProfileTransactionsTableProps {
  21. error: string | null;
  22. isLoading: boolean;
  23. transactions: ProfileTransaction[];
  24. }
  25. function ProfileTransactionsTable(props: ProfileTransactionsTableProps) {
  26. const location = useLocation();
  27. const organization = useOrganization();
  28. const {projects} = useProjects();
  29. const transactions: TableDataRow[] = useMemo(() => {
  30. return props.transactions.map(transaction => {
  31. const project = projects.find(proj => proj.id === transaction.project_id);
  32. return {
  33. transaction: project ? (
  34. <Link
  35. to={generateProfileSummaryRouteWithQuery({
  36. location,
  37. orgSlug: organization.slug,
  38. projectSlug: project.slug,
  39. transaction: transaction.name,
  40. })}
  41. >
  42. {transaction.name}
  43. </Link>
  44. ) : (
  45. transaction.name
  46. ),
  47. count: transaction.profiles_count,
  48. project,
  49. p50: transaction.duration_ms.p50,
  50. p75: transaction.duration_ms.p75,
  51. p90: transaction.duration_ms.p90,
  52. p95: transaction.duration_ms.p95,
  53. p99: transaction.duration_ms.p99,
  54. lastSeen: transaction.last_profile_at,
  55. };
  56. });
  57. }, [props.transactions, location, organization, projects]);
  58. return (
  59. <GridEditable
  60. isLoading={props.isLoading}
  61. error={props.error}
  62. data={transactions}
  63. columnOrder={COLUMN_ORDER.map(key => COLUMNS[key])}
  64. columnSortBy={[]}
  65. grid={{
  66. renderHeadCell: renderTableHead({rightAlignedColumns: RIGHT_ALIGNED_COLUMNS}),
  67. renderBodyCell: renderTableBody,
  68. }}
  69. location={location}
  70. />
  71. );
  72. }
  73. const RIGHT_ALIGNED_COLUMNS = new Set<TableColumnKey>([
  74. 'count',
  75. 'p50',
  76. 'p75',
  77. 'p90',
  78. 'p95',
  79. 'p99',
  80. ]);
  81. function renderTableBody(
  82. column: TableColumn,
  83. dataRow: TableDataRow,
  84. rowIndex: number,
  85. columnIndex: number
  86. ) {
  87. return (
  88. <ProfilingTransactionsTableCell
  89. column={column}
  90. dataRow={dataRow}
  91. rowIndex={rowIndex}
  92. columnIndex={columnIndex}
  93. />
  94. );
  95. }
  96. interface ProfilingTransactionsTableCellProps {
  97. column: TableColumn;
  98. columnIndex: number;
  99. dataRow: TableDataRow;
  100. rowIndex: number;
  101. }
  102. function ProfilingTransactionsTableCell({
  103. column,
  104. dataRow,
  105. }: ProfilingTransactionsTableCellProps) {
  106. const value = dataRow[column.key];
  107. switch (column.key) {
  108. case 'project':
  109. if (!defined(value)) {
  110. // should never happen but just in case
  111. return <Container>{t('n/a')}</Container>;
  112. }
  113. return (
  114. <Container>
  115. <ProjectBadge project={value} avatarSize={16} />
  116. </Container>
  117. );
  118. case 'count':
  119. return (
  120. <NumberContainer>
  121. <Count value={value} />
  122. </NumberContainer>
  123. );
  124. case 'p50':
  125. case 'p75':
  126. case 'p90':
  127. case 'p95':
  128. case 'p99':
  129. return (
  130. <NumberContainer>
  131. <PerformanceDuration milliseconds={value} abbreviation />
  132. </NumberContainer>
  133. );
  134. case 'lastSeen':
  135. return (
  136. <Container>
  137. <DateTime date={value} />
  138. </Container>
  139. );
  140. default:
  141. return <Container>{value}</Container>;
  142. }
  143. }
  144. type TableColumnKey =
  145. | 'transaction'
  146. | 'count'
  147. | 'project'
  148. | 'p50'
  149. | 'p75'
  150. | 'p90'
  151. | 'p95'
  152. | 'p99'
  153. | 'lastSeen';
  154. type TableDataRow = Record<TableColumnKey, any>;
  155. type TableColumn = GridColumnOrder<TableColumnKey>;
  156. const COLUMN_ORDER: TableColumnKey[] = [
  157. 'transaction',
  158. 'project',
  159. 'lastSeen',
  160. 'p75',
  161. 'p95',
  162. 'count',
  163. ];
  164. const COLUMNS: Record<TableColumnKey, TableColumn> = {
  165. transaction: {
  166. key: 'transaction',
  167. name: t('Transaction'),
  168. width: COL_WIDTH_UNDEFINED,
  169. },
  170. count: {
  171. key: 'count',
  172. name: t('Count'),
  173. width: COL_WIDTH_UNDEFINED,
  174. },
  175. project: {
  176. key: 'project',
  177. name: t('Project'),
  178. width: COL_WIDTH_UNDEFINED,
  179. },
  180. p50: {
  181. key: 'p50',
  182. name: t('P50'),
  183. width: COL_WIDTH_UNDEFINED,
  184. },
  185. p75: {
  186. key: 'p75',
  187. name: t('P75'),
  188. width: COL_WIDTH_UNDEFINED,
  189. },
  190. p90: {
  191. key: 'p90',
  192. name: t('P90'),
  193. width: COL_WIDTH_UNDEFINED,
  194. },
  195. p95: {
  196. key: 'p95',
  197. name: t('P95'),
  198. width: COL_WIDTH_UNDEFINED,
  199. },
  200. p99: {
  201. key: 'p99',
  202. name: t('P99'),
  203. width: COL_WIDTH_UNDEFINED,
  204. },
  205. lastSeen: {
  206. key: 'lastSeen',
  207. name: t('Last Seen'),
  208. width: COL_WIDTH_UNDEFINED,
  209. },
  210. };
  211. export {ProfileTransactionsTable};