feedbackListPage.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import {Fragment} from 'react';
  2. import type {RouteComponentProps} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import ErrorBoundary from 'sentry/components/errorBoundary';
  5. import FeedbackFilters from 'sentry/components/feedback/feedbackFilters';
  6. import FeedbackItemLoader from 'sentry/components/feedback/feedbackItem/feedbackItemLoader';
  7. import FeedbackSearch from 'sentry/components/feedback/feedbackSearch';
  8. import FeedbackSetupPanel from 'sentry/components/feedback/feedbackSetupPanel';
  9. import FeedbackWhatsNewBanner from 'sentry/components/feedback/feedbackWhatsNewBanner';
  10. import FeedbackList from 'sentry/components/feedback/list/feedbackList';
  11. import OldFeedbackButton from 'sentry/components/feedback/oldFeedbackButton';
  12. import useCurrentFeedbackId from 'sentry/components/feedback/useCurrentFeedbackId';
  13. import useHaveSelectedProjectsSetupFeedback, {
  14. useHaveSelectedProjectsSetupNewFeedback,
  15. } from 'sentry/components/feedback/useFeedbackOnboarding';
  16. import {FeedbackQueryKeys} from 'sentry/components/feedback/useFeedbackQueryKeys';
  17. import FullViewport from 'sentry/components/layouts/fullViewport';
  18. import * as Layout from 'sentry/components/layouts/thirds';
  19. import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
  20. import {PageHeadingQuestionTooltip} from 'sentry/components/pageHeadingQuestionTooltip';
  21. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  22. import {t} from 'sentry/locale';
  23. import {space} from 'sentry/styles/space';
  24. import useOrganization from 'sentry/utils/useOrganization';
  25. import FluidHeight from 'sentry/views/replays/detail/layout/fluidHeight';
  26. interface Props extends RouteComponentProps<{}, {}, {}> {}
  27. export default function FeedbackListPage({}: Props) {
  28. const organization = useOrganization();
  29. const {hasSetupOneFeedback} = useHaveSelectedProjectsSetupFeedback();
  30. const {hasSetupNewFeedback} = useHaveSelectedProjectsSetupNewFeedback();
  31. const showWhatsNewBanner = hasSetupOneFeedback && !hasSetupNewFeedback;
  32. const feedbackSlug = useCurrentFeedbackId();
  33. const hasSlug = Boolean(feedbackSlug);
  34. return (
  35. <SentryDocumentTitle title={t('User Feedback')} orgSlug={organization.slug}>
  36. <FullViewport>
  37. <FeedbackQueryKeys organization={organization}>
  38. <Layout.Header>
  39. <Layout.HeaderContent>
  40. <Layout.Title>
  41. {t('User Feedback')}
  42. <PageHeadingQuestionTooltip
  43. title={t(
  44. 'The User Feedback Widget allows users to submit feedback quickly and easily any time they encounter something that isn’t working as expected.'
  45. )}
  46. docsUrl="https://docs.sentry.io/product/user-feedback/"
  47. />
  48. </Layout.Title>
  49. </Layout.HeaderContent>
  50. <Layout.HeaderActions>
  51. <OldFeedbackButton />
  52. </Layout.HeaderActions>
  53. </Layout.Header>
  54. <PageFiltersContainer>
  55. <ErrorBoundary>
  56. <LayoutGrid data-banner={showWhatsNewBanner}>
  57. {showWhatsNewBanner ? (
  58. <FeedbackWhatsNewBanner style={{gridArea: 'banner'}} />
  59. ) : null}
  60. <FeedbackFilters style={{gridArea: 'filters'}} />
  61. {hasSetupOneFeedback || hasSlug ? (
  62. <Fragment>
  63. <Container style={{gridArea: 'list'}}>
  64. <FeedbackList />
  65. </Container>
  66. <FeedbackSearch style={{gridArea: 'search'}} />
  67. <Container style={{gridArea: 'details'}}>
  68. <FeedbackItemLoader />
  69. </Container>
  70. </Fragment>
  71. ) : (
  72. <SetupContainer>
  73. <FeedbackSetupPanel />
  74. </SetupContainer>
  75. )}
  76. </LayoutGrid>
  77. </ErrorBoundary>
  78. </PageFiltersContainer>
  79. </FeedbackQueryKeys>
  80. </FullViewport>
  81. </SentryDocumentTitle>
  82. );
  83. }
  84. const LayoutGrid = styled('div')`
  85. background: ${p => p.theme.background};
  86. overflow: hidden;
  87. flex-grow: 1;
  88. display: grid;
  89. gap: ${space(2)};
  90. place-items: stretch;
  91. grid-template-rows: max-content 1fr;
  92. grid-template-areas:
  93. 'filters search'
  94. 'list details';
  95. &[data-banner='true'] {
  96. grid-template-rows: max-content max-content 1fr;
  97. grid-template-areas:
  98. 'banner banner'
  99. 'filters search'
  100. 'list details';
  101. }
  102. @media (max-width: ${p => p.theme.breakpoints.medium}) {
  103. padding: ${space(2)};
  104. grid-template-columns: 1fr;
  105. grid-template-areas:
  106. 'filters'
  107. 'search'
  108. 'list'
  109. 'details';
  110. &[data-banner='true'] {
  111. grid-template-areas:
  112. 'banner'
  113. 'filters'
  114. 'search'
  115. 'list'
  116. 'details';
  117. }
  118. }
  119. @media (min-width: ${p => p.theme.breakpoints.medium}) {
  120. padding: ${space(2)};
  121. grid-template-columns: minmax(1fr, 195px) 1fr;
  122. }
  123. @media (min-width: ${p => p.theme.breakpoints.large}) {
  124. padding: ${space(2)} ${space(4)} ${space(2)} ${space(4)};
  125. grid-template-columns: 390px 1fr;
  126. }
  127. @media (min-width: ${p => p.theme.breakpoints.large}) {
  128. grid-template-columns: minmax(390px, 1fr) 2fr;
  129. }
  130. `;
  131. const Container = styled(FluidHeight)`
  132. border: 1px solid ${p => p.theme.border};
  133. border-radius: ${p => p.theme.borderRadius};
  134. `;
  135. const SetupContainer = styled('div')`
  136. overflow: hidden;
  137. grid-column: 1 / -1;
  138. `;