mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-09 00:18:26 +01:00
refactor: remove unused code (#1036)
* refactor: remove unused code * refactor: remove more unused code
This commit is contained in:
parent
ae012d62e6
commit
eb5e83cdb4
@ -1,75 +1,9 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import React from 'react';
|
||||
import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons';
|
||||
import { AvailableAddons } from './AvailableAddons/AvailableAddons';
|
||||
import { Avatar } from '@mui/material';
|
||||
import { DeviceHub } from '@mui/icons-material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import slackIcon from 'assets/icons/slack.svg';
|
||||
import jiraIcon from 'assets/icons/jira.svg';
|
||||
import webhooksIcon from 'assets/icons/webhooks.svg';
|
||||
import teamsIcon from 'assets/icons/teams.svg';
|
||||
import dataDogIcon from 'assets/icons/datadog.svg';
|
||||
import { formatAssetPath } from 'utils/formatPath';
|
||||
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
width: '32.5px',
|
||||
height: '32.5px',
|
||||
marginRight: '16px',
|
||||
borderRadius: '50%',
|
||||
};
|
||||
|
||||
export const getAddonIcon = (name: string): ReactElement => {
|
||||
switch (name) {
|
||||
case 'slack':
|
||||
return (
|
||||
<img
|
||||
style={style}
|
||||
alt="Slack logo"
|
||||
src={formatAssetPath(slackIcon)}
|
||||
/>
|
||||
);
|
||||
case 'jira-comment':
|
||||
return (
|
||||
<img
|
||||
style={style}
|
||||
alt="JIRA logo"
|
||||
src={formatAssetPath(jiraIcon)}
|
||||
/>
|
||||
);
|
||||
case 'webhook':
|
||||
return (
|
||||
<img
|
||||
style={style}
|
||||
alt="Generic Webhook logo"
|
||||
src={formatAssetPath(webhooksIcon)}
|
||||
/>
|
||||
);
|
||||
case 'teams':
|
||||
return (
|
||||
<img
|
||||
style={style}
|
||||
alt="Microsoft Teams logo"
|
||||
src={formatAssetPath(teamsIcon)}
|
||||
/>
|
||||
);
|
||||
case 'datadog':
|
||||
return (
|
||||
<img
|
||||
style={style}
|
||||
alt="Datadog logo"
|
||||
src={formatAssetPath(dataDogIcon)}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<Avatar>
|
||||
<DeviceHub />
|
||||
</Avatar>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const AddonList = () => {
|
||||
const { providers, addons, loading } = useAddons();
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
panel: {
|
||||
width: '100%',
|
||||
marginBottom: theme.spacing(2),
|
||||
borderRadius: theme.spacing(1.5),
|
||||
paddingBottom: theme.spacing(4),
|
||||
},
|
||||
}));
|
@ -1,18 +0,0 @@
|
||||
import { forwardRef } from 'react';
|
||||
import { Paper, PaperProps } from '@mui/material';
|
||||
import { useStyles } from './TableContainer.styles';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const TableContainer = forwardRef<HTMLDivElement, PaperProps>(
|
||||
({ children, ...props }, ref) => {
|
||||
const { classes } = useStyles();
|
||||
|
||||
return (
|
||||
<Paper ref={ref} className={classes.panel} {...props}>
|
||||
{children}
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
);
|
@ -1,7 +1,6 @@
|
||||
export const P = 'P';
|
||||
export const C = 'C';
|
||||
export const E = 'E';
|
||||
export const RBAC = 'RBAC';
|
||||
export const EEA = 'EEA';
|
||||
export const RE = 'RE';
|
||||
export const SE = 'SE';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useContext, useMemo, useState, VFC } from 'react';
|
||||
import { useMemo, useState, VFC } from 'react';
|
||||
import { useGlobalFilter, useSortBy, useTable } from 'react-table';
|
||||
import {
|
||||
Table,
|
||||
@ -12,7 +12,6 @@ import {
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { UPDATE_CONTEXT_FIELD } from 'component/providers/AccessProvider/permissions';
|
||||
import { Dialogue as ConfirmDialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
|
||||
import useContextsApi from 'hooks/api/actions/useContextsApi/useContextsApi';
|
||||
@ -25,7 +24,6 @@ import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
||||
import { ContextActionsCell } from './ContextActionsCell/ContextActionsCell';
|
||||
import { Adjust } from '@mui/icons-material';
|
||||
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
|
||||
const ContextList: VFC = () => {
|
||||
const [showDelDialogue, setShowDelDialogue] = useState(false);
|
||||
@ -33,7 +31,6 @@ const ContextList: VFC = () => {
|
||||
const { context, refetchUnleashContext, loading } = useUnleashContext();
|
||||
const { removeContext } = useContextsApi();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
|
||||
const data = useMemo(() => {
|
||||
if (loading) {
|
||||
|
@ -1,58 +0,0 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
tableRow: {
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.grey[200],
|
||||
},
|
||||
},
|
||||
tableCell: {
|
||||
border: 'none',
|
||||
padding: '0.25rem 0',
|
||||
},
|
||||
tableCellHeader: {
|
||||
paddingBottom: '0.5rem',
|
||||
fontWeight: 'normal',
|
||||
color: theme.palette.grey[600],
|
||||
borderBottom: '1px solid ' + theme.palette.grey[200],
|
||||
},
|
||||
tableCellHeaderSortable: {
|
||||
cursor: 'pointer',
|
||||
background: theme.palette.grey[50],
|
||||
},
|
||||
tableCellStatus: {
|
||||
width: '60px',
|
||||
},
|
||||
tableCellName: {
|
||||
paddingLeft: '10px',
|
||||
},
|
||||
tableCellEnv: {
|
||||
width: '90px',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
tableCellCreated: {
|
||||
[theme.breakpoints.down('md')]: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
tableCellType: {
|
||||
width: '32px',
|
||||
alignItems: 'center',
|
||||
[theme.breakpoints.down(600)]: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
link: {
|
||||
textDecoration: 'none',
|
||||
color: theme.palette.primary.main,
|
||||
fontWeight: theme.fontWeight.bold,
|
||||
},
|
||||
sortButton: {
|
||||
all: 'unset',
|
||||
'&:focus-visible, &:active': {
|
||||
outline: 'revert',
|
||||
},
|
||||
},
|
||||
}));
|
@ -1,297 +0,0 @@
|
||||
import { useState, useEffect, useContext } from 'react';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
} from '@mui/material';
|
||||
import classnames from 'classnames';
|
||||
import { useStyles } from './FeatureToggleListNew.styles';
|
||||
import FeatureToggleListNewItem from './FeatureToggleListNewItem/FeatureToggleListNewItem';
|
||||
import usePagination from 'hooks/usePagination';
|
||||
import loadingFeatures from './FeatureToggleListNewItem/loadingFeatures';
|
||||
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
||||
import PaginateUI from 'component/common/PaginateUI/PaginateUI';
|
||||
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
|
||||
import { createGlobalStateHook } from 'hooks/useGlobalState';
|
||||
import { AnnouncerContext } from 'component/common/Announcer/AnnouncerContext/AnnouncerContext';
|
||||
|
||||
interface IFeatureToggleListNewProps {
|
||||
features: IFeatureToggleListItem[];
|
||||
loading: boolean;
|
||||
projectId: string;
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
const sortList = (list, sortOpt) => {
|
||||
if (!list) {
|
||||
return list;
|
||||
}
|
||||
if (!sortOpt.field) {
|
||||
return list;
|
||||
}
|
||||
if (sortOpt.type === 'string') {
|
||||
// @ts-expect-error
|
||||
return list.sort((a, b) => {
|
||||
const fieldA = a[sortOpt.field]?.toUpperCase();
|
||||
const fieldB = b[sortOpt.field]?.toUpperCase();
|
||||
const direction = sortOpt.direction;
|
||||
|
||||
if (fieldA < fieldB) {
|
||||
return direction === 0 ? -1 : 1;
|
||||
}
|
||||
if (fieldA > fieldB) {
|
||||
return direction === 0 ? 1 : -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
if (sortOpt.type === 'date') {
|
||||
// @ts-expect-error
|
||||
return list.sort((a, b) => {
|
||||
const fieldA = new Date(a[sortOpt.field]);
|
||||
const fieldB = new Date(b[sortOpt.field]);
|
||||
|
||||
if (fieldA < fieldB) {
|
||||
return sortOpt.direction === 0 ? 1 : -1;
|
||||
}
|
||||
if (fieldA > fieldB) {
|
||||
return sortOpt.direction === 0 ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
interface ISortedState {
|
||||
field: string;
|
||||
type: string;
|
||||
direction: number;
|
||||
}
|
||||
|
||||
const useFeatureToggLeProjectSort = createGlobalStateHook<ISortedState>(
|
||||
'useFeatureToggLeProjectSort',
|
||||
{ field: 'name', type: 'string', direction: 0 }
|
||||
);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
const FeatureToggleListNew = ({
|
||||
features,
|
||||
loading,
|
||||
projectId,
|
||||
}: IFeatureToggleListNewProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { setAnnouncement } = useContext(AnnouncerContext);
|
||||
const [sortOpt, setSortOpt] = useFeatureToggLeProjectSort();
|
||||
|
||||
const [sortedFeatures, setSortedFeatures] = useState<
|
||||
IFeatureToggleListItem[]
|
||||
>(sortList([...features], sortOpt));
|
||||
|
||||
const { page, pages, nextPage, prevPage, setPageIndex, pageIndex } =
|
||||
usePagination(sortedFeatures, 50);
|
||||
|
||||
useEffect(() => {
|
||||
setSortedFeatures(sortList([...features], sortOpt));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [features]);
|
||||
|
||||
const updateSort = (field: string) => {
|
||||
let newSortOpt;
|
||||
if (field === sortOpt.field) {
|
||||
newSortOpt = { ...sortOpt, direction: (sortOpt.direction + 1) % 2 };
|
||||
} else if (['createdAt', 'lastSeenAt'].includes(field)) {
|
||||
newSortOpt = {
|
||||
field,
|
||||
type: 'date',
|
||||
direction: 0,
|
||||
};
|
||||
} else {
|
||||
newSortOpt = {
|
||||
field,
|
||||
type: 'string',
|
||||
direction: 0,
|
||||
};
|
||||
}
|
||||
setSortOpt(newSortOpt);
|
||||
setSortedFeatures(sortList([...features], newSortOpt));
|
||||
setPageIndex(0);
|
||||
|
||||
setAnnouncement(
|
||||
`Sorted table by ${field}, ${
|
||||
sortOpt.direction ? 'ascending' : 'descending'
|
||||
}`
|
||||
);
|
||||
};
|
||||
|
||||
const getEnvironments = () => {
|
||||
if (features.length > 0) {
|
||||
const envs = features[0].environments || [];
|
||||
return envs;
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'default',
|
||||
enabled: false,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const renderFeatures = () => {
|
||||
if (loading) {
|
||||
return loadingFeatures.map((feature: IFeatureToggleListItem) => {
|
||||
return (
|
||||
<FeatureToggleListNewItem
|
||||
key={feature.name}
|
||||
name={feature.name}
|
||||
type={feature.type}
|
||||
environments={feature.environments}
|
||||
projectId={projectId}
|
||||
createdAt={new Date().toISOString()}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return page.map((feature: IFeatureToggleListItem) => {
|
||||
return (
|
||||
<FeatureToggleListNewItem
|
||||
key={feature.name}
|
||||
name={feature.name}
|
||||
type={feature.type}
|
||||
environments={feature.environments}
|
||||
projectId={projectId}
|
||||
lastSeenAt={feature.lastSeenAt}
|
||||
createdAt={feature.createdAt}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const ariaSort = (field: string) => {
|
||||
return field === sortOpt.field
|
||||
? sortOpt.direction
|
||||
? 'ascending'
|
||||
: 'descending'
|
||||
: undefined;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell
|
||||
className={classnames(
|
||||
styles.tableCell,
|
||||
styles.tableCellStatus,
|
||||
styles.tableCellHeader,
|
||||
styles.tableCellHeaderSortable
|
||||
)}
|
||||
align="left"
|
||||
aria-sort={ariaSort('lastSeenAt')}
|
||||
>
|
||||
<button
|
||||
data-loading
|
||||
onClick={() => updateSort('lastSeenAt')}
|
||||
className={styles.sortButton}
|
||||
>
|
||||
Last use
|
||||
</button>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classnames(
|
||||
styles.tableCell,
|
||||
styles.tableCellType,
|
||||
styles.tableCellHeader,
|
||||
styles.tableCellHeaderSortable
|
||||
)}
|
||||
align="center"
|
||||
aria-sort={ariaSort('type')}
|
||||
>
|
||||
<button
|
||||
data-loading
|
||||
onClick={() => updateSort('type')}
|
||||
className={styles.sortButton}
|
||||
>
|
||||
Type
|
||||
</button>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classnames(
|
||||
styles.tableCell,
|
||||
styles.tableCellName,
|
||||
styles.tableCellHeader,
|
||||
styles.tableCellHeaderSortable
|
||||
)}
|
||||
align="left"
|
||||
aria-sort={ariaSort('name')}
|
||||
>
|
||||
<button
|
||||
data-loading
|
||||
onClick={() => updateSort('name')}
|
||||
className={styles.sortButton}
|
||||
>
|
||||
Name
|
||||
</button>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classnames(
|
||||
styles.tableCell,
|
||||
styles.tableCellCreated,
|
||||
styles.tableCellHeader,
|
||||
styles.tableCellHeaderSortable
|
||||
)}
|
||||
align="left"
|
||||
aria-sort={ariaSort('createdAt')}
|
||||
>
|
||||
<button
|
||||
data-loading
|
||||
onClick={() => updateSort('createdAt')}
|
||||
className={styles.sortButton}
|
||||
>
|
||||
Created
|
||||
</button>
|
||||
</TableCell>
|
||||
{getEnvironments().map((env: any) => {
|
||||
return (
|
||||
<TableCell
|
||||
key={env.name}
|
||||
className={classnames(
|
||||
styles.tableCell,
|
||||
styles.tableCellEnv,
|
||||
styles.tableCellHeader,
|
||||
styles.tableCellHeaderSortable
|
||||
)}
|
||||
align="center"
|
||||
>
|
||||
<StringTruncator
|
||||
text={env.name}
|
||||
maxLength={15}
|
||||
maxWidth="90"
|
||||
data-loading
|
||||
/>
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>{renderFeatures()}</TableBody>
|
||||
</Table>
|
||||
<PaginateUI
|
||||
pages={pages}
|
||||
pageIndex={pageIndex}
|
||||
setPageIndex={setPageIndex}
|
||||
nextPage={nextPage}
|
||||
prevPage={prevPage}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeatureToggleListNew;
|
@ -1,25 +0,0 @@
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||
import { formatDateYMD, formatDateYMDHMS } from 'utils/formatDate';
|
||||
|
||||
interface ICreatedAtProps {
|
||||
time: string;
|
||||
}
|
||||
|
||||
const CreatedAt = ({ time }: ICreatedAtProps) => {
|
||||
const { locationSettings } = useLocationSettings();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={`Created at ${formatDateYMDHMS(
|
||||
time,
|
||||
locationSettings.locale
|
||||
)}`}
|
||||
arrow
|
||||
>
|
||||
<span>{formatDateYMD(time, locationSettings.locale)}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreatedAt;
|
@ -1,158 +0,0 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { TableCell, TableRow } from '@mui/material';
|
||||
import { useStyles } from '../FeatureToggleListNew.styles';
|
||||
import useToggleFeatureByEnv from 'hooks/api/actions/useToggleFeatureByEnv/useToggleFeatureByEnv';
|
||||
import { IEnvironments } from 'interfaces/featureToggle';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { getTogglePath } from 'utils/routePathHelpers';
|
||||
import FeatureStatus from 'component/feature/FeatureView/FeatureStatus/FeatureStatus';
|
||||
import FeatureType from 'component/feature/FeatureView/FeatureType/FeatureType';
|
||||
import classNames from 'classnames';
|
||||
import CreatedAt from './CreatedAt';
|
||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import { UPDATE_FEATURE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions';
|
||||
import PermissionSwitch from 'component/common/PermissionSwitch/PermissionSwitch';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ENVIRONMENT_STRATEGY_ERROR } from 'constants/apiErrors';
|
||||
import EnvironmentStrategyDialog from 'component/common/EnvironmentStrategiesDialog/EnvironmentStrategyDialog';
|
||||
|
||||
interface IFeatureToggleListNewItemProps {
|
||||
name: string;
|
||||
type: string;
|
||||
environments: IEnvironments[];
|
||||
projectId: string;
|
||||
lastSeenAt?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
const FeatureToggleListNewItem = ({
|
||||
name,
|
||||
lastSeenAt,
|
||||
type,
|
||||
environments,
|
||||
projectId,
|
||||
createdAt,
|
||||
}: IFeatureToggleListNewItemProps) => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { toggleFeatureByEnvironment } = useToggleFeatureByEnv(
|
||||
projectId,
|
||||
name
|
||||
);
|
||||
|
||||
const { refetch } = useProject(projectId);
|
||||
const { classes: styles } = useStyles();
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
const [showInfoBox, setShowInfoBox] = useState(false);
|
||||
const [environmentName, setEnvironmentName] = useState('');
|
||||
|
||||
const closeInfoBox = () => {
|
||||
setShowInfoBox(false);
|
||||
};
|
||||
|
||||
const handleToggle = (env: IEnvironments) => {
|
||||
toggleFeatureByEnvironment(env.name, env.enabled)
|
||||
.then(() => {
|
||||
setToastData({
|
||||
type: 'success',
|
||||
title: 'Updated toggle status',
|
||||
text: 'Successfully updated toggle status.',
|
||||
});
|
||||
refetch();
|
||||
})
|
||||
.catch(e => {
|
||||
if (e.message === ENVIRONMENT_STRATEGY_ERROR) {
|
||||
setShowInfoBox(true);
|
||||
} else {
|
||||
setToastApiError(e.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableRow className={styles.tableRow}>
|
||||
<TableCell
|
||||
className={classNames(
|
||||
styles.tableCell,
|
||||
styles.tableCellStatus
|
||||
)}
|
||||
align="left"
|
||||
>
|
||||
<FeatureStatus
|
||||
lastSeenAt={lastSeenAt}
|
||||
tooltipPlacement="left"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classNames(
|
||||
styles.tableCell,
|
||||
styles.tableCellType
|
||||
)}
|
||||
align="center"
|
||||
>
|
||||
<FeatureType type={type} />
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classNames(
|
||||
styles.tableCell,
|
||||
styles.tableCellName
|
||||
)}
|
||||
align="left"
|
||||
>
|
||||
<Link
|
||||
to={getTogglePath(projectId, name)}
|
||||
className={styles.link}
|
||||
>
|
||||
<span data-loading>{name}</span>
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classNames(
|
||||
styles.tableCell,
|
||||
styles.tableCellCreated
|
||||
)}
|
||||
align="left"
|
||||
>
|
||||
<CreatedAt time={createdAt} />
|
||||
</TableCell>
|
||||
|
||||
{environments.map((env: IEnvironments) => {
|
||||
return (
|
||||
<TableCell
|
||||
className={classNames(
|
||||
styles.tableCell,
|
||||
styles.tableCellEnv
|
||||
)}
|
||||
align="center"
|
||||
key={env.name}
|
||||
>
|
||||
<span data-loading style={{ display: 'block' }}>
|
||||
<PermissionSwitch
|
||||
checked={env.enabled}
|
||||
environmentId={env.name}
|
||||
projectId={projectId}
|
||||
permission={UPDATE_FEATURE_ENVIRONMENT}
|
||||
inputProps={{ 'aria-label': env.name }}
|
||||
ref={ref}
|
||||
onClick={() => {
|
||||
handleToggle(env);
|
||||
setEnvironmentName(env.name);
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
<EnvironmentStrategyDialog
|
||||
open={showInfoBox}
|
||||
onClose={closeInfoBox}
|
||||
projectId={projectId}
|
||||
featureId={name}
|
||||
environmentName={environmentName}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeatureToggleListNewItem;
|
@ -1,105 +0,0 @@
|
||||
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
||||
|
||||
const loadingFeatures: IFeatureToggleListItem[] = [
|
||||
{
|
||||
type: 'release',
|
||||
name: 'loading1',
|
||||
createdAt: '2006-01-02T15:04:05Z',
|
||||
environments: [
|
||||
{
|
||||
name: ':global:',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'loadg2',
|
||||
createdAt: '2006-01-02T15:04:05Z',
|
||||
environments: [
|
||||
{
|
||||
name: ':global:',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'loading3',
|
||||
createdAt: '2006-01-02T15:04:05Z',
|
||||
environments: [
|
||||
{
|
||||
name: ':global:',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'loadi4',
|
||||
createdAt: '2006-01-02T15:04:05Z',
|
||||
environments: [
|
||||
{
|
||||
name: ':global:',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'loadi5',
|
||||
createdAt: '2006-01-02T15:04:05Z',
|
||||
environments: [
|
||||
{
|
||||
name: ':global:',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'loadg6',
|
||||
createdAt: '2006-01-02T15:04:05Z',
|
||||
environments: [
|
||||
{
|
||||
name: ':global:',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'loading7',
|
||||
createdAt: '2006-01-02T15:04:05Z',
|
||||
environments: [
|
||||
{
|
||||
name: ':global:',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'ln8',
|
||||
createdAt: '2006-01-02T15:04:05Z',
|
||||
environments: [
|
||||
{
|
||||
name: ':global:',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'release',
|
||||
name: 'load9',
|
||||
createdAt: '2006-01-02T15:04:05Z',
|
||||
environments: [
|
||||
{
|
||||
name: ':global:',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default loadingFeatures;
|
@ -136,5 +136,3 @@ const useFeatureMetricsApplications = (featureId: string): Set<string> => {
|
||||
|
||||
return new Set(applications);
|
||||
};
|
||||
|
||||
export default FeatureMetrics;
|
||||
|
@ -1,98 +0,0 @@
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import * as jsonpatch from 'fast-json-patch';
|
||||
import { TextField } from '@mui/material';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import FeatureTypeSelect from './FeatureTypeSelect/FeatureTypeSelect';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import useToast from 'hooks/useToast';
|
||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
const FeatureSettingsMetadata = () => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const featureId = useRequiredPathParam('featureId');
|
||||
const { feature, refetchFeature } = useFeature(projectId, featureId);
|
||||
const [description, setDescription] = useState(feature.description);
|
||||
const [type, setType] = useState(feature.type);
|
||||
const editable = hasAccess(UPDATE_FEATURE, projectId);
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const [dirty, setDirty] = useState(false);
|
||||
const { patchFeatureToggle } = useFeatureApi();
|
||||
|
||||
useEffect(() => {
|
||||
setType(feature.type);
|
||||
setDescription(feature.description);
|
||||
}, [feature]);
|
||||
|
||||
useEffect(() => {
|
||||
if (description !== feature.description || type !== feature.type) {
|
||||
setDirty(true);
|
||||
return;
|
||||
}
|
||||
setDirty(false);
|
||||
/* eslint-disable-next-line */
|
||||
}, [description, type]);
|
||||
|
||||
const createPatch = () => {
|
||||
const comparison = { ...feature, type, description };
|
||||
const patch = jsonpatch.compare(feature, comparison);
|
||||
return patch;
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const patch = createPatch();
|
||||
await patchFeatureToggle(projectId, featureId, patch);
|
||||
setToastData({
|
||||
title: 'Updated metadata',
|
||||
type: 'success',
|
||||
text: 'Successfully updated feature toggle metadata',
|
||||
});
|
||||
setDirty(false);
|
||||
refetchFeature();
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FeatureTypeSelect
|
||||
value={type}
|
||||
id="feature-type-select"
|
||||
onChange={setType}
|
||||
label="Feature type"
|
||||
editable={editable}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Description"
|
||||
required
|
||||
multiline
|
||||
rows={4}
|
||||
variant="outlined"
|
||||
value={description}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={dirty}
|
||||
show={
|
||||
<PermissionButton
|
||||
permission={UPDATE_FEATURE}
|
||||
onClick={handleSubmit}
|
||||
projectId={projectId}
|
||||
>
|
||||
Save changes
|
||||
</PermissionButton>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeatureSettingsMetadata;
|
@ -1,119 +0,0 @@
|
||||
import { useContext, useMemo, useState } from 'react';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { SearchField } from 'component/common/SearchField/SearchField';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
|
||||
import FeatureToggleListNew from 'component/feature/FeatureToggleListNew/FeatureToggleListNew';
|
||||
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
||||
import { getCreateTogglePath } from 'utils/routePathHelpers';
|
||||
import { useStyles } from './ProjectFeatureToggles.styles';
|
||||
import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import classnames from 'classnames';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
interface IProjectFeatureTogglesProps {
|
||||
features: IFeatureToggleListItem[];
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const ProjectFeatureToggles = ({
|
||||
features,
|
||||
loading,
|
||||
}: IProjectFeatureTogglesProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const navigate = useNavigate();
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { uiConfig } = useUiConfig();
|
||||
const [filter, setFilter] = useState('');
|
||||
|
||||
const filteredFeatures = useMemo(() => {
|
||||
const regExp = new RegExp(filter, 'i');
|
||||
return filter
|
||||
? features.filter(feature => regExp.test(feature.name))
|
||||
: features;
|
||||
}, [features, filter]);
|
||||
|
||||
return (
|
||||
<PageContent
|
||||
className={styles.container}
|
||||
bodyClass={styles.bodyClass}
|
||||
header={
|
||||
<PageHeader
|
||||
className={styles.title}
|
||||
title={`Project feature toggles (${filteredFeatures.length})`}
|
||||
actions={
|
||||
<div className={styles.actionsContainer}>
|
||||
<SearchField
|
||||
initialValue={filter}
|
||||
updateValue={setFilter}
|
||||
className={classnames(styles.search, {
|
||||
skeleton: loading,
|
||||
})}
|
||||
/>
|
||||
|
||||
<ResponsiveButton
|
||||
onClick={() =>
|
||||
navigate(
|
||||
getCreateTogglePath(
|
||||
projectId,
|
||||
uiConfig.flags.E
|
||||
)
|
||||
)
|
||||
}
|
||||
maxWidth="700px"
|
||||
Icon={Add}
|
||||
projectId={projectId}
|
||||
permission={CREATE_FEATURE}
|
||||
className={styles.button}
|
||||
>
|
||||
New feature toggle
|
||||
</ResponsiveButton>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={filteredFeatures.length > 0}
|
||||
show={
|
||||
<FeatureToggleListNew
|
||||
features={filteredFeatures}
|
||||
loading={loading}
|
||||
projectId={projectId}
|
||||
/>
|
||||
}
|
||||
elseShow={
|
||||
<>
|
||||
<p data-loading className={styles.noTogglesFound}>
|
||||
No feature toggles added yet.
|
||||
</p>
|
||||
<ConditionallyRender
|
||||
condition={hasAccess(CREATE_FEATURE, projectId)}
|
||||
show={
|
||||
<Link
|
||||
to={getCreateTogglePath(
|
||||
projectId,
|
||||
uiConfig.flags.E
|
||||
)}
|
||||
className={styles.link}
|
||||
data-loading
|
||||
>
|
||||
Add your first toggle
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</PageContent>
|
||||
);
|
||||
};
|
@ -1,36 +0,0 @@
|
||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import useAPI from '../useApi/useApi';
|
||||
|
||||
const useToggleFeatureByEnv = (projectId: string, name: string) => {
|
||||
const { refetch } = useProject(projectId);
|
||||
const { makeRequest, createRequest, errors } = useAPI({
|
||||
propagateErrors: true,
|
||||
});
|
||||
|
||||
const toggleFeatureByEnvironment = async (
|
||||
env: string,
|
||||
enabled: boolean
|
||||
) => {
|
||||
const path = getToggleAPIPath(env, enabled);
|
||||
const req = createRequest(path, { method: 'POST' });
|
||||
|
||||
try {
|
||||
const res = await makeRequest(req.caller, req.id);
|
||||
refetch();
|
||||
return res;
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
const getToggleAPIPath = (env: string, enabled: boolean) => {
|
||||
if (enabled) {
|
||||
return `api/admin/projects/${projectId}/features/${name}/environments/${env}/off`;
|
||||
}
|
||||
return `api/admin/projects/${projectId}/features/${name}/environments/${env}/on`;
|
||||
};
|
||||
|
||||
return { toggleFeatureByEnvironment, errors };
|
||||
};
|
||||
|
||||
export default useToggleFeatureByEnv;
|
@ -18,11 +18,6 @@ export interface IUsersSortOutput {
|
||||
setSort: React.Dispatch<React.SetStateAction<IUsersSort>>;
|
||||
}
|
||||
|
||||
export interface IUsersFilterSortOption {
|
||||
type: UsersSortType;
|
||||
name: string;
|
||||
}
|
||||
|
||||
// Store the users sort state globally, and in localStorage.
|
||||
// When changing the format of IUsersSort, change the version as well.
|
||||
const useUsersSortState = createPersistentGlobalStateHook<IUsersSort>(
|
||||
@ -45,15 +40,6 @@ export const useUsersSort = (users: IUser[]): IUsersSortOutput => {
|
||||
};
|
||||
};
|
||||
|
||||
export const createUsersFilterSortOptions = (): IUsersFilterSortOption[] => {
|
||||
return [
|
||||
{ type: 'created', name: 'Created' },
|
||||
{ type: 'name', name: 'Name' },
|
||||
{ type: 'role', name: 'Role' },
|
||||
{ type: 'last-seen', name: 'Last seen' },
|
||||
];
|
||||
};
|
||||
|
||||
const sortAscendingUsers = (
|
||||
users: IUser[],
|
||||
roles: IRole[],
|
||||
|
@ -14,14 +14,6 @@ export interface IEnvironments {
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface IFeatureTogglePayload {
|
||||
description: string;
|
||||
name: string;
|
||||
projectId: string;
|
||||
type: string;
|
||||
impressionData: boolean;
|
||||
}
|
||||
|
||||
export interface IFeatureToggle {
|
||||
stale: boolean;
|
||||
archived: boolean;
|
||||
|
@ -1,8 +0,0 @@
|
||||
export interface IInvoice {
|
||||
amountFormatted: string;
|
||||
invoicePDF: string;
|
||||
invoiceURL: string;
|
||||
paid: boolean;
|
||||
status: string;
|
||||
dueDate?: Date;
|
||||
}
|
@ -3,24 +3,6 @@ import { formatDateYMDHMS } from 'utils/formatDate';
|
||||
import { ILocationSettings } from 'hooks/useLocationSettings';
|
||||
import { CURRENT_TIME_CONTEXT_FIELD } from 'utils/operatorsForContext';
|
||||
|
||||
export const formatConstraintValuesOrValue = (
|
||||
constraint: IConstraint,
|
||||
locationSettings: ILocationSettings
|
||||
): string | undefined => {
|
||||
return (
|
||||
formatConstraintValues(constraint) ??
|
||||
formatConstraintValue(constraint, locationSettings)
|
||||
);
|
||||
};
|
||||
|
||||
export const formatConstraintValues = (
|
||||
constraint: IConstraint
|
||||
): string | undefined => {
|
||||
if (constraint.values && constraint.values.length > 0) {
|
||||
return constraint.values.join(', ');
|
||||
}
|
||||
};
|
||||
|
||||
export const formatConstraintValue = (
|
||||
constraint: IConstraint,
|
||||
locationSettings: ILocationSettings
|
||||
|
@ -31,7 +31,6 @@ export const FEATURE_ENVIRONMENT_ACCORDION = 'FEATURE_ENVIRONMENT_ACCORDION';
|
||||
export const ADD_NEW_STRATEGY_ID = 'ADD_NEW_STRATEGY_ID';
|
||||
export const ROLLOUT_SLIDER_ID = 'ROLLOUT_SLIDER_ID';
|
||||
export const DIALOGUE_CONFIRM_ID = 'DIALOGUE_CONFIRM_ID';
|
||||
export const CONSTRAINT_AUTOCOMPLETE_ID = 'CONSTRAINT_AUTOCOMPLETE_ID';
|
||||
export const FLEXIBLE_STRATEGY_STICKINESS_ID =
|
||||
'FLEXIBLE_STRATEGY_STICKINESS_ID';
|
||||
export const FLEXIBLE_STRATEGY_GROUP_ID = 'FLEXIBLE_STRATEGY_GROUP_ID';
|
||||
|
Loading…
Reference in New Issue
Block a user