sampleTable.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import {useCallback} from 'react';
  2. import {Link} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import {LinkButton} from 'sentry/components/button';
  5. import GridEditable, {
  6. COL_WIDTH_UNDEFINED,
  7. GridColumnHeader,
  8. GridColumnOrder,
  9. } from 'sentry/components/gridEditable';
  10. import {Tooltip} from 'sentry/components/tooltip';
  11. import {IconPlay, IconProfiling} from 'sentry/icons';
  12. import {t} from 'sentry/locale';
  13. import {MRI} from 'sentry/types';
  14. import {getDuration} from 'sentry/utils/formatters';
  15. import {useMetricsSpans} from 'sentry/utils/metrics/useMetricsCodeLocations';
  16. import {useLocation} from 'sentry/utils/useLocation';
  17. import useOrganization from 'sentry/utils/useOrganization';
  18. import useProjects from 'sentry/utils/useProjects';
  19. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  20. import {MetricRange, MetricSpan} from '../../utils/metrics/index';
  21. export type SamplesTableProps = MetricRange & {
  22. mri: MRI;
  23. };
  24. type Column = GridColumnHeader<keyof MetricSpan>;
  25. const columnOrder: GridColumnOrder<keyof MetricSpan>[] = [
  26. {key: 'spanId', width: COL_WIDTH_UNDEFINED, name: 'Event ID'},
  27. {key: 'duration', width: COL_WIDTH_UNDEFINED, name: 'Duration'},
  28. {key: 'traceId', width: COL_WIDTH_UNDEFINED, name: 'Trace ID'},
  29. {key: 'profileId', width: COL_WIDTH_UNDEFINED, name: 'Profile'},
  30. {key: 'replayId', width: COL_WIDTH_UNDEFINED, name: 'Replay'},
  31. ];
  32. export function SampleTable({mri, ...range}: SamplesTableProps) {
  33. const location = useLocation();
  34. const organization = useOrganization();
  35. const {projects} = useProjects();
  36. const {data, isLoading} = useMetricsSpans(mri, range);
  37. const getProjectSlug = useCallback(
  38. (projectId: number) => {
  39. return projects.find(p => parseInt(p.id, 10) === projectId)?.slug;
  40. },
  41. [projects]
  42. );
  43. const rows = data?.metrics
  44. .map(m => m.metricSpans)
  45. .flat()
  46. .filter(Boolean) as MetricSpan[];
  47. function renderHeadCell(col: Column) {
  48. if (col.key === 'profileId' || col.key === 'replayId') {
  49. return <AlignCenter>{col.name}</AlignCenter>;
  50. }
  51. return <span>{col.name}</span>;
  52. }
  53. function renderBodyCell(col: Column, row: MetricSpan) {
  54. const {key} = col;
  55. if (!row[key]) {
  56. return <span>{'\u2014'}</span>;
  57. }
  58. if (key === 'spanId') {
  59. return (
  60. <span>
  61. <Link
  62. to={normalizeUrl(
  63. `/organizations/${organization.slug}/performance/${getProjectSlug(
  64. row.projectId
  65. )}:${row.transactionId}/#span-${row.spanId}`
  66. )}
  67. >
  68. {row.spanId.slice(0, 8)}
  69. </Link>
  70. </span>
  71. );
  72. }
  73. if (key === 'duration') {
  74. // We get duration in miliseconds, but getDuration expects seconds
  75. return <span>{getDuration(row.duration / 1000, 2, true)}</span>;
  76. }
  77. if (key === 'traceId') {
  78. return (
  79. <span>
  80. <Link
  81. to={normalizeUrl(
  82. `/organizations/${organization.slug}/performance/trace/${row.traceId}/`
  83. )}
  84. >
  85. {row.traceId.slice(0, 8)}
  86. </Link>
  87. </span>
  88. );
  89. }
  90. if (key === 'profileId') {
  91. return (
  92. <AlignCenter>
  93. <Tooltip title={t('View Profile')}>
  94. <LinkButton
  95. to={normalizeUrl(
  96. `/organizations/${organization.slug}/profiling/profile/${getProjectSlug(
  97. row.projectId
  98. )}/${row.profileId}/flamegraph/`
  99. )}
  100. size="xs"
  101. >
  102. <IconProfiling size="xs" />
  103. </LinkButton>
  104. </Tooltip>
  105. </AlignCenter>
  106. );
  107. }
  108. if (key === 'replayId') {
  109. return (
  110. <AlignCenter>
  111. <Tooltip title={t('View Replay')}>
  112. <LinkButton
  113. to={normalizeUrl(
  114. `/organizations/${organization.slug}/replays/${row.replayId}/`
  115. )}
  116. size="xs"
  117. >
  118. <IconPlay size="xs" />
  119. </LinkButton>
  120. </Tooltip>
  121. </AlignCenter>
  122. );
  123. }
  124. return <span>{row[col.key]}</span>;
  125. }
  126. return (
  127. <GridEditable
  128. isLoading={isLoading}
  129. columnOrder={columnOrder}
  130. columnSortBy={[]}
  131. data={rows}
  132. grid={{
  133. renderHeadCell,
  134. renderBodyCell,
  135. }}
  136. location={location}
  137. />
  138. );
  139. }
  140. const AlignCenter = styled('span')`
  141. display: block;
  142. margin: auto;
  143. text-align: center;
  144. width: 100%;
  145. `;