headers.tsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import IssueStreamHeaderLabel from 'sentry/components/IssueStreamHeaderLabel';
  4. import ToolbarHeader from 'sentry/components/toolbarHeader';
  5. import {t} from 'sentry/locale';
  6. import {space} from 'sentry/styles/space';
  7. import type {PageFilters} from 'sentry/types/core';
  8. import useOrganization from 'sentry/utils/useOrganization';
  9. import {HeaderDivider} from 'sentry/views/issueList/actions';
  10. import {COLUMN_BREAKPOINTS} from 'sentry/views/issueList/actions/utils';
  11. type Props = {
  12. isReprocessingQuery: boolean;
  13. onSelectStatsPeriod: (statsPeriod: string) => void;
  14. selection: PageFilters;
  15. statsPeriod: string;
  16. isSavedSearchesOpen?: boolean;
  17. };
  18. function Headers({
  19. selection,
  20. statsPeriod,
  21. onSelectStatsPeriod,
  22. isReprocessingQuery,
  23. isSavedSearchesOpen,
  24. }: Props) {
  25. const organization = useOrganization();
  26. return (
  27. <Fragment>
  28. {isReprocessingQuery ? (
  29. <Fragment>
  30. <StartedColumn>{t('Started')}</StartedColumn>
  31. <EventsReprocessedColumn>{t('Events Reprocessed')}</EventsReprocessedColumn>
  32. <ProgressColumn>{t('Progress')}</ProgressColumn>
  33. </Fragment>
  34. ) : (
  35. <Fragment>
  36. {organization.features.includes('issue-stream-table-layout') ? (
  37. <NarrowGraphLabel breakpoint={COLUMN_BREAKPOINTS.TREND}>
  38. <NarrowGraphLabelContents>
  39. {t('Trend')}
  40. <NarrowGraphToggles>
  41. {selection.datetime.period !== '24h' && (
  42. <GraphToggle
  43. active={statsPeriod === '24h'}
  44. onClick={() => onSelectStatsPeriod('24h')}
  45. >
  46. {t('24h')}
  47. </GraphToggle>
  48. )}
  49. <GraphToggle
  50. active={statsPeriod === 'auto'}
  51. onClick={() => onSelectStatsPeriod('auto')}
  52. >
  53. {selection.datetime.period || t('Custom')}
  54. </GraphToggle>
  55. </NarrowGraphToggles>
  56. </NarrowGraphLabelContents>
  57. <HeaderDivider />
  58. </NarrowGraphLabel>
  59. ) : (
  60. <GraphHeaderWrapper isSavedSearchesOpen={isSavedSearchesOpen}>
  61. <GraphHeader>
  62. <StyledToolbarHeader>{t('Graph:')}</StyledToolbarHeader>
  63. {selection.datetime.period !== '24h' && (
  64. <GraphToggle
  65. active={statsPeriod === '24h'}
  66. onClick={() => onSelectStatsPeriod('24h')}
  67. >
  68. {t('24h')}
  69. </GraphToggle>
  70. )}
  71. <GraphToggle
  72. active={statsPeriod === 'auto'}
  73. onClick={() => onSelectStatsPeriod('auto')}
  74. >
  75. {selection.datetime.period || t('Custom')}
  76. </GraphToggle>
  77. </GraphHeader>
  78. </GraphHeaderWrapper>
  79. )}
  80. {organization.features.includes('issue-stream-table-layout') ? (
  81. <Fragment>
  82. <TimestampLabel breakpoint={COLUMN_BREAKPOINTS.AGE}>
  83. {t('First Seen')}
  84. <HeaderDivider />
  85. </TimestampLabel>
  86. <TimestampLabel breakpoint={COLUMN_BREAKPOINTS.SEEN}>
  87. {t('Last Seen')}
  88. <HeaderDivider />
  89. </TimestampLabel>
  90. </Fragment>
  91. ) : null}
  92. {organization.features.includes('issue-stream-table-layout') ? (
  93. <Fragment>
  94. <NarrowEventsOrUsersLabel breakpoint={COLUMN_BREAKPOINTS.EVENTS}>
  95. {t('Events')}
  96. <HeaderDivider />
  97. </NarrowEventsOrUsersLabel>
  98. <NarrowEventsOrUsersLabel breakpoint={COLUMN_BREAKPOINTS.USERS}>
  99. {t('Users')}
  100. <HeaderDivider />
  101. </NarrowEventsOrUsersLabel>
  102. <NarrowPriorityLabel breakpoint={COLUMN_BREAKPOINTS.PRIORITY}>
  103. {t('Priority')}
  104. <HeaderDivider />
  105. </NarrowPriorityLabel>
  106. <NarrowAssigneeLabel breakpoint={COLUMN_BREAKPOINTS.ASSIGNEE}>
  107. {t('Assignee')}
  108. </NarrowAssigneeLabel>
  109. </Fragment>
  110. ) : (
  111. <Fragment>
  112. <EventsOrUsersLabel>{t('Events')}</EventsOrUsersLabel>
  113. <EventsOrUsersLabel>{t('Users')}</EventsOrUsersLabel>
  114. <PriorityLabel isSavedSearchesOpen={isSavedSearchesOpen}>
  115. <ToolbarHeader>{t('Priority')}</ToolbarHeader>
  116. </PriorityLabel>
  117. <AssigneeLabel isSavedSearchesOpen={isSavedSearchesOpen}>
  118. <ToolbarHeader>{t('Assignee')}</ToolbarHeader>
  119. </AssigneeLabel>
  120. </Fragment>
  121. )}
  122. </Fragment>
  123. )}
  124. </Fragment>
  125. );
  126. }
  127. export default Headers;
  128. const GraphHeaderWrapper = styled('div')<{isSavedSearchesOpen?: boolean}>`
  129. width: 200px;
  130. margin-right: ${space(2)};
  131. @media (max-width: ${p =>
  132. p.isSavedSearchesOpen ? p.theme.breakpoints.xlarge : p.theme.breakpoints.large}) {
  133. display: none;
  134. }
  135. `;
  136. const NarrowGraphLabel = styled(IssueStreamHeaderLabel)`
  137. width: 175px;
  138. flex: 1;
  139. display: flex;
  140. justify-content: space-between;
  141. `;
  142. const NarrowGraphLabelContents = styled('div')`
  143. display: flex;
  144. flex: 1;
  145. justify-content: space-between;
  146. `;
  147. const NarrowGraphToggles = styled('div')`
  148. font-weight: ${p => p.theme.fontWeightNormal};
  149. margin-right: ${space(2)};
  150. `;
  151. const GraphHeader = styled('div')`
  152. display: flex;
  153. margin-right: ${space(1.5)};
  154. `;
  155. const StyledToolbarHeader = styled(ToolbarHeader)`
  156. flex: 1;
  157. `;
  158. const GraphToggle = styled('a')<{active: boolean}>`
  159. font-size: 13px;
  160. padding-left: ${space(1)};
  161. &,
  162. &:hover,
  163. &:focus,
  164. &:active {
  165. color: ${p => (p.active ? p.theme.textColor : p.theme.disabled)};
  166. }
  167. `;
  168. const TimestampLabel = styled(IssueStreamHeaderLabel)`
  169. width: 75px;
  170. display: flex;
  171. flex-direction: row;
  172. justify-content: space-between;
  173. text-align: left;
  174. @media (max-width: ${p => p.theme.breakpoints.xlarge}) {
  175. display: none;
  176. }
  177. `;
  178. const EventsOrUsersLabel = styled(ToolbarHeader)`
  179. display: inline-grid;
  180. align-items: center;
  181. justify-content: flex-end;
  182. text-align: right;
  183. width: 60px;
  184. margin: 0 ${space(2)};
  185. @media (min-width: ${p => p.theme.breakpoints.xlarge}) {
  186. width: 80px;
  187. }
  188. `;
  189. const NarrowEventsOrUsersLabel = styled(IssueStreamHeaderLabel)`
  190. display: flex;
  191. justify-content: space-between;
  192. width: 60px;
  193. @media (max-width: ${p => p.theme.breakpoints.medium}) {
  194. display: none;
  195. }
  196. `;
  197. const PriorityLabel = styled(ToolbarHeader)<{isSavedSearchesOpen?: boolean}>`
  198. justify-content: flex-end;
  199. text-align: right;
  200. width: 70px;
  201. margin: 0 ${space(2)};
  202. @media (max-width: ${p =>
  203. p.isSavedSearchesOpen ? p.theme.breakpoints.large : p.theme.breakpoints.medium}) {
  204. display: none;
  205. }
  206. `;
  207. const NarrowPriorityLabel = styled(IssueStreamHeaderLabel)`
  208. display: flex;
  209. justify-content: space-between;
  210. width: 70px;
  211. @media (max-width: ${p => p.theme.breakpoints.large}) {
  212. display: none;
  213. }
  214. `;
  215. const AssigneeLabel = styled(ToolbarHeader)<{isSavedSearchesOpen?: boolean}>`
  216. justify-content: flex-end;
  217. text-align: right;
  218. width: 60px;
  219. margin-left: ${space(2)};
  220. margin-right: ${space(2)};
  221. @media (max-width: ${p =>
  222. p.isSavedSearchesOpen ? p.theme.breakpoints.large : p.theme.breakpoints.medium}) {
  223. display: none;
  224. }
  225. `;
  226. export const NarrowAssigneeLabel = styled(IssueStreamHeaderLabel)`
  227. justify-content: flex-end;
  228. text-align: right;
  229. width: 60px;
  230. `;
  231. // Reprocessing
  232. const StartedColumn = styled(ToolbarHeader)`
  233. margin: 0 ${space(2)};
  234. ${p => p.theme.overflowEllipsis};
  235. width: 85px;
  236. @media (min-width: ${p => p.theme.breakpoints.small}) {
  237. width: 140px;
  238. }
  239. `;
  240. const EventsReprocessedColumn = styled(ToolbarHeader)`
  241. margin: 0 ${space(2)};
  242. ${p => p.theme.overflowEllipsis};
  243. width: 75px;
  244. @media (min-width: ${p => p.theme.breakpoints.small}) {
  245. width: 140px;
  246. }
  247. `;
  248. const ProgressColumn = styled(ToolbarHeader)`
  249. margin: 0 ${space(2)};
  250. display: none;
  251. @media (min-width: ${p => p.theme.breakpoints.small}) {
  252. display: block;
  253. width: 160px;
  254. }
  255. `;