releaseCard.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import styled from '@emotion/styled';
  2. import {Location} from 'history';
  3. import GuideAnchor from 'app/components/assistant/guideAnchor';
  4. import GlobalSelectionLink from 'app/components/globalSelectionLink';
  5. import {Panel} from 'app/components/panels';
  6. import ReleaseStats from 'app/components/releaseStats';
  7. import TextOverflow from 'app/components/textOverflow';
  8. import TimeSince from 'app/components/timeSince';
  9. import Version from 'app/components/version';
  10. import overflowEllipsis from 'app/styles/overflowEllipsis';
  11. import space from 'app/styles/space';
  12. import {GlobalSelection, Organization, Release} from 'app/types';
  13. import {ReleaseHealthRequestRenderProps} from '../utils/releaseHealthRequest';
  14. import ReleaseHealth from './releaseHealth';
  15. import {DisplayOption} from './utils';
  16. function getReleaseProjectId(release: Release, selection: GlobalSelection) {
  17. // if a release has only one project
  18. if (release.projects.length === 1) {
  19. return release.projects[0].id;
  20. }
  21. // if only one project is selected in global header and release has it (second condition will prevent false positives like -1)
  22. if (
  23. selection.projects.length === 1 &&
  24. release.projects.map(p => p.id).includes(selection.projects[0])
  25. ) {
  26. return selection.projects[0];
  27. }
  28. // project selector on release detail page will pick it up
  29. return undefined;
  30. }
  31. type Props = {
  32. release: Release;
  33. organization: Organization;
  34. activeDisplay: DisplayOption;
  35. location: Location;
  36. selection: GlobalSelection;
  37. reloading: boolean;
  38. showHealthPlaceholders: boolean;
  39. isTopRelease: boolean;
  40. getHealthData: ReleaseHealthRequestRenderProps['getHealthData'];
  41. };
  42. const ReleaseCard = ({
  43. release,
  44. organization,
  45. activeDisplay,
  46. location,
  47. reloading,
  48. selection,
  49. showHealthPlaceholders,
  50. isTopRelease,
  51. getHealthData,
  52. }: Props) => {
  53. const {version, commitCount, lastDeploy, dateCreated, versionInfo} = release;
  54. return (
  55. <StyledPanel reloading={reloading ? 1 : 0}>
  56. <ReleaseInfo>
  57. <ReleaseInfoHeader>
  58. <GlobalSelectionLink
  59. to={{
  60. pathname: `/organizations/${
  61. organization.slug
  62. }/releases/${encodeURIComponent(version)}/`,
  63. query: {project: getReleaseProjectId(release, selection)},
  64. }}
  65. >
  66. <GuideAnchor disabled={!isTopRelease} target="release_version">
  67. <VersionWrapper>
  68. <StyledVersion version={version} tooltipRawVersion anchor={false} />
  69. </VersionWrapper>
  70. </GuideAnchor>
  71. </GlobalSelectionLink>
  72. {commitCount > 0 && <ReleaseStats release={release} withHeading={false} />}
  73. </ReleaseInfoHeader>
  74. <ReleaseInfoSubheader>
  75. {versionInfo?.package && (
  76. <PackageName ellipsisDirection="left">{versionInfo.package}</PackageName>
  77. )}
  78. <TimeSince date={lastDeploy?.dateFinished || dateCreated} />
  79. {lastDeploy?.dateFinished && ` \u007C ${lastDeploy.environment}`}
  80. </ReleaseInfoSubheader>
  81. </ReleaseInfo>
  82. <ReleaseProjects>
  83. <ReleaseHealth
  84. release={release}
  85. organization={organization}
  86. activeDisplay={activeDisplay}
  87. location={location}
  88. showPlaceholders={showHealthPlaceholders}
  89. reloading={reloading}
  90. selection={selection}
  91. isTopRelease={isTopRelease}
  92. getHealthData={getHealthData}
  93. />
  94. </ReleaseProjects>
  95. </StyledPanel>
  96. );
  97. };
  98. const VersionWrapper = styled('div')`
  99. display: flex;
  100. align-items: center;
  101. `;
  102. const StyledVersion = styled(Version)`
  103. ${overflowEllipsis};
  104. `;
  105. const StyledPanel = styled(Panel)<{reloading: number}>`
  106. opacity: ${p => (p.reloading ? 0.5 : 1)};
  107. pointer-events: ${p => (p.reloading ? 'none' : 'auto')};
  108. @media (min-width: ${p => p.theme.breakpoints[1]}) {
  109. display: flex;
  110. }
  111. `;
  112. const ReleaseInfo = styled('div')`
  113. padding: ${space(1.5)} ${space(2)};
  114. flex-shrink: 0;
  115. @media (min-width: ${p => p.theme.breakpoints[1]}) {
  116. border-right: 1px solid ${p => p.theme.border};
  117. min-width: 260px;
  118. width: 22%;
  119. max-width: 300px;
  120. }
  121. `;
  122. const ReleaseInfoSubheader = styled('div')`
  123. font-size: ${p => p.theme.fontSizeSmall};
  124. color: ${p => p.theme.gray400};
  125. `;
  126. const PackageName = styled(TextOverflow)`
  127. font-size: ${p => p.theme.fontSizeMedium};
  128. color: ${p => p.theme.textColor};
  129. `;
  130. const ReleaseProjects = styled('div')`
  131. border-top: 1px solid ${p => p.theme.border};
  132. flex-grow: 1;
  133. display: grid;
  134. @media (min-width: ${p => p.theme.breakpoints[1]}) {
  135. border-top: none;
  136. }
  137. `;
  138. const ReleaseInfoHeader = styled('div')`
  139. font-size: ${p => p.theme.fontSizeExtraLarge};
  140. display: grid;
  141. grid-template-columns: minmax(0, 1fr) max-content;
  142. grid-gap: ${space(2)};
  143. align-items: center;
  144. `;
  145. export default ReleaseCard;