mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-09 01:17:06 +02:00
Favorite features on project (#2580)
This commit is contained in:
parent
0a3823e188
commit
ef6ec4a83b
@ -22,8 +22,8 @@ import { Search } from 'component/common/Search/Search';
|
|||||||
import { FeatureTagCell } from 'component/common/Table/cells/FeatureTagCell/FeatureTagCell';
|
import { FeatureTagCell } from 'component/common/Table/cells/FeatureTagCell/FeatureTagCell';
|
||||||
import { usePinnedFavorites } from 'hooks/usePinnedFavorites';
|
import { usePinnedFavorites } from 'hooks/usePinnedFavorites';
|
||||||
import { useFavoriteFeaturesApi } from 'hooks/api/actions/useFavoriteFeaturesApi/useFavoriteFeaturesApi';
|
import { useFavoriteFeaturesApi } from 'hooks/api/actions/useFavoriteFeaturesApi/useFavoriteFeaturesApi';
|
||||||
import { FavoriteIconCell } from './FavoriteIconCell/FavoriteIconCell';
|
import { FavoriteIconCell } from 'component/common/Table/cells/FavoriteIconCell/FavoriteIconCell';
|
||||||
import { FavoriteIconHeader } from './FavoriteIconHeader/FavoriteIconHeader';
|
import { FavoriteIconHeader } from 'component/common/Table/FavoriteIconHeader/FavoriteIconHeader';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { usePlausibleTracker } from '../../../hooks/usePlausibleTracker';
|
import { usePlausibleTracker } from '../../../hooks/usePlausibleTracker';
|
||||||
|
|
||||||
|
@ -38,6 +38,10 @@ interface IColumnsMenuProps {
|
|||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const columnNameMap: Record<string, string> = {
|
||||||
|
favorite: 'Favorite',
|
||||||
|
};
|
||||||
|
|
||||||
export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
|
export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
|
||||||
allColumns,
|
allColumns,
|
||||||
staticColumns = [],
|
staticColumns = [],
|
||||||
@ -183,7 +187,10 @@ export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
|
|||||||
show={() => (
|
show={() => (
|
||||||
<>{column.Header}</>
|
<>{column.Header}</>
|
||||||
)}
|
)}
|
||||||
elseShow={() => column.id}
|
elseShow={() =>
|
||||||
|
columnNameMap[column.id] ||
|
||||||
|
column.id
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTheme } from '@mui/system';
|
import { useMediaQuery, useTheme } from '@mui/material';
|
||||||
import { Add } from '@mui/icons-material';
|
import { Add } from '@mui/icons-material';
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
import { useFlexLayout, useSortBy, useTable, SortingRule } from 'react-table';
|
import { useFlexLayout, useSortBy, useTable, SortingRule } from 'react-table';
|
||||||
@ -15,7 +15,6 @@ import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
|
|||||||
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
||||||
import { FeatureSeenCell } from 'component/common/Table/cells/FeatureSeenCell/FeatureSeenCell';
|
import { FeatureSeenCell } from 'component/common/Table/cells/FeatureSeenCell/FeatureSeenCell';
|
||||||
import { FeatureTypeCell } from 'component/common/Table/cells/FeatureTypeCell/FeatureTypeCell';
|
import { FeatureTypeCell } from 'component/common/Table/cells/FeatureTypeCell/FeatureTypeCell';
|
||||||
import { sortTypes } from 'utils/sortTypes';
|
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { IProject } from 'interfaces/project';
|
import { IProject } from 'interfaces/project';
|
||||||
import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
|
import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
|
||||||
@ -25,23 +24,26 @@ import { createLocalStorage } from 'utils/createLocalStorage';
|
|||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { ENVIRONMENT_STRATEGY_ERROR } from 'constants/apiErrors';
|
import { ENVIRONMENT_STRATEGY_ERROR } from 'constants/apiErrors';
|
||||||
import EnvironmentStrategyDialog from 'component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog';
|
import EnvironmentStrategyDialog from 'component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog';
|
||||||
|
import { FeatureStaleDialog } from 'component/common/FeatureStaleDialog/FeatureStaleDialog';
|
||||||
|
import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog';
|
||||||
|
import { useSearch } from 'hooks/useSearch';
|
||||||
|
import { Search } from 'component/common/Search/Search';
|
||||||
|
import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle';
|
||||||
|
import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
|
||||||
|
import { UpdateEnabledMessage } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/UpdateEnabledMessage';
|
||||||
|
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
||||||
|
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
||||||
|
import { FeatureTagCell } from 'component/common/Table/cells/FeatureTagCell/FeatureTagCell';
|
||||||
|
import { FavoriteIconHeader } from 'component/common/Table/FavoriteIconHeader/FavoriteIconHeader';
|
||||||
|
import { FavoriteIconCell } from 'component/common/Table/cells/FavoriteIconCell/FavoriteIconCell';
|
||||||
import { useEnvironmentsRef } from './hooks/useEnvironmentsRef';
|
import { useEnvironmentsRef } from './hooks/useEnvironmentsRef';
|
||||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||||
import { FeatureToggleSwitch } from './FeatureToggleSwitch/FeatureToggleSwitch';
|
import { FeatureToggleSwitch } from './FeatureToggleSwitch/FeatureToggleSwitch';
|
||||||
import { ActionsCell } from './ActionsCell/ActionsCell';
|
import { ActionsCell } from './ActionsCell/ActionsCell';
|
||||||
import { ColumnsMenu } from './ColumnsMenu/ColumnsMenu';
|
import { ColumnsMenu } from './ColumnsMenu/ColumnsMenu';
|
||||||
import { useStyles } from './ProjectFeatureToggles.styles';
|
import { useStyles } from './ProjectFeatureToggles.styles';
|
||||||
import { FeatureStaleDialog } from 'component/common/FeatureStaleDialog/FeatureStaleDialog';
|
import { usePinnedFavorites } from 'hooks/usePinnedFavorites';
|
||||||
import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog';
|
import { useFavoriteFeaturesApi } from 'hooks/api/actions/useFavoriteFeaturesApi/useFavoriteFeaturesApi';
|
||||||
import { useSearch } from 'hooks/useSearch';
|
|
||||||
import { useMediaQuery } from '@mui/material';
|
|
||||||
import { Search } from 'component/common/Search/Search';
|
|
||||||
import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle';
|
|
||||||
import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
|
|
||||||
import { UpdateEnabledMessage } from '../../../changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/UpdateEnabledMessage';
|
|
||||||
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
|
||||||
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
|
||||||
import { FeatureTagCell } from 'component/common/Table/cells/FeatureTagCell/FeatureTagCell';
|
|
||||||
|
|
||||||
interface IProjectFeatureTogglesProps {
|
interface IProjectFeatureTogglesProps {
|
||||||
features: IProject['features'];
|
features: IProject['features'];
|
||||||
@ -51,7 +53,7 @@ interface IProjectFeatureTogglesProps {
|
|||||||
|
|
||||||
type ListItemType = Pick<
|
type ListItemType = Pick<
|
||||||
IProject['features'][number],
|
IProject['features'][number],
|
||||||
'name' | 'lastSeenAt' | 'createdAt' | 'type' | 'stale'
|
'name' | 'lastSeenAt' | 'createdAt' | 'type' | 'stale' | 'favorite'
|
||||||
> & {
|
> & {
|
||||||
environments: {
|
environments: {
|
||||||
[key in string]: {
|
[key in string]: {
|
||||||
@ -65,6 +67,7 @@ const staticColumns = ['Actions', 'name'];
|
|||||||
|
|
||||||
const defaultSort: SortingRule<string> & {
|
const defaultSort: SortingRule<string> & {
|
||||||
columns?: string[];
|
columns?: string[];
|
||||||
|
favorites?: boolean;
|
||||||
} = { id: 'createdAt' };
|
} = { id: 'createdAt' };
|
||||||
|
|
||||||
export const ProjectFeatureToggles = ({
|
export const ProjectFeatureToggles = ({
|
||||||
@ -103,9 +106,15 @@ export const ProjectFeatureToggles = ({
|
|||||||
);
|
);
|
||||||
const { refetch } = useProject(projectId);
|
const { refetch } = useProject(projectId);
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
|
const { isFavoritesPinned, sortTypes, onChangeIsFavoritePinned } =
|
||||||
|
usePinnedFavorites(
|
||||||
|
searchParams.has('favorites')
|
||||||
|
? searchParams.get('favorites') === 'true'
|
||||||
|
: storedParams.favorites
|
||||||
|
);
|
||||||
const { toggleFeatureEnvironmentOn, toggleFeatureEnvironmentOff } =
|
const { toggleFeatureEnvironmentOn, toggleFeatureEnvironmentOff } =
|
||||||
useFeatureApi();
|
useFeatureApi();
|
||||||
|
const { favorite, unfavorite } = useFavoriteFeaturesApi();
|
||||||
const {
|
const {
|
||||||
onChangeRequestToggle,
|
onChangeRequestToggle,
|
||||||
onChangeRequestToggleClose,
|
onChangeRequestToggleClose,
|
||||||
@ -167,8 +176,42 @@ export const ProjectFeatureToggles = ({
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onFavorite = useCallback(
|
||||||
|
async (feature: IFeatureToggleListItem) => {
|
||||||
|
if (feature?.favorite) {
|
||||||
|
await unfavorite(projectId, feature.name);
|
||||||
|
} else {
|
||||||
|
await favorite(projectId, feature.name);
|
||||||
|
}
|
||||||
|
refetch();
|
||||||
|
},
|
||||||
|
[projectId, refetch]
|
||||||
|
);
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
...(uiConfig?.flags?.favorites
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
id: 'favorite',
|
||||||
|
Header: (
|
||||||
|
<FavoriteIconHeader
|
||||||
|
isActive={isFavoritesPinned}
|
||||||
|
onClick={onChangeIsFavoritePinned}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
accessor: 'favorite',
|
||||||
|
Cell: ({ row: { original: feature } }: any) => (
|
||||||
|
<FavoriteIconCell
|
||||||
|
value={feature?.favorite}
|
||||||
|
onClick={() => onFavorite(feature)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
maxWidth: 50,
|
||||||
|
disableSortBy: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
Header: 'Seen',
|
Header: 'Seen',
|
||||||
accessor: 'lastSeenAt',
|
accessor: 'lastSeenAt',
|
||||||
@ -197,18 +240,20 @@ export const ProjectFeatureToggles = ({
|
|||||||
sortType: 'alphanumeric',
|
sortType: 'alphanumeric',
|
||||||
searchable: true,
|
searchable: true,
|
||||||
},
|
},
|
||||||
{
|
// FIXME: no tags on project feature toggles from backend
|
||||||
id: 'tags',
|
// {
|
||||||
Header: 'Tags',
|
// id: 'tags',
|
||||||
accessor: (row: IFeatureToggleListItem) =>
|
// Header: 'Tags',
|
||||||
row.tags
|
// accessor: (row: IFeatureToggleListItem) =>
|
||||||
?.map(({ type, value }) => `${type}:${value}`)
|
// row.tags
|
||||||
.join('\n') || '',
|
// ?.map(({ type, value }) => `${type}:${value}`)
|
||||||
Cell: FeatureTagCell,
|
// .join('\n') || '',
|
||||||
width: 80,
|
// Cell: FeatureTagCell,
|
||||||
hideInMenu: true,
|
// width: 80,
|
||||||
searchable: true,
|
// hideInMenu: true,
|
||||||
},
|
// searchable: true,
|
||||||
|
// isVisible: false,
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
Header: 'Created',
|
Header: 'Created',
|
||||||
accessor: 'createdAt',
|
accessor: 'createdAt',
|
||||||
@ -219,28 +264,25 @@ export const ProjectFeatureToggles = ({
|
|||||||
...environments.map(name => ({
|
...environments.map(name => ({
|
||||||
Header: loading ? () => '' : name,
|
Header: loading ? () => '' : name,
|
||||||
maxWidth: 90,
|
maxWidth: 90,
|
||||||
accessor: `environments.${name}`,
|
id: `environments.${name}`,
|
||||||
|
accessor: `environments.${name}.enabled`,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
Cell: ({
|
Cell: ({
|
||||||
value,
|
value,
|
||||||
row: { original: feature },
|
row: { original: feature },
|
||||||
}: {
|
}: {
|
||||||
value: { name: string; enabled: boolean };
|
value: boolean;
|
||||||
row: { original: ListItemType };
|
row: { original: ListItemType };
|
||||||
}) => (
|
}) => (
|
||||||
<FeatureToggleSwitch
|
<FeatureToggleSwitch
|
||||||
value={value?.enabled || false}
|
value={value}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
featureName={feature?.name}
|
featureName={feature?.name}
|
||||||
environmentName={value?.name}
|
environmentName={name}
|
||||||
onToggle={onToggle}
|
onToggle={onToggle}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
sortType: (v1: any, v2: any, id: string) => {
|
sortType: 'boolean',
|
||||||
const a = v1?.values?.[id]?.enabled;
|
|
||||||
const b = v2?.values?.[id]?.enabled;
|
|
||||||
return a === b ? 0 : a ? -1 : 1;
|
|
||||||
},
|
|
||||||
filterName: name,
|
filterName: name,
|
||||||
filterParsing: (value: any) =>
|
filterParsing: (value: any) =>
|
||||||
value.enabled ? 'enabled' : 'disabled',
|
value.enabled ? 'enabled' : 'disabled',
|
||||||
@ -260,7 +302,14 @@ export const ProjectFeatureToggles = ({
|
|||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[projectId, environments, loading, onToggle]
|
[
|
||||||
|
projectId,
|
||||||
|
environments,
|
||||||
|
loading,
|
||||||
|
onToggle,
|
||||||
|
isFavoritesPinned,
|
||||||
|
uiConfig?.flags?.favorites,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const [searchValue, setSearchValue] = useState(
|
const [searchValue, setSearchValue] = useState(
|
||||||
@ -277,6 +326,7 @@ export const ProjectFeatureToggles = ({
|
|||||||
type,
|
type,
|
||||||
stale,
|
stale,
|
||||||
tags,
|
tags,
|
||||||
|
favorite,
|
||||||
environments: featureEnvironments,
|
environments: featureEnvironments,
|
||||||
}) => ({
|
}) => ({
|
||||||
name,
|
name,
|
||||||
@ -285,6 +335,7 @@ export const ProjectFeatureToggles = ({
|
|||||||
type,
|
type,
|
||||||
stale,
|
stale,
|
||||||
tags,
|
tags,
|
||||||
|
favorite,
|
||||||
environments: Object.fromEntries(
|
environments: Object.fromEntries(
|
||||||
environments.map(env => [
|
environments.map(env => [
|
||||||
env,
|
env,
|
||||||
@ -324,7 +375,6 @@ export const ProjectFeatureToggles = ({
|
|||||||
|
|
||||||
const initialState = useMemo(
|
const initialState = useMemo(
|
||||||
() => {
|
() => {
|
||||||
const searchParams = new URLSearchParams();
|
|
||||||
const allColumnIds = columns.map(
|
const allColumnIds = columns.map(
|
||||||
(column: any) => column?.accessor || column?.id
|
(column: any) => column?.accessor || column?.id
|
||||||
);
|
);
|
||||||
@ -364,9 +414,7 @@ export const ProjectFeatureToggles = ({
|
|||||||
[environments] // eslint-disable-line react-hooks/exhaustive-deps
|
[environments] // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
);
|
);
|
||||||
|
|
||||||
const getRowId = useCallback((row: any) => {
|
const getRowId = useCallback((row: any) => row.name, []);
|
||||||
return row.name;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
allColumns,
|
allColumns,
|
||||||
@ -389,15 +437,16 @@ export const ProjectFeatureToggles = ({
|
|||||||
useSortBy
|
useSortBy
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
// TODO: update after tags are added, move to other useEffect
|
||||||
if (!features.some(({ tags }) => tags?.length)) {
|
// useEffect(() => {
|
||||||
setHiddenColumns(hiddenColumns => [...hiddenColumns, 'tags']);
|
// if (!features.some(({ tags }) => tags?.length)) {
|
||||||
} else {
|
// setHiddenColumns(hiddenColumns => [...hiddenColumns, 'tags']);
|
||||||
setHiddenColumns(hiddenColumns =>
|
// } else {
|
||||||
hiddenColumns.filter(column => column !== 'tags')
|
// setHiddenColumns(hiddenColumns =>
|
||||||
);
|
// hiddenColumns.filter(column => column !== 'tags')
|
||||||
}
|
// );
|
||||||
}, [setHiddenColumns, features]);
|
// }
|
||||||
|
// }, [setHiddenColumns, features]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@ -411,6 +460,9 @@ export const ProjectFeatureToggles = ({
|
|||||||
if (searchValue) {
|
if (searchValue) {
|
||||||
tableState.search = searchValue;
|
tableState.search = searchValue;
|
||||||
}
|
}
|
||||||
|
if (isFavoritesPinned) {
|
||||||
|
tableState.favorites = 'true';
|
||||||
|
}
|
||||||
tableState.columns = allColumns
|
tableState.columns = allColumns
|
||||||
.map(({ id }) => id)
|
.map(({ id }) => id)
|
||||||
.filter(
|
.filter(
|
||||||
@ -427,9 +479,17 @@ export const ProjectFeatureToggles = ({
|
|||||||
id: sortBy[0].id,
|
id: sortBy[0].id,
|
||||||
desc: sortBy[0].desc || false,
|
desc: sortBy[0].desc || false,
|
||||||
columns: tableState.columns.split(','),
|
columns: tableState.columns.split(','),
|
||||||
|
favorites: isFavoritesPinned || false,
|
||||||
}));
|
}));
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [loading, sortBy, hiddenColumns, searchValue, setSearchParams]);
|
}, [
|
||||||
|
loading,
|
||||||
|
sortBy,
|
||||||
|
hiddenColumns,
|
||||||
|
searchValue,
|
||||||
|
setSearchParams,
|
||||||
|
isFavoritesPinned,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent
|
<PageContent
|
||||||
|
@ -3,6 +3,7 @@ import useToast from 'hooks/useToast';
|
|||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { useFeatures } from 'hooks/api/getters/useFeatures/useFeatures';
|
import { useFeatures } from 'hooks/api/getters/useFeatures/useFeatures';
|
||||||
import useAPI from '../useApi/useApi';
|
import useAPI from '../useApi/useApi';
|
||||||
|
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||||
import { usePlausibleTracker } from '../../../usePlausibleTracker';
|
import { usePlausibleTracker } from '../../../usePlausibleTracker';
|
||||||
|
|
||||||
export const useFavoriteFeaturesApi = () => {
|
export const useFavoriteFeaturesApi = () => {
|
||||||
|
@ -22,7 +22,7 @@ const data = [
|
|||||||
id: 5,
|
id: 5,
|
||||||
favorite: false,
|
favorite: false,
|
||||||
},
|
},
|
||||||
].map(d => ({ values: d })) as unknown as Row<object>[];
|
].map(d => ({ values: d, original: d })) as unknown as Row<object>[];
|
||||||
|
|
||||||
test('puts favorite items first', () => {
|
test('puts favorite items first', () => {
|
||||||
const output = data.sort((a, b) =>
|
const output = data.sort((a, b) =>
|
||||||
|
@ -20,9 +20,9 @@ export const sortTypesWithFavorites: Record<
|
|||||||
id: string,
|
id: string,
|
||||||
desc?: boolean
|
desc?: boolean
|
||||||
) => {
|
) => {
|
||||||
if (v1?.values?.favorite && !v2?.values?.favorite)
|
if (v1?.original?.favorite && !v2?.original?.favorite)
|
||||||
return desc ? 1 : -1;
|
return desc ? 1 : -1;
|
||||||
if (!v1?.values?.favorite && v2?.values?.favorite)
|
if (!v1?.original?.favorite && v2?.original?.favorite)
|
||||||
return desc ? -1 : 1;
|
return desc ? -1 : 1;
|
||||||
return value(v1, v2, id, desc);
|
return value(v1, v2, id, desc);
|
||||||
},
|
},
|
||||||
@ -45,10 +45,9 @@ export const usePinnedFavorites = (initialState = false) => {
|
|||||||
setIsFavoritesPinned(!isFavoritesPinned);
|
setIsFavoritesPinned(!isFavoritesPinned);
|
||||||
};
|
};
|
||||||
|
|
||||||
const enhancedSortTypes = useMemo(
|
const enhancedSortTypes = useMemo(() => {
|
||||||
() => (isFavoritesPinned ? sortTypesWithFavorites : sortTypes),
|
return isFavoritesPinned ? sortTypesWithFavorites : sortTypes;
|
||||||
[isFavoritesPinned]
|
}, [isFavoritesPinned]);
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isFavoritesPinned,
|
isFavoritesPinned,
|
||||||
|
@ -9,6 +9,7 @@ export interface IFeatureToggleListItem {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
environments: IEnvironments[];
|
environments: IEnvironments[];
|
||||||
tags?: ITag[];
|
tags?: ITag[];
|
||||||
|
favorite?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEnvironments {
|
export interface IEnvironments {
|
||||||
|
Loading…
Reference in New Issue
Block a user