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