mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-21 13:47:39 +02:00
Refactor: convert jsx files to typescript (#881)
* refactor: convert remaining js files to typescript * refactor: conditionally render remove index * refactor: dialog component to tsx * refactor: migrate some files from jsx to tsx * refactor: convert dropdown element to tsx * refactor: feature toggle list to tsx * refactor: update context name in use overrides * refactor: variant overrides to tsx refactor: remove unused strategy constraint file * fix: tsx imports * fix: update refectored components after rebase * refactor: rename report list files to tsx * fix: project health list types * refactor: addon form - add types * refactor: copy feature component types * fix: projects toggle style after tsx refactor * refactor: update ts types from openapi * fix: ts refactor changes after review * fix: header title prop * fix: update after PR comments * add test to useoverrides hook * fix conditionally render time ago * fix: toggle list empty tooltip * fix: remove unused variable * remove unused variable * fix: remove faulty snapshot
This commit is contained in:
parent
00341c6d67
commit
23a874d051
@ -1,4 +1,6 @@
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { createElement } from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { FeedbackNPS } from 'component/feedback/FeedbackNPS/FeedbackNPS';
|
||||
import { LayoutPicker } from 'component/layout/LayoutPicker/LayoutPicker';
|
||||
import Loader from 'component/common/Loader/Loader';
|
||||
@ -6,12 +8,12 @@ import NotFound from 'component/common/NotFound/NotFound';
|
||||
import ProtectedRoute from 'component/common/ProtectedRoute/ProtectedRoute';
|
||||
import SWRProvider from 'component/providers/SWRProvider/SWRProvider';
|
||||
import ToastRenderer from 'component/common/ToastRenderer/ToastRenderer';
|
||||
import styles from 'component/styles.module.scss';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { routes } from 'component/menu/routes';
|
||||
import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails';
|
||||
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
||||
import { SplashPageRedirect } from 'component/splash/SplashPageRedirect/SplashPageRedirect';
|
||||
import { IRoute } from 'interfaces/route';
|
||||
import styles from 'component/styles.module.scss';
|
||||
|
||||
export const App = () => {
|
||||
const { authDetails } = useAuthDetails();
|
||||
@ -23,8 +25,7 @@ export const App = () => {
|
||||
return !isLoggedIn;
|
||||
};
|
||||
|
||||
// Change this to IRoute once snags with HashRouter and TS is worked out
|
||||
const renderRoute = (route: any) => {
|
||||
const renderRoute = (route: IRoute) => {
|
||||
if (route.type === 'protected') {
|
||||
const unauthorized = isUnauthorized();
|
||||
return (
|
||||
@ -40,13 +41,7 @@ export const App = () => {
|
||||
<Route
|
||||
key={route.path}
|
||||
path={route.path}
|
||||
render={props => (
|
||||
<route.component
|
||||
{...props}
|
||||
isUnauthorized={isUnauthorized}
|
||||
authDetails={authDetails}
|
||||
/>
|
||||
)}
|
||||
render={() => createElement(route.component, {}, null)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -65,8 +60,9 @@ export const App = () => {
|
||||
exact
|
||||
path="/"
|
||||
unauthorized={isUnauthorized()}
|
||||
component={Redirect}
|
||||
renderProps={{ to: '/features' }}
|
||||
component={() => (
|
||||
<Redirect to="/features" />
|
||||
)}
|
||||
/>
|
||||
{routes.map(renderRoute)}
|
||||
<Route path="/404" component={NotFound} />
|
||||
|
@ -2,7 +2,7 @@ import classnames from 'classnames';
|
||||
import { Paper } from '@material-ui/core';
|
||||
import CheckIcon from '@material-ui/icons/Check';
|
||||
import ReportProblemOutlinedIcon from '@material-ui/icons/ReportProblemOutlined';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import styles from './ReportCard.module.scss';
|
||||
import ReactTimeAgo from 'react-timeago';
|
||||
import { IProjectHealthReport } from 'interfaces/project';
|
||||
|
@ -1,25 +1,34 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, VFC } from 'react';
|
||||
import { Paper, MenuItem } from '@material-ui/core';
|
||||
import PropTypes from 'prop-types';
|
||||
import ReportToggleListItem from './ReportToggleListItem/ReportToggleListItem';
|
||||
import ReportToggleListHeader from './ReportToggleListHeader/ReportToggleListHeader';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useFeaturesSort } from 'hooks/useFeaturesSort';
|
||||
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import DropdownMenu from 'component/common/DropdownMenu/DropdownMenu';
|
||||
import {
|
||||
getObjectProperties,
|
||||
getCheckedState,
|
||||
applyCheckedToFeatures,
|
||||
} from '../utils';
|
||||
import { ReportToggleListItem } from './ReportToggleListItem/ReportToggleListItem';
|
||||
import { ReportToggleListHeader } from './ReportToggleListHeader/ReportToggleListHeader';
|
||||
import { useStyles } from './ReportToggleList.styles';
|
||||
import { useFeaturesSort } from 'hooks/useFeaturesSort';
|
||||
|
||||
/* FLAG TO TOGGLE UNFINISHED BULK ACTIONS FEATURE */
|
||||
const BULK_ACTIONS_ON = false;
|
||||
|
||||
const ReportToggleList = ({ features, selectedProject }) => {
|
||||
interface IReportToggleListProps {
|
||||
selectedProject: string;
|
||||
features: IFeatureToggleListItem[];
|
||||
}
|
||||
|
||||
export const ReportToggleList: VFC<IReportToggleListProps> = ({
|
||||
features,
|
||||
selectedProject,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
const [checkAll, setCheckAll] = useState(false);
|
||||
const [localFeatures, setFeatures] = useState([]);
|
||||
const [localFeatures, setFeatures] = useState<IFeatureToggleListItem[]>([]);
|
||||
// @ts-expect-error
|
||||
const { setSort, sorted } = useFeaturesSort(localFeatures);
|
||||
|
||||
useEffect(() => {
|
||||
@ -32,10 +41,12 @@ const ReportToggleList = ({ features, selectedProject }) => {
|
||||
'stale',
|
||||
'type'
|
||||
),
|
||||
// @ts-expect-error
|
||||
checked: getCheckedState(feature.name, features),
|
||||
setFeatures,
|
||||
}));
|
||||
|
||||
// @ts-expect-error
|
||||
setFeatures(formattedFeatures);
|
||||
}, [features, selectedProject]);
|
||||
|
||||
@ -50,6 +61,7 @@ const ReportToggleList = ({ features, selectedProject }) => {
|
||||
|
||||
const renderListRows = () =>
|
||||
sorted.map(feature => (
|
||||
// @ts-expect-error
|
||||
<ReportToggleListItem
|
||||
key={feature.name}
|
||||
{...feature}
|
||||
@ -85,6 +97,7 @@ const ReportToggleList = ({ features, selectedProject }) => {
|
||||
<ReportToggleListHeader
|
||||
handleCheckAll={handleCheckAll}
|
||||
checkAll={checkAll}
|
||||
// @ts-expect-error
|
||||
setSort={setSort}
|
||||
bulkActionsOn={BULK_ACTIONS_ON}
|
||||
/>
|
||||
@ -95,10 +108,3 @@ const ReportToggleList = ({ features, selectedProject }) => {
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
ReportToggleList.propTypes = {
|
||||
selectedProject: PropTypes.string.isRequired,
|
||||
features: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
export default ReportToggleList;
|
@ -1,25 +1,27 @@
|
||||
import React from 'react';
|
||||
import { Dispatch, SetStateAction, VFC } from 'react';
|
||||
import { Checkbox } from '@material-ui/core';
|
||||
import UnfoldMoreOutlinedIcon from '@material-ui/icons/UnfoldMoreOutlined';
|
||||
import PropTypes from 'prop-types';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import {
|
||||
NAME,
|
||||
LAST_SEEN,
|
||||
CREATED,
|
||||
EXPIRED,
|
||||
STATUS,
|
||||
} from 'component/Reporting/constants';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ReportingSortType } from 'component/Reporting/constants';
|
||||
import { useStyles } from '../ReportToggleList.styles';
|
||||
|
||||
const ReportToggleListHeader = ({
|
||||
interface IReportToggleListHeaderProps {
|
||||
checkAll: boolean;
|
||||
setSort: Dispatch<
|
||||
SetStateAction<{ type: ReportingSortType; desc?: boolean }>
|
||||
>;
|
||||
bulkActionsOn: boolean;
|
||||
handleCheckAll: () => void;
|
||||
}
|
||||
|
||||
export const ReportToggleListHeader: VFC<IReportToggleListHeaderProps> = ({
|
||||
handleCheckAll,
|
||||
checkAll,
|
||||
setSort,
|
||||
bulkActionsOn,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
const handleSort = type => {
|
||||
const handleSort = (type: ReportingSortType) => {
|
||||
setSort(prev => ({
|
||||
type,
|
||||
desc: !prev.desc,
|
||||
@ -47,7 +49,7 @@ const ReportToggleListHeader = ({
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
style={{ width: '150px' }}
|
||||
onClick={() => handleSort(NAME)}
|
||||
onClick={() => handleSort('name')}
|
||||
>
|
||||
Name
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
@ -56,7 +58,7 @@ const ReportToggleListHeader = ({
|
||||
role="button"
|
||||
className={styles.hideColumnLastSeen}
|
||||
tabIndex={0}
|
||||
onClick={() => handleSort(LAST_SEEN)}
|
||||
onClick={() => handleSort('last-seen')}
|
||||
>
|
||||
Last seen
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
@ -65,7 +67,7 @@ const ReportToggleListHeader = ({
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={styles.hideColumn}
|
||||
onClick={() => handleSort(CREATED)}
|
||||
onClick={() => handleSort('created')}
|
||||
>
|
||||
Created
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
@ -74,7 +76,7 @@ const ReportToggleListHeader = ({
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={styles.hideColumn}
|
||||
onClick={() => handleSort(EXPIRED)}
|
||||
onClick={() => handleSort('expired')}
|
||||
>
|
||||
Expired
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
@ -83,7 +85,7 @@ const ReportToggleListHeader = ({
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={styles.hideColumnStatus}
|
||||
onClick={() => handleSort(STATUS)}
|
||||
onClick={() => handleSort('status')}
|
||||
>
|
||||
Status
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
@ -91,7 +93,7 @@ const ReportToggleListHeader = ({
|
||||
<th
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => handleSort(EXPIRED)}
|
||||
onClick={() => handleSort('expired')}
|
||||
>
|
||||
Report
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
@ -100,12 +102,3 @@ const ReportToggleListHeader = ({
|
||||
</thead>
|
||||
);
|
||||
};
|
||||
|
||||
ReportToggleListHeader.propTypes = {
|
||||
checkAll: PropTypes.bool.isRequired,
|
||||
setSort: PropTypes.func.isRequired,
|
||||
bulkActionsOn: PropTypes.bool.isRequired,
|
||||
handleCheckAll: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ReportToggleListHeader;
|
@ -1,159 +0,0 @@
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Checkbox } from '@material-ui/core';
|
||||
import CheckIcon from '@material-ui/icons/Check';
|
||||
import ReportProblemOutlinedIcon from '@material-ui/icons/ReportProblemOutlined';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import FeatureStatus from 'component/feature/FeatureView/FeatureStatus/FeatureStatus';
|
||||
import {
|
||||
pluralize,
|
||||
getDates,
|
||||
expired,
|
||||
toggleExpiryByTypeMap,
|
||||
getDiffInDays,
|
||||
} from 'component/Reporting/utils';
|
||||
import { KILLSWITCH, PERMISSION } from 'constants/featureToggleTypes';
|
||||
import { useStyles } from '../ReportToggleList.styles';
|
||||
import { getTogglePath } from 'utils/routePathHelpers';
|
||||
|
||||
const ReportToggleListItem = ({
|
||||
name,
|
||||
stale,
|
||||
lastSeenAt,
|
||||
createdAt,
|
||||
project,
|
||||
type,
|
||||
checked,
|
||||
bulkActionsOn,
|
||||
setFeatures,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
const nameMatches = feature => feature.name === name;
|
||||
|
||||
const handleChange = () => {
|
||||
setFeatures(prevState => {
|
||||
const newState = [...prevState];
|
||||
|
||||
return newState.map(feature => {
|
||||
if (nameMatches(feature)) {
|
||||
return { ...feature, checked: !feature.checked };
|
||||
}
|
||||
return feature;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const formatCreatedAt = () => {
|
||||
const [date, now] = getDates(createdAt);
|
||||
|
||||
const diff = getDiffInDays(date, now);
|
||||
if (diff === 0) return '1 day';
|
||||
|
||||
const formatted = pluralize(diff, 'day');
|
||||
|
||||
return `${formatted} ago`;
|
||||
};
|
||||
|
||||
const formatExpiredAt = () => {
|
||||
if (type === KILLSWITCH || type === PERMISSION) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
const [date, now] = getDates(createdAt);
|
||||
const diff = getDiffInDays(date, now);
|
||||
|
||||
if (expired(diff, type)) {
|
||||
const result = diff - toggleExpiryByTypeMap[type];
|
||||
if (result === 0) return '1 day';
|
||||
|
||||
return pluralize(result, 'day');
|
||||
}
|
||||
return 'N/A';
|
||||
};
|
||||
|
||||
const formatLastSeenAt = () => {
|
||||
return (
|
||||
<FeatureStatus lastSeenAt={lastSeenAt} tooltipPlacement="bottom" />
|
||||
);
|
||||
};
|
||||
|
||||
const renderStatus = (icon, text) => (
|
||||
<span className={styles.reportStatus}>
|
||||
{icon}
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
|
||||
const formatReportStatus = () => {
|
||||
if (type === KILLSWITCH || type === PERMISSION) {
|
||||
return renderStatus(
|
||||
<CheckIcon className={styles.reportIcon} />,
|
||||
'Healthy'
|
||||
);
|
||||
}
|
||||
|
||||
const [date, now] = getDates(createdAt);
|
||||
const diff = getDiffInDays(date, now);
|
||||
|
||||
if (expired(diff, type)) {
|
||||
return renderStatus(
|
||||
<ReportProblemOutlinedIcon className={styles.reportIcon} />,
|
||||
'Potentially stale'
|
||||
);
|
||||
}
|
||||
|
||||
return renderStatus(
|
||||
<CheckIcon className={styles.reportIcon} />,
|
||||
'Healthy'
|
||||
);
|
||||
};
|
||||
|
||||
const statusClasses = classnames(styles.active, styles.hideColumnStatus, {
|
||||
[styles.stale]: stale,
|
||||
});
|
||||
|
||||
return (
|
||||
<tr className={styles.tableRow}>
|
||||
<ConditionallyRender
|
||||
condition={bulkActionsOn}
|
||||
show={
|
||||
<td>
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
value={checked}
|
||||
onChange={handleChange}
|
||||
className={styles.checkbox}
|
||||
/>
|
||||
</td>
|
||||
}
|
||||
/>
|
||||
<td>
|
||||
<Link to={getTogglePath(project, name)} className={styles.link}>
|
||||
{name}
|
||||
</Link>
|
||||
</td>
|
||||
<td className={styles.hideColumnLastSeen}>{formatLastSeenAt()}</td>
|
||||
<td className={styles.hideColumn}>{formatCreatedAt()}</td>
|
||||
<td className={`${styles.expired} ${styles.hideColumn}`}>
|
||||
{formatExpiredAt()}
|
||||
</td>
|
||||
<td className={statusClasses}>{stale ? 'Stale' : 'Active'}</td>
|
||||
<td>{formatReportStatus()}</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
ReportToggleListItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
stale: PropTypes.bool.isRequired,
|
||||
lastSeenAt: PropTypes.string,
|
||||
createdAt: PropTypes.string.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
checked: PropTypes.bool.isRequired,
|
||||
bulkActionsOn: PropTypes.bool.isRequired,
|
||||
setFeatures: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default React.memo(ReportToggleListItem);
|
@ -0,0 +1,173 @@
|
||||
import { memo, ReactNode } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Checkbox } from '@material-ui/core';
|
||||
import CheckIcon from '@material-ui/icons/Check';
|
||||
import ReportProblemOutlinedIcon from '@material-ui/icons/ReportProblemOutlined';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import FeatureStatus from 'component/feature/FeatureView/FeatureStatus/FeatureStatus';
|
||||
import {
|
||||
pluralize,
|
||||
getDates,
|
||||
expired,
|
||||
toggleExpiryByTypeMap,
|
||||
getDiffInDays,
|
||||
} from 'component/Reporting/utils';
|
||||
import { KILLSWITCH, PERMISSION } from 'constants/featureToggleTypes';
|
||||
import { useStyles } from '../ReportToggleList.styles';
|
||||
import { getTogglePath } from 'utils/routePathHelpers';
|
||||
|
||||
interface IReportToggleListItemProps {
|
||||
name: string;
|
||||
stale: boolean;
|
||||
project: string;
|
||||
lastSeenAt?: string;
|
||||
createdAt: string;
|
||||
type: string;
|
||||
checked: boolean;
|
||||
bulkActionsOn: boolean;
|
||||
setFeatures: () => void;
|
||||
}
|
||||
|
||||
export const ReportToggleListItem = memo<IReportToggleListItemProps>(
|
||||
({
|
||||
name,
|
||||
stale,
|
||||
lastSeenAt,
|
||||
createdAt,
|
||||
project,
|
||||
type,
|
||||
checked,
|
||||
bulkActionsOn,
|
||||
setFeatures,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
const nameMatches = (feature: { name: string }) =>
|
||||
feature.name === name;
|
||||
|
||||
const handleChange = () => {
|
||||
// @ts-expect-error
|
||||
setFeatures(prevState => {
|
||||
const newState = [...prevState];
|
||||
|
||||
return newState.map(feature => {
|
||||
if (nameMatches(feature)) {
|
||||
return { ...feature, checked: !feature.checked };
|
||||
}
|
||||
return feature;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const formatCreatedAt = () => {
|
||||
const [date, now] = getDates(createdAt);
|
||||
|
||||
const diff = getDiffInDays(date, now);
|
||||
if (diff === 0) return '1 day';
|
||||
|
||||
const formatted = pluralize(diff, 'day');
|
||||
|
||||
return `${formatted} ago`;
|
||||
};
|
||||
|
||||
const formatExpiredAt = () => {
|
||||
if (type === KILLSWITCH || type === PERMISSION) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
const [date, now] = getDates(createdAt);
|
||||
const diff = getDiffInDays(date, now);
|
||||
|
||||
if (expired(diff, type)) {
|
||||
const result = diff - toggleExpiryByTypeMap[type];
|
||||
if (result === 0) return '1 day';
|
||||
|
||||
return pluralize(result, 'day');
|
||||
}
|
||||
return 'N/A';
|
||||
};
|
||||
|
||||
const formatLastSeenAt = () => {
|
||||
return (
|
||||
<FeatureStatus
|
||||
lastSeenAt={lastSeenAt}
|
||||
tooltipPlacement="bottom"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderStatus = (icon: ReactNode, text: ReactNode) => (
|
||||
<span className={styles.reportStatus}>
|
||||
{icon}
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
|
||||
const formatReportStatus = () => {
|
||||
if (type === KILLSWITCH || type === PERMISSION) {
|
||||
return renderStatus(
|
||||
<CheckIcon className={styles.reportIcon} />,
|
||||
'Healthy'
|
||||
);
|
||||
}
|
||||
|
||||
const [date, now] = getDates(createdAt);
|
||||
const diff = getDiffInDays(date, now);
|
||||
|
||||
if (expired(diff, type)) {
|
||||
return renderStatus(
|
||||
<ReportProblemOutlinedIcon className={styles.reportIcon} />,
|
||||
'Potentially stale'
|
||||
);
|
||||
}
|
||||
|
||||
return renderStatus(
|
||||
<CheckIcon className={styles.reportIcon} />,
|
||||
'Healthy'
|
||||
);
|
||||
};
|
||||
|
||||
const statusClasses = classnames(
|
||||
styles.active,
|
||||
styles.hideColumnStatus,
|
||||
{
|
||||
[styles.stale]: stale,
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<tr className={styles.tableRow}>
|
||||
<ConditionallyRender
|
||||
condition={bulkActionsOn}
|
||||
show={
|
||||
<td>
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
value={checked}
|
||||
onChange={handleChange}
|
||||
className={styles.checkbox}
|
||||
/>
|
||||
</td>
|
||||
}
|
||||
/>
|
||||
<td>
|
||||
<Link
|
||||
to={getTogglePath(project, name)}
|
||||
className={styles.link}
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
</td>
|
||||
<td className={styles.hideColumnLastSeen}>
|
||||
{formatLastSeenAt()}
|
||||
</td>
|
||||
<td className={styles.hideColumn}>{formatCreatedAt()}</td>
|
||||
<td className={`${styles.expired} ${styles.hideColumn}`}>
|
||||
{formatExpiredAt()}
|
||||
</td>
|
||||
<td className={statusClasses}>{stale ? 'Stale' : 'Active'}</td>
|
||||
<td>{formatReportStatus()}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
);
|
@ -1,11 +1,7 @@
|
||||
/* SORT TYPES */
|
||||
export const NAME = 'name';
|
||||
export const LAST_SEEN = 'last-seen';
|
||||
export const CREATED = 'created';
|
||||
export const EXPIRED = 'expired';
|
||||
export const STATUS = 'status';
|
||||
export const REPORT = 'report';
|
||||
|
||||
/* DAYS */
|
||||
export const FOURTYDAYS = 40;
|
||||
export const SEVENDAYS = 7;
|
||||
export type ReportingSortType =
|
||||
| 'name'
|
||||
| 'last-seen'
|
||||
| 'created'
|
||||
| 'expired'
|
||||
| 'status'
|
||||
| 'report';
|
||||
|
@ -3,13 +3,15 @@ import differenceInDays from 'date-fns/differenceInDays';
|
||||
|
||||
import { EXPERIMENT, OPERATIONAL, RELEASE } from 'constants/featureToggleTypes';
|
||||
|
||||
import { FOURTYDAYS, SEVENDAYS } from './constants';
|
||||
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
||||
|
||||
const FORTY_DAYS = 40;
|
||||
const SEVEN_DAYS = 7;
|
||||
|
||||
export const toggleExpiryByTypeMap: Record<string, number> = {
|
||||
[EXPERIMENT]: FOURTYDAYS,
|
||||
[RELEASE]: FOURTYDAYS,
|
||||
[OPERATIONAL]: SEVENDAYS,
|
||||
[EXPERIMENT]: FORTY_DAYS,
|
||||
[RELEASE]: FORTY_DAYS,
|
||||
[OPERATIONAL]: SEVEN_DAYS,
|
||||
};
|
||||
|
||||
export interface IFeatureToggleListItemCheck extends IFeatureToggleListItem {
|
||||
|
@ -5,12 +5,12 @@ import { styles as commonStyles } from 'component/common';
|
||||
import { IAddonProvider } from 'interfaces/addons';
|
||||
|
||||
interface IAddonProps {
|
||||
provider: IAddonProvider;
|
||||
provider?: IAddonProvider;
|
||||
checkedEvents: string[];
|
||||
setEventValue: (
|
||||
name: string
|
||||
) => (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
error: Record<string, string>;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const AddonEvents = ({
|
||||
|
@ -1,8 +1,17 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
useState,
|
||||
useEffect,
|
||||
ChangeEvent,
|
||||
VFC,
|
||||
ChangeEventHandler,
|
||||
FormEventHandler,
|
||||
MouseEventHandler,
|
||||
} from 'react';
|
||||
import { TextField, FormControlLabel, Switch, Button } from '@material-ui/core';
|
||||
import produce from 'immer';
|
||||
import { styles as commonStyles } from 'component/common';
|
||||
import { trim } from 'component/common/util';
|
||||
import { IAddon, IAddonProvider } from 'interfaces/addons';
|
||||
import { AddonParameters } from './AddonParameters/AddonParameters';
|
||||
import { AddonEvents } from './AddonEvents/AddonEvents';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
@ -11,22 +20,48 @@ import { useHistory } from 'react-router-dom';
|
||||
import useAddonsApi from 'hooks/api/actions/useAddonsApi/useAddonsApi';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
nameInput: {
|
||||
marginRight: '1.5rem',
|
||||
},
|
||||
formSection: { padding: '10px 28px' },
|
||||
buttonsSection: {
|
||||
padding: '10px 28px',
|
||||
'& > *': {
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export const AddonForm = ({ editMode, provider, addon, fetch }) => {
|
||||
interface IAddonFormProps {
|
||||
provider?: IAddonProvider;
|
||||
addon: IAddon;
|
||||
fetch: () => void;
|
||||
editMode: boolean;
|
||||
}
|
||||
|
||||
export const AddonForm: VFC<IAddonFormProps> = ({
|
||||
editMode,
|
||||
provider,
|
||||
addon: initialValues,
|
||||
fetch,
|
||||
}) => {
|
||||
const { createAddon, updateAddon } = useAddonsApi();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const history = useHistory();
|
||||
const styles = useStyles();
|
||||
|
||||
const [config, setConfig] = useState(addon);
|
||||
const [errors, setErrors] = useState({
|
||||
const [formValues, setFormValues] = useState(initialValues);
|
||||
const [errors, setErrors] = useState<{
|
||||
containsErrors: boolean;
|
||||
parameters: Record<string, string>;
|
||||
events?: string;
|
||||
general?: string;
|
||||
description?: string;
|
||||
}>({
|
||||
containsErrors: false,
|
||||
parameters: {},
|
||||
});
|
||||
const submitText = editMode ? 'Update' : 'Create';
|
||||
@ -38,68 +73,73 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {
|
||||
}, [fetch, provider]); // empty array => fetch only first time
|
||||
|
||||
useEffect(() => {
|
||||
setConfig({ ...addon });
|
||||
setFormValues({ ...initialValues });
|
||||
/* eslint-disable-next-line */
|
||||
}, [addon.description, addon.provider]);
|
||||
}, [initialValues.description, initialValues.provider]);
|
||||
|
||||
useEffect(() => {
|
||||
if (provider && !config.provider) {
|
||||
setConfig({ ...addon, provider: provider.name });
|
||||
if (provider && !formValues.provider) {
|
||||
setFormValues({ ...initialValues, provider: provider.name });
|
||||
}
|
||||
}, [provider, addon, config.provider]);
|
||||
}, [provider, initialValues, formValues.provider]);
|
||||
|
||||
const setFieldValue = field => evt => {
|
||||
evt.preventDefault();
|
||||
const newConfig = { ...config };
|
||||
newConfig[field] = evt.target.value;
|
||||
setConfig(newConfig);
|
||||
const setFieldValue =
|
||||
(field: string): ChangeEventHandler<HTMLInputElement> =>
|
||||
event => {
|
||||
event.preventDefault();
|
||||
setFormValues({ ...formValues, [field]: event.target.value });
|
||||
};
|
||||
|
||||
const onEnabled: MouseEventHandler = event => {
|
||||
event.preventDefault();
|
||||
setFormValues(({ enabled }) => ({ ...formValues, enabled: !enabled }));
|
||||
};
|
||||
|
||||
const onEnabled = evt => {
|
||||
evt.preventDefault();
|
||||
const enabled = !config.enabled;
|
||||
setConfig({ ...config, enabled });
|
||||
};
|
||||
const setParameterValue =
|
||||
(param: string): ChangeEventHandler<HTMLInputElement> =>
|
||||
event => {
|
||||
event.preventDefault();
|
||||
setFormValues(
|
||||
produce(draft => {
|
||||
draft.parameters[param] = event.target.value;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const setParameterValue = param => evt => {
|
||||
evt.preventDefault();
|
||||
const newConfig = { ...config };
|
||||
newConfig.parameters[param] = evt.target.value;
|
||||
setConfig(newConfig);
|
||||
};
|
||||
|
||||
const setEventValue = name => evt => {
|
||||
const newConfig = { ...config };
|
||||
if (evt.target.checked) {
|
||||
newConfig.events.push(name);
|
||||
} else {
|
||||
newConfig.events = newConfig.events.filter(e => e !== name);
|
||||
}
|
||||
setConfig(newConfig);
|
||||
setErrors({ ...errors, events: undefined });
|
||||
};
|
||||
const setEventValue =
|
||||
(name: string) => (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const newConfig = { ...formValues };
|
||||
if (event.target.checked) {
|
||||
newConfig.events.push(name);
|
||||
} else {
|
||||
newConfig.events = newConfig.events.filter(e => e !== name);
|
||||
}
|
||||
setFormValues(newConfig);
|
||||
setErrors({ ...errors, events: undefined });
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
const onSubmit = async evt => {
|
||||
evt.preventDefault();
|
||||
const onSubmit: FormEventHandler<HTMLFormElement> = async event => {
|
||||
event.preventDefault();
|
||||
if (!provider) return;
|
||||
|
||||
const updatedErrors = cloneDeep(errors);
|
||||
updatedErrors.containsErrors = false;
|
||||
|
||||
// Validations
|
||||
if (config.events.length === 0) {
|
||||
if (formValues.events.length === 0) {
|
||||
updatedErrors.events = 'You must listen to at least one event';
|
||||
updatedErrors.containsErrors = true;
|
||||
}
|
||||
|
||||
provider.parameters.forEach(p => {
|
||||
const value = trim(config.parameters[p.name]);
|
||||
if (p.required && !value) {
|
||||
updatedErrors.parameters[p.name] = 'This field is required';
|
||||
provider.parameters.forEach(parameterConfig => {
|
||||
const value = trim(formValues.parameters[parameterConfig.name]);
|
||||
if (parameterConfig.required && !value) {
|
||||
updatedErrors.parameters[parameterConfig.name] =
|
||||
'This field is required';
|
||||
updatedErrors.containsErrors = true;
|
||||
}
|
||||
});
|
||||
@ -111,14 +151,14 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {
|
||||
|
||||
try {
|
||||
if (editMode) {
|
||||
await updateAddon(config);
|
||||
await updateAddon(formValues);
|
||||
history.push('/addons');
|
||||
setToastData({
|
||||
type: 'success',
|
||||
title: 'Addon updated successfully',
|
||||
});
|
||||
} else {
|
||||
await createAddon(config);
|
||||
await createAddon(formValues);
|
||||
history.push('/addons');
|
||||
setToastData({
|
||||
type: 'success',
|
||||
@ -126,11 +166,14 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {
|
||||
title: 'Addon created successfully',
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
setToastApiError({
|
||||
text: e.toString(),
|
||||
} catch (error) {
|
||||
const message = formatUnknownError(error);
|
||||
setToastApiError(message);
|
||||
setErrors({
|
||||
parameters: {},
|
||||
general: message,
|
||||
containsErrors: true,
|
||||
});
|
||||
setErrors({ parameters: {}, general: e.message });
|
||||
}
|
||||
};
|
||||
|
||||
@ -138,7 +181,7 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {
|
||||
name,
|
||||
description,
|
||||
documentationUrl = 'https://unleash.github.io/docs/addons',
|
||||
} = provider ? provider : {};
|
||||
} = provider ? provider : ({} as Partial<IAddonProvider>);
|
||||
|
||||
return (
|
||||
<PageContent headerContent={`Configure ${name} addon`}>
|
||||
@ -155,7 +198,7 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {
|
||||
size="small"
|
||||
label="Provider"
|
||||
name="provider"
|
||||
value={config.provider}
|
||||
value={formValues.provider}
|
||||
disabled
|
||||
variant="outlined"
|
||||
className={styles.nameInput}
|
||||
@ -163,24 +206,25 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={config.enabled}
|
||||
onChange={onEnabled}
|
||||
checked={formValues.enabled}
|
||||
onClick={onEnabled}
|
||||
/>
|
||||
}
|
||||
label={config.enabled ? 'Enabled' : 'Disabled'}
|
||||
label={formValues.enabled ? 'Enabled' : 'Disabled'}
|
||||
/>
|
||||
</section>
|
||||
<section className={styles.formSection}>
|
||||
<TextField
|
||||
size="small"
|
||||
style={{ width: '80%' }}
|
||||
rows={4}
|
||||
minRows={4}
|
||||
multiline
|
||||
label="Description"
|
||||
name="description"
|
||||
placeholder=""
|
||||
value={config.description}
|
||||
error={errors.description}
|
||||
value={formValues.description}
|
||||
error={Boolean(errors.description)}
|
||||
helperText={errors.description}
|
||||
onChange={setFieldValue('description')}
|
||||
variant="outlined"
|
||||
/>
|
||||
@ -188,7 +232,7 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {
|
||||
<section className={styles.formSection}>
|
||||
<AddonEvents
|
||||
provider={provider}
|
||||
checkedEvents={config.events}
|
||||
checkedEvents={formValues.events}
|
||||
setEventValue={setEventValue}
|
||||
error={errors.events}
|
||||
/>
|
||||
@ -196,13 +240,13 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {
|
||||
<section className={styles.formSection}>
|
||||
<AddonParameters
|
||||
provider={provider}
|
||||
config={config}
|
||||
errors={errors}
|
||||
config={formValues}
|
||||
parametersErrors={errors.parameters}
|
||||
editMode={editMode}
|
||||
setParameterValue={setParameterValue}
|
||||
/>
|
||||
</section>
|
||||
<section className={styles.formSection}>
|
||||
<section className={styles.buttonsSection}>
|
||||
<Button type="submit" color="primary" variant="contained">
|
||||
{submitText}
|
||||
</Button>
|
||||
@ -214,12 +258,3 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {
|
||||
</PageContent>
|
||||
);
|
||||
};
|
||||
|
||||
AddonForm.propTypes = {
|
||||
provider: PropTypes.object,
|
||||
addon: PropTypes.object.isRequired,
|
||||
fetch: PropTypes.func.isRequired,
|
||||
submit: PropTypes.func.isRequired,
|
||||
cancel: PropTypes.func.isRequired,
|
||||
editMode: PropTypes.bool.isRequired,
|
||||
};
|
@ -1,9 +1,6 @@
|
||||
import { TextField } from '@material-ui/core';
|
||||
import {
|
||||
IAddonConfig,
|
||||
IAddonProvider,
|
||||
IAddonProviderParams,
|
||||
} from 'interfaces/addons';
|
||||
import { IAddonConfig, IAddonProviderParams } from 'interfaces/addons';
|
||||
import { ChangeEventHandler } from 'react';
|
||||
|
||||
const resolveType = ({ type = 'text', sensitive = false }, value: string) => {
|
||||
if (sensitive && value === MASKED_VALUE) {
|
||||
@ -17,31 +14,29 @@ const resolveType = ({ type = 'text', sensitive = false }, value: string) => {
|
||||
|
||||
const MASKED_VALUE = '*****';
|
||||
|
||||
interface IAddonParameterProps {
|
||||
provider: IAddonProvider;
|
||||
errors: Record<string, string>;
|
||||
export interface IAddonParameterProps {
|
||||
parametersErrors: Record<string, string>;
|
||||
definition: IAddonProviderParams;
|
||||
setParameterValue: (param: string) => void;
|
||||
setParameterValue: (param: string) => ChangeEventHandler<HTMLInputElement>;
|
||||
config: IAddonConfig;
|
||||
}
|
||||
|
||||
export const AddonParameter = ({
|
||||
definition,
|
||||
config,
|
||||
errors,
|
||||
parametersErrors,
|
||||
setParameterValue,
|
||||
}: IAddonParameterProps) => {
|
||||
const value = config.parameters[definition.name] || '';
|
||||
const type = resolveType(definition, value);
|
||||
// @ts-expect-error
|
||||
const error = errors.parameters[definition.name];
|
||||
const error = parametersErrors[definition.name];
|
||||
|
||||
return (
|
||||
<div style={{ width: '80%', marginTop: '25px' }}>
|
||||
<TextField
|
||||
size="small"
|
||||
style={{ width: '100%' }}
|
||||
rows={definition.type === 'textfield' ? 9 : 0}
|
||||
minRows={definition.type === 'textfield' ? 9 : 0}
|
||||
multiline={definition.type === 'textfield'}
|
||||
type={type}
|
||||
label={definition.displayName}
|
||||
@ -51,8 +46,7 @@ export const AddonParameter = ({
|
||||
shrink: true,
|
||||
}}
|
||||
value={value}
|
||||
error={error}
|
||||
// @ts-expect-error
|
||||
error={Boolean(error)}
|
||||
onChange={setParameterValue(definition.name)}
|
||||
variant="outlined"
|
||||
helperText={definition.description}
|
||||
|
@ -1,19 +1,22 @@
|
||||
import React from 'react';
|
||||
import { IAddonConfig, IAddonProvider } from 'interfaces/addons';
|
||||
import { AddonParameter } from './AddonParameter/AddonParameter';
|
||||
import { IAddonProvider } from 'interfaces/addons';
|
||||
import {
|
||||
AddonParameter,
|
||||
IAddonParameterProps,
|
||||
} from './AddonParameter/AddonParameter';
|
||||
|
||||
interface IAddonParametersProps {
|
||||
provider: IAddonProvider;
|
||||
errors: Record<string, string>;
|
||||
provider?: IAddonProvider;
|
||||
parametersErrors: IAddonParameterProps['parametersErrors'];
|
||||
editMode: boolean;
|
||||
setParameterValue: (param: string) => void;
|
||||
config: IAddonConfig;
|
||||
setParameterValue: IAddonParameterProps['setParameterValue'];
|
||||
config: IAddonParameterProps['config'];
|
||||
}
|
||||
|
||||
export const AddonParameters = ({
|
||||
provider,
|
||||
config,
|
||||
errors,
|
||||
parametersErrors,
|
||||
setParameterValue,
|
||||
editMode,
|
||||
}: IAddonParametersProps) => {
|
||||
@ -30,11 +33,10 @@ export const AddonParameters = ({
|
||||
</p>
|
||||
) : null}
|
||||
{provider.parameters.map(parameter => (
|
||||
// @ts-expect-error
|
||||
<AddonParameter
|
||||
key={parameter.name}
|
||||
definition={parameter}
|
||||
errors={errors}
|
||||
parametersErrors={parametersErrors}
|
||||
config={config}
|
||||
setParameterValue={setParameterValue}
|
||||
/>
|
||||
|
@ -3,7 +3,7 @@ import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons';
|
||||
import { AvailableAddons } from './AvailableAddons/AvailableAddons';
|
||||
import { Avatar } from '@material-ui/core';
|
||||
import { DeviceHub } from '@material-ui/icons';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
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';
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
ListItemText,
|
||||
} from '@material-ui/core';
|
||||
import { Delete, Edit, Visibility, VisibilityOff } from '@material-ui/icons';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import {
|
||||
DELETE_ADDON,
|
||||
UPDATE_ADDON,
|
||||
@ -20,7 +20,7 @@ import { ReactElement, useContext, useState } from 'react';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { IAddon } from 'interfaces/addons';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
|
||||
interface IConfigureAddonsProps {
|
||||
|
@ -2,18 +2,19 @@ import { useParams } from 'react-router-dom';
|
||||
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
||||
import { AddonForm } from '../AddonForm/AddonForm';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import { IAddon } from 'interfaces/addons';
|
||||
|
||||
interface IAddonCreateParams {
|
||||
providerId: string;
|
||||
}
|
||||
|
||||
const DEFAULT_DATA = {
|
||||
export const DEFAULT_DATA = {
|
||||
provider: '',
|
||||
description: '',
|
||||
enabled: true,
|
||||
parameters: {},
|
||||
events: [],
|
||||
};
|
||||
} as unknown as IAddon; // TODO: improve type
|
||||
|
||||
export const CreateAddon = () => {
|
||||
const { providerId } = useParams<IAddonCreateParams>();
|
||||
@ -31,7 +32,6 @@ export const CreateAddon = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
// @ts-expect-error
|
||||
<AddonForm
|
||||
editMode={editMode}
|
||||
provider={provider}
|
||||
|
@ -3,19 +3,12 @@ import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
||||
import { AddonForm } from '../AddonForm/AddonForm';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import { IAddon } from 'interfaces/addons';
|
||||
import { DEFAULT_DATA } from '../CreateAddon/CreateAddon';
|
||||
|
||||
interface IAddonEditParams {
|
||||
addonId: string;
|
||||
}
|
||||
|
||||
const DEFAULT_DATA = {
|
||||
provider: '',
|
||||
description: '',
|
||||
enabled: true,
|
||||
parameters: {},
|
||||
events: [],
|
||||
};
|
||||
|
||||
export const EditAddon = () => {
|
||||
const { addonId } = useParams<IAddonEditParams>();
|
||||
|
||||
@ -26,12 +19,10 @@ export const EditAddon = () => {
|
||||
(addon: IAddon) => addon.id === Number(addonId)
|
||||
) || { ...cloneDeep(DEFAULT_DATA) };
|
||||
const provider = addon
|
||||
? // @ts-expect-error
|
||||
providers.find(provider => provider.name === addon.provider)
|
||||
? providers.find(provider => provider.name === addon.provider)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
// @ts-expect-error
|
||||
<AddonForm
|
||||
editMode={editMode}
|
||||
provider={provider}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import AdminMenu from '../menu/AdminMenu';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ApiTokenPage } from 'component/admin/apiToken/ApiTokenPage/ApiTokenPage';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ApiTokenPage } from 'component/admin/apiToken/ApiTokenPage/ApiTokenPage';
|
||||
import AdminMenu from '../menu/AdminMenu';
|
||||
|
||||
const ApiPage = () => {
|
||||
const { pathname } = useLocation();
|
||||
|
@ -17,7 +17,7 @@ import CheckBoxIcon from '@material-ui/icons/CheckBox';
|
||||
import { IAutocompleteBoxOption } from 'component/common/AutocompleteBox/AutocompleteBox';
|
||||
import { useStyles } from '../ApiTokenForm.styles';
|
||||
import { SelectAllButton } from './SelectAllButton/SelectAllButton';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
const ALL_PROJECTS = '*';
|
||||
|
||||
@ -107,7 +107,7 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
|
||||
const renderInput = (params: AutocompleteRenderInputParams) => (
|
||||
<TextField
|
||||
{...params}
|
||||
error={!!error}
|
||||
error={Boolean(error)}
|
||||
helperText={error}
|
||||
variant="outlined"
|
||||
label="Projects"
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useContext, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
IconButton,
|
||||
Table,
|
||||
TableBody,
|
||||
@ -15,16 +16,15 @@ import useApiTokens from 'hooks/api/getters/useApiTokens/useApiTokens';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useApiTokensApi from 'hooks/api/actions/useApiTokensApi/useApiTokensApi';
|
||||
import ApiError from 'component/common/ApiError/ApiError';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { DELETE_API_TOKEN } from 'component/providers/AccessProvider/permissions';
|
||||
import { useStyles } from './ApiTokenList.styles';
|
||||
import Secret from './secret';
|
||||
import { Delete, FileCopy } from '@material-ui/icons';
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||
import { formatDateYMD } from 'utils/formatDate';
|
||||
import { ProjectsList } from './ProjectsList/ProjectsList';
|
||||
import { useStyles } from './ApiTokenList.styles';
|
||||
|
||||
interface IApiToken {
|
||||
createdAt: Date;
|
||||
@ -182,7 +182,13 @@ export const ApiTokenList = () => {
|
||||
}
|
||||
/>
|
||||
<TableCell className={styles.hideMD}>
|
||||
<Secret value={item.secret} />
|
||||
<Box
|
||||
component="span"
|
||||
display="inline-block"
|
||||
width="250px"
|
||||
>
|
||||
************************************
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell className={styles.actionsContainer}>
|
||||
<Tooltip title="Copy token">
|
||||
|
@ -1,16 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
function Secret({ value }) {
|
||||
return (
|
||||
<div>
|
||||
<span style={{ width: '250px', display: 'inline-block' }}>
|
||||
************************************
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Secret.propTypes = {
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Secret;
|
@ -4,8 +4,8 @@ import { Button } from '@material-ui/core';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import PageContent from 'component/common/PageContent';
|
||||
import HeaderTitle from 'component/common/HeaderTitle';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import {
|
||||
CREATE_API_TOKEN,
|
||||
READ_API_TOKEN,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { useCommonStyles } from 'themes/commonStyles';
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import { UserToken } from './UserToken/UserToken';
|
||||
|
||||
interface IConfirmUserLink {
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import AdminMenu from '../menu/AdminMenu';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { OidcAuth } from './OidcAuth/OidcAuth';
|
||||
import { SamlAuth } from './SamlAuth/SamlAuth';
|
||||
|
@ -104,7 +104,7 @@ export const AutoCreateForm = ({
|
||||
label="Email domains"
|
||||
name="emailDomains"
|
||||
disabled={!data.autoCreate || !data.enabled}
|
||||
required={!!data.autoCreate}
|
||||
required={Boolean(data.autoCreate)}
|
||||
value={data.emailDomains || ''}
|
||||
placeholder="@company.com, @anotherCompany.com"
|
||||
style={{ width: '400px' }}
|
||||
|
@ -2,7 +2,7 @@ import { useContext } from 'react';
|
||||
import InvoiceList from './InvoiceList';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
|
||||
const InvoiceAdminPage = () => {
|
||||
|
@ -9,8 +9,8 @@ import {
|
||||
} from '@material-ui/core';
|
||||
import OpenInNew from '@material-ui/icons/OpenInNew';
|
||||
import PageContent from 'component/common/PageContent';
|
||||
import HeaderTitle from 'component/common/HeaderTitle';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import useInvoices from 'hooks/api/getters/useInvoices/useInvoices';
|
||||
import { IInvoice } from 'interfaces/invoice';
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
import useProjectRolePermissions from 'hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions';
|
||||
|
||||
import { useStyles } from './ProjectRoleForm.styles';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { IPermission } from 'interfaces/project';
|
||||
import {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import React from 'react';
|
||||
import { IProjectRole } from 'interfaces/role';
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import Input from 'component/common/Input/Input';
|
||||
import { useStyles } from './ProjectRoleDeleteConfirm.styles';
|
||||
|
||||
|
@ -2,8 +2,8 @@ import { Button } from '@material-ui/core';
|
||||
import { useContext } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import HeaderTitle from 'component/common/HeaderTitle';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import PageContent from 'component/common/PageContent';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import AdminMenu from 'component/admin/menu/AdminMenu';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Typography } from '@material-ui/core';
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
|
||||
import { ReactComponent as EmailIcon } from 'assets/icons/email.svg';
|
||||
import { useStyles } from './ConfirmUserEmail.styles';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { useCommonStyles } from 'themes/commonStyles';
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import UserInviteLink from './UserInviteLink/UserInviteLink';
|
||||
|
||||
interface IConfirmUserLink {
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
import { useStyles } from './UserForm.styles';
|
||||
import React from 'react';
|
||||
import useUsers from 'hooks/api/getters/useUsers/useUsers';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { EDIT } from 'constants/misc';
|
||||
import useUiBootstrap from 'hooks/api/getters/useUiBootstrap/useUiBootstrap';
|
||||
|
||||
|
@ -3,10 +3,10 @@ import UsersList from './UsersList/UsersList';
|
||||
import AdminMenu from '../menu/AdminMenu';
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import HeaderTitle from 'component/common/HeaderTitle';
|
||||
import { TableActions } from 'component/common/Table/TableActions/TableActions';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import { Button } from '@material-ui/core';
|
||||
import { useStyles } from './UserAdmin.styles';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
@ -3,11 +3,11 @@ import classnames from 'classnames';
|
||||
import { Avatar, TextField, Typography } from '@material-ui/core';
|
||||
import { trim } from 'component/common/util';
|
||||
import { modalStyles } from 'component/admin/users/util';
|
||||
import Dialogue from 'component/common/Dialogue/Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import PasswordChecker from 'component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker';
|
||||
import { useCommonStyles } from 'themes/commonStyles';
|
||||
import PasswordMatcher from 'component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { IUser } from 'interfaces/user';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import Dialogue from 'component/common/Dialogue/Dialogue';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { REMOVE_USER_ERROR } from 'hooks/api/actions/useAdminUsersApi/useAdminUsersApi';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import useLoading from 'hooks/useLoading';
|
||||
|
@ -10,7 +10,7 @@ import classnames from 'classnames';
|
||||
import { Delete, Edit, Lock } from '@material-ui/icons';
|
||||
import { SyntheticEvent, useContext } from 'react';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { IUser } from 'interfaces/user';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import classnames from 'classnames';
|
||||
import ChangePassword from './ChangePassword/ChangePassword';
|
||||
import DeleteUser from './DeleteUser/DeleteUser';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import ConfirmUserAdded from '../ConfirmUserAdded/ConfirmUserAdded';
|
||||
|
@ -9,13 +9,14 @@ import {
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import { Link as LinkIcon } from '@material-ui/icons';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { UPDATE_APPLICATION } from 'component/providers/AccessProvider/permissions';
|
||||
import { ApplicationView } from '../ApplicationView/ApplicationView';
|
||||
import { ApplicationUpdate } from '../ApplicationUpdate/ApplicationUpdate';
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { TabNav } from 'component/common/TabNav/TabNav/TabNav';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import PageContent from 'component/common/PageContent';
|
||||
import HeaderTitle from 'component/common/HeaderTitle';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import useApplicationsApi from 'hooks/api/actions/useApplicationsApi/useApplicationsApi';
|
||||
import useApplication from 'hooks/api/getters/useApplication/useApplication';
|
||||
@ -25,7 +26,6 @@ import useToast from 'hooks/useToast';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import { formatDateYMD } from 'utils/formatDate';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { TabNav } from 'component/common/TabNav/TabNav/TabNav';
|
||||
|
||||
export const ApplicationEdit = () => {
|
||||
const history = useHistory();
|
||||
@ -93,7 +93,7 @@ export const ApplicationEdit = () => {
|
||||
<PageContent
|
||||
headerContent={
|
||||
<HeaderTitle
|
||||
title={
|
||||
titleElement={
|
||||
<span
|
||||
style={{
|
||||
display: 'flex',
|
||||
@ -106,6 +106,7 @@ export const ApplicationEdit = () => {
|
||||
{appName}
|
||||
</span>
|
||||
}
|
||||
title={appName}
|
||||
actions={
|
||||
<>
|
||||
<ConditionallyRender
|
||||
|
@ -4,9 +4,9 @@ import { Warning } from '@material-ui/icons';
|
||||
import { AppsLinkList, styles as commonStyles } from 'component/common';
|
||||
import { SearchField } from 'component/common/SearchField/SearchField';
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
import HeaderTitle from 'component/common/HeaderTitle';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import useApplications from 'hooks/api/getters/useApplications/useApplications';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
export const ApplicationList = () => {
|
||||
const { applications, loading } = useApplications();
|
||||
|
@ -19,7 +19,7 @@ import {
|
||||
CREATE_FEATURE,
|
||||
CREATE_STRATEGY,
|
||||
} from 'component/providers/AccessProvider/permissions';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { getTogglePath } from 'utils/routePathHelpers';
|
||||
import useApplication from 'hooks/api/getters/useApplication/useApplication';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useFeaturesArchive } from 'hooks/api/getters/useFeaturesArchive/useFeaturesArchive';
|
||||
import FeatureToggleList from '../feature/FeatureToggleList/FeatureToggleList';
|
||||
import { FeatureToggleList } from '../feature/FeatureToggleList/FeatureToggleList';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||
import { useFeatureArchiveApi } from 'hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi';
|
||||
@ -20,7 +20,7 @@ export const ArchiveListContainer = () => {
|
||||
const { filtered, filter, setFilter } = useFeaturesFilter(archivedFeatures);
|
||||
const { sorted, sort, setSort } = useFeaturesSort(filtered);
|
||||
|
||||
const revive = (feature: string) => {
|
||||
const onRevive = (feature: string) => {
|
||||
reviveFeature(feature)
|
||||
.then(refetchArchived)
|
||||
.then(() =>
|
||||
@ -38,13 +38,13 @@ export const ArchiveListContainer = () => {
|
||||
<FeatureToggleList
|
||||
features={sorted}
|
||||
loading={loading}
|
||||
revive={revive}
|
||||
onRevive={onRevive}
|
||||
flags={uiConfig.flags}
|
||||
filter={filter}
|
||||
setFilter={setFilter}
|
||||
sort={sort}
|
||||
setSort={setSort}
|
||||
archive
|
||||
isArchive
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState, useRef, FC } from 'react';
|
||||
import ConditionallyRender from '../ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface IAnimateOnMountProps {
|
||||
mounted: boolean;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Breadcrumbs from '@material-ui/core/Breadcrumbs';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import ConditionallyRender from '../ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from './BreadcrumbNav.styles';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { useContext } from 'react';
|
||||
|
@ -15,7 +15,7 @@ type TargetElement =
|
||||
|
||||
type RenderFunc = () => JSX.Element;
|
||||
|
||||
const ConditionallyRender = ({
|
||||
export const ConditionallyRender = ({
|
||||
condition,
|
||||
show,
|
||||
elseShow,
|
||||
@ -51,5 +51,3 @@ const ConditionallyRender = ({
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default ConditionallyRender;
|
||||
|
@ -1,3 +0,0 @@
|
||||
import ConditionallyRender from './ConditionallyRender';
|
||||
|
||||
export default ConditionallyRender;
|
@ -1,5 +1,5 @@
|
||||
import { IConstraint } from 'interfaces/strategy';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
import { ConstraintAccordionEdit } from './ConstraintAccordionEdit/ConstraintAccordionEdit';
|
||||
import { ConstraintAccordionView } from './ConstraintAccordionView/ConstraintAccordionView';
|
||||
|
@ -6,7 +6,7 @@ import { ConstraintFormHeader } from './ConstraintFormHeader/ConstraintFormHeade
|
||||
import { useStyles } from './ConstraintAccordionEditBody.styles';
|
||||
import React from 'react';
|
||||
import { newOperators } from 'constants/operators';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { oneOf } from 'utils/oneOf';
|
||||
import { OperatorUpgradeAlert } from 'component/common/OperatorUpgradeAlert/OperatorUpgradeAlert';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Checkbox } from '@material-ui/core';
|
||||
import { useCommonStyles } from 'themes/commonStyles';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ConstraintValueSearch } from 'component/common/ConstraintAccordion/ConstraintValueSearch/ConstraintValueSearch';
|
||||
import { ConstraintFormHeader } from '../ConstraintFormHeader/ConstraintFormHeader';
|
||||
import { ILegalValue } from 'interfaces/context';
|
||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { ConstraintFormHeader } from '../ConstraintFormHeader/ConstraintFormHeader';
|
||||
import { FormControl, RadioGroup, Radio } from '@material-ui/core';
|
||||
import { ConstraintValueSearch } from 'component/common/ConstraintAccordion/ConstraintValueSearch/ConstraintValueSearch';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useCommonStyles } from 'themes/commonStyles';
|
||||
import { ILegalValue } from 'interfaces/context';
|
||||
import {
|
||||
|
@ -5,7 +5,7 @@ import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashCon
|
||||
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
|
||||
import { ConstraintIcon } from 'component/common/ConstraintAccordion/ConstraintIcon';
|
||||
import { Help } from '@material-ui/icons';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { dateOperators, DATE_AFTER, IN } from 'constants/operators';
|
||||
import { SAVE } from '../ConstraintAccordionEdit';
|
||||
import { resolveText } from './helpers';
|
||||
|
@ -7,8 +7,8 @@ import { useWeakMap } from 'hooks/useWeakMap';
|
||||
import { objectId } from 'utils/objectId';
|
||||
import { useStyles } from './ConstraintAccordionList.styles';
|
||||
import { createEmptyConstraint } from 'component/common/ConstraintAccordion/ConstraintAccordionList/createEmptyConstraint';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { Button } from '@material-ui/core';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface IConstraintAccordionListProps {
|
||||
constraints: IConstraint[];
|
||||
|
@ -5,7 +5,7 @@ import { useState } from 'react';
|
||||
import { stringOperators } from 'constants/operators';
|
||||
import { IConstraint } from 'interfaces/strategy';
|
||||
import { oneOf } from 'utils/oneOf';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles';
|
||||
import { ConstraintValueSearch } from 'component/common/ConstraintAccordion/ConstraintValueSearch/ConstraintValueSearch';
|
||||
import { formatConstraintValue } from 'utils/formatConstraintValue';
|
||||
|
@ -5,7 +5,7 @@ import { Delete, Edit } from '@material-ui/icons';
|
||||
import { IConstraint } from 'interfaces/strategy';
|
||||
|
||||
import { useStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import React from 'react';
|
||||
import { formatConstraintValue } from 'utils/formatConstraintValue';
|
||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { TextField, InputAdornment, Chip } from '@material-ui/core';
|
||||
import { Search } from '@material-ui/icons';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface IConstraintValueSearchProps {
|
||||
filter: string;
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
DialogTitle,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import ConditionallyRender from '../ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from './Dialogue.styles';
|
||||
import { DIALOGUE_CONFIRM_ID } from 'utils/testIds';
|
||||
|
||||
@ -15,7 +15,7 @@ interface IDialogue {
|
||||
primaryButtonText?: string;
|
||||
secondaryButtonText?: string;
|
||||
open: boolean;
|
||||
onClick: (e: React.SyntheticEvent) => void;
|
||||
onClick?: (e: React.SyntheticEvent) => void;
|
||||
onClose?: (e: React.SyntheticEvent) => void;
|
||||
style?: object;
|
||||
title: string;
|
||||
@ -26,7 +26,7 @@ interface IDialogue {
|
||||
permissionButton?: JSX.Element;
|
||||
}
|
||||
|
||||
const Dialogue: React.FC<IDialogue> = ({
|
||||
export const Dialogue: React.FC<IDialogue> = ({
|
||||
children,
|
||||
open,
|
||||
onClick,
|
||||
@ -44,7 +44,9 @@ const Dialogue: React.FC<IDialogue> = ({
|
||||
const handleClick = formId
|
||||
? (e: React.SyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
onClick(e);
|
||||
if (onClick) {
|
||||
onClick(e);
|
||||
}
|
||||
}
|
||||
: onClick;
|
||||
return (
|
||||
@ -103,5 +105,3 @@ const Dialogue: React.FC<IDialogue> = ({
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dialogue;
|
||||
|
@ -1,3 +0,0 @@
|
||||
import Dialogue from './Dialogue';
|
||||
|
||||
export default Dialogue;
|
@ -0,0 +1,23 @@
|
||||
import { ReactNode, VFC } from 'react';
|
||||
import { Button, ButtonProps, Icon } from '@material-ui/core';
|
||||
|
||||
interface IDropdownButtonProps {
|
||||
label: string;
|
||||
id?: string;
|
||||
title?: ButtonProps['title'];
|
||||
className?: string;
|
||||
icon?: ReactNode;
|
||||
startIcon?: ButtonProps['startIcon'];
|
||||
style?: ButtonProps['style'];
|
||||
onClick: ButtonProps['onClick'];
|
||||
}
|
||||
|
||||
export const DropdownButton: VFC<IDropdownButtonProps> = ({
|
||||
label,
|
||||
icon,
|
||||
...rest
|
||||
}) => (
|
||||
<Button {...rest} endIcon={<Icon>{icon}</Icon>}>
|
||||
{label}
|
||||
</Button>
|
||||
);
|
@ -1,13 +1,26 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
CSSProperties,
|
||||
MouseEventHandler,
|
||||
ReactNode,
|
||||
useState,
|
||||
VFC,
|
||||
} from 'react';
|
||||
import { Menu } from '@material-ui/core';
|
||||
import { ArrowDropDown } from '@material-ui/icons';
|
||||
import { DropdownButton } from './DropdownButton/DropdownButton';
|
||||
|
||||
import { DropdownButton } from '..';
|
||||
export interface IDropdownMenuProps {
|
||||
renderOptions: () => ReactNode;
|
||||
id: string;
|
||||
title?: string;
|
||||
callback?: MouseEventHandler;
|
||||
icon?: ReactNode;
|
||||
label: string;
|
||||
startIcon?: ReactNode;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
import styles from '../common.module.scss';
|
||||
|
||||
const DropdownMenu = ({
|
||||
const DropdownMenu: VFC<IDropdownMenuProps> = ({
|
||||
renderOptions,
|
||||
id,
|
||||
title,
|
||||
@ -18,11 +31,13 @@ const DropdownMenu = ({
|
||||
startIcon,
|
||||
...rest
|
||||
}) => {
|
||||
const [anchor, setAnchor] = React.useState(null);
|
||||
const [anchor, setAnchor] = useState<Element | null>(null);
|
||||
|
||||
const handleOpen = e => setAnchor(e.currentTarget);
|
||||
const handleOpen: MouseEventHandler<HTMLButtonElement> = e => {
|
||||
setAnchor(e.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = e => {
|
||||
const handleClose: MouseEventHandler<HTMLDivElement> = e => {
|
||||
if (callback && typeof callback === 'function') {
|
||||
callback(e);
|
||||
}
|
||||
@ -46,7 +61,6 @@ const DropdownMenu = ({
|
||||
/>
|
||||
<Menu
|
||||
id={id}
|
||||
className={styles.dropdownMenu}
|
||||
onClick={handleClose}
|
||||
anchorEl={anchor}
|
||||
open={Boolean(anchor)}
|
||||
@ -57,14 +71,4 @@ const DropdownMenu = ({
|
||||
);
|
||||
};
|
||||
|
||||
DropdownMenu.propTypes = {
|
||||
renderOptions: PropTypes.func,
|
||||
id: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
callback: PropTypes.func,
|
||||
icon: PropTypes.object,
|
||||
label: PropTypes.string,
|
||||
startIcon: PropTypes.object,
|
||||
};
|
||||
|
||||
export default DropdownMenu;
|
@ -1,6 +1,6 @@
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
||||
import Dialogue from '../Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import PermissionButton from '../PermissionButton/PermissionButton';
|
||||
import { useStyles } from './EnvironmentStrategyDialog.styles';
|
||||
import { formatCreateStrategyPath } from 'component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate';
|
||||
@ -35,7 +35,6 @@ const EnvironmentStrategyDialog = ({
|
||||
};
|
||||
|
||||
return (
|
||||
// @ts-expect-error
|
||||
<Dialogue
|
||||
open={open}
|
||||
maxWidth="sm"
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
Tooltip,
|
||||
} from '@material-ui/core';
|
||||
import { FileCopy, Info } from '@material-ui/icons';
|
||||
import ConditionallyRender from '../ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import Loader from '../Loader/Loader';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import useToast from 'hooks/useToast';
|
||||
|
@ -1,15 +1,26 @@
|
||||
import React from 'react';
|
||||
import { ReactNode, VFC } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Typography } from '@material-ui/core';
|
||||
import ConditionallyRender from '../ConditionallyRender/ConditionallyRender';
|
||||
import { Variant } from '@material-ui/core/styles/createTypography';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
import { useStyles } from './styles';
|
||||
import { usePageTitle } from 'hooks/usePageTitle';
|
||||
|
||||
const HeaderTitle = ({
|
||||
interface IHeaderTitleProps {
|
||||
title: string;
|
||||
titleElement?: ReactNode;
|
||||
subtitle?: string;
|
||||
variant?: 'inherit' | Variant;
|
||||
loading?: boolean;
|
||||
actions?: ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const HeaderTitle: VFC<IHeaderTitleProps> = ({
|
||||
title,
|
||||
titleElement,
|
||||
actions,
|
||||
subtitle,
|
||||
variant,
|
||||
@ -28,25 +39,15 @@ const HeaderTitle = ({
|
||||
variant={variant || 'h1'}
|
||||
className={classnames(styles.headerTitle, className)}
|
||||
>
|
||||
{title}
|
||||
{titleElement || title}
|
||||
</Typography>
|
||||
{subtitle && <small>{subtitle}</small>}
|
||||
</div>
|
||||
|
||||
<ConditionallyRender
|
||||
condition={actions}
|
||||
condition={Boolean(actions)}
|
||||
show={<div className={styles.headerActions}>{actions}</div>}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderTitle;
|
||||
|
||||
HeaderTitle.propTypes = {
|
||||
title: PropTypes.oneOfType([PropTypes.element, PropTypes.string]),
|
||||
subtitle: PropTypes.string,
|
||||
variant: PropTypes.string,
|
||||
loading: PropTypes.bool,
|
||||
actions: PropTypes.element,
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
import HeaderTitle from './HeaderTitle';
|
||||
|
||||
export default HeaderTitle;
|
@ -1,62 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { TextField } from '@material-ui/core';
|
||||
|
||||
function InputListField({
|
||||
label,
|
||||
values = [],
|
||||
error,
|
||||
errorText,
|
||||
name,
|
||||
updateValues,
|
||||
placeholder = '',
|
||||
onBlur = () => {},
|
||||
FormHelperTextProps,
|
||||
}) {
|
||||
const handleChange = evt => {
|
||||
const values = evt.target.value.split(/,\s?/);
|
||||
const trimmedValues = values.map(v => v.trim());
|
||||
updateValues(trimmedValues);
|
||||
};
|
||||
|
||||
const handleKeyDown = evt => {
|
||||
if (evt.key === 'Backspace') {
|
||||
const currentValue = evt.target.value;
|
||||
if (currentValue.endsWith(', ')) {
|
||||
evt.preventDefault();
|
||||
const value = currentValue.slice(0, -2);
|
||||
updateValues(value.split(/,\s*/));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TextField
|
||||
name={name}
|
||||
error={error}
|
||||
helperText={errorText}
|
||||
placeholder={placeholder}
|
||||
value={values ? values.join(', ') : ''}
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={handleChange}
|
||||
onBlur={onBlur}
|
||||
label={label}
|
||||
style={{ width: '100%' }}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
FormHelperTextProps={FormHelperTextProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
InputListField.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
values: PropTypes.array,
|
||||
error: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
updateValues: PropTypes.func.isRequired,
|
||||
onBlur: PropTypes.func,
|
||||
};
|
||||
|
||||
export default InputListField;
|
@ -0,0 +1,53 @@
|
||||
import { VFC } from 'react';
|
||||
import { TextField, TextFieldProps } from '@material-ui/core';
|
||||
|
||||
interface IInputListFieldProps {
|
||||
label: string;
|
||||
values?: any[];
|
||||
error?: boolean;
|
||||
placeholder?: string;
|
||||
name: string;
|
||||
updateValues: (values: string[]) => void;
|
||||
onBlur?: TextFieldProps['onBlur'];
|
||||
helperText?: TextFieldProps['helperText'];
|
||||
FormHelperTextProps?: TextFieldProps['FormHelperTextProps'];
|
||||
}
|
||||
|
||||
export const InputListField: VFC<IInputListFieldProps> = ({
|
||||
values = [],
|
||||
updateValues,
|
||||
placeholder = '',
|
||||
error,
|
||||
...rest
|
||||
}) => {
|
||||
const handleChange: TextFieldProps['onChange'] = event => {
|
||||
const values = event.target.value.split(/,\s?/);
|
||||
const trimmedValues = values.map(v => v.trim());
|
||||
updateValues(trimmedValues);
|
||||
};
|
||||
|
||||
const handleKeyDown: TextFieldProps['onKeyDown'] = event => {
|
||||
if (event.key === 'Backspace') {
|
||||
const currentValue = (event.target as HTMLInputElement).value;
|
||||
if (currentValue.endsWith(', ')) {
|
||||
event.preventDefault();
|
||||
const value = currentValue.slice(0, -2);
|
||||
updateValues(value.split(/,\s*/));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TextField
|
||||
{...rest}
|
||||
error={error}
|
||||
placeholder={placeholder}
|
||||
value={values ? values.join(', ') : ''}
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={handleChange}
|
||||
style={{ width: '100%' }}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
import { ListItem } from '@material-ui/core';
|
||||
import { Link } from 'react-router-dom';
|
||||
import ConditionallyRender from '../ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from 'component/common/ListPlaceholder/ListPlaceholder.styles';
|
||||
|
||||
interface IListPlaceholderProps {
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classnames from 'classnames';
|
||||
import HeaderTitle from '../HeaderTitle';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import { Paper } from '@material-ui/core';
|
||||
import { useStyles } from './styles';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ConditionallyRender from '../ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import classnames from 'classnames';
|
||||
import { useStyles } from './PaginationUI.styles';
|
||||
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { IconButton, InputAdornment, TextField } from '@material-ui/core';
|
||||
import {
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
TextField,
|
||||
TextFieldProps,
|
||||
} from '@material-ui/core';
|
||||
import { Visibility, VisibilityOff } from '@material-ui/icons';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, VFC } from 'react';
|
||||
|
||||
const PasswordField = ({ ...rest }) => {
|
||||
const PasswordField: VFC<TextFieldProps> = ({ ...rest }) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const handleClickShowPassword = () => {
|
||||
|
@ -2,7 +2,7 @@ import { Button, ButtonProps } from '@material-ui/core';
|
||||
import { Lock } from '@material-ui/icons';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import React, { useContext } from 'react';
|
||||
import ConditionallyRender from '../ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
|
||||
import { formatAccessText } from 'utils/formatAccessText';
|
||||
import { useId } from 'hooks/useId';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import ConditionallyRender from '../ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { useStyles } from './Proclamation.styles';
|
||||
import { IProclamationToast } from 'interfaces/uiConfig';
|
||||
|
@ -1,79 +0,0 @@
|
||||
import React from 'react';
|
||||
import { MenuItem } from '@material-ui/core';
|
||||
import PropTypes from 'prop-types';
|
||||
import DropdownMenu from '../DropdownMenu/DropdownMenu';
|
||||
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||
|
||||
const ALL_PROJECTS = { id: '*', name: '> All projects' };
|
||||
|
||||
const ProjectSelect = ({ currentProjectId, updateCurrentProject, ...rest }) => {
|
||||
const { projects } = useProjects();
|
||||
|
||||
const setProject = v => {
|
||||
const id = v && typeof v === 'string' ? v.trim() : '*';
|
||||
updateCurrentProject(id);
|
||||
};
|
||||
|
||||
// TODO fixme
|
||||
let curentProject = projects.find(i => i.id === currentProjectId);
|
||||
if (!curentProject) {
|
||||
curentProject = ALL_PROJECTS;
|
||||
}
|
||||
|
||||
const handleChangeProject = e => {
|
||||
const target = e.target.getAttribute('data-target');
|
||||
setProject(target);
|
||||
};
|
||||
|
||||
const renderProjectItem = (selectedId, item) => (
|
||||
<MenuItem
|
||||
disabled={selectedId === item.id}
|
||||
data-target={item.id}
|
||||
key={item.id}
|
||||
style={{ fontSize: '14px' }}
|
||||
>
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
const renderProjectOptions = () => {
|
||||
const start = [
|
||||
<MenuItem
|
||||
disabled={curentProject === ALL_PROJECTS}
|
||||
data-target={ALL_PROJECTS.id}
|
||||
key={ALL_PROJECTS.id}
|
||||
style={{ fontSize: '14px' }}
|
||||
>
|
||||
{ALL_PROJECTS.name}
|
||||
</MenuItem>,
|
||||
];
|
||||
|
||||
return [
|
||||
...start,
|
||||
...projects.map(p => renderProjectItem(currentProjectId, p)),
|
||||
];
|
||||
};
|
||||
|
||||
const { updateSetting, ...passDown } = rest;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DropdownMenu
|
||||
id={'project'}
|
||||
title="Select project"
|
||||
label={`${curentProject.name}`}
|
||||
callback={handleChangeProject}
|
||||
renderOptions={renderProjectOptions}
|
||||
className=""
|
||||
{...passDown}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
ProjectSelect.propTypes = {
|
||||
currentProjectId: PropTypes.string.isRequired,
|
||||
updateCurrentProject: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ProjectSelect;
|
@ -0,0 +1,74 @@
|
||||
import React, { MouseEventHandler, useMemo, VFC } from 'react';
|
||||
import { MenuItem, Typography } from '@material-ui/core';
|
||||
import DropdownMenu, { IDropdownMenuProps } from '../DropdownMenu/DropdownMenu';
|
||||
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||
import { IProjectCard } from 'interfaces/project';
|
||||
|
||||
const ALL_PROJECTS = { id: '*', name: '> All projects' };
|
||||
|
||||
interface IProjectSelectProps {
|
||||
currentProjectId: string;
|
||||
updateCurrentProject: (id: string) => void;
|
||||
}
|
||||
|
||||
const ProjectSelect: VFC<IProjectSelectProps & Partial<IDropdownMenuProps>> = ({
|
||||
currentProjectId,
|
||||
updateCurrentProject,
|
||||
...rest
|
||||
}) => {
|
||||
const { projects } = useProjects();
|
||||
|
||||
const setProject = (value?: string | null) => {
|
||||
const id = value && typeof value === 'string' ? value.trim() : '*';
|
||||
updateCurrentProject(id);
|
||||
};
|
||||
|
||||
const currentProject = useMemo(() => {
|
||||
const project = projects.find(i => i.id === currentProjectId);
|
||||
return project || ALL_PROJECTS;
|
||||
}, [currentProjectId, projects]);
|
||||
|
||||
const handleChangeProject: MouseEventHandler = event => {
|
||||
const target = (event.target as Element).getAttribute('data-target');
|
||||
setProject(target);
|
||||
};
|
||||
|
||||
const renderProjectItem = (selectedId: string, item: IProjectCard) => (
|
||||
<MenuItem
|
||||
disabled={selectedId === item.id}
|
||||
data-target={item.id}
|
||||
key={item.id}
|
||||
style={{ fontSize: '14px' }}
|
||||
>
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
const renderProjectOptions = () => [
|
||||
<MenuItem
|
||||
disabled={currentProject === ALL_PROJECTS}
|
||||
data-target={ALL_PROJECTS.id}
|
||||
key={ALL_PROJECTS.id}
|
||||
>
|
||||
<Typography variant="body2">{ALL_PROJECTS.name}</Typography>
|
||||
</MenuItem>,
|
||||
...projects.map(project =>
|
||||
renderProjectItem(currentProjectId, project)
|
||||
),
|
||||
];
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DropdownMenu
|
||||
id={'project'}
|
||||
title="Select project"
|
||||
label={`${currentProject.name}`}
|
||||
callback={handleChangeProject}
|
||||
renderOptions={renderProjectOptions}
|
||||
{...rest}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectSelect;
|
@ -1,27 +0,0 @@
|
||||
import { Route, useLocation, Redirect } from 'react-router-dom';
|
||||
|
||||
const ProtectedRoute = ({
|
||||
component: Component,
|
||||
unauthorized,
|
||||
renderProps = {},
|
||||
...rest
|
||||
}) => {
|
||||
const { pathname, search } = useLocation();
|
||||
const redirect = encodeURIComponent(pathname + search);
|
||||
const loginLink = `/login?redirect=${redirect}`;
|
||||
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={props => {
|
||||
if (unauthorized) {
|
||||
return <Redirect to={loginLink} />;
|
||||
} else {
|
||||
return <Component {...props} {...renderProps} />;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProtectedRoute;
|
@ -0,0 +1,24 @@
|
||||
import { VFC } from 'react';
|
||||
import { Route, useLocation, Redirect, RouteProps } from 'react-router-dom';
|
||||
|
||||
interface IProtectedRouteProps {
|
||||
unauthorized?: boolean;
|
||||
}
|
||||
|
||||
const ProtectedRoute: VFC<IProtectedRouteProps & RouteProps> = ({
|
||||
component: Component,
|
||||
unauthorized,
|
||||
...rest
|
||||
}) => {
|
||||
const { pathname, search } = useLocation();
|
||||
const redirect = encodeURIComponent(pathname + search);
|
||||
const loginLink = `/login?redirect=${redirect}`;
|
||||
|
||||
return unauthorized ? (
|
||||
<Route {...rest} render={() => <Redirect to={loginLink} />} />
|
||||
) : (
|
||||
<Route {...rest} component={Component} />
|
||||
);
|
||||
};
|
||||
|
||||
export default ProtectedRoute;
|
@ -1,5 +1,5 @@
|
||||
import { useMediaQuery } from '@material-ui/core';
|
||||
import ConditionallyRender from '../ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import PermissionButton from '../PermissionButton/PermissionButton';
|
||||
import PermissionIconButton from '../PermissionIconButton/PermissionIconButton';
|
||||
import React from 'react';
|
||||
|
@ -1,24 +1,24 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, VFC } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { debounce } from 'debounce';
|
||||
import { InputBase, Chip } from '@material-ui/core';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
import { useStyles } from 'component/common/SearchField/styles';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface ISearchFieldProps {
|
||||
updateValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
updateValue: (value: string) => void;
|
||||
initialValue?: string;
|
||||
className?: string;
|
||||
showValueChip?: boolean;
|
||||
}
|
||||
|
||||
export const SearchField = ({
|
||||
export const SearchField: VFC<ISearchFieldProps> = ({
|
||||
updateValue,
|
||||
initialValue = '',
|
||||
className = '',
|
||||
showValueChip,
|
||||
}: ISearchFieldProps) => {
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
const [localValue, setLocalValue] = useState(initialValue);
|
||||
const debounceUpdateValue = debounce(updateValue, 500);
|
||||
|
@ -1,3 +0,0 @@
|
||||
import SearchField from './SearchField';
|
||||
|
||||
export default SearchField;
|
@ -3,7 +3,7 @@ import { useStyles } from './StatusChip.styles';
|
||||
|
||||
interface IStatusChip {
|
||||
stale: boolean;
|
||||
showActive?: true;
|
||||
showActive?: boolean;
|
||||
}
|
||||
|
||||
const StatusChip = ({ stale, showActive = true }: IStatusChip) => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Tooltip } from '@material-ui/core';
|
||||
import ConditionallyRender from '../ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface IStringTruncatorProps {
|
||||
text: string;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { IconButton, Tooltip } from '@material-ui/core';
|
||||
import { Search } from '@material-ui/icons';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import AnimateOnMount from 'component/common/AnimateOnMount/AnimateOnMount';
|
||||
import { TableSearchField } from 'component/common/Table/TableActions/TableSearchField/TableSearchField';
|
||||
import { useStyles } from 'component/common/Table/TableActions/TableActions.styles';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { IconButton, InputBase, Tooltip } from '@material-ui/core';
|
||||
import { Search, Close } from '@material-ui/icons';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from 'component/common/Table/TableActions/TableSearchField/TableSearchField.styles';
|
||||
import classnames from 'classnames';
|
||||
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
KeyboardArrowUp,
|
||||
} from '@material-ui/icons';
|
||||
import { IUsersSort, UsersSortType } from 'hooks/useUsersSort';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from 'component/common/Table/TableCellSortable/TableCellSortable.styles';
|
||||
import { AnnouncerContext } from 'component/common/Announcer/AnnouncerContext/AnnouncerContext';
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { useContext } from 'react';
|
||||
import { IconButton, Tooltip } from '@material-ui/core';
|
||||
import CheckMarkBadge from 'component/common/CheckmarkBadge/CheckMarkBadge';
|
||||
import UIContext from 'contexts/UIContext';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import Close from '@material-ui/icons/Close';
|
||||
import { IToast } from 'interfaces/toast';
|
||||
|
||||
|
@ -83,10 +83,6 @@
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.dropdownButton {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.toggleName {
|
||||
color: #37474f !important;
|
||||
font-weight: 700;
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
import { Apps } from '@material-ui/icons';
|
||||
|
||||
import styles from './common.module.scss';
|
||||
import ConditionallyRender from './ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from './ConditionallyRender/ConditionallyRender';
|
||||
|
||||
export { styles };
|
||||
|
||||
@ -114,38 +114,6 @@ IconLink.propTypes = {
|
||||
icon: PropTypes.object,
|
||||
};
|
||||
|
||||
export const DropdownButton = ({
|
||||
label,
|
||||
id,
|
||||
className = styles.dropdownButton,
|
||||
title,
|
||||
icon,
|
||||
startIcon,
|
||||
style,
|
||||
...rest
|
||||
}) => (
|
||||
<Button
|
||||
id={id}
|
||||
className={className}
|
||||
title={title}
|
||||
style={style}
|
||||
{...rest}
|
||||
startIcon={startIcon}
|
||||
endIcon={<Icon>{icon}</Icon>}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
);
|
||||
|
||||
DropdownButton.propTypes = {
|
||||
label: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
id: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
icon: PropTypes.object,
|
||||
startIcon: PropTypes.object,
|
||||
};
|
||||
|
||||
export const MenuItemWithIcon = React.forwardRef(
|
||||
({ icon: IconComponent, label, disabled, ...menuItemProps }, ref) => (
|
||||
<MenuItem
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useStyles } from 'component/context/ContectFormChip/ContextFormChip.styles';
|
||||
import { Cancel } from '@material-ui/icons';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface IContextFormChipProps {
|
||||
label: string;
|
||||
|
@ -1,11 +1,6 @@
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
import HeaderTitle from 'component/common/HeaderTitle';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import {
|
||||
CREATE_CONTEXT_FIELD,
|
||||
DELETE_CONTEXT_FIELD,
|
||||
UPDATE_CONTEXT_FIELD,
|
||||
} from 'component/providers/AccessProvider/permissions';
|
||||
import { useContext, useState, VFC } from 'react';
|
||||
import { Add, Album, Delete, Edit } from '@material-ui/icons';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
IconButton,
|
||||
@ -16,30 +11,38 @@ import {
|
||||
Tooltip,
|
||||
useMediaQuery,
|
||||
} from '@material-ui/core';
|
||||
import { Add, Album, Delete, Edit } from '@material-ui/icons';
|
||||
import { useContext, useState } from 'react';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { useStyles } from './styles';
|
||||
import ConfirmDialogue from 'component/common/Dialogue';
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import {
|
||||
CREATE_CONTEXT_FIELD,
|
||||
DELETE_CONTEXT_FIELD,
|
||||
UPDATE_CONTEXT_FIELD,
|
||||
} from 'component/providers/AccessProvider/permissions';
|
||||
import { Dialogue as ConfirmDialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
|
||||
import useContextsApi from 'hooks/api/actions/useContextsApi/useContextsApi';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useStyles } from './styles';
|
||||
|
||||
const ContextList = () => {
|
||||
const ContextList: VFC = () => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const [showDelDialogue, setShowDelDialogue] = useState(false);
|
||||
const smallScreen = useMediaQuery('(max-width:700px)');
|
||||
const [name, setName] = useState();
|
||||
const [name, setName] = useState<string>();
|
||||
const { context, refetchUnleashContext } = useUnleashContext();
|
||||
const { removeContext } = useContextsApi();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const history = useHistory();
|
||||
const styles = useStyles();
|
||||
|
||||
const onDeleteContext = async name => {
|
||||
const onDeleteContext = async () => {
|
||||
try {
|
||||
if (name === undefined) {
|
||||
throw new Error();
|
||||
}
|
||||
await removeContext(name);
|
||||
refetchUnleashContext();
|
||||
setToastData({
|
||||
@ -93,6 +96,7 @@ const ContextList = () => {
|
||||
show={
|
||||
<Tooltip title="Delete context field">
|
||||
<IconButton
|
||||
aria-label="delete"
|
||||
onClick={() => {
|
||||
setName(field.name);
|
||||
setShowDelDialogue(true);
|
||||
@ -133,6 +137,7 @@ const ContextList = () => {
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<PageContent
|
||||
headerContent={
|
||||
@ -151,7 +156,7 @@ const ContextList = () => {
|
||||
</List>
|
||||
<ConfirmDialogue
|
||||
open={showDelDialogue}
|
||||
onClick={() => onDeleteContext(name)}
|
||||
onClick={onDeleteContext}
|
||||
onClose={() => {
|
||||
setName(undefined);
|
||||
setShowDelDialogue(false);
|
@ -10,10 +10,10 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import useProjectRolePermissions from 'hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import HeaderTitle from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
|
||||
const CreateEnvironment = () => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import React from 'react';
|
||||
import { IEnvironment } from 'interfaces/environments';
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import Input from 'component/common/Input/Input';
|
||||
import EnvironmentCard from '../EnvironmentCard/EnvironmentCard';
|
||||
import { useStyles } from './EnvironmentDeleteConfirm.styles';
|
||||
|
@ -1,21 +1,21 @@
|
||||
import HeaderTitle from 'component/common/HeaderTitle';
|
||||
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
|
||||
import { Add } from '@material-ui/icons';
|
||||
import PageContent from 'component/common/PageContent';
|
||||
import { List } from '@material-ui/core';
|
||||
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import { IEnvironment, ISortOrderPayload } from 'interfaces/environments';
|
||||
import { useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import EnvironmentDeleteConfirm from './EnvironmentDeleteConfirm/EnvironmentDeleteConfirm';
|
||||
import { List } from '@material-ui/core';
|
||||
import { Add } from '@material-ui/icons';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { IEnvironment, ISortOrderPayload } from 'interfaces/environments';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
|
||||
import PageContent from 'component/common/PageContent';
|
||||
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||
import useEnvironmentApi from 'hooks/api/actions/useEnvironmentApi/useEnvironmentApi';
|
||||
import EnvironmentListItem from './EnvironmentListItem/EnvironmentListItem';
|
||||
import EnvironmentToggleConfirm from './EnvironmentToggleConfirm/EnvironmentToggleConfirm';
|
||||
import useProjectRolePermissions from 'hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import EnvironmentListItem from './EnvironmentListItem/EnvironmentListItem';
|
||||
import EnvironmentToggleConfirm from './EnvironmentToggleConfirm/EnvironmentToggleConfirm';
|
||||
import EnvironmentDeleteConfirm from './EnvironmentDeleteConfirm/EnvironmentDeleteConfirm';
|
||||
|
||||
const EnvironmentList = () => {
|
||||
const defaultEnv = {
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
Edit,
|
||||
OfflineBolt,
|
||||
} from '@material-ui/icons';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
import { IEnvironment } from 'interfaces/environments';
|
||||
import React, { useContext, useRef } from 'react';
|
||||
|
@ -2,8 +2,8 @@ import { capitalize } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import React from 'react';
|
||||
import { IEnvironment } from 'interfaces/environments';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import EnvironmentCard from '../EnvironmentCard/EnvironmentCard';
|
||||
|
||||
interface IEnvironmentToggleConfirmProps {
|
||||
|
@ -1,4 +1,10 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import {
|
||||
useState,
|
||||
useRef,
|
||||
useEffect,
|
||||
FormEventHandler,
|
||||
ChangeEventHandler,
|
||||
} from 'react';
|
||||
import { Link, useHistory, useParams } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
@ -9,33 +15,35 @@ import {
|
||||
} from '@material-ui/core';
|
||||
import { FileCopy } from '@material-ui/icons';
|
||||
import { styles as commonStyles } from 'component/common';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import styles from './CopyFeature.module.scss';
|
||||
import { trim } from 'component/common/util';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { getTogglePath } from 'utils/routePathHelpers';
|
||||
import useFeatureApi from 'hooks/api/actions/useFeatureApi/useFeatureApi';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
|
||||
export const CopyFeatureToggle = () => {
|
||||
const [replaceGroupId, setReplaceGroupId] = useState(true);
|
||||
const [apiError, setApiError] = useState('');
|
||||
const [nameError, setNameError] = useState(undefined);
|
||||
const [newToggleName, setNewToggleName] = useState();
|
||||
const [nameError, setNameError] = useState<string | undefined>();
|
||||
const [newToggleName, setNewToggleName] = useState<string>();
|
||||
const { cloneFeatureToggle, validateFeatureToggleName } = useFeatureApi();
|
||||
const inputRef = useRef();
|
||||
const { name: copyToggleName, id: projectId } = useParams();
|
||||
const inputRef = useRef<HTMLInputElement>();
|
||||
const { name: copyToggleName, id: projectId } = useParams<{
|
||||
name: string;
|
||||
id: string;
|
||||
}>();
|
||||
const { feature } = useFeature(projectId, copyToggleName);
|
||||
const { uiConfig } = useUiConfig();
|
||||
const history = useHistory();
|
||||
|
||||
useEffect(() => {
|
||||
inputRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
const setValue = evt => {
|
||||
const value = trim(evt.target.value);
|
||||
const setValue: ChangeEventHandler<HTMLInputElement> = event => {
|
||||
const value = trim(event.target.value);
|
||||
setNewToggleName(value);
|
||||
};
|
||||
|
||||
@ -48,13 +56,13 @@ export const CopyFeatureToggle = () => {
|
||||
await validateFeatureToggleName(newToggleName);
|
||||
|
||||
setNameError(undefined);
|
||||
} catch (err) {
|
||||
setNameError(err.message);
|
||||
} catch (error) {
|
||||
setNameError(formatUnknownError(error));
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async evt => {
|
||||
evt.preventDefault();
|
||||
const onSubmit: FormEventHandler = async event => {
|
||||
event.preventDefault();
|
||||
|
||||
if (nameError) {
|
||||
return;
|
||||
@ -62,14 +70,12 @@ export const CopyFeatureToggle = () => {
|
||||
|
||||
try {
|
||||
await cloneFeatureToggle(projectId, copyToggleName, {
|
||||
name: newToggleName,
|
||||
name: newToggleName as string,
|
||||
replaceGroupId,
|
||||
});
|
||||
history.push(
|
||||
getTogglePath(projectId, newToggleName, uiConfig.flags.E)
|
||||
);
|
||||
} catch (e) {
|
||||
setApiError(e.toString());
|
||||
history.push(getTogglePath(projectId, newToggleName as string));
|
||||
} catch (error) {
|
||||
setApiError(formatUnknownError(error));
|
||||
}
|
||||
};
|
||||
|
||||
@ -84,7 +90,7 @@ export const CopyFeatureToggle = () => {
|
||||
<h1>Copy {copyToggleName}</h1>
|
||||
</div>
|
||||
<ConditionallyRender
|
||||
condition={apiError}
|
||||
condition={Boolean(apiError)}
|
||||
show={<Alert severity="error">{apiError}</Alert>}
|
||||
/>
|
||||
<section className={styles.content}>
|
||||
@ -116,7 +122,6 @@ export const CopyFeatureToggle = () => {
|
||||
<Switch
|
||||
value={replaceGroupId}
|
||||
checked={replaceGroupId}
|
||||
label="Replace groupId"
|
||||
onChange={toggleReplaceGroupId}
|
||||
/>
|
||||
}
|
@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
|
||||
import { Button, IconButton, Tooltip } from '@material-ui/core';
|
||||
import useMediaQuery from '@material-ui/core/useMediaQuery';
|
||||
import { Add } from '@material-ui/icons';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { NAVIGATE_TO_CREATE_FEATURE } from 'utils/testIds';
|
||||
import { IFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||
import { useCreateFeaturePath } from 'component/feature/CreateFeatureButton/useCreateFeaturePath';
|
||||
|
@ -12,7 +12,7 @@ import useFeatureTypes from 'hooks/api/getters/useFeatureTypes/useFeatureTypes';
|
||||
import { KeyboardArrowDownOutlined } from '@material-ui/icons';
|
||||
import { projectFilterGenerator } from 'utils/projectFilterGenerator';
|
||||
import FeatureProjectSelect from '../FeatureView/FeatureSettings/FeatureSettingsProject/FeatureProjectSelect/FeatureProjectSelect';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { trim } from 'component/common/util';
|
||||
import Input from 'component/common/Input/Input';
|
||||
import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { IFeatureToggle } from 'interfaces/featureToggle';
|
||||
import { formatFeaturePath } from '../FeatureStrategyEdit/FeatureStrategyEdit';
|
||||
|
@ -13,7 +13,7 @@ import { useStyles } from './FeatureStrategyForm.styles';
|
||||
import { formatFeaturePath } from '../FeatureStrategyEdit/FeatureStrategyEdit';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { STRATEGY_FORM_SUBMIT_ID } from 'utils/testIds';
|
||||
import { useConstraintsValidation } from 'hooks/api/getters/useConstraintsValidation/useConstraintsValidation';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { Checkbox, FormControlLabel } from '@material-ui/core';
|
||||
import { PRODUCTION } from 'constants/environmentTypes';
|
||||
|
@ -4,13 +4,13 @@ import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatFeaturePath } from '../FeatureStrategyEdit/FeatureStrategyEdit';
|
||||
import Dialogue from 'component/common/Dialogue';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import { DELETE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { STRATEGY_FORM_REMOVE_ID } from 'utils/testIds';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import { Delete } from '@material-ui/icons';
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
|
||||
import { ISegment } from 'interfaces/segment';
|
||||
import { Clear, VisibilityOff, Visibility } from '@material-ui/icons';
|
||||
import { useStyles } from './FeatureStrategySegmentChip.styles';
|
||||
import ConditionallyRender from 'component/common/ConditionallyRender';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { constraintAccordionListId } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList';
|
||||
import { Tooltip } from '@material-ui/core';
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user