mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
remove dead code
This commit is contained in:
parent
88f2bef1be
commit
d50edfa294
@ -1,178 +0,0 @@
|
||||
import { Dispatch, SetStateAction, useContext, VFC } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { List, ListItem } from '@mui/material';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { IFlags } from 'interfaces/uiConfig';
|
||||
import { SearchField } from 'component/common/SearchField/SearchField';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { IFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||
import { FeatureToggleListItem } from './FeatureToggleListItem/FeatureToggleListItem';
|
||||
import { FeatureToggleListActions } from './FeatureToggleListActions/FeatureToggleListActions';
|
||||
import { CreateFeatureButton } from '../CreateFeatureButton/CreateFeatureButton';
|
||||
import { IFeaturesSort } from 'hooks/useFeaturesSort';
|
||||
import { FeatureSchema } from 'openapi';
|
||||
import { useStyles } from './styles';
|
||||
|
||||
interface IFeatureToggleListProps {
|
||||
features: FeatureSchema[];
|
||||
loading?: boolean;
|
||||
flags?: IFlags;
|
||||
filter: IFeaturesFilter;
|
||||
setFilter: Dispatch<SetStateAction<IFeaturesFilter>>;
|
||||
sort: IFeaturesSort;
|
||||
setSort: Dispatch<SetStateAction<IFeaturesSort>>;
|
||||
onRevive?: (feature: string) => void;
|
||||
inProject?: boolean;
|
||||
isArchive?: boolean;
|
||||
}
|
||||
|
||||
const loadingFeaturesPlaceholder: FeatureSchema[] = Array(10)
|
||||
.fill({
|
||||
createdAt: '2021-03-19T09:16:21.329Z',
|
||||
description: ' ',
|
||||
enabled: true,
|
||||
lastSeenAt: '2021-03-24T10:46:38.036Z',
|
||||
name: '',
|
||||
project: 'default',
|
||||
stale: true,
|
||||
strategies: [],
|
||||
variants: [],
|
||||
type: 'release',
|
||||
archived: false,
|
||||
environments: [],
|
||||
impressionData: false,
|
||||
})
|
||||
.map((feature, index) => ({ ...feature, name: `${index}` })); // ID for React key
|
||||
|
||||
export const FeatureToggleList: VFC<IFeatureToggleListProps> = ({
|
||||
features,
|
||||
onRevive,
|
||||
inProject,
|
||||
isArchive,
|
||||
loading,
|
||||
flags,
|
||||
filter,
|
||||
setFilter,
|
||||
sort,
|
||||
setSort,
|
||||
}) => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { classes: styles } = useStyles();
|
||||
const smallScreen = useMediaQuery('(max-width:800px)');
|
||||
const mobileView = useMediaQuery('(max-width:600px)');
|
||||
|
||||
const setFilterQuery = (v: string) => {
|
||||
const query = v && typeof v === 'string' ? v.trim() : '';
|
||||
setFilter(prev => ({ ...prev, query }));
|
||||
};
|
||||
|
||||
const renderFeatures = () => {
|
||||
if (loading) {
|
||||
return loadingFeaturesPlaceholder.map(feature => (
|
||||
<FeatureToggleListItem
|
||||
key={feature.name}
|
||||
feature={feature}
|
||||
onRevive={onRevive}
|
||||
hasAccess={hasAccess}
|
||||
className={'skeleton'}
|
||||
flags={flags}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={features.length > 0}
|
||||
show={features.map(feature => (
|
||||
<FeatureToggleListItem
|
||||
key={feature.name}
|
||||
feature={feature}
|
||||
onRevive={onRevive}
|
||||
hasAccess={hasAccess}
|
||||
flags={flags}
|
||||
inProject={inProject}
|
||||
/>
|
||||
))}
|
||||
elseShow={
|
||||
<ListItem className={styles.emptyStateListItem}>
|
||||
No archived features.
|
||||
</ListItem>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const searchResultsHeader = filter.query
|
||||
? ` (${features.length} matches)`
|
||||
: '';
|
||||
|
||||
const headerTitle = isArchive
|
||||
? inProject
|
||||
? `Project Archived Features ${searchResultsHeader}`
|
||||
: `Archived Features ${searchResultsHeader}`
|
||||
: `Features ${searchResultsHeader}`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className={classnames(styles.searchBarContainer, {
|
||||
dense: inProject,
|
||||
})}
|
||||
>
|
||||
<SearchField
|
||||
initialValue={filter.query}
|
||||
updateValue={setFilterQuery}
|
||||
showValueChip={!mobileView}
|
||||
className={classnames(styles.searchBar, {
|
||||
skeleton: loading,
|
||||
})}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={!mobileView && !isArchive}
|
||||
show={<Link to="/archive">Archive</Link>}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PageContent
|
||||
header={
|
||||
<PageHeader
|
||||
loading={loading}
|
||||
title={headerTitle}
|
||||
actions={
|
||||
<div className={styles.actionsContainer}>
|
||||
<ConditionallyRender
|
||||
condition={!smallScreen}
|
||||
show={
|
||||
<FeatureToggleListActions
|
||||
filter={filter}
|
||||
setFilter={setFilter}
|
||||
sort={sort}
|
||||
setSort={setSort}
|
||||
loading={loading}
|
||||
inProject={inProject}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={!isArchive}
|
||||
show={
|
||||
<CreateFeatureButton
|
||||
filter={filter}
|
||||
loading={Boolean(loading)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<List>{renderFeatures()}</List>
|
||||
</PageContent>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,99 +0,0 @@
|
||||
import { Dispatch, MouseEventHandler, SetStateAction, VFC } from 'react';
|
||||
import { MenuItem, Typography } from '@mui/material';
|
||||
import DropdownMenu from 'component/common/DropdownMenu/DropdownMenu';
|
||||
import ProjectSelect from 'component/common/ProjectSelect/ProjectSelect';
|
||||
import useLoading from 'hooks/useLoading';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import {
|
||||
createFeaturesFilterSortOptions,
|
||||
FeaturesSortType,
|
||||
IFeaturesSort,
|
||||
} from 'hooks/useFeaturesSort';
|
||||
import { useStyles } from './styles';
|
||||
import { IFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||
|
||||
let sortOptions = createFeaturesFilterSortOptions();
|
||||
|
||||
interface IFeatureToggleListActionsProps {
|
||||
filter: IFeaturesFilter;
|
||||
setFilter: Dispatch<SetStateAction<IFeaturesFilter>>;
|
||||
sort: IFeaturesSort;
|
||||
setSort: Dispatch<SetStateAction<IFeaturesSort>>;
|
||||
loading?: boolean;
|
||||
inProject?: boolean;
|
||||
}
|
||||
|
||||
export const FeatureToggleListActions: VFC<IFeatureToggleListActionsProps> = ({
|
||||
filter,
|
||||
setFilter,
|
||||
sort,
|
||||
setSort,
|
||||
loading = false,
|
||||
inProject,
|
||||
}) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const ref = useLoading(loading);
|
||||
|
||||
const handleSort: MouseEventHandler = e => {
|
||||
const type = (e.target as Element)
|
||||
.getAttribute('data-target')
|
||||
?.trim() as FeaturesSortType;
|
||||
if (type) {
|
||||
setSort(prev => ({ ...prev, type }));
|
||||
}
|
||||
};
|
||||
|
||||
const selectedOption =
|
||||
sortOptions.find(o => o.type === sort.type) || sortOptions[0];
|
||||
|
||||
if (inProject) {
|
||||
sortOptions = sortOptions.filter(option => option.type !== 'project');
|
||||
}
|
||||
|
||||
const renderSortingOptions = () =>
|
||||
sortOptions.map(option => (
|
||||
<MenuItem
|
||||
style={{ fontSize: '14px' }}
|
||||
key={option.type}
|
||||
disabled={option.type === sort.type}
|
||||
data-target={option.type}
|
||||
>
|
||||
{option.name}
|
||||
</MenuItem>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className={styles.actions} ref={ref}>
|
||||
<Typography variant="body2" data-loading>
|
||||
Sorted by:
|
||||
</Typography>
|
||||
<DropdownMenu
|
||||
id={'sorting'}
|
||||
label={`By ${selectedOption.name}`}
|
||||
callback={handleSort}
|
||||
renderOptions={renderSortingOptions}
|
||||
title="Sort by"
|
||||
style={{ textTransform: 'lowercase', fontWeight: 'normal' }}
|
||||
data-loading
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={uiConfig.flags.P && !inProject}
|
||||
show={
|
||||
<ProjectSelect
|
||||
currentProjectId={filter.project}
|
||||
updateCurrentProject={project =>
|
||||
setFilter(prev => ({ ...prev, project }))
|
||||
}
|
||||
style={{
|
||||
textTransform: 'lowercase',
|
||||
fontWeight: 'normal',
|
||||
}}
|
||||
data-loading
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,12 +0,0 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()({
|
||||
actions: {
|
||||
'& > *': {
|
||||
margin: '0 0.25rem',
|
||||
},
|
||||
marginRight: '0.25rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
@ -1,239 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { basePath } from 'utils/formatPath';
|
||||
import { createPersistentGlobalStateHook } from './usePersistentGlobalState';
|
||||
import {
|
||||
expired,
|
||||
getDiffInDays,
|
||||
toggleExpiryByTypeMap,
|
||||
} from 'component/Reporting/utils';
|
||||
import { FeatureSchema } from 'openapi';
|
||||
|
||||
export type FeaturesSortType =
|
||||
| 'name'
|
||||
| 'expired'
|
||||
| 'type'
|
||||
| 'enabled'
|
||||
| 'stale'
|
||||
| 'created'
|
||||
| 'archived'
|
||||
| 'last-seen'
|
||||
| 'status'
|
||||
| 'project';
|
||||
|
||||
export interface IFeaturesSort {
|
||||
type: FeaturesSortType;
|
||||
desc?: boolean;
|
||||
}
|
||||
|
||||
export interface IFeaturesSortOutput {
|
||||
sort: IFeaturesSort;
|
||||
sorted: FeatureSchema[];
|
||||
setSort: React.Dispatch<React.SetStateAction<IFeaturesSort>>;
|
||||
}
|
||||
|
||||
export interface IFeaturesFilterSortOption {
|
||||
type: FeaturesSortType;
|
||||
name: string;
|
||||
}
|
||||
|
||||
// Store the features sort state globally, and in localStorage.
|
||||
// When changing the format of IFeaturesSort, change the version as well.
|
||||
const useFeaturesSortState = createPersistentGlobalStateHook<IFeaturesSort>(
|
||||
`${basePath}:useFeaturesSort:v1`,
|
||||
{ type: 'name' }
|
||||
);
|
||||
|
||||
export const useFeaturesSort = (
|
||||
features: FeatureSchema[]
|
||||
): IFeaturesSortOutput => {
|
||||
const [sort, setSort] = useFeaturesSortState();
|
||||
|
||||
const sorted = useMemo(() => {
|
||||
return sortFeatures(features, sort);
|
||||
}, [features, sort]);
|
||||
|
||||
return {
|
||||
setSort,
|
||||
sort,
|
||||
sorted,
|
||||
};
|
||||
};
|
||||
|
||||
export const createFeaturesFilterSortOptions =
|
||||
(): IFeaturesFilterSortOption[] => {
|
||||
return [
|
||||
{ type: 'name', name: 'Name' },
|
||||
{ type: 'type', name: 'Type' },
|
||||
{ type: 'enabled', name: 'Enabled' },
|
||||
{ type: 'stale', name: 'Stale' },
|
||||
{ type: 'status', name: 'Status' },
|
||||
{ type: 'created', name: 'Created' },
|
||||
{ type: 'archived', name: 'Archived' },
|
||||
{ type: 'last-seen', name: 'Last seen' },
|
||||
{ type: 'project', name: 'Project' },
|
||||
];
|
||||
};
|
||||
|
||||
const sortAscendingFeatures = (
|
||||
features: FeatureSchema[],
|
||||
sort: IFeaturesSort
|
||||
): FeatureSchema[] => {
|
||||
switch (sort.type) {
|
||||
case 'enabled':
|
||||
return sortByEnabled(features);
|
||||
case 'stale':
|
||||
return sortByStale(features);
|
||||
case 'created':
|
||||
return sortByCreated(features);
|
||||
case 'archived':
|
||||
return sortByArchived(features);
|
||||
case 'last-seen':
|
||||
return sortByLastSeen(features);
|
||||
case 'name':
|
||||
return sortByName(features);
|
||||
case 'project':
|
||||
return sortByProject(features);
|
||||
case 'type':
|
||||
return sortByType(features);
|
||||
case 'expired':
|
||||
return sortByExpired(features);
|
||||
case 'status':
|
||||
return sortByStatus(features);
|
||||
default:
|
||||
console.error(`Unknown feature sort type: ${sort.type}`);
|
||||
return features;
|
||||
}
|
||||
};
|
||||
|
||||
const sortFeatures = (
|
||||
features: FeatureSchema[],
|
||||
sort: IFeaturesSort
|
||||
): FeatureSchema[] => {
|
||||
const sorted = sortAscendingFeatures(features, sort);
|
||||
|
||||
if (sort.desc) {
|
||||
return [...sorted].reverse();
|
||||
}
|
||||
|
||||
return sorted;
|
||||
};
|
||||
|
||||
const sortByEnabled = (
|
||||
features: Readonly<FeatureSchema[]>
|
||||
): FeatureSchema[] => {
|
||||
return [...features].sort((a, b) =>
|
||||
a.enabled === b.enabled ? 0 : a.enabled ? -1 : 1
|
||||
);
|
||||
};
|
||||
|
||||
const sortByStale = (features: Readonly<FeatureSchema[]>): FeatureSchema[] => {
|
||||
return [...features].sort((a, b) =>
|
||||
a.stale === b.stale ? 0 : a.stale ? -1 : 1
|
||||
);
|
||||
};
|
||||
|
||||
const sortByLastSeen = (
|
||||
features: Readonly<FeatureSchema[]>
|
||||
): FeatureSchema[] => {
|
||||
return [...features].sort((a, b) =>
|
||||
a.lastSeenAt && b.lastSeenAt
|
||||
? compareNullableDates(b.lastSeenAt, a.lastSeenAt)
|
||||
: a.lastSeenAt
|
||||
? -1
|
||||
: b.lastSeenAt
|
||||
? 1
|
||||
: compareNullableDates(b.createdAt, a.createdAt)
|
||||
);
|
||||
};
|
||||
|
||||
const sortByCreated = (
|
||||
features: Readonly<FeatureSchema[]>
|
||||
): FeatureSchema[] => {
|
||||
return [...features].sort((a, b) =>
|
||||
compareNullableDates(b.createdAt, a.createdAt)
|
||||
);
|
||||
};
|
||||
|
||||
const sortByArchived = (
|
||||
features: Readonly<FeatureSchema[]>
|
||||
): FeatureSchema[] => {
|
||||
return [...features].sort((a, b) =>
|
||||
compareNullableDates(b.archivedAt, a.archivedAt)
|
||||
);
|
||||
};
|
||||
|
||||
const sortByName = (features: Readonly<FeatureSchema[]>): FeatureSchema[] => {
|
||||
return [...features].sort((a, b) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
||||
const sortByProject = (
|
||||
features: Readonly<FeatureSchema[]>
|
||||
): FeatureSchema[] => {
|
||||
return [...features].sort((a, b) =>
|
||||
a.project && b.project
|
||||
? a.project.localeCompare(b.project)
|
||||
: a.project
|
||||
? 1
|
||||
: b.project
|
||||
? -1
|
||||
: 0
|
||||
);
|
||||
};
|
||||
|
||||
const sortByType = (features: Readonly<FeatureSchema[]>): FeatureSchema[] => {
|
||||
return [...features].sort((a, b) =>
|
||||
a.type && b.type
|
||||
? a.type.localeCompare(b.type)
|
||||
: a.type
|
||||
? 1
|
||||
: b.type
|
||||
? -1
|
||||
: 0
|
||||
);
|
||||
};
|
||||
|
||||
const compareNullableDates = (
|
||||
a: Date | null | undefined,
|
||||
b: Date | null | undefined
|
||||
): number => {
|
||||
return a && b ? a?.getTime?.() - b?.getTime?.() : a ? 1 : b ? -1 : 0;
|
||||
};
|
||||
|
||||
const sortByExpired = (
|
||||
features: Readonly<FeatureSchema[]>
|
||||
): FeatureSchema[] => {
|
||||
return [...features].sort((a, b) => {
|
||||
const now = new Date();
|
||||
const dateA = a.createdAt!;
|
||||
const dateB = b.createdAt!;
|
||||
|
||||
const diffA = getDiffInDays(dateA, now);
|
||||
const diffB = getDiffInDays(dateB, now);
|
||||
|
||||
if (!expired(diffA, a.type!) && expired(diffB, b.type!)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (expired(diffA, a.type!) && !expired(diffB, b.type!)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const expiration = toggleExpiryByTypeMap as Record<string, number>;
|
||||
const expiredByA = a.type ? diffA - expiration[a.type] : 0;
|
||||
const expiredByB = b.type ? diffB - expiration[b.type] : 0;
|
||||
|
||||
return expiredByB - expiredByA;
|
||||
});
|
||||
};
|
||||
|
||||
const sortByStatus = (features: Readonly<FeatureSchema[]>): FeatureSchema[] => {
|
||||
return [...features].sort((a, b) => {
|
||||
if (a.stale) {
|
||||
return 1;
|
||||
} else if (b.stale) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
};
|
Loading…
Reference in New Issue
Block a user