diff --git a/static/app/components/feedback/feedbackSummary.tsx b/static/app/components/feedback/feedbackSummary.tsx
new file mode 100644
index 00000000000000..8cf3c23dcb88e5
--- /dev/null
+++ b/static/app/components/feedback/feedbackSummary.tsx
@@ -0,0 +1,64 @@
+import styled from '@emotion/styled';
+
+import useFeedbackSummary from 'sentry/components/feedback/list/useFeedbackSummary';
+import Placeholder from 'sentry/components/placeholder';
+import {IconSeer} from 'sentry/icons/iconSeer';
+import {t} from 'sentry/locale';
+import {space} from 'sentry/styles/space';
+import useOrganization from 'sentry/utils/useOrganization';
+
+export default function FeedbackSummary() {
+ const {isError, isPending, summary, tooFewFeedbacks} = useFeedbackSummary();
+
+ const organization = useOrganization();
+
+ if (
+ !organization.features.includes('user-feedback-ai-summaries') ||
+ tooFewFeedbacks ||
+ isError
+ ) {
+ return null;
+ }
+
+ if (isPending) {
+ return ;
+ }
+
+ return (
+
+
+
+ {t('Feedback Summary')}
+ {summary}
+
+
+ );
+}
+
+const SummaryContainer = styled('div')`
+ display: flex;
+ flex-direction: column;
+ gap: ${space(1)};
+ width: 100%;
+`;
+
+const SummaryHeader = styled('p')`
+ font-size: ${p => p.theme.fontSizeMedium};
+ font-weight: ${p => p.theme.fontWeightBold};
+ margin: 0;
+`;
+
+const SummaryContent = styled('p')`
+ font-size: ${p => p.theme.fontSizeSmall};
+ color: ${p => p.theme.subText};
+ margin: 0;
+`;
+
+const SummaryIconContainer = styled('div')`
+ display: flex;
+ gap: ${space(1)};
+ padding: ${space(2)};
+ border: 1px solid ${p => p.theme.border};
+ border-radius: ${p => p.theme.borderRadius};
+ align-items: baseline;
+`;
diff --git a/static/app/components/feedback/list/useFeedbackSummary.tsx b/static/app/components/feedback/list/useFeedbackSummary.tsx
new file mode 100644
index 00000000000000..e593e29b0cc93f
--- /dev/null
+++ b/static/app/components/feedback/list/useFeedbackSummary.tsx
@@ -0,0 +1,67 @@
+import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
+import {useApiQuery} from 'sentry/utils/queryClient';
+import useOrganization from 'sentry/utils/useOrganization';
+import usePageFilters from 'sentry/utils/usePageFilters';
+
+type FeedbackSummaryResponse = {
+ numFeedbacksUsed: number;
+ success: boolean;
+ summary: string | null;
+};
+
+export default function useFeedbackSummary(): {
+ isError: boolean;
+ isPending: boolean;
+ summary: string | null;
+ tooFewFeedbacks: boolean;
+} {
+ const organization = useOrganization();
+
+ const {selection} = usePageFilters();
+
+ const normalizedDateRange = normalizeDateTimeParams(selection.datetime);
+
+ const {data, isPending, isError} = useApiQuery(
+ [
+ `/organizations/${organization.slug}/feedback-summary/`,
+ {
+ query: {
+ ...normalizedDateRange,
+ project: selection.projects,
+ },
+ },
+ ],
+ {
+ staleTime: 5000,
+ enabled:
+ Boolean(normalizedDateRange) &&
+ organization.features.includes('user-feedback-ai-summaries'),
+ retry: 1,
+ }
+ );
+
+ if (isPending) {
+ return {
+ summary: null,
+ isPending: true,
+ isError: false,
+ tooFewFeedbacks: false,
+ };
+ }
+
+ if (isError) {
+ return {
+ summary: null,
+ isPending: false,
+ isError: true,
+ tooFewFeedbacks: false,
+ };
+ }
+
+ return {
+ summary: data.summary,
+ isPending: false,
+ isError: false,
+ tooFewFeedbacks: data.numFeedbacksUsed === 0 && !data.success,
+ };
+}
diff --git a/static/app/views/feedback/feedbackListPage.tsx b/static/app/views/feedback/feedbackListPage.tsx
index 2ca5c65d4dae1f..ca4f173b99c38f 100644
--- a/static/app/views/feedback/feedbackListPage.tsx
+++ b/static/app/views/feedback/feedbackListPage.tsx
@@ -7,6 +7,7 @@ import FeedbackItemLoader from 'sentry/components/feedback/feedbackItem/feedback
import FeedbackWidgetBanner from 'sentry/components/feedback/feedbackOnboarding/feedbackWidgetBanner';
import FeedbackSearch from 'sentry/components/feedback/feedbackSearch';
import FeedbackSetupPanel from 'sentry/components/feedback/feedbackSetupPanel';
+import FeedbackSummary from 'sentry/components/feedback/feedbackSummary';
import FeedbackWhatsNewBanner from 'sentry/components/feedback/feedbackWhatsNewBanner';
import FeedbackList from 'sentry/components/feedback/list/feedbackList';
import useCurrentFeedbackId from 'sentry/components/feedback/useCurrentFeedbackId';
@@ -84,9 +85,12 @@ export default function FeedbackListPage() {
{hasSetupOneFeedback || hasSlug ? (
-
-
-
+
+
+
+
+
+
@@ -118,6 +122,12 @@ const Background = styled('div')`
gap: ${space(2)};
`;
+const SummaryListContainer = styled('div')`
+ display: flex;
+ flex-direction: column;
+ gap: ${space(1)};
+`;
+
const LayoutGrid = styled('div')`
overflow: hidden;
flex-grow: 1;
@@ -153,7 +163,7 @@ const LayoutGrid = styled('div')`
}
@media (min-width: ${p => p.theme.breakpoints.medium}) {
- grid-template-columns: minmax(1fr, 195px) 1fr;
+ grid-template-columns: minmax(195px, 1fr) 1.5fr;
}
@media (min-width: ${p => p.theme.breakpoints.large}) {