diff --git a/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx b/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx
index c7a3977d9b..53e9a4aad1 100644
--- a/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx
+++ b/frontend/src/component/executiveDashboard/FlagsProjectChart/FlagsProjectChart.tsx
@@ -1,12 +1,7 @@
-import { useMemo, type VFC } from 'react';
+import { type VFC } from 'react';
import 'chartjs-adapter-date-fns';
-import { useTheme } from '@mui/material';
-import {
- ExecutiveSummarySchema,
- ExecutiveSummarySchemaProjectFlagTrendsItem,
-} from 'openapi';
+import { ExecutiveSummarySchema } from 'openapi';
import { LineChart } from '../LineChart/LineChart';
-import { getRandomColor } from '../executive-dashboard-utils';
import { useProjectChartData } from '../useProjectChartData';
interface IFlagsProjectChartProps {
diff --git a/frontend/src/component/executiveDashboard/ProjectHealthChart/ProjectHealthChart.tsx b/frontend/src/component/executiveDashboard/ProjectHealthChart/ProjectHealthChart.tsx
index 08acf7f210..b54989a185 100644
--- a/frontend/src/component/executiveDashboard/ProjectHealthChart/ProjectHealthChart.tsx
+++ b/frontend/src/component/executiveDashboard/ProjectHealthChart/ProjectHealthChart.tsx
@@ -1,12 +1,7 @@
-import { useMemo, type VFC } from 'react';
+import { type VFC } from 'react';
import 'chartjs-adapter-date-fns';
-import { useTheme } from '@mui/material';
-import {
- ExecutiveSummarySchema,
- ExecutiveSummarySchemaProjectFlagTrendsItem,
-} from 'openapi';
+import { ExecutiveSummarySchema } from 'openapi';
import { LineChart } from '../LineChart/LineChart';
-import { getRandomColor } from '../executive-dashboard-utils';
import { useProjectChartData } from '../useProjectChartData';
interface IFlagsProjectChartProps {
diff --git a/frontend/src/component/executiveDashboard/TimeToProductionChart/TimeToProductionChart.tsx b/frontend/src/component/executiveDashboard/TimeToProductionChart/TimeToProductionChart.tsx
index f1f33be090..5c3ec80cff 100644
--- a/frontend/src/component/executiveDashboard/TimeToProductionChart/TimeToProductionChart.tsx
+++ b/frontend/src/component/executiveDashboard/TimeToProductionChart/TimeToProductionChart.tsx
@@ -1,12 +1,7 @@
-import { useMemo, type VFC } from 'react';
+import { type VFC } from 'react';
import 'chartjs-adapter-date-fns';
-import { useTheme } from '@mui/material';
-import {
- ExecutiveSummarySchema,
- ExecutiveSummarySchemaProjectFlagTrendsItem,
-} from 'openapi';
+import { ExecutiveSummarySchema } from 'openapi';
import { LineChart } from '../LineChart/LineChart';
-import { getRandomColor } from '../executive-dashboard-utils';
import { useProjectChartData } from '../useProjectChartData';
interface IFlagsProjectChartProps {
diff --git a/frontend/src/component/feedbackNew/FeedbackList.tsx b/frontend/src/component/feedbackNew/FeedbackList.tsx
new file mode 100644
index 0000000000..94315566d5
--- /dev/null
+++ b/frontend/src/component/feedbackNew/FeedbackList.tsx
@@ -0,0 +1,161 @@
+import useFeedbackPosted from 'hooks/api/getters/useFeedbackPosted/useFeedbackPosted';
+import { VirtualizedTable } from 'component/common/Table';
+import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
+import { useFlexLayout, useSortBy, useTable } from 'react-table';
+import { sortTypes } from 'utils/sortTypes';
+import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
+import { PageContent } from 'component/common/PageContent/PageContent';
+import { PageHeader } from 'component/common/PageHeader/PageHeader';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { Search } from 'component/common/Search/Search';
+import { useMediaQuery } from '@mui/material';
+import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
+import { useSearch } from 'hooks/useSearch';
+import theme from 'themes/theme';
+import { useState } from 'react';
+import { FeedbackSchema } from '../../openapi';
+
+interface IFeedbackSchemaCellProps {
+ value?: string | null; // FIXME: proper type
+ row: { original: FeedbackSchema };
+}
+
+export const FeedbackList = () => {
+ const { feedback } = useFeedbackPosted();
+
+ const [searchValue, setSearchValue] = useState('');
+
+ const columns = [
+ {
+ Header: 'Category',
+ accessor: 'category',
+ Cell: ({
+ row: { original: feedback },
+ }: IFeedbackSchemaCellProps) => (
+ {feedback.category}
+ ),
+ searchable: true,
+ },
+ {
+ Header: 'UserType',
+ accessor: 'userType',
+ Cell: ({
+ row: { original: feedback },
+ }: IFeedbackSchemaCellProps) => (
+ {feedback.userType}
+ ),
+ searchable: true,
+ },
+ {
+ Header: 'DifficultyScore',
+ accessor: 'difficultyScore',
+ Cell: ({
+ row: { original: feedback },
+ }: IFeedbackSchemaCellProps) => (
+ {feedback.difficultyScore}
+ ),
+ },
+ {
+ Header: 'Positive',
+ accessor: 'positive',
+ minWidth: 100,
+ Cell: ({
+ row: { original: feedback },
+ }: IFeedbackSchemaCellProps) => (
+ {feedback.positive}
+ ),
+ disableSortBy: true,
+ searchable: true,
+ },
+ {
+ Header: 'Areas for improvement',
+ accessor: 'areasForImprovement',
+ minWidth: 100,
+ Cell: ({
+ row: { original: feedback },
+ }: IFeedbackSchemaCellProps) => (
+ {feedback.areasForImprovement}
+ ),
+ disableSortBy: true,
+ searchable: true,
+ },
+ {
+ Header: 'Created at',
+ accessor: 'createdAt',
+ Cell: DateCell,
+ },
+ ];
+
+ const { data, getSearchText } = useSearch(columns, searchValue, feedback);
+
+ const { headerGroups, rows, prepareRow } = useTable(
+ {
+ columns: columns as any,
+ data,
+ initialState: {
+ sortBy: [
+ {
+ id: 'createdAt',
+ desc: true,
+ },
+ ],
+ },
+ sortTypes,
+ autoResetHiddenColumns: false,
+ autoResetSortBy: false,
+ disableSortRemove: true,
+ disableMultiSort: true,
+ defaultColumn: {
+ Cell: TextCell,
+ },
+ },
+ useSortBy,
+ useFlexLayout,
+ );
+
+ const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
+
+ return (
+
+
+
+
+ >
+ }
+ />
+ >
+ }
+ >
+
+ }
+ />
+
+ }
+ >
+
+
+
+
+ );
+};
diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
index 6110b83b6a..434e846304 100644
--- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
+++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
@@ -254,6 +254,14 @@ exports[`returns all baseRoutes 1`] = `
"title": "Environments",
"type": "protected",
},
+ {
+ "component": [Function],
+ "flag": "featureSearchFeedbackPosting",
+ "menu": {},
+ "path": "/feedback",
+ "title": "Feedback",
+ "type": "protected",
+ },
{
"component": [Function],
"menu": {},
diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts
index 3315f8aaaf..2b4a6e2792 100644
--- a/frontend/src/component/menu/routes.ts
+++ b/frontend/src/component/menu/routes.ts
@@ -46,6 +46,7 @@ import { ViewIntegration } from 'component/integrations/ViewIntegration/ViewInte
import { ApplicationList } from '../application/ApplicationList/ApplicationList';
import { AddonRedirect } from 'component/integrations/AddonRedirect/AddonRedirect';
import { ExecutiveDashboard } from 'component/executiveDashboard/ExecutiveDashboard';
+import { FeedbackList } from '../feedbackNew/FeedbackList';
export const routes: IRoute[] = [
// Splash
@@ -266,6 +267,14 @@ export const routes: IRoute[] = [
flag: EEA,
menu: { mobile: true, advanced: true },
},
+ {
+ path: '/feedback',
+ title: 'Feedback',
+ component: FeedbackList,
+ type: 'protected',
+ flag: 'featureSearchFeedbackPosting',
+ menu: {},
+ },
// Tags
{
diff --git a/frontend/src/hooks/api/getters/useFeedbackPosted/useFeedbackPosted.ts b/frontend/src/hooks/api/getters/useFeedbackPosted/useFeedbackPosted.ts
new file mode 100644
index 0000000000..1362fd67be
--- /dev/null
+++ b/frontend/src/hooks/api/getters/useFeedbackPosted/useFeedbackPosted.ts
@@ -0,0 +1,38 @@
+import useSWR, { mutate, SWRConfiguration } from 'swr';
+import { useState, useEffect } from 'react';
+import { formatApiPath } from 'utils/formatPath';
+import handleErrorResponses from '../httpErrorResponseHandler';
+import { FeedbackListSchema } from '../../../../openapi';
+
+const KEY = `api/admin/feedback`;
+const path = formatApiPath(KEY);
+
+const useFeedbackPosted = (options: SWRConfiguration = {}) => {
+ const fetcher = () => {
+ return fetch(path, {
+ method: 'GET',
+ })
+ .then(handleErrorResponses('FeedbackPosted'))
+ .then((res) => res.json());
+ };
+
+ const { data, error } = useSWR(KEY, fetcher, options);
+ const [loading, setLoading] = useState(!error && !data);
+
+ const refetchFeedback = () => {
+ mutate(KEY);
+ };
+
+ useEffect(() => {
+ setLoading(!error && !data);
+ }, [data, error]);
+
+ return {
+ feedback: data || [],
+ error,
+ loading,
+ refetchFeedback,
+ };
+};
+
+export default useFeedbackPosted;
diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts
index 04cf9988de..4de5bd9201 100644
--- a/frontend/src/interfaces/uiConfig.ts
+++ b/frontend/src/interfaces/uiConfig.ts
@@ -79,6 +79,7 @@ export type UiFlags = {
feedbackComments?: Variant;
displayUpgradeEdgeBanner?: boolean;
showInactiveUsers?: boolean;
+ featureSearchFeedbackPosting?: boolean;
};
export interface IVersionInfo {
diff --git a/src/server-dev.ts b/src/server-dev.ts
index 7cedcb8278..309d0e50c5 100644
--- a/src/server-dev.ts
+++ b/src/server-dev.ts
@@ -46,6 +46,7 @@ process.nextTick(async () => {
celebrateUnleash: true,
increaseUnleashWidth: true,
newStrategyConfigurationFeedback: true,
+ featureSearchFeedbackPosting: true,
extendedUsageMetricsUI: true,
executiveDashboard: true,
},