projectReleaseDetails.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import styled from '@emotion/styled';
  2. import moment from 'moment-timezone';
  3. import {Button} from 'sentry/components/button';
  4. import {Flex} from 'sentry/components/container/flex';
  5. import Count from 'sentry/components/count';
  6. import {DateTime} from 'sentry/components/dateTime';
  7. import {KeyValueTable, KeyValueTableRow} from 'sentry/components/keyValueTable';
  8. import ExternalLink from 'sentry/components/links/externalLink';
  9. import Link from 'sentry/components/links/link';
  10. import * as SidebarSection from 'sentry/components/sidebarSection';
  11. import TextOverflow from 'sentry/components/textOverflow';
  12. import TimeSince from 'sentry/components/timeSince';
  13. import {Tooltip} from 'sentry/components/tooltip';
  14. import Version from 'sentry/components/version';
  15. import {IconInfo} from 'sentry/icons/iconInfo';
  16. import {t, tct, tn} from 'sentry/locale';
  17. import {space} from 'sentry/styles/space';
  18. import type {ReleaseMeta, ReleaseWithHealth} from 'sentry/types/release';
  19. import useOrganization from 'sentry/utils/useOrganization';
  20. import {useUser} from 'sentry/utils/useUser';
  21. import useFinalizeRelease from 'sentry/views/releases/components/useFinalizeRelease';
  22. import {isVersionInfoSemver} from 'sentry/views/releases/utils';
  23. type Props = {
  24. projectSlug: string;
  25. release: ReleaseWithHealth;
  26. releaseMeta: ReleaseMeta;
  27. };
  28. function ProjectReleaseDetails({release, releaseMeta, projectSlug}: Props) {
  29. const organization = useOrganization();
  30. const orgSlug = organization.slug;
  31. const user = useUser();
  32. const options = user ? user.options : null;
  33. const {version, versionInfo, dateCreated, dateReleased, firstEvent, lastEvent} =
  34. release;
  35. const {releaseFileCount, isArtifactBundle} = releaseMeta;
  36. const finalizeRelease = useFinalizeRelease();
  37. return (
  38. <SidebarSection.Wrap>
  39. <SidebarSection.Title>{t('Project Release Details')}</SidebarSection.Title>
  40. <SidebarSection.Content>
  41. <KeyValueTable>
  42. <KeyValueTableRow
  43. keyName={t('Created')}
  44. value={<DateTime date={dateCreated} seconds={false} />}
  45. />
  46. <KeyValueTableRow
  47. keyName={
  48. <Flex gap={space(0.75)} align="center">
  49. {t('Finalized')}
  50. <Tooltip
  51. skipWrapper
  52. title={tct(
  53. 'By default a release is created "unreleased".[br]Finalizing a release means that we populate a second timestamp on the release record, which is prioritized over [code:date_created] when sorting releases. [docs:Read more].',
  54. {
  55. br: <br />,
  56. code: <code />,
  57. docs: (
  58. <ExternalLink href="https://docs.sentry.io/cli/releases/#finalizing-releases" />
  59. ),
  60. }
  61. )}
  62. >
  63. <IconInfo />
  64. </Tooltip>
  65. </Flex>
  66. }
  67. value={
  68. dateReleased ? (
  69. <DateTime date={dateReleased} seconds={false} />
  70. ) : (
  71. <Tooltip
  72. title={t(
  73. 'Set release date to %s',
  74. moment
  75. .tz(
  76. release.firstEvent ?? release.dateCreated,
  77. options?.timezone ?? ''
  78. )
  79. .format(
  80. options?.clock24Hours
  81. ? 'MMMM D, YYYY HH:mm z'
  82. : 'MMMM D, YYYY h:mm A z'
  83. )
  84. )}
  85. >
  86. <Button
  87. size="xs"
  88. style={{marginRight: '-8px'}}
  89. onClick={() => {
  90. finalizeRelease.mutate([release], {
  91. onSettled() {
  92. window.location.reload();
  93. },
  94. });
  95. }}
  96. >
  97. {t('Finalize')}
  98. </Button>
  99. </Tooltip>
  100. )
  101. }
  102. />
  103. <KeyValueTableRow
  104. keyName={t('Version')}
  105. value={<Version version={version} anchor={false} />}
  106. />
  107. <KeyValueTableRow
  108. keyName={t('Semver')}
  109. value={isVersionInfoSemver(versionInfo.version) ? t('Yes') : t('No')}
  110. />
  111. <KeyValueTableRow
  112. keyName={t('Package')}
  113. value={
  114. <StyledTextOverflow ellipsisDirection="left">
  115. {versionInfo.package ?? '\u2014'}
  116. </StyledTextOverflow>
  117. }
  118. />
  119. <KeyValueTableRow
  120. keyName={t('First Event')}
  121. value={firstEvent ? <TimeSince date={firstEvent} /> : '\u2014'}
  122. />
  123. <KeyValueTableRow
  124. keyName={t('Last Event')}
  125. value={lastEvent ? <TimeSince date={lastEvent} /> : '\u2014'}
  126. />
  127. <KeyValueTableRow
  128. keyName={t('Source Maps')}
  129. value={
  130. <Link
  131. to={
  132. isArtifactBundle
  133. ? `/settings/${orgSlug}/projects/${projectSlug}/source-maps/?query=${encodeURIComponent(
  134. version
  135. )}`
  136. : `/settings/${orgSlug}/projects/${projectSlug}/source-maps/${encodeURIComponent(
  137. version
  138. )}/`
  139. }
  140. >
  141. <Count value={releaseFileCount} />{' '}
  142. {tn('artifact', 'artifacts', releaseFileCount)}
  143. </Link>
  144. }
  145. />
  146. </KeyValueTable>
  147. </SidebarSection.Content>
  148. </SidebarSection.Wrap>
  149. );
  150. }
  151. const StyledTextOverflow = styled(TextOverflow)`
  152. line-height: inherit;
  153. text-align: right;
  154. `;
  155. export default ProjectReleaseDetails;