Browse Source

feat(feedback): Bootstrap empty Feedback v2 list and details pages (#55817)

There's a new sidebar item, and two new pages, both similar but empty.
| List | Details |
| --- | --- |
|
![SCR-20230906-nrrf](https://github.com/getsentry/sentry/assets/187460/351c3b8a-8821-4290-a9e8-2ce38d64aaf1)
|
![SCR-20230906-nrsq](https://github.com/getsentry/sentry/assets/187460/ef690dfb-0310-4e6b-9019-3a07d7fbe998)
|


![SCR-20230906-nrnu](https://github.com/getsentry/sentry/assets/187460/1a80c46b-3781-408a-abc6-062303f070a1)


See also https://github.com/getsentry/sentry/pull/55819

Relates to https://github.com/getsentry/sentry/issues/55809
Ryan Albrecht 1 year ago
parent
commit
33133ab3d1

+ 15 - 0
static/app/components/sidebar/index.tsx

@@ -27,6 +27,7 @@ import {
   IconSupport,
   IconTelescope,
   IconTimer,
+  IconUser,
 } from 'sentry/icons';
 import {t} from 'sentry/locale';
 import ConfigStore from 'sentry/stores/configStore';
@@ -277,6 +278,19 @@ function Sidebar({location, organization}: Props) {
     />
   );
 
+  const feedback = hasOrganization && (
+    <Feature features={['user-feedback-ui']} organization={organization}>
+      <SidebarItem
+        {...sidebarItemProps}
+        icon={<IconUser />}
+        label={t('Feedback')}
+        to={`/organizations/${organization.slug}/feedback/`}
+        id="feedback"
+        isAlpha
+      />
+    </Feature>
+  );
+
   const alerts = hasOrganization && (
     <SidebarItem
       {...sidebarItemProps}
@@ -408,6 +422,7 @@ function Sidebar({location, organization}: Props) {
                 {dashboards}
                 {releases}
                 {userFeedback}
+                {feedback}
               </SidebarSection>
 
               <SidebarSection>

+ 33 - 0
static/app/routes.tsx

@@ -1775,6 +1775,38 @@ function buildRoutes() {
     </Fragment>
   );
 
+  const feedbackChildRoutes = (
+    <Fragment>
+      <IndexRoute component={make(() => import('sentry/views/feedback/list'))} />
+      <Route
+        path=":feedbackId/"
+        component={make(() => import('sentry/views/feedback/details'))}
+      />
+    </Fragment>
+  );
+  const feedbackv2Routes = (
+    <Fragment>
+      {usingCustomerDomain && (
+        <Route
+          path="/feedback/"
+          component={withDomainRequired(
+            make(() => import('sentry/views/feedback/index'))
+          )}
+          key="orgless-feedback-list-route"
+        >
+          {feedbackChildRoutes}
+        </Route>
+      )}
+      <Route
+        path="/organizations/:orgId/feedback/"
+        component={withDomainRedirect(make(() => import('sentry/views/feedback/index')))}
+        key="org-feedback-list-route"
+      >
+        {feedbackChildRoutes}
+      </Route>
+    </Fragment>
+  );
+
   const issueListRoutes = (
     <Fragment>
       {usingCustomerDomain && (
@@ -2162,6 +2194,7 @@ function buildRoutes() {
       {projectsRoutes}
       {dashboardRoutes}
       {userFeedbackRoutes}
+      {feedbackv2Routes}
       {issueListRoutes}
       {issueDetailsRoutes}
       {alertRoutes}

+ 33 - 0
static/app/views/feedback/details.tsx

@@ -0,0 +1,33 @@
+import * as Layout from 'sentry/components/layouts/thirds';
+import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
+import {PageHeadingQuestionTooltip} from 'sentry/components/pageHeadingQuestionTooltip';
+import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
+import {t} from 'sentry/locale';
+import useOrganization from 'sentry/utils/useOrganization';
+
+export default function Details() {
+  const organization = useOrganization();
+
+  return (
+    <SentryDocumentTitle title={`Feedback v2 — ${organization.slug}`}>
+      <Layout.Header>
+        <Layout.HeaderContent>
+          <Layout.Title>
+            {t('Feedback v2')}
+            <PageHeadingQuestionTooltip
+              title={t(
+                'Feedback submitted by users who experienced an error while using your application, including their name, email address, and any additional comments.'
+              )}
+              docsUrl="https://docs.sentry.io/product/user-feedback/"
+            />
+          </Layout.Title>
+        </Layout.HeaderContent>
+      </Layout.Header>
+      <PageFiltersContainer>
+        <Layout.Body>
+          <Layout.Main fullWidth>TODO details page</Layout.Main>
+        </Layout.Body>
+      </PageFiltersContainer>
+    </SentryDocumentTitle>
+  );
+}

+ 34 - 0
static/app/views/feedback/index.tsx

@@ -0,0 +1,34 @@
+import {RouteComponentProps} from 'react-router';
+
+import Feature from 'sentry/components/acl/feature';
+import Alert from 'sentry/components/alert';
+import * as Layout from 'sentry/components/layouts/thirds';
+import NoProjectMessage from 'sentry/components/noProjectMessage';
+import {t} from 'sentry/locale';
+import useOrganization from 'sentry/utils/useOrganization';
+
+type Props = RouteComponentProps<{}, {}> & {
+  children: React.ReactNode;
+};
+
+export default function FeedbackContainer({children}: Props) {
+  const organization = useOrganization();
+
+  return (
+    <Feature
+      features={['user-feedback-ui']}
+      organization={organization}
+      renderDisabled={NoAccess}
+    >
+      <NoProjectMessage organization={organization}>{children}</NoProjectMessage>
+    </Feature>
+  );
+}
+
+function NoAccess() {
+  return (
+    <Layout.Page withPadding>
+      <Alert type="warning">{t("You don't have access to this feature")}</Alert>
+    </Layout.Page>
+  );
+}

+ 33 - 0
static/app/views/feedback/list.tsx

@@ -0,0 +1,33 @@
+import * as Layout from 'sentry/components/layouts/thirds';
+import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
+import {PageHeadingQuestionTooltip} from 'sentry/components/pageHeadingQuestionTooltip';
+import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
+import {t} from 'sentry/locale';
+import useOrganization from 'sentry/utils/useOrganization';
+
+export default function List() {
+  const organization = useOrganization();
+
+  return (
+    <SentryDocumentTitle title={`Feedback v2 — ${organization.slug}`}>
+      <Layout.Header>
+        <Layout.HeaderContent>
+          <Layout.Title>
+            {t('Feedback v2')}
+            <PageHeadingQuestionTooltip
+              title={t(
+                'Feedback submitted by users who experienced an error while using your application, including their name, email address, and any additional comments.'
+              )}
+              docsUrl="https://docs.sentry.io/product/user-feedback/"
+            />
+          </Layout.Title>
+        </Layout.HeaderContent>
+      </Layout.Header>
+      <PageFiltersContainer>
+        <Layout.Body>
+          <Layout.Main fullWidth>TODO List page</Layout.Main>
+        </Layout.Body>
+      </PageFiltersContainer>
+    </SentryDocumentTitle>
+  );
+}

+ 4 - 6
static/app/views/replays/index.tsx

@@ -1,16 +1,14 @@
 import {RouteComponentProps} from 'react-router';
 
 import NoProjectMessage from 'sentry/components/noProjectMessage';
-import {Organization} from 'sentry/types';
-import withOrganization from 'sentry/utils/withOrganization';
+import useOrganization from 'sentry/utils/useOrganization';
 
 type Props = RouteComponentProps<{}, {}> & {
   children: React.ReactNode;
-  organization: Organization;
 };
 
-function ReplaysContainer({organization, children}: Props) {
+export default function ReplaysContainer({children}: Props) {
+  const organization = useOrganization();
+
   return <NoProjectMessage organization={organization}>{children}</NoProjectMessage>;
 }
-
-export default withOrganization(ReplaysContainer);