mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
feat: add posted feedback table (#6113)
The page can only be navigated if you know direct url `/feedback` ![image](https://github.com/Unleash/unleash/assets/964450/6018a6c0-9fee-4fb2-9b68-2d3e87674441)
This commit is contained in:
parent
1834f9f8bc
commit
0cf8396ec2
@ -1,12 +1,7 @@
|
|||||||
import { useMemo, type VFC } from 'react';
|
import { type VFC } from 'react';
|
||||||
import 'chartjs-adapter-date-fns';
|
import 'chartjs-adapter-date-fns';
|
||||||
import { useTheme } from '@mui/material';
|
import { ExecutiveSummarySchema } from 'openapi';
|
||||||
import {
|
|
||||||
ExecutiveSummarySchema,
|
|
||||||
ExecutiveSummarySchemaProjectFlagTrendsItem,
|
|
||||||
} from 'openapi';
|
|
||||||
import { LineChart } from '../LineChart/LineChart';
|
import { LineChart } from '../LineChart/LineChart';
|
||||||
import { getRandomColor } from '../executive-dashboard-utils';
|
|
||||||
import { useProjectChartData } from '../useProjectChartData';
|
import { useProjectChartData } from '../useProjectChartData';
|
||||||
|
|
||||||
interface IFlagsProjectChartProps {
|
interface IFlagsProjectChartProps {
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
import { useMemo, type VFC } from 'react';
|
import { type VFC } from 'react';
|
||||||
import 'chartjs-adapter-date-fns';
|
import 'chartjs-adapter-date-fns';
|
||||||
import { useTheme } from '@mui/material';
|
import { ExecutiveSummarySchema } from 'openapi';
|
||||||
import {
|
|
||||||
ExecutiveSummarySchema,
|
|
||||||
ExecutiveSummarySchemaProjectFlagTrendsItem,
|
|
||||||
} from 'openapi';
|
|
||||||
import { LineChart } from '../LineChart/LineChart';
|
import { LineChart } from '../LineChart/LineChart';
|
||||||
import { getRandomColor } from '../executive-dashboard-utils';
|
|
||||||
import { useProjectChartData } from '../useProjectChartData';
|
import { useProjectChartData } from '../useProjectChartData';
|
||||||
|
|
||||||
interface IFlagsProjectChartProps {
|
interface IFlagsProjectChartProps {
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
import { useMemo, type VFC } from 'react';
|
import { type VFC } from 'react';
|
||||||
import 'chartjs-adapter-date-fns';
|
import 'chartjs-adapter-date-fns';
|
||||||
import { useTheme } from '@mui/material';
|
import { ExecutiveSummarySchema } from 'openapi';
|
||||||
import {
|
|
||||||
ExecutiveSummarySchema,
|
|
||||||
ExecutiveSummarySchemaProjectFlagTrendsItem,
|
|
||||||
} from 'openapi';
|
|
||||||
import { LineChart } from '../LineChart/LineChart';
|
import { LineChart } from '../LineChart/LineChart';
|
||||||
import { getRandomColor } from '../executive-dashboard-utils';
|
|
||||||
import { useProjectChartData } from '../useProjectChartData';
|
import { useProjectChartData } from '../useProjectChartData';
|
||||||
|
|
||||||
interface IFlagsProjectChartProps {
|
interface IFlagsProjectChartProps {
|
||||||
|
161
frontend/src/component/feedbackNew/FeedbackList.tsx
Normal file
161
frontend/src/component/feedbackNew/FeedbackList.tsx
Normal file
@ -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) => (
|
||||||
|
<TextCell>{feedback.category}</TextCell>
|
||||||
|
),
|
||||||
|
searchable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'UserType',
|
||||||
|
accessor: 'userType',
|
||||||
|
Cell: ({
|
||||||
|
row: { original: feedback },
|
||||||
|
}: IFeedbackSchemaCellProps) => (
|
||||||
|
<TextCell>{feedback.userType}</TextCell>
|
||||||
|
),
|
||||||
|
searchable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'DifficultyScore',
|
||||||
|
accessor: 'difficultyScore',
|
||||||
|
Cell: ({
|
||||||
|
row: { original: feedback },
|
||||||
|
}: IFeedbackSchemaCellProps) => (
|
||||||
|
<TextCell>{feedback.difficultyScore}</TextCell>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Positive',
|
||||||
|
accessor: 'positive',
|
||||||
|
minWidth: 100,
|
||||||
|
Cell: ({
|
||||||
|
row: { original: feedback },
|
||||||
|
}: IFeedbackSchemaCellProps) => (
|
||||||
|
<TextCell>{feedback.positive}</TextCell>
|
||||||
|
),
|
||||||
|
disableSortBy: true,
|
||||||
|
searchable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Areas for improvement',
|
||||||
|
accessor: 'areasForImprovement',
|
||||||
|
minWidth: 100,
|
||||||
|
Cell: ({
|
||||||
|
row: { original: feedback },
|
||||||
|
}: IFeedbackSchemaCellProps) => (
|
||||||
|
<TextCell>{feedback.areasForImprovement}</TextCell>
|
||||||
|
),
|
||||||
|
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 (
|
||||||
|
<PageContent
|
||||||
|
header={
|
||||||
|
<PageHeader
|
||||||
|
title={`Feedbacks posted (${rows.length})`}
|
||||||
|
actions={
|
||||||
|
<>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={!isSmallScreen}
|
||||||
|
show={
|
||||||
|
<>
|
||||||
|
<Search
|
||||||
|
initialValue={searchValue}
|
||||||
|
onChange={setSearchValue}
|
||||||
|
/>
|
||||||
|
<PageHeader.Divider />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={isSmallScreen}
|
||||||
|
show={
|
||||||
|
<Search
|
||||||
|
initialValue={searchValue}
|
||||||
|
onChange={setSearchValue}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</PageHeader>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SearchHighlightProvider value={getSearchText(searchValue)}>
|
||||||
|
<VirtualizedTable
|
||||||
|
rows={rows}
|
||||||
|
headerGroups={headerGroups}
|
||||||
|
prepareRow={prepareRow}
|
||||||
|
/>
|
||||||
|
</SearchHighlightProvider>
|
||||||
|
</PageContent>
|
||||||
|
);
|
||||||
|
};
|
@ -254,6 +254,14 @@ exports[`returns all baseRoutes 1`] = `
|
|||||||
"title": "Environments",
|
"title": "Environments",
|
||||||
"type": "protected",
|
"type": "protected",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"component": [Function],
|
||||||
|
"flag": "featureSearchFeedbackPosting",
|
||||||
|
"menu": {},
|
||||||
|
"path": "/feedback",
|
||||||
|
"title": "Feedback",
|
||||||
|
"type": "protected",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"component": [Function],
|
"component": [Function],
|
||||||
"menu": {},
|
"menu": {},
|
||||||
|
@ -46,6 +46,7 @@ import { ViewIntegration } from 'component/integrations/ViewIntegration/ViewInte
|
|||||||
import { ApplicationList } from '../application/ApplicationList/ApplicationList';
|
import { ApplicationList } from '../application/ApplicationList/ApplicationList';
|
||||||
import { AddonRedirect } from 'component/integrations/AddonRedirect/AddonRedirect';
|
import { AddonRedirect } from 'component/integrations/AddonRedirect/AddonRedirect';
|
||||||
import { ExecutiveDashboard } from 'component/executiveDashboard/ExecutiveDashboard';
|
import { ExecutiveDashboard } from 'component/executiveDashboard/ExecutiveDashboard';
|
||||||
|
import { FeedbackList } from '../feedbackNew/FeedbackList';
|
||||||
|
|
||||||
export const routes: IRoute[] = [
|
export const routes: IRoute[] = [
|
||||||
// Splash
|
// Splash
|
||||||
@ -266,6 +267,14 @@ export const routes: IRoute[] = [
|
|||||||
flag: EEA,
|
flag: EEA,
|
||||||
menu: { mobile: true, advanced: true },
|
menu: { mobile: true, advanced: true },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/feedback',
|
||||||
|
title: 'Feedback',
|
||||||
|
component: FeedbackList,
|
||||||
|
type: 'protected',
|
||||||
|
flag: 'featureSearchFeedbackPosting',
|
||||||
|
menu: {},
|
||||||
|
},
|
||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
{
|
{
|
||||||
|
@ -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<FeedbackListSchema>(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;
|
@ -79,6 +79,7 @@ export type UiFlags = {
|
|||||||
feedbackComments?: Variant;
|
feedbackComments?: Variant;
|
||||||
displayUpgradeEdgeBanner?: boolean;
|
displayUpgradeEdgeBanner?: boolean;
|
||||||
showInactiveUsers?: boolean;
|
showInactiveUsers?: boolean;
|
||||||
|
featureSearchFeedbackPosting?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IVersionInfo {
|
export interface IVersionInfo {
|
||||||
|
@ -46,6 +46,7 @@ process.nextTick(async () => {
|
|||||||
celebrateUnleash: true,
|
celebrateUnleash: true,
|
||||||
increaseUnleashWidth: true,
|
increaseUnleashWidth: true,
|
||||||
newStrategyConfigurationFeedback: true,
|
newStrategyConfigurationFeedback: true,
|
||||||
|
featureSearchFeedbackPosting: true,
|
||||||
extendedUsageMetricsUI: true,
|
extendedUsageMetricsUI: true,
|
||||||
executiveDashboard: true,
|
executiveDashboard: true,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user