1
0
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:
olav 2022-05-27 13:28:58 +02:00 committed by GitHub
parent ae012d62e6
commit eb5e83cdb4
19 changed files with 2 additions and 1047 deletions

View File

@ -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();

View File

@ -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),
},
}));

View File

@ -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>
);
}
);

View File

@ -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';

View File

@ -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) {

View File

@ -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',
},
},
}));

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -136,5 +136,3 @@ const useFeatureMetricsApplications = (featureId: string): Set<string> => {
return new Set(applications);
};
export default FeatureMetrics;

View File

@ -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;

View File

@ -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>
);
};

View File

@ -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;

View File

@ -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[],

View File

@ -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;

View File

@ -1,8 +0,0 @@
export interface IInvoice {
amountFormatted: string;
invoicePDF: string;
invoiceURL: string;
paid: boolean;
status: string;
dueDate?: Date;
}

View File

@ -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

View File

@ -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';