diff --git a/frontend/src/component/admin/users/AddUser/AddUser.tsx b/frontend/src/component/admin/users/AddUser/AddUser.tsx deleted file mode 100644 index e2a9d363b8..0000000000 --- a/frontend/src/component/admin/users/AddUser/AddUser.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useState } from 'react'; -import Dialogue from '../../../common/Dialogue'; - -import { IUserApiErrors } from '../../../../hooks/api/actions/useAdminUsersApi/useAdminUsersApi'; -import IRole from '../../../../interfaces/role'; -import AddUserForm from './AddUserForm/AddUserForm'; - -interface IAddUserProps { - showDialog: boolean; - closeDialog: () => void; - addUser: (data: any) => any; - validatePassword: () => boolean; - userLoading: boolean; - userApiErrors: IUserApiErrors; - roles: IRole[]; -} - -interface IAddUserFormData { - name: string; - email: string; - rootRole: number; - sendEmail: boolean; -} - -const EDITOR_ROLE_ID = 2; - -const initialData = { email: '', name: '', rootRole: EDITOR_ROLE_ID, sendEmail: true }; - -const AddUser = ({ - showDialog, - closeDialog, - userLoading, - addUser, - userApiErrors, - roles, -}: IAddUserProps) => { - const [data, setData] = useState(initialData); - const [error, setError] = useState({}); - - const submit = async (e: Event) => { - e.preventDefault(); - if (!data.email) { - setError({ general: 'You must specify the email address' }); - return; - } - - if (!data.rootRole) { - setError({ general: 'You must specify a role for the user' }); - return; - } - - await addUser(data); - setData(initialData); - setError({}); - }; - - const onCancel = (e: Event) => { - e.preventDefault(); - setData(initialData); - setError({}); - closeDialog(); - }; - - const formId = 'add-user-dialog-form'; - - return ( - { - submit(e); - }} - formId={formId} - open={showDialog} - onClose={onCancel} - primaryButtonText="Add user" - secondaryButtonText="Cancel" - title="Add team member" - fullWidth - > - - - ); -}; - -export default AddUser; diff --git a/frontend/src/component/admin/users/AddUser/AddUserForm/AddUserForm.jsx b/frontend/src/component/admin/users/AddUser/AddUserForm/AddUserForm.jsx deleted file mode 100644 index 9cc53fed5e..0000000000 --- a/frontend/src/component/admin/users/AddUser/AddUserForm/AddUserForm.jsx +++ /dev/null @@ -1,217 +0,0 @@ -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { - DialogContent, - FormControl, - FormControlLabel, - Radio, - RadioGroup, - Switch, - TextField, - Typography, -} from '@material-ui/core'; - -import { trim } from '../../../../common/util'; -import { useCommonStyles } from '../../../../../common.styles'; -import ConditionallyRender from '../../../../common/ConditionallyRender'; -import { useStyles } from './AddUserForm.styles'; -import useLoading from '../../../../../hooks/useLoading'; -import { - ADD_USER_ERROR, - UPDATE_USER_ERROR, -} from '../../../../../hooks/api/actions/useAdminUsersApi/useAdminUsersApi'; -import { Alert } from '@material-ui/lab'; - -function AddUserForm({ - submit, - data, - error, - setData, - roles, - userLoading, - userApiErrors, - formId, -}) { - const ref = useLoading(userLoading); - const commonStyles = useCommonStyles(); - const styles = useStyles(); - - const updateField = e => { - setData({ - ...data, - [e.target.name]: e.target.value, - }); - }; - - const updateFieldWithTrim = e => { - setData({ - ...data, - [e.target.name]: trim(e.target.value), - }); - }; - - const toggleBooleanField = e => { - setData({ - ...data, - [e.target.name]: !data[e.target.name], - }); - }; - - const updateNumberField = e => { - setData({ - ...data, - [e.target.name]: +e.target.value, - }); - }; - - const sortRoles = (a, b) => { - if (b.name[0] < a.name[0]) { - return 1; - } else if (a.name[0] < b.name[0]) { - return -1; - } - return 0; - }; - - const apiError = - userApiErrors[ADD_USER_ERROR] || userApiErrors[UPDATE_USER_ERROR]; - return ( -
-
- - - {apiError} - - } - /> -
- - Who is your team member? - - - {error.general} -

- } - /> - - - -
-
-
- - - What is your team member allowed to do? - - - {roles.sort(sortRoles).map(role => ( - - {role.name} - - {role.description} - -
- } - control={ - - } - value={role.id} - /> - ))} - - -
-
-
- - - Should we send an email to your new team member - -
- - - {data.sendEmail ? 'Yes' : 'No'} - -
-
-
- - - - ); -} - -AddUserForm.propTypes = { - data: PropTypes.object.isRequired, - error: PropTypes.object.isRequired, - submit: PropTypes.func.isRequired, - setData: PropTypes.func.isRequired, - roles: PropTypes.array.isRequired, -}; - -export default AddUserForm; diff --git a/frontend/src/component/admin/users/AddUser/AddUserForm/AddUserForm.styles.js b/frontend/src/component/admin/users/AddUser/AddUserForm/AddUserForm.styles.js deleted file mode 100644 index 5c0893ff09..0000000000 --- a/frontend/src/component/admin/users/AddUser/AddUserForm/AddUserForm.styles.js +++ /dev/null @@ -1,21 +0,0 @@ -import { makeStyles } from '@material-ui/styles'; - -export const useStyles = makeStyles(theme => ({ - roleBox: { - margin: '3px 0', - border: '1px solid #EFEFEF', - padding: '1rem', - }, - userInfoContainer: { - margin: '-20px 0', - }, - roleRadio: { - marginRight: '15px', - }, - roleSubtitle: { - marginBottom: '0.5rem', - }, - errorAlert: { - marginBottom: '1rem', - }, -})); diff --git a/frontend/src/component/admin/users/CreateUser/CreateUser.tsx b/frontend/src/component/admin/users/CreateUser/CreateUser.tsx new file mode 100644 index 0000000000..586427f1fb --- /dev/null +++ b/frontend/src/component/admin/users/CreateUser/CreateUser.tsx @@ -0,0 +1,119 @@ +import FormTemplate from '../../../common/FormTemplate/FormTemplate'; +import { useHistory } from 'react-router-dom'; +import UserForm from '../UserForm/UserForm'; +import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; +import useToast from '../../../../hooks/useToast'; +import useAddUserForm from '../hooks/useAddUserForm'; +import useAdminUsersApi from '../../../../hooks/api/actions/useAdminUsersApi/useAdminUsersApi'; +import ConfirmUserAdded from '../ConfirmUserAdded/ConfirmUserAdded'; +import { useState } from 'react'; +import { scrollToTop } from '../../../common/util'; +import PermissionButton from '../../../common/PermissionButton/PermissionButton'; +import { ADMIN } from '../../../providers/AccessProvider/permissions'; + +const CreateUser = () => { + /* @ts-ignore */ + const { setToastApiError } = useToast(); + const { uiConfig } = useUiConfig(); + const history = useHistory(); + const { + name, + setName, + email, + setEmail, + sendEmail, + setSendEmail, + rootRole, + setRootRole, + getAddUserPayload, + validateName, + validateEmail, + errors, + clearErrors, + } = useAddUserForm(); + const [showConfirm, setShowConfirm] = useState(false); + const [inviteLink, setInviteLink] = useState(''); + + const { addUser, userLoading: loading } = useAdminUsersApi(); + + const handleSubmit = async (e: Event) => { + e.preventDefault(); + clearErrors(); + const validName = validateName(); + const validEmail = validateEmail(); + + if (validName && validEmail) { + const payload = getAddUserPayload(); + try { + await addUser(payload) + .then(res => res.json()) + .then(user => { + scrollToTop(); + setInviteLink(user.inviteLink); + setShowConfirm(true); + }); + } catch (e: any) { + setToastApiError(e.toString()); + } + } + }; + const closeConfirm = () => { + setShowConfirm(false); + history.push('/admin/user-admin'); + }; + + const formatApiCode = () => { + return `curl --location --request POST '${ + uiConfig.unleashUrl + }/api/admin/user-admin' \\ +--header 'Authorization: INSERT_API_KEY' \\ +--header 'Content-Type: application/json' \\ +--data-raw '${JSON.stringify(getAddUserPayload(), undefined, 2)}'`; + }; + + const handleCancel = () => { + history.goBack(); + }; + + return ( + + + + Create user + + + + + ); +}; + +export default CreateUser; diff --git a/frontend/src/component/admin/users/EditUser/EditUser.tsx b/frontend/src/component/admin/users/EditUser/EditUser.tsx new file mode 100644 index 0000000000..b836602e9f --- /dev/null +++ b/frontend/src/component/admin/users/EditUser/EditUser.tsx @@ -0,0 +1,114 @@ +import FormTemplate from '../../../common/FormTemplate/FormTemplate'; +import { useHistory, useParams } from 'react-router-dom'; +import UserForm from '../UserForm/UserForm'; +import useAddUserForm from '../hooks/useAddUserForm'; +import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; +import useToast from '../../../../hooks/useToast'; +import useAdminUsersApi from '../../../../hooks/api/actions/useAdminUsersApi/useAdminUsersApi'; +import useUserInfo from '../../../../hooks/api/getters/useUserInfo/useUserInfo'; +import { scrollToTop } from '../../../common/util'; +import { useEffect } from 'react'; +import PermissionButton from '../../../common/PermissionButton/PermissionButton'; +import { ADMIN } from '../../../providers/AccessProvider/permissions'; +import { EDIT } from '../../../../constants/misc'; + +const EditUser = () => { + useEffect(() => { + scrollToTop(); + }, []); + const { uiConfig } = useUiConfig(); + const { setToastData, setToastApiError } = useToast(); + const { id } = useParams<{ id: string }>(); + const { user, refetch } = useUserInfo(id); + const { updateUser, userLoading: loading } = useAdminUsersApi(); + const history = useHistory(); + const { + name, + setName, + email, + setEmail, + sendEmail, + setSendEmail, + rootRole, + setRootRole, + getAddUserPayload, + validateName, + errors, + clearErrors, + } = useAddUserForm( + user?.name, + user?.email, + user?.sendEmail, + user?.rootRole + ); + + const formatApiCode = () => { + return `curl --location --request PUT '${ + uiConfig.unleashUrl + }/api/admin/user-admin/${id}' \\ +--header 'Authorization: INSERT_API_KEY' \\ +--header 'Content-Type: application/json' \\ +--data-raw '${JSON.stringify(getAddUserPayload(), undefined, 2)}'`; + }; + + const handleSubmit = async (e: Event) => { + e.preventDefault(); + const payload = getAddUserPayload(); + const validName = validateName(); + + if (validName) { + try { + await updateUser({ ...payload, id }); + refetch(); + history.push('/admin/users'); + setToastData({ + title: 'User information updated', + type: 'success', + }); + } catch (e: any) { + setToastApiError(e.toString()); + } + } + }; + + const handleCancel = () => { + history.goBack(); + }; + + return ( + + + + Edit user + + + + ); +}; + +export default EditUser; diff --git a/frontend/src/component/admin/users/UserForm/UserForm.styles.ts b/frontend/src/component/admin/users/UserForm/UserForm.styles.ts new file mode 100644 index 0000000000..93a28c55fe --- /dev/null +++ b/frontend/src/component/admin/users/UserForm/UserForm.styles.ts @@ -0,0 +1,68 @@ +import { makeStyles } from '@material-ui/core/styles'; + +export const useStyles = makeStyles(theme => ({ + container: { + maxWidth: '400px', + }, + form: { + display: 'flex', + flexDirection: 'column', + height: '100%', + }, + input: { width: '100%', marginBottom: '1rem' }, + label: { + minWidth: '300px', + [theme.breakpoints.down(600)]: { + minWidth: 'auto', + }, + }, + buttonContainer: { + marginTop: 'auto', + display: 'flex', + justifyContent: 'flex-end', + }, + cancelButton: { + marginRight: '1.5rem', + }, + inputDescription: { + marginBottom: '0.5rem', + }, + formHeader: { + fontWeight: 'normal', + marginTop: '0', + }, + header: { + fontWeight: 'normal', + }, + permissionErrorContainer: { + position: 'relative', + }, + errorMessage: { + //@ts-ignore + fontSize: theme.fontSizes.smallBody, + color: theme.palette.error.main, + position: 'absolute', + top: '-8px', + }, + roleBox: { + margin: '3px 0', + border: '1px solid #EFEFEF', + padding: '1rem', + }, + userInfoContainer: { + margin: '-20px 0', + }, + roleRadio: { + marginRight: '15px', + }, + roleSubtitle: { + margin: '0.5rem 0', + }, + errorAlert: { + marginBottom: '1rem', + }, + flexRow: { + display: 'flex', + alignItems: 'center', + }, +})); diff --git a/frontend/src/component/admin/users/UserForm/UserForm.tsx b/frontend/src/component/admin/users/UserForm/UserForm.tsx new file mode 100644 index 0000000000..d9926b7344 --- /dev/null +++ b/frontend/src/component/admin/users/UserForm/UserForm.tsx @@ -0,0 +1,161 @@ +import Input from '../../../common/Input/Input'; +import { + FormControlLabel, + Button, + RadioGroup, + FormControl, + Typography, + Radio, + Switch, +} from '@material-ui/core'; +import { useStyles } from './UserForm.styles'; +import React from 'react'; +import useUsers from '../../../../hooks/api/getters/useUsers/useUsers'; +import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender'; +import { EDIT } from '../../../../constants/misc'; + +interface IUserForm { + email: string; + name: string; + rootRole: number; + sendEmail: boolean; + setEmail: React.Dispatch>; + setName: React.Dispatch>; + setSendEmail: React.Dispatch>; + setRootRole: React.Dispatch>; + handleSubmit: (e: any) => void; + handleCancel: () => void; + errors: { [key: string]: string }; + clearErrors: () => void; + mode?: string; +} + +const UserForm: React.FC = ({ + children, + email, + name, + rootRole, + sendEmail, + setEmail, + setName, + setSendEmail, + setRootRole, + handleSubmit, + handleCancel, + errors, + clearErrors, + mode, +}) => { + const styles = useStyles(); + const { roles } = useUsers(); + + const sortRoles = (a, b) => { + if (b.name[0] < a.name[0]) { + return 1; + } else if (a.name[0] < b.name[0]) { + return -1; + } + return 0; + }; + + return ( +
+

User information

+ +
+

+ Who is the new Unleash user? +

+ setName(e.target.value)} + error={Boolean(errors.name)} + errorText={errors.name} + onFocus={() => clearErrors()} + /> + setEmail(e.target.value)} + error={Boolean(errors.email)} + errorText={errors.email} + onFocus={() => clearErrors()} + /> + + + What is your team member allowed to do? + + setRootRole(+e.target.value)} + data-loading + > + {roles.sort(sortRoles).map(role => ( + + {role.name} + + {role.description} + +
+ } + control={ + + } + value={role.id} + /> + ))} + + + + + Should we send an email to your new team member + +
+ setSendEmail(!sendEmail)} + checked={sendEmail} + /> + + {sendEmail ? 'Yes' : 'No'} + +
+ + } + /> + +
+ + {children} +
+ + ); +}; + +export default UserForm; diff --git a/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx b/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx index 523913e258..dcbd26dc58 100644 --- a/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx +++ b/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx @@ -13,6 +13,7 @@ import { formatDateWithLocale } from '../../../../common/util'; import AccessContext from '../../../../../contexts/AccessContext'; import { IUser } from '../../../../../interfaces/user'; import { useStyles } from './UserListItem.styles'; +import { useHistory } from 'react-router-dom'; interface IUserListItemProps { user: IUser; @@ -36,6 +37,7 @@ const UserListItem = ({ location, }: IUserListItemProps) => { const { hasAccess } = useContext(AccessContext); + const history = useHistory() const styles = useStyles(); return ( @@ -78,7 +80,7 @@ const UserListItem = ({ data-loading aria-label="Edit" title="Edit" - onClick={openUpdateDialog(user)} + onClick={()=> history.push(`/admin/users/${user.id}/edit`)} > diff --git a/frontend/src/component/admin/users/UsersList/UsersList.jsx b/frontend/src/component/admin/users/UsersList/UsersList.jsx index 1ee8288299..736c55e6c2 100644 --- a/frontend/src/component/admin/users/UsersList/UsersList.jsx +++ b/frontend/src/component/admin/users/UsersList/UsersList.jsx @@ -8,9 +8,7 @@ import { TableHead, TableRow, } from '@material-ui/core'; -import AddUser from '../AddUser/AddUser'; import ChangePassword from '../change-password-component'; -import UpdateUser from '../update-user-component'; import DelUser from '../del-user-component'; import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender'; import AccessContext from '../../../../contexts/AccessContext'; @@ -27,9 +25,7 @@ import PaginateUI from '../../../common/PaginateUI/PaginateUI'; function UsersList({ location, closeDialog, showDialog }) { const { users, roles, refetch, loading } = useUsers(); const { - addUser, removeUser, - updateUser, changePassword, validatePassword, userLoading, @@ -42,7 +38,6 @@ function UsersList({ location, closeDialog, showDialog }) { const [emailSent, setEmailSent] = useState(false); const [inviteLink, setInviteLink] = useState(''); const [delUser, setDelUser] = useState(); - const [updateDialog, setUpdateDialog] = useState({ open: false }); const ref = useLoading(loading); const { page, pages, nextPage, prevPage, setPageIndex, pageIndex } = usePagination(users, 50); @@ -66,28 +61,6 @@ function UsersList({ location, closeDialog, showDialog }) { setPwDialog({ open: false }); }; - const openUpdateDialog = user => e => { - e.preventDefault(); - setUpdateDialog({ open: true, user }); - }; - - const closeUpdateDialog = () => { - setUpdateDialog({ open: false }); - }; - - const onAddUser = data => { - addUser(data) - .then(res => res.json()) - .then(user => { - setEmailSent(user.emailSent); - setInviteLink(user.inviteLink); - closeDialog(); - refetch(); - setShowConfirm(true); - }) - .catch(handleCatch); - }; - const onDeleteUser = () => { removeUser(delUser) .then(() => { @@ -97,15 +70,6 @@ function UsersList({ location, closeDialog, showDialog }) { .catch(handleCatch); }; - const onUpdateUser = data => { - updateUser(data) - .then(() => { - refetch(); - closeUpdateDialog(); - }) - .catch(handleCatch); - }; - const handleCatch = () => console.log('An exception was thrown and handled.'); @@ -126,7 +90,6 @@ function UsersList({ location, closeDialog, showDialog }) { - - - -

{error.general}

- - { + const [name, setName] = useState(initialName); + const [email, setEmail] = useState(initialEmail); + const [sendEmail, setSendEmail] = useState(initialSendEmail); + const [rootRole, setRootRole] = useState(initialRootRole); + const [errors, setErrors] = useState({}); + + const { users } = useUsers(); + + useEffect(() => { + setName(initialName); + }, [initialName]); + + useEffect(() => { + setEmail(initialEmail); + }, [initialEmail]); + + useEffect(() => { + setSendEmail(initialSendEmail); + }, [initialSendEmail]); + + useEffect(() => { + setRootRole(initialRootRole); + }, [initialRootRole]); + + const getAddUserPayload = () => { + return { + name: name, + email: email, + sendEmail: sendEmail, + rootRole: rootRole, + }; + }; + + const validateName = () => { + if (name.length === 0) { + setErrors(prev => ({ ...prev, name: 'Name can not be empty.' })); + return false; + } + if (email.length === 0) { + setErrors(prev => ({ ...prev, email: 'Email can not be empty.' })); + return false; + } + return true; + }; + + const validateEmail = () => { + if (users.some(user => user['email'] === email)) { + setErrors(prev => ({ ...prev, email: 'Email already exists' })); + return false; + } + return true; + }; + + const clearErrors = () => { + setErrors({}); + }; + + return { + name, + setName, + email, + setEmail, + sendEmail, + setSendEmail, + rootRole, + setRootRole, + getAddUserPayload, + validateName, + validateEmail, + clearErrors, + errors, + }; +}; + +export default useProjectRoleForm; diff --git a/frontend/src/component/admin/users/index.js b/frontend/src/component/admin/users/index.js index bd175339ac..eed8f9297f 100644 --- a/frontend/src/component/admin/users/index.js +++ b/frontend/src/component/admin/users/index.js @@ -40,7 +40,9 @@ const UsersAdmin = ({ history }) => { diff --git a/frontend/src/component/admin/users/update-user-component.jsx b/frontend/src/component/admin/users/update-user-component.jsx deleted file mode 100644 index 5332ce84a0..0000000000 --- a/frontend/src/component/admin/users/update-user-component.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; -import Dialogue from '../../common/Dialogue'; -import UserForm from './AddUser/AddUserForm/AddUserForm'; - -function AddUser({ - user, - showDialog, - closeDialog, - updateUser, - roles, - userApiErrors, - userLoading, -}) { - const [data, setData] = useState({}); - const [error, setError] = useState({}); - - useEffect(() => { - setData({ - ...user, - }); - }, [user]); - - if (!user) { - return null; - } - - const submit = async e => { - e.preventDefault(); - - try { - await updateUser(data); - setData({}); - setError({}); - } catch (error) { - setError({ general: 'Could not update user' }); - } - }; - - const onCancel = e => { - e.preventDefault(); - setData({}); - setError({}); - closeDialog(); - }; - - const formId = 'update-user-dialog-form' - - return ( - { - submit(e); - }} - formId={formId} - open={showDialog} - onClose={onCancel} - primaryButtonText="Update user" - secondaryButtonText="Cancel" - title="Update team member" - fullWidth - > - - - ); -} - -AddUser.propTypes = { - showDialog: PropTypes.bool.isRequired, - closeDialog: PropTypes.func.isRequired, - updateUser: PropTypes.func.isRequired, - user: PropTypes.object, - roles: PropTypes.array.isRequired, -}; - -export default AddUser; diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap index 02bc53fc68..46d9dbc194 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap @@ -388,6 +388,15 @@ Array [ "title": "Users", "type": "protected", }, + Object { + "component": [Function], + "layout": "main", + "menu": Object {}, + "parent": "/admin", + "path": "/admin/create-user", + "title": "Users", + "type": "protected", + }, Object { "component": Object { "$$typeof": Symbol(react.memo), diff --git a/frontend/src/component/menu/routes.js b/frontend/src/component/menu/routes.js index 63fa2fc10d..ede9e30b5d 100644 --- a/frontend/src/component/menu/routes.js +++ b/frontend/src/component/menu/routes.js @@ -42,6 +42,8 @@ import FeatureCreate from '../feature/FeatureCreate/FeatureCreate'; import ProjectRoles from '../admin/project-roles/ProjectRoles/ProjectRoles'; import CreateProjectRole from '../admin/project-roles/CreateProjectRole/CreateProjectRole'; import EditProjectRole from '../admin/project-roles/EditProjectRole/EditProjectRole'; +import CreateUser from '../admin/users/CreateUser/CreateUser'; +import EditUser from '../admin/users/EditUser/EditUser'; import CreateApiToken from '../admin/api-token/CreateApiToken/CreateApiToken'; import CreateEnvironment from '../environments/CreateEnvironment/CreateEnvironment'; import EditEnvironment from '../environments/EditEnvironment/EditEnvironment'; @@ -416,6 +418,15 @@ export const routes = [ menu: {}, flag: RE, }, + { + path: '/admin/users/:id/edit', + title: 'Edit', + component: EditUser, + type: 'protected', + layout: 'main', + menu: {}, + hidden: true, + }, { path: '/admin/api', parent: '/admin', @@ -434,6 +445,15 @@ export const routes = [ layout: 'main', menu: { adminSettings: true }, }, + { + path: '/admin/create-user', + parent: '/admin', + title: 'Users', + component: CreateUser, + type: 'protected', + layout: 'main', + menu: {}, + }, { path: '/admin/auth', parent: '/admin', diff --git a/frontend/src/constants/misc.ts b/frontend/src/constants/misc.ts new file mode 100644 index 0000000000..c03df75b8f --- /dev/null +++ b/frontend/src/constants/misc.ts @@ -0,0 +1,2 @@ +export const EDIT = 'Edit'; +export const CREATE = 'Create'; diff --git a/frontend/src/hooks/api/getters/useUserInfo/useUserInfo.ts b/frontend/src/hooks/api/getters/useUserInfo/useUserInfo.ts new file mode 100644 index 0000000000..2c823dca88 --- /dev/null +++ b/frontend/src/hooks/api/getters/useUserInfo/useUserInfo.ts @@ -0,0 +1,35 @@ +import useSWR, { mutate, SWRConfiguration } from 'swr'; +import { useState, useEffect } from 'react'; +import { formatApiPath } from '../../../../utils/format-path'; +import handleErrorResponses from '../httpErrorResponseHandler'; + +const useUserInfo = (id: string, options: SWRConfiguration = {}) => { + const fetcher = () => { + const path = formatApiPath(`api/admin/user-admin/${id}`); + return fetch(path, { + method: 'GET', + }) + .then(handleErrorResponses('Users')) + .then(res => res.json()); + }; + + const { data, error } = useSWR(`api/admin/user-admin/${id}`, fetcher, options); + const [loading, setLoading] = useState(!error && !data); + + const refetch = () => { + mutate(`api/admin/user-admin/${id}`); + }; + + useEffect(() => { + setLoading(!error && !data); + }, [data, error]); + + return { + user: data || {}, + error, + loading, + refetch, + }; +}; + +export default useUserInfo;