row.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import {Fragment, useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import moment from 'moment-timezone';
  4. import ActorAvatar from 'sentry/components/avatar/actorAvatar';
  5. import Tag from 'sentry/components/badge/tag';
  6. import Duration from 'sentry/components/duration';
  7. import ErrorBoundary from 'sentry/components/errorBoundary';
  8. import IdBadge from 'sentry/components/idBadge';
  9. import Link from 'sentry/components/links/link';
  10. import TimeSince from 'sentry/components/timeSince';
  11. import {t} from 'sentry/locale';
  12. import TeamStore from 'sentry/stores/teamStore';
  13. import {space} from 'sentry/styles/space';
  14. import type {Actor} from 'sentry/types/core';
  15. import type {Organization} from 'sentry/types/organization';
  16. import type {Project} from 'sentry/types/project';
  17. import getDynamicText from 'sentry/utils/getDynamicText';
  18. import type {Incident} from 'sentry/views/alerts/types';
  19. import {IncidentStatus} from 'sentry/views/alerts/types';
  20. import {alertDetailsLink} from 'sentry/views/alerts/utils';
  21. type Props = {
  22. incident: Incident;
  23. organization: Organization;
  24. projects: Project[];
  25. projectsLoaded: boolean;
  26. };
  27. function AlertListRow({incident, projectsLoaded, projects, organization}: Props) {
  28. const slug = incident.projects[0];
  29. const started = moment(incident.dateStarted);
  30. const duration = moment
  31. .duration(moment(incident.dateClosed || new Date()).diff(started))
  32. .as('seconds');
  33. const project = useMemo(() => projects.find(p => p.slug === slug), [slug, projects]);
  34. const alertLink = {
  35. pathname: alertDetailsLink(organization, incident),
  36. query: {alert: incident.identifier},
  37. };
  38. const ownerId = incident.alertRule.owner?.split(':')[1];
  39. let teamName = '';
  40. if (ownerId) {
  41. teamName = TeamStore.getById(ownerId)?.name ?? '';
  42. }
  43. const teamActor = ownerId
  44. ? {type: 'team' as Actor['type'], id: ownerId, name: teamName}
  45. : null;
  46. return (
  47. <ErrorBoundary>
  48. <FlexCenter>
  49. <Title data-test-id="alert-title">
  50. <Link to={alertLink}>{incident.title}</Link>
  51. </Title>
  52. </FlexCenter>
  53. <NoWrapNumeric>
  54. {getDynamicText({
  55. value: <TimeSince date={incident.dateStarted} unitStyle="extraShort" />,
  56. fixed: '1w ago',
  57. })}
  58. </NoWrapNumeric>
  59. <NoWrapNumeric>
  60. {incident.status === IncidentStatus.CLOSED ? (
  61. <Duration seconds={getDynamicText({value: duration, fixed: 1200})} />
  62. ) : (
  63. <Tag type="warning">{t('Still Active')}</Tag>
  64. )}
  65. </NoWrapNumeric>
  66. <FlexCenter>
  67. <ProjectBadge avatarSize={18} project={!projectsLoaded ? {slug} : project} />
  68. </FlexCenter>
  69. <NoWrapNumeric>#{incident.id}</NoWrapNumeric>
  70. <FlexCenter>
  71. {teamActor ? (
  72. <Fragment>
  73. <StyledActorAvatar actor={teamActor} size={18} hasTooltip={false} />{' '}
  74. <TeamWrapper>{teamActor.name}</TeamWrapper>
  75. </Fragment>
  76. ) : (
  77. '-'
  78. )}
  79. </FlexCenter>
  80. </ErrorBoundary>
  81. );
  82. }
  83. const Title = styled('div')`
  84. ${p => p.theme.overflowEllipsis}
  85. min-width: 130px;
  86. `;
  87. const ProjectBadge = styled(IdBadge)`
  88. flex-shrink: 0;
  89. `;
  90. const FlexCenter = styled('div')`
  91. ${p => p.theme.overflowEllipsis}
  92. display: flex;
  93. align-items: center;
  94. line-height: 1.6;
  95. `;
  96. const NoWrapNumeric = styled(FlexCenter)`
  97. white-space: nowrap;
  98. font-variant-numeric: tabular-nums;
  99. `;
  100. const TeamWrapper = styled('span')`
  101. ${p => p.theme.overflowEllipsis}
  102. `;
  103. const StyledActorAvatar = styled(ActorAvatar)`
  104. margin-right: ${space(1)};
  105. `;
  106. export default AlertListRow;