tableCell.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import styled from '@emotion/styled';
  2. import Duration from 'sentry/components/duration';
  3. import ProjectBadge from 'sentry/components/idBadge/projectBadge';
  4. import UserBadge from 'sentry/components/idBadge/userBadge';
  5. import Link from 'sentry/components/links/link';
  6. import {StringWalker} from 'sentry/components/replays/walker/urlWalker';
  7. import ScoreBar from 'sentry/components/scoreBar';
  8. import TimeSince from 'sentry/components/timeSince';
  9. import CHART_PALETTE from 'sentry/constants/chartPalette';
  10. import space from 'sentry/styles/space';
  11. import type {Organization} from 'sentry/types';
  12. import EventView from 'sentry/utils/discover/eventView';
  13. import {spanOperationRelativeBreakdownRenderer} from 'sentry/utils/discover/fieldRenderers';
  14. import {useLocation} from 'sentry/utils/useLocation';
  15. import useProjects from 'sentry/utils/useProjects';
  16. import type {ReplayListRecordWithTx} from 'sentry/views/performance/transactionSummary/transactionReplays/useReplaysWithTxData';
  17. import type {ReplayListRecord} from 'sentry/views/replays/types';
  18. type Props = {
  19. replay: ReplayListRecord | ReplayListRecordWithTx;
  20. };
  21. export function SessionCell({
  22. eventView,
  23. organization,
  24. referrer,
  25. replay,
  26. }: Props & {eventView: EventView; organization: Organization; referrer: string}) {
  27. const {projects} = useProjects();
  28. const project = projects.find(p => p.id === replay.project_id);
  29. return (
  30. <UserBadge
  31. avatarSize={32}
  32. displayName={
  33. <Link
  34. to={{
  35. pathname: `/organizations/${organization.slug}/replays/${project?.slug}:${replay.id}/`,
  36. query: {
  37. referrer,
  38. ...eventView.generateQueryStringObject(),
  39. },
  40. }}
  41. >
  42. {replay.user.display_name || ''}
  43. </Link>
  44. }
  45. user={{
  46. username: replay.user.display_name || '',
  47. email: replay.user.email || '',
  48. id: replay.user.id || '',
  49. ip_address: replay.user.ip || '',
  50. name: replay.user.username || '',
  51. }}
  52. // this is the subheading for the avatar, so displayEmail in this case is a misnomer
  53. displayEmail={<StringWalker urls={replay.urls} />}
  54. />
  55. );
  56. }
  57. export function ProjectCell({replay}: Props) {
  58. const {projects} = useProjects();
  59. const project = projects.find(p => p.id === replay.project_id);
  60. return (
  61. <Item>{project ? <ProjectBadge project={project} avatarSize={16} /> : null}</Item>
  62. );
  63. }
  64. export function TransactionCell({
  65. organization,
  66. replay,
  67. }: Props & {organization: Organization}) {
  68. const location = useLocation();
  69. const hasTxEvent = 'txEvent' in replay;
  70. const txDuration = hasTxEvent ? replay.txEvent?.['transaction.duration'] : undefined;
  71. return hasTxEvent ? (
  72. <SpanOperationBreakdown>
  73. {txDuration ? <div>{txDuration}ms</div> : null}
  74. {spanOperationRelativeBreakdownRenderer(
  75. replay.txEvent,
  76. {
  77. organization,
  78. location,
  79. },
  80. {
  81. enableOnClick: false,
  82. }
  83. )}
  84. </SpanOperationBreakdown>
  85. ) : null;
  86. }
  87. export function StartedAtCell({replay}: Props) {
  88. return (
  89. <Item>
  90. <TimeSince date={replay.started_at} />
  91. </Item>
  92. );
  93. }
  94. export function DurationCell({replay}: Props) {
  95. return (
  96. <Item>
  97. <Duration seconds={replay.duration.asSeconds()} exact abbreviation />
  98. </Item>
  99. );
  100. }
  101. export function ErrorCountCell({replay}: Props) {
  102. return <Item data-test-id="replay-table-count-errors">{replay.count_errors || 0}</Item>;
  103. }
  104. export function ActivityCell({replay}: Props) {
  105. const scoreBarPalette = new Array(10).fill([CHART_PALETTE[0][0]]);
  106. return (
  107. <Item>
  108. <ScoreBar
  109. size={20}
  110. score={replay?.activity ?? 1}
  111. palette={scoreBarPalette}
  112. radius={0}
  113. />
  114. </Item>
  115. );
  116. }
  117. const Item = styled('div')`
  118. display: flex;
  119. align-items: center;
  120. gap: ${space(1)};
  121. `;
  122. const SpanOperationBreakdown = styled('div')`
  123. width: 100%;
  124. display: flex;
  125. flex-direction: column;
  126. gap: ${space(0.5)};
  127. color: ${p => p.theme.gray500};
  128. font-size: ${p => p.theme.fontSizeMedium};
  129. text-align: right;
  130. `;