firstLastSeenSection.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import styled from '@emotion/styled';
  2. import {Flex} from 'sentry/components/container/flex';
  3. import SeenInfo from 'sentry/components/group/seenInfo';
  4. import Version from 'sentry/components/version';
  5. import VersionHoverCard from 'sentry/components/versionHoverCard';
  6. import {t, tct} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import type {Group} from 'sentry/types/group';
  9. import type {Project} from 'sentry/types/project';
  10. import type {Release} from 'sentry/types/release';
  11. import {useApiQuery} from 'sentry/utils/queryClient';
  12. import useOrganization from 'sentry/utils/useOrganization';
  13. import {useFetchAllEnvsGroupData} from 'sentry/views/issueDetails/groupSidebar';
  14. export interface GroupRelease {
  15. firstRelease: Release;
  16. lastRelease: Release;
  17. }
  18. export default function FirstLastSeenSection({group}: {group: Group}) {
  19. const organization = useOrganization();
  20. const {project} = group;
  21. const {data: allEnvironments} = useFetchAllEnvsGroupData(organization, group);
  22. const {data: groupReleaseData} = useApiQuery<GroupRelease>(
  23. [`/organizations/${organization.slug}/issues/${group.id}/first-last-release/`],
  24. {
  25. staleTime: 30000,
  26. gcTime: 30000,
  27. }
  28. );
  29. return (
  30. <Flex column gap={space(0.75)}>
  31. <div>
  32. <Flex gap={space(0.5)}>
  33. <Title>{t('Last seen')}</Title>
  34. {group.lastSeen ? (
  35. <SeenInfo
  36. date={group.lastSeen}
  37. dateGlobal={allEnvironments?.lastSeen ?? group.lastSeen}
  38. organization={organization}
  39. projectId={project.id}
  40. projectSlug={project.slug}
  41. />
  42. ) : (
  43. t('N/A')
  44. )}
  45. </Flex>
  46. <ReleaseText project={group.project} release={groupReleaseData?.lastRelease} />
  47. </div>
  48. <div>
  49. <Flex gap={space(0.5)}>
  50. <Title>{t('First seen')}</Title>
  51. {group.firstSeen ? (
  52. <SeenInfo
  53. date={group.firstSeen}
  54. dateGlobal={allEnvironments?.firstSeen ?? group.firstSeen}
  55. organization={organization}
  56. projectId={project.id}
  57. projectSlug={project.slug}
  58. />
  59. ) : (
  60. t('N/A')
  61. )}
  62. </Flex>
  63. <ReleaseText project={group.project} release={groupReleaseData?.firstRelease} />
  64. </div>
  65. </Flex>
  66. );
  67. }
  68. function ReleaseText({project, release}: {project: Project; release?: Release}) {
  69. const organization = useOrganization();
  70. if (!release) {
  71. return null;
  72. }
  73. return (
  74. <Subtitle>
  75. {tct('in release [release]', {
  76. release: (
  77. <VersionHoverCard
  78. organization={organization}
  79. projectSlug={project.slug}
  80. releaseVersion={release.version}
  81. >
  82. <ReleaseWrapper>
  83. <Version version={release.version} projectId={project.id} />
  84. </ReleaseWrapper>
  85. </VersionHoverCard>
  86. ),
  87. })}
  88. </Subtitle>
  89. );
  90. }
  91. const ReleaseWrapper = styled('span')`
  92. a {
  93. color: ${p => p.theme.gray300};
  94. text-decoration: underline;
  95. text-decoration-style: dotted;
  96. }
  97. `;
  98. const Title = styled('div')`
  99. font-weight: ${p => p.theme.fontWeightBold};
  100. `;
  101. const Subtitle = styled('div')`
  102. font-size: ${p => p.theme.fontSizeSmall};
  103. color: ${p => p.theme.subText};
  104. `;