From cbd4773cf637e6ef80796064bff47143534ac2b2 Mon Sep 17 00:00:00 2001 From: Fredrik Strand Oseberg Date: Tue, 18 May 2021 12:59:48 +0200 Subject: [PATCH] Fix/customer journey (#297) * fix: add onClose to archive toggle dialoge * fix: add link to ConfirmUserLink component * fix: remove icons from admin menu * fix: move button on user list to top right * refactor: move add new api key to header * refactor: button order * fix: lowercase dropdown buttons on feature toggle list * refactor: reorganize reporting dashboard * refactor: consistent buttons * feat: enhance gradual rollout strategy creation * feat: ui tweaks on project access * fix: adjust divider * fix: remove unused imports * fix: update snapshots * fix: add auth options to new user page * fix: add divider * fix: uncontrolled input * fix: add data-loading to sorted by * fix: update snapshots * fix: navigate to project view on create and edit * fix: rename project * fix: add placeholder for feature toggle list component * fix: conditonally render link --- frontend/src/app.css | 2 +- frontend/src/assets/icons/google.svg | 99 +++++++++ .../Reporting/ReportCard/ReportCard.jsx | 28 +-- .../ReportToggleListItem.jsx | 41 +++- .../ListPlaceholder/ListPlaceholder.styles.js | 12 ++ .../ListPlaceholder/ListPlaceholder.tsx | 26 +++ .../src/component/common/common.module.scss | 1 - frontend/src/component/common/index.js | 1 - .../context/ContextList/ContextList.jsx | 29 ++- .../FeatureToggleList/FeatureToggleList.jsx | 16 +- .../FeatureToggleListActions.jsx | 8 +- .../FeatureToggleListActions/styles.js | 2 + .../list-component-test.jsx.snap | 32 +++ .../feature/FeatureView/FeatureView.jsx | 9 +- .../EditStrategyModal/FlexibleStrategy.jsx | 66 +++++- .../StrategyConstraintInput.jsx | 42 +++- .../view-component-test.jsx.snap | 52 ++--- frontend/src/component/menu/routes.js | 2 +- .../project/ProjectList/ProjectList.jsx | 34 +++- .../ProjectView.js} | 20 +- .../{ViewProject => ProjectView}/index.js | 2 +- .../src/component/project/access-add-user.js | 15 +- .../src/component/project/access-component.js | 113 ++++++++--- .../project/form-project-component.jsx | 46 +++-- .../strategies/CreateStrategy/index.js | 8 +- .../tag-type-create-component-test.js.snap | 20 -- .../component/user/HostedAuth/HostedAuth.jsx | 27 +-- .../component/user/NewUser/NewUser.styles.ts | 3 + .../src/component/user/NewUser/NewUser.tsx | 62 ++++-- frontend/src/component/user/NewUser/index.js | 8 + .../StandaloneBanner.styles.ts | 2 +- .../user/common/AuthOptions/AuthOptions.tsx | 47 +++++ .../ResetPasswordForm/ResetPasswordForm.tsx | 6 +- frontend/src/interfaces/user.ts | 8 +- frontend/src/page/admin/admin-menu.jsx | 64 +++--- .../src/page/admin/api/api-key-create.jsx | 6 +- frontend/src/page/admin/api/api-key-list.jsx | 190 ++++++++++-------- frontend/src/page/admin/api/index.js | 16 +- .../ConfirmUserLink/ConfirmUserLink.tsx | 2 +- .../page/admin/users/UsersList/UsersList.jsx | 26 +-- .../src/page/admin/users/UsersList/index.js | 8 +- .../page/admin/users/del-user-component.jsx | 2 +- frontend/src/page/admin/users/index.js | 49 ++++- frontend/src/page/project/view.js | 2 +- 44 files changed, 891 insertions(+), 363 deletions(-) create mode 100644 frontend/src/assets/icons/google.svg create mode 100644 frontend/src/component/common/ListPlaceholder/ListPlaceholder.styles.js create mode 100644 frontend/src/component/common/ListPlaceholder/ListPlaceholder.tsx rename frontend/src/component/project/{ViewProject/ViewProject.js => ProjectView/ProjectView.js} (80%) rename frontend/src/component/project/{ViewProject => ProjectView}/index.js (95%) create mode 100644 frontend/src/component/user/NewUser/index.js create mode 100644 frontend/src/component/user/common/AuthOptions/AuthOptions.tsx diff --git a/frontend/src/app.css b/frontend/src/app.css index fb97157b8e..a56797a0e7 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -13,7 +13,7 @@ body { } .MuiButton-root { - border-radius: 25px; + border-radius: 3px; } .skeleton { diff --git a/frontend/src/assets/icons/google.svg b/frontend/src/assets/icons/google.svg new file mode 100644 index 0000000000..2cdc5e2781 --- /dev/null +++ b/frontend/src/assets/icons/google.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/component/Reporting/ReportCard/ReportCard.jsx b/frontend/src/component/Reporting/ReportCard/ReportCard.jsx index 6e93c9b027..927db4b64b 100644 --- a/frontend/src/component/Reporting/ReportCard/ReportCard.jsx +++ b/frontend/src/component/Reporting/ReportCard/ReportCard.jsx @@ -47,9 +47,8 @@ const ReportCard = ({ features }) => { const total = features.length; const activeTogglesArray = getActiveToggles(); - const potentiallyStaleToggles = getPotentiallyStaleToggles( - activeTogglesArray - ); + const potentiallyStaleToggles = + getPotentiallyStaleToggles(activeTogglesArray); const activeTogglesCount = activeTogglesArray.length; const staleTogglesCount = features.length - activeTogglesCount; @@ -95,6 +94,17 @@ const ReportCard = ({ features }) => { return (
+
+

Health rating

+
+ -1} + show={ +

{healthRating}%

+ } + /> +
+

Toggle report

    @@ -118,17 +128,7 @@ const ReportCard = ({ features }) => {
-
-

Health rating

-
- -1} - show={ -

{healthRating}%

- } - /> -
-
+

Potential actions

diff --git a/frontend/src/component/Reporting/ReportToggleList/ReportToggleListItem/ReportToggleListItem.jsx b/frontend/src/component/Reporting/ReportToggleList/ReportToggleListItem/ReportToggleListItem.jsx index 8d3d561eb6..cb5ca47a82 100644 --- a/frontend/src/component/Reporting/ReportToggleList/ReportToggleListItem/ReportToggleListItem.jsx +++ b/frontend/src/component/Reporting/ReportToggleList/ReportToggleListItem/ReportToggleListItem.jsx @@ -8,12 +8,27 @@ import CheckIcon from '@material-ui/icons/Check'; import ReportProblemOutlinedIcon from '@material-ui/icons/ReportProblemOutlined'; import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender'; -import { pluralize, getDates, expired, toggleExpiryByTypeMap, getDiffInDays } from '../../utils'; +import { + pluralize, + getDates, + expired, + toggleExpiryByTypeMap, + getDiffInDays, +} from '../../utils'; import { KILLSWITCH, PERMISSION } from '../../constants'; import styles from '../ReportToggleList.module.scss'; -const ReportToggleListItem = ({ name, stale, lastSeenAt, createdAt, type, checked, bulkActionsOn, setFeatures }) => { +const ReportToggleListItem = ({ + name, + stale, + lastSeenAt, + createdAt, + type, + checked, + bulkActionsOn, + setFeatures, +}) => { const nameMatches = feature => feature.name === name; const history = useHistory(); @@ -80,17 +95,26 @@ const ReportToggleListItem = ({ name, stale, lastSeenAt, createdAt, type, checke const formatReportStatus = () => { if (type === KILLSWITCH || type === PERMISSION) { - return renderStatus(, 'Active'); + return renderStatus( + , + 'Healthy' + ); } const [date, now] = getDates(createdAt); const diff = getDiffInDays(date, now); if (expired(diff, type)) { - return renderStatus(, 'Potentially stale'); + return renderStatus( + , + 'Potentially stale' + ); } - return renderStatus(, 'Active'); + return renderStatus( + , + 'Healthy' + ); }; const navigateToFeature = () => { @@ -102,7 +126,12 @@ const ReportToggleListItem = ({ name, stale, lastSeenAt, createdAt, type, checke }); return ( - + ({ + emptyStateListItem: { + border: `2px dashed ${theme.palette.borders.main}`, + padding: '0.8rem', + textAlign: 'center', + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, +})); diff --git a/frontend/src/component/common/ListPlaceholder/ListPlaceholder.tsx b/frontend/src/component/common/ListPlaceholder/ListPlaceholder.tsx new file mode 100644 index 0000000000..77e7fa1660 --- /dev/null +++ b/frontend/src/component/common/ListPlaceholder/ListPlaceholder.tsx @@ -0,0 +1,26 @@ +import { ListItem } from '@material-ui/core'; +import { Link } from 'react-router-dom'; +import ConditionallyRender from '../ConditionallyRender'; +import { useStyles } from './ListPlaceholder.styles'; + +interface IListPlaceholderProps { + text: string; + link?: string; + linkText?: string; +} + +const ListPlaceholder = ({ text, link, linkText }: IListPlaceholderProps) => { + const styles = useStyles(); + + return ( + + {text} + Add your first toggle} + /> + + ); +}; + +export default ListPlaceholder; diff --git a/frontend/src/component/common/common.module.scss b/frontend/src/component/common/common.module.scss index b35152d53a..9cb2fa3b00 100644 --- a/frontend/src/component/common/common.module.scss +++ b/frontend/src/component/common/common.module.scss @@ -78,7 +78,6 @@ } .dropdownButton { - text-transform: none; font-weight: normal; } diff --git a/frontend/src/component/common/index.js b/frontend/src/component/common/index.js index 68f4b6da43..d653c3d12e 100644 --- a/frontend/src/component/common/index.js +++ b/frontend/src/component/common/index.js @@ -84,7 +84,6 @@ export const FormButtons = ({ type="submit" color="primary" variant="contained" - startIcon={add} > {submitText} diff --git a/frontend/src/component/context/ContextList/ContextList.jsx b/frontend/src/component/context/ContextList/ContextList.jsx index 0ba5874cd8..e7cae50cc7 100644 --- a/frontend/src/component/context/ContextList/ContextList.jsx +++ b/frontend/src/component/context/ContextList/ContextList.jsx @@ -14,6 +14,8 @@ import { ListItemIcon, ListItemText, Tooltip, + useMediaQuery, + Button, } from '@material-ui/core'; import { useContext, useState } from 'react'; import { Link } from 'react-router-dom'; @@ -24,6 +26,7 @@ import AccessContext from '../../../contexts/AccessContext'; const ContextList = ({ removeContextField, history, contextFields }) => { const { hasAccess } = useContext(AccessContext); const [showDelDialogue, setShowDelDialogue] = useState(false); + const smallScreen = useMediaQuery('(max-width:700px)'); const [name, setName] = useState(); const styles = useStyles(); @@ -63,11 +66,27 @@ const ContextList = ({ removeContextField, history, contextFields }) => { - history.push('/context/create')}> - add - - + + history.push('/context/create')} + > + add + + + } + elseShow={ + + } + /> } /> ); diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx b/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx index ed2f4c118a..599376b006 100644 --- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx +++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx @@ -26,6 +26,7 @@ import { CREATE_FEATURE } from '../../AccessProvider/permissions'; import AccessContext from '../../../contexts/AccessContext'; import { useStyles } from './styles'; +import ListPlaceholder from '../../common/ListPlaceholder/ListPlaceholder'; const FeatureToggleList = ({ fetcher, @@ -41,7 +42,7 @@ const FeatureToggleList = ({ }) => { const { hasAccess } = useContext(AccessContext); const styles = useStyles(); - const smallScreen = useMediaQuery('(max-width:700px)'); + const smallScreen = useMediaQuery('(max-width:800px)'); useLayoutEffect(() => { fetcher(); @@ -102,13 +103,12 @@ const FeatureToggleList = ({ } elseShow={ - - No features available. Get started by adding a - new feature toggle. - - Add your first toggle - - + } /> } diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListActions/FeatureToggleListActions.jsx b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListActions/FeatureToggleListActions.jsx index 16aa50eda0..dde36ca6a1 100644 --- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListActions/FeatureToggleListActions.jsx +++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListActions/FeatureToggleListActions.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { MenuItem } from '@material-ui/core'; +import { MenuItem, Typography } from '@material-ui/core'; import { MenuItemWithIcon } from '../../../common'; import DropdownMenu from '../../../common/DropdownMenu/DropdownMenu'; import ProjectSelect from '../../../common/ProjectSelect'; @@ -66,6 +66,9 @@ const FeatureToggleListActions = ({ return (
+ + Sorted by: +
diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListActions/styles.js b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListActions/styles.js index a9c119e368..8800e17d1c 100644 --- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleListActions/styles.js +++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleListActions/styles.js @@ -6,5 +6,7 @@ export const useStyles = makeStyles({ margin: '0 0.25rem', }, marginRight: '0.25rem', + display: 'flex', + alignItems: 'center', }, }); diff --git a/frontend/src/component/feature/FeatureToggleList/__tests__/__snapshots__/list-component-test.jsx.snap b/frontend/src/component/feature/FeatureToggleList/__tests__/__snapshots__/list-component-test.jsx.snap index f17034d80b..4787dadf0c 100644 --- a/frontend/src/component/feature/FeatureToggleList/__tests__/__snapshots__/list-component-test.jsx.snap +++ b/frontend/src/component/feature/FeatureToggleList/__tests__/__snapshots__/list-component-test.jsx.snap @@ -66,6 +66,12 @@ exports[`renders correctly with one feature 1`] = `
+

+ Sorted by: +

+ + } + /> + } + > {'Error'} - {error} + + {error} + - +
{users.map(user => { const labelId = `checkbox-list-secondary-label-${user.id}`; @@ -112,25 +148,50 @@ function AccessComponent({ projectId, project }) { - - - + + Choose role - ))} - + {roles.map(role => ( + + {role.name} + + ))} + + - + ); } diff --git a/frontend/src/component/project/form-project-component.jsx b/frontend/src/component/project/form-project-component.jsx index 25c293f0f6..3518fb7856 100644 --- a/frontend/src/component/project/form-project-component.jsx +++ b/frontend/src/component/project/form-project-component.jsx @@ -75,8 +75,15 @@ class ProjectFormComponent extends Component { }; onCancel = evt => { + const { editMode } = this.props; + const { project } = this.state; + evt.preventDefault(); - this.props.history.push('/projects'); + if (editMode) { + this.props.history.push(`/projects/view/${project.id}`); + } else { + this.props.history.push('/projects'); + } }; onSubmit = async evt => { @@ -87,7 +94,7 @@ class ProjectFormComponent extends Component { if (valid) { await this.props.submit(project); - this.props.history.push('/projects'); + this.props.history.push(`/projects/view/${project.id}`); } }; @@ -98,21 +105,24 @@ class ProjectFormComponent extends Component { const submitText = editMode ? 'Update' : 'Create'; return ( - - {submitText} Project - - Manage access - - } - /> -
}> + + {submitText} Project + + Manage access + + } + /> +
+ } + > - - { evt.preventDefault(); - const { - createStrategy, - updateStrategy, - history, - editMode, - } = this.props; + const { createStrategy, updateStrategy, history, editMode } = + this.props; const { strategy } = this.state; const parameters = (strategy.parameters || []) diff --git a/frontend/src/component/tag-types/__tests__/__snapshots__/tag-type-create-component-test.js.snap b/frontend/src/component/tag-types/__tests__/__snapshots__/tag-type-create-component-test.js.snap index 3a7a5768f2..7eb1a64021 100644 --- a/frontend/src/component/tag-types/__tests__/__snapshots__/tag-type-create-component-test.js.snap +++ b/frontend/src/component/tag-types/__tests__/__snapshots__/tag-type-create-component-test.js.snap @@ -60,16 +60,6 @@ exports[`it supports editMode 1`] = ` - - - add - - Update @@ -166,16 +156,6 @@ exports[`renders correctly for creating 1`] = ` - - - add - - Create diff --git a/frontend/src/component/user/HostedAuth/HostedAuth.jsx b/frontend/src/component/user/HostedAuth/HostedAuth.jsx index 662a2c66c1..5fac62f9f9 100644 --- a/frontend/src/component/user/HostedAuth/HostedAuth.jsx +++ b/frontend/src/component/user/HostedAuth/HostedAuth.jsx @@ -2,13 +2,12 @@ import React, { useState } from 'react'; import classnames from 'classnames'; import PropTypes from 'prop-types'; import { Button, Grid, TextField, Typography } from '@material-ui/core'; -import LockRounded from '@material-ui/icons/LockRounded'; import { useHistory } from 'react-router'; import { useCommonStyles } from '../../../common.styles'; import { useStyles } from './HostedAuth.styles'; import { Link } from 'react-router-dom'; -import { GoogleSvg } from './Icons'; import useQueryParams from '../../../hooks/useQueryParams'; +import AuthOptions from '../common/AuthOptions/AuthOptions'; const PasswordAuth = ({ authDetails, passwordLogin }) => { const commonStyles = useCommonStyles(); @@ -64,7 +63,6 @@ const PasswordAuth = ({ authDetails, passwordLogin }) => { } }; - const { usernameError, passwordError, apiError } = errors; const { options = [] } = authDetails; @@ -72,19 +70,7 @@ const PasswordAuth = ({ authDetails, passwordLogin }) => {

- {options.map(o => ( -
- -
- ))} +

or

@@ -140,9 +126,12 @@ const PasswordAuth = ({ authDetails, passwordLogin }) => {

- Don't have an account?
Sign up
- - + + Don't have an account?
{' '} + + Sign up + +
diff --git a/frontend/src/component/user/NewUser/NewUser.styles.ts b/frontend/src/component/user/NewUser/NewUser.styles.ts index 67c4152ed2..5b91dbfad0 100644 --- a/frontend/src/component/user/NewUser/NewUser.styles.ts +++ b/frontend/src/component/user/NewUser/NewUser.styles.ts @@ -23,6 +23,9 @@ export const useStyles = makeStyles(theme => ({ marginBottom: '0.5rem', fontSize: '1.1rem', }, + passwordHeader: { + marginTop: '2rem', + }, emailField: { minWidth: '300px', [theme.breakpoints.down('xs')]: { diff --git a/frontend/src/component/user/NewUser/NewUser.tsx b/frontend/src/component/user/NewUser/NewUser.tsx index 8b2596c340..ed1fea3ab1 100644 --- a/frontend/src/component/user/NewUser/NewUser.tsx +++ b/frontend/src/component/user/NewUser/NewUser.tsx @@ -10,15 +10,16 @@ import useResetPassword from '../../../hooks/useResetPassword'; import StandaloneLayout from '../common/StandaloneLayout/StandaloneLayout'; import ConditionallyRender from '../../common/ConditionallyRender'; import InvalidToken from '../common/InvalidToken/InvalidToken'; +import { IAuthStatus } from '../../../interfaces/user'; +import AuthOptions from '../common/AuthOptions/AuthOptions'; -const NewUser = () => { - const { - token, - data, - loading, - setLoading, - invalidToken, - } = useResetPassword(); +interface INewUserProps { + user: IAuthStatus; +} + +const NewUser = ({ user }: INewUserProps) => { + const { token, data, loading, setLoading, invalidToken } = + useResetPassword(); const ref = useLoading(loading); const commonStyles = useCommonStyles(); const styles = useStyles(); @@ -57,7 +58,7 @@ const NewUser = () => { { className={commonStyles.largeDivider} data-loading /> - - Set a password for your account. - + 0 + } + show={ + <> + + Login with 3rd party providers + + +
+ + OR set a new password for your + account + + + } + elseShow={ + + Set a password for your account. + + } + />
} diff --git a/frontend/src/component/user/NewUser/index.js b/frontend/src/component/user/NewUser/index.js new file mode 100644 index 0000000000..d2d6eb8f8e --- /dev/null +++ b/frontend/src/component/user/NewUser/index.js @@ -0,0 +1,8 @@ +import { connect } from 'react-redux'; +import NewUser from './NewUser'; + +const mapStateToProps = (state: any) => ({ + user: state.user.toJS(), +}); + +export default connect(mapStateToProps)(NewUser); diff --git a/frontend/src/component/user/StandaloneBanner/StandaloneBanner.styles.ts b/frontend/src/component/user/StandaloneBanner/StandaloneBanner.styles.ts index 2bfd4cc876..4460fe2670 100644 --- a/frontend/src/component/user/StandaloneBanner/StandaloneBanner.styles.ts +++ b/frontend/src/component/user/StandaloneBanner/StandaloneBanner.styles.ts @@ -19,7 +19,7 @@ export const useStyles = makeStyles(theme => ({ }, }, switchesContainer: { - position: 'fixed', + position: 'absolute', bottom: '40px', display: 'flex', flexDirection: 'column', diff --git a/frontend/src/component/user/common/AuthOptions/AuthOptions.tsx b/frontend/src/component/user/common/AuthOptions/AuthOptions.tsx new file mode 100644 index 0000000000..42a85211c3 --- /dev/null +++ b/frontend/src/component/user/common/AuthOptions/AuthOptions.tsx @@ -0,0 +1,47 @@ +import { Button } from '@material-ui/core'; +import classnames from 'classnames'; +import { useCommonStyles } from '../../../../common.styles'; +import { IAuthOptions } from '../../../../interfaces/user'; +import { ReactComponent as GoogleSvg } from '../../../../assets/icons/google.svg'; +import LockRounded from '@material-ui/icons/LockRounded'; + +interface IAuthOptionProps { + options?: IAuthOptions[]; +} + +const AuthOptions = ({ options }: IAuthOptionProps) => { + const commonStyles = useCommonStyles(); + return ( + <> + {options?.map(o => ( +
+ +
+ ))} + + ); +}; + +export default AuthOptions; diff --git a/frontend/src/component/user/common/ResetPasswordForm/ResetPasswordForm.tsx b/frontend/src/component/user/common/ResetPasswordForm/ResetPasswordForm.tsx index ea8d660667..25acdae8af 100644 --- a/frontend/src/component/user/common/ResetPasswordForm/ResetPasswordForm.tsx +++ b/frontend/src/component/user/common/ResetPasswordForm/ResetPasswordForm.tsx @@ -111,17 +111,19 @@ const ResetPasswordForm = ({ token, setLoading }: IResetPasswordProps) => { size="small" type="password" placeholder="Password" - value={password} + value={password || ''} onChange={e => setPassword(e.target.value)} + autoComplete="password" data-loading /> setConfirmPassword(e.target.value)} + autoComplete="confirm-password" data-loading /> - - - supervised_user_circle + + Users - + } - > - - - apps - API Access - - }> - - - lock - Authentication - - }> - + > + + API Access + + } + > + + Authentication + + } + > ); diff --git a/frontend/src/page/admin/api/api-key-create.jsx b/frontend/src/page/admin/api/api-key-create.jsx index 1e66f83fce..266445ec09 100644 --- a/frontend/src/page/admin/api/api-key-create.jsx +++ b/frontend/src/page/admin/api/api-key-create.jsx @@ -13,10 +13,9 @@ import classnames from 'classnames'; import { styles as commonStyles } from '../../../component/common'; import { useStyles } from './styles'; -function CreateApiKey({ addKey }) { +function CreateApiKey({ addKey, show, setShow }) { const styles = useStyles(); const [type, setType] = useState('CLIENT'); - const [show, setShow] = useState(false); const [username, setUsername] = useState(); const [error, setError] = useState(); @@ -107,6 +106,9 @@ function CreateApiKey({ addKey }) { CreateApiKey.propTypes = { addKey: PropTypes.func.isRequired, + setShow: PropTypes.func.isRequired, + show: PropTypes.bool.isRequired, + toggle: PropTypes.func.isRequired, }; export default CreateApiKey; diff --git a/frontend/src/page/admin/api/api-key-list.jsx b/frontend/src/page/admin/api/api-key-list.jsx index cfdddcb831..a560e00d6d 100644 --- a/frontend/src/page/admin/api/api-key-list.jsx +++ b/frontend/src/page/admin/api/api-key-list.jsx @@ -21,6 +21,8 @@ import { DELETE_API_TOKEN, CREATE_API_TOKEN, } from '../../../component/AccessProvider/permissions'; +import PageContent from '../../../component/common/PageContent'; +import HeaderTitle from '../../../component/common/HeaderTitle'; function ApiKeyList({ location, @@ -30,6 +32,8 @@ function ApiKeyList({ keys, unleashUrl, }) { + const [show, setShow] = useState(false); + const { hasAccess } = useContext(AccessContext); const [showDelete, setShowDelete] = useState(false); const [delKey, setDelKey] = useState(undefined); @@ -45,93 +49,111 @@ function ApiKeyList({ }, []); return ( -
- -

- Read the{' '} - - Getting started guide - {' '} - to learn how to connect to the Unleash API from your - application or programmatically. Please note it can take up - to 1 minute before a new API key is activated. -

+ + } + /> + } + /> + } + > +
+ +

+ Read the{' '} + + Getting started guide + {' '} + to learn how to connect to the Unleash API from your + application or programmatically. Please note it can take + up to 1 minute before a new API key is activated. +

+
+ API URL: {' '} +
{unleashUrl}/api/
+
+ +

- API URL: {' '} -
{unleashUrl}/api/
- -
-
- -
- - - - Created - Username - Access Type - Secret - Action - - - - {keys.map(item => ( - - - {formatFullDateTimeWithLocale( - item.createdAt, - location.locale - )} - - - {item.username} - - - {item.type} - - - - - - { - setDelKey(item.secret); - setShowDelete(true); - }} - > - delete - - - } - /> +
+
+ + + Created + Username + Access Type + Secret + Action - ))} - -
- { - setShowDelete(false); - setDelKey(undefined); - }} - title="Really delete API key?" - > -
Are you sure you want to delete?
-
- } - /> -
+ + + {keys.map(item => ( + + + {formatFullDateTimeWithLocale( + item.createdAt, + location.locale + )} + + + {item.username} + + + {item.type} + + + + + + { + setDelKey(item.secret); + setShowDelete(true); + }} + > + delete + + + } + /> + + ))} + + + { + setShowDelete(false); + setDelKey(undefined); + }} + title="Really delete API key?" + > +
Are you sure you want to delete?
+
+
+ ); } diff --git a/frontend/src/page/admin/api/index.js b/frontend/src/page/admin/api/index.js index 4d6ada91cc..7a69775d4d 100644 --- a/frontend/src/page/admin/api/index.js +++ b/frontend/src/page/admin/api/index.js @@ -1,18 +1,16 @@ -import React from 'react'; import PropTypes from 'prop-types'; import ApiKeyList from './api-key-list-container'; import AdminMenu from '../admin-menu'; -import PageContent from '../../../component/common/PageContent/PageContent'; -const render = ({history}) => ( -
- - +const render = ({ history }) => { + return ( +
+ - -
-); +
+ ); +}; render.propTypes = { match: PropTypes.object.isRequired, diff --git a/frontend/src/page/admin/users/ConfirmUserAdded/ConfirmUserLink/ConfirmUserLink.tsx b/frontend/src/page/admin/users/ConfirmUserAdded/ConfirmUserLink/ConfirmUserLink.tsx index 2a63419217..c799c919e2 100644 --- a/frontend/src/page/admin/users/ConfirmUserAdded/ConfirmUserLink/ConfirmUserLink.tsx +++ b/frontend/src/page/admin/users/ConfirmUserAdded/ConfirmUserLink/ConfirmUserLink.tsx @@ -40,7 +40,7 @@ const ConfirmUserLink = ({ Want to avoid this step in the future?{' '} {/* TODO - ADD LINK HERE ONCE IT EXISTS*/} diff --git a/frontend/src/page/admin/users/UsersList/UsersList.jsx b/frontend/src/page/admin/users/UsersList/UsersList.jsx index 5f341382b5..08123c3a5f 100644 --- a/frontend/src/page/admin/users/UsersList/UsersList.jsx +++ b/frontend/src/page/admin/users/UsersList/UsersList.jsx @@ -2,7 +2,6 @@ import { useContext, useState } from 'react'; import PropTypes from 'prop-types'; import { - Button, Table, TableBody, TableCell, @@ -23,7 +22,7 @@ import UserListItem from './UserListItem/UserListItem'; import loadingData from './loadingData'; import useLoading from '../../../../hooks/useLoading'; -function UsersList({ location }) { +function UsersList({ location, closeDialog, showDialog }) { const { users, roles, refetch, loading } = useUsers(); const { addUser, @@ -35,7 +34,6 @@ function UsersList({ location }) { userApiErrors, } = useAdminUsersApi(); const { hasAccess } = useContext(AccessContext); - const [showDialog, setDialog] = useState(false); const [pwDialog, setPwDialog] = useState({ open: false }); const [delDialog, setDelDialog] = useState(false); const [showConfirm, setShowConfirm] = useState(false); @@ -45,15 +43,6 @@ function UsersList({ location }) { const [updateDialog, setUpdateDialog] = useState({ open: false }); const ref = useLoading(loading); - const openDialog = e => { - e.preventDefault(); - setDialog(true); - }; - - const closeDialog = () => { - setDialog(false); - }; - const closeDelDialog = () => { setDelDialog(false); setDelUser(undefined); @@ -177,19 +166,6 @@ function UsersList({ location }) { {renderUsers()}
- - Add new user - - } - elseShow={PS! Only admins can add/remove users.} - /> ({ - location: state.settings.toJS().location || {}, -}); +const mapStateToProps = state => { + return { + location: state.settings.toJS().location || {}, + }; +}; const Container = connect(mapStateToProps)(UsersList); diff --git a/frontend/src/page/admin/users/del-user-component.jsx b/frontend/src/page/admin/users/del-user-component.jsx index b2ee4ac289..bde257c8eb 100644 --- a/frontend/src/page/admin/users/del-user-component.jsx +++ b/frontend/src/page/admin/users/del-user-component.jsx @@ -78,7 +78,7 @@ const DelUserComponent = ({ DelUserComponent.propTypes = { showDialog: propTypes.bool.isRequired, closeDialog: propTypes.func.isRequired, - user: propTypes.object.isRequired, + user: propTypes.object, removeUser: propTypes.func.isRequired, }; diff --git a/frontend/src/page/admin/users/index.js b/frontend/src/page/admin/users/index.js index 4d779c7cea..d0762bfa43 100644 --- a/frontend/src/page/admin/users/index.js +++ b/frontend/src/page/admin/users/index.js @@ -1,4 +1,4 @@ -import { useContext } from 'react'; +import { useContext, useState } from 'react'; import PropTypes from 'prop-types'; import UsersList from './UsersList'; import AdminMenu from '../admin-menu'; @@ -7,17 +7,60 @@ import AccessContext from '../../../contexts/AccessContext'; import ConditionallyRender from '../../../component/common/ConditionallyRender'; import { ADMIN } from '../../../component/AccessProvider/permissions'; import { Alert } from '@material-ui/lab'; +import HeaderTitle from '../../../component/common/HeaderTitle'; +import { Button } from '@material-ui/core'; const UsersAdmin = ({ history }) => { const { hasAccess } = useContext(AccessContext); + const [showDialog, setDialog] = useState(false); + + const openDialog = e => { + e.preventDefault(); + setDialog(true); + }; + + const closeDialog = () => { + setDialog(false); + }; return (
- + + Add new user + + } + elseShow={ + + PS! Only admins can add/remove users. + + } + /> + } + /> + } + > } + show={ + + } elseShow={ You need instance admin to access this section. diff --git a/frontend/src/page/project/view.js b/frontend/src/page/project/view.js index 46c8b83739..a9565d331d 100644 --- a/frontend/src/page/project/view.js +++ b/frontend/src/page/project/view.js @@ -1,5 +1,5 @@ import React from 'react'; -import ViewProject from '../../component/project/ViewProject'; +import ViewProject from '../../component/project/ProjectView'; import PropTypes from 'prop-types'; const render = ({ match: { params }, history }) => (