diff --git a/packages/backend/src/graphql/mutations/update-user.ee.ts b/packages/backend/src/graphql/mutations/update-user.ee.ts index 1ba58381..54b4cf0b 100644 --- a/packages/backend/src/graphql/mutations/update-user.ee.ts +++ b/packages/backend/src/graphql/mutations/update-user.ee.ts @@ -32,11 +32,10 @@ const updateUser = async ( // void } - const user = await User.query() - .patchAndFetchById( - params.input.id, - userPayload, - ); + const user = await User.query().patchAndFetchById( + params.input.id, + userPayload + ); return user; }; diff --git a/packages/web/src/components/DeleteRoleButton/index.ee.tsx b/packages/web/src/components/DeleteRoleButton/index.ee.tsx index 19a06e22..c16fd48c 100644 --- a/packages/web/src/components/DeleteRoleButton/index.ee.tsx +++ b/packages/web/src/components/DeleteRoleButton/index.ee.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { useMutation } from '@apollo/client'; import IconButton from '@mui/material/IconButton'; import DeleteIcon from '@mui/icons-material/Delete'; +import { useSnackbar } from 'notistack'; import Can from 'components/Can'; import ConfirmationDialog from 'components/ConfirmationDialog'; @@ -11,7 +12,7 @@ import useFormatMessage from 'hooks/useFormatMessage'; type DeleteRoleButtonProps = { disabled?: boolean; roleId: string; -} +}; export default function DeleteRoleButton(props: DeleteRoleButtonProps) { const { disabled, roleId } = props; @@ -21,17 +22,25 @@ export default function DeleteRoleButton(props: DeleteRoleButtonProps) { refetchQueries: ['GetRoles'], }); const formatMessage = useFormatMessage(); + const { enqueueSnackbar } = useSnackbar(); const handleConfirm = React.useCallback(async () => { - await deleteRole(); + try { + await deleteRole(); - setShowConfirmation(false); + setShowConfirmation(false); + enqueueSnackbar(formatMessage('deleteRoleButton.successfullyDeleted'), { + variant: 'success', + }); + } catch (error) { + throw new Error('Failed while deleting!'); + } }, [deleteRole]); return ( <> - {allowed => ( + {(allowed) => ( setShowConfirmation(true)} diff --git a/packages/web/src/components/DeleteUserButton/index.ee.tsx b/packages/web/src/components/DeleteUserButton/index.ee.tsx index 428cb953..4289b4a4 100644 --- a/packages/web/src/components/DeleteUserButton/index.ee.tsx +++ b/packages/web/src/components/DeleteUserButton/index.ee.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { useMutation } from '@apollo/client'; import IconButton from '@mui/material/IconButton'; import DeleteIcon from '@mui/icons-material/Delete'; +import { useSnackbar } from 'notistack'; import ConfirmationDialog from 'components/ConfirmationDialog'; import { DELETE_USER } from 'graphql/mutations/delete-user.ee'; @@ -9,7 +10,7 @@ import useFormatMessage from 'hooks/useFormatMessage'; type DeleteUserButtonProps = { userId: string; -} +}; export default function DeleteUserButton(props: DeleteUserButtonProps) { const { userId } = props; @@ -19,11 +20,19 @@ export default function DeleteUserButton(props: DeleteUserButtonProps) { refetchQueries: ['GetUsers'], }); const formatMessage = useFormatMessage(); + const { enqueueSnackbar } = useSnackbar(); const handleConfirm = React.useCallback(async () => { - await deleteUser(); + try { + await deleteUser(); - setShowConfirmation(false); + setShowConfirmation(false); + enqueueSnackbar(formatMessage('deleteUserButton.successfullyDeleted'), { + variant: 'success', + }); + } catch (error) { + throw new Error('Failed while deleting!'); + } }, [deleteUser]); return ( diff --git a/packages/web/src/components/RoleList/index.ee.tsx b/packages/web/src/components/RoleList/index.ee.tsx index 4b009b64..94d02b53 100644 --- a/packages/web/src/components/RoleList/index.ee.tsx +++ b/packages/web/src/components/RoleList/index.ee.tsx @@ -17,7 +17,6 @@ import useFormatMessage from 'hooks/useFormatMessage'; import useRoles from 'hooks/useRoles.ee'; import * as URLS from 'config/urls'; -// TODO: introduce interaction feedback upon deletion (successful + failure) // TODO: introduce loading bar export default function RoleList(): React.ReactElement { const formatMessage = useFormatMessage(); @@ -56,19 +55,11 @@ export default function RoleList(): React.ReactElement { sx={{ '&:last-child td, &:last-child th': { border: 0 } }} > - - {role.name} - + {role.name} - - {role.description} - + {role.description} @@ -81,10 +72,7 @@ export default function RoleList(): React.ReactElement { - + diff --git a/packages/web/src/components/UserList/index.tsx b/packages/web/src/components/UserList/index.tsx index 6b2e6af3..f394936c 100644 --- a/packages/web/src/components/UserList/index.tsx +++ b/packages/web/src/components/UserList/index.tsx @@ -17,7 +17,6 @@ import useUsers from 'hooks/useUsers'; import useFormatMessage from 'hooks/useFormatMessage'; import * as URLS from 'config/urls'; -// TODO: introduce interaction feedback upon deletion (successful + failure) // TODO: introduce loading bar export default function UserList(): React.ReactElement { const formatMessage = useFormatMessage(); @@ -56,19 +55,11 @@ export default function UserList(): React.ReactElement { sx={{ '&:last-child td, &:last-child th': { border: 0 } }} > - - {user.fullName} - + {user.fullName} - - {user.email} - + {user.email} diff --git a/packages/web/src/locales/en.json b/packages/web/src/locales/en.json index 1202f311..3df96d32 100644 --- a/packages/web/src/locales/en.json +++ b/packages/web/src/locales/en.json @@ -177,6 +177,7 @@ "deleteUserButton.description": "This will permanently delete the user and all the associated data with it.", "deleteUserButton.cancel": "Cancel", "deleteUserButton.confirm": "Delete", + "deleteUserButton.successfullyDeleted": "The user has been deleted.", "editUserPage.title": "Edit user", "createUserPage.title": "Create user", "userForm.fullName": "Full name", @@ -184,7 +185,9 @@ "userForm.role": "Role", "userForm.password": "Password", "createUser.submit": "Create", + "createUser.successfullyCreated": "The user has been created.", "editUser.submit": "Update", + "editUser.successfullyUpdated": "The user has been updated.", "userList.fullName": "Full name", "userList.email": "Email", "rolesPage.title": "Role management", @@ -193,12 +196,15 @@ "deleteRoleButton.description": "This will permanently delete the role.", "deleteRoleButton.cancel": "Cancel", "deleteRoleButton.confirm": "Delete", + "deleteRoleButton.successfullyDeleted": "The role has been deleted.", "editRolePage.title": "Edit role", "createRolePage.title": "Create role", "roleForm.name": "Name", "roleForm.description": "Description", "createRole.submit": "Create", + "createRole.successfullyCreated": "The role has been created.", "editRole.submit": "Update", + "editRole.successfullyUpdated": "The role has been updated.", "roleList.name": "Name", "roleList.description": "Description", "permissionSettings.cancel": "Cancel", diff --git a/packages/web/src/pages/CreateRole/index.ee.tsx b/packages/web/src/pages/CreateRole/index.ee.tsx index 8c11a2d6..bfd3abad 100644 --- a/packages/web/src/pages/CreateRole/index.ee.tsx +++ b/packages/web/src/pages/CreateRole/index.ee.tsx @@ -6,6 +6,7 @@ import Stack from '@mui/material/Stack'; import * as React from 'react'; import { useNavigate } from 'react-router-dom'; import PermissionCatalogField from 'components/PermissionCatalogField/index.ee'; +import { useSnackbar } from 'notistack'; import Form from 'components/Form'; import PageTitle from 'components/PageTitle'; @@ -22,21 +23,32 @@ export default function CreateRole(): React.ReactElement { const navigate = useNavigate(); const formatMessage = useFormatMessage(); const [createRole, { loading }] = useMutation(CREATE_ROLE); + const { enqueueSnackbar } = useSnackbar(); - const handleRoleCreation = async (roleData: Partial) => { - const permissions = getPermissions(roleData.computedPermissions); + const handleRoleCreation = async ( + roleData: Partial + ) => { + try { + const permissions = getPermissions(roleData.computedPermissions); - await createRole({ - variables: { - input: { - name: roleData.name, - description: roleData.description, - permissions, - } - } - }); + await createRole({ + variables: { + input: { + name: roleData.name, + description: roleData.description, + permissions, + }, + }, + }); - navigate(URLS.ROLES); + enqueueSnackbar(formatMessage('createRole.successfullyCreated'), { + variant: 'success', + }); + + navigate(URLS.ROLES); + } catch (error) { + throw new Error('Failed while creating!'); + } }; return ( @@ -62,7 +74,7 @@ export default function CreateRole(): React.ReactElement { fullWidth /> - + ) => { - await createUser({ - variables: { - input: { - fullName: userData.fullName, - password: userData.password, - email: userData.email, - role: { - id: userData.role?.id - } - } - } - }); + try { + await createUser({ + variables: { + input: { + fullName: userData.fullName, + password: userData.password, + email: userData.email, + role: { + id: userData.role?.id, + }, + }, + }, + }); - navigate(URLS.USERS); + enqueueSnackbar(formatMessage('createUser.successfullyCreated'), { + variant: 'success', + }); + + navigate(URLS.USERS); + } catch (error) { + throw new Error('Failed while creating!'); + } }; return ( @@ -77,14 +87,19 @@ export default function CreateUser(): React.ReactElement { fullWidth /> - + } + renderInput={(params) => ( + + )} loading={rolesLoading} /> diff --git a/packages/web/src/pages/EditRole/index.ee.tsx b/packages/web/src/pages/EditRole/index.ee.tsx index fdd10bb3..64915cfd 100644 --- a/packages/web/src/pages/EditRole/index.ee.tsx +++ b/packages/web/src/pages/EditRole/index.ee.tsx @@ -5,6 +5,7 @@ import Grid from '@mui/material/Grid'; import Stack from '@mui/material/Stack'; import * as React from 'react'; import { useNavigate, useParams } from 'react-router-dom'; +import { useSnackbar } from 'notistack'; import Form from 'components/Form'; import PageTitle from 'components/PageTitle'; @@ -22,9 +23,8 @@ import useRole from 'hooks/useRole.ee'; type EditRoleParams = { roleId: string; -} +}; -// TODO: introduce interaction feedback upon deletion (successful + failure) // TODO: introduce loading bar export default function EditRole(): React.ReactElement { const formatMessage = useFormatMessage(); @@ -32,22 +32,33 @@ export default function EditRole(): React.ReactElement { const navigate = useNavigate(); const { roleId } = useParams(); const { role, loading: roleLoading } = useRole(roleId); + const { enqueueSnackbar } = useSnackbar(); - const handleRoleUpdate = async (roleData: Partial) => { - const newPermissions = getPermissions(roleData.computedPermissions); + const handleRoleUpdate = async ( + roleData: Partial + ) => { + try { + const newPermissions = getPermissions(roleData.computedPermissions); - await updateRole({ - variables: { - input: { - id: roleId, - name: roleData.name, - description: roleData.description, - permissions: newPermissions, - } - }, - }); + await updateRole({ + variables: { + input: { + id: roleId, + name: roleData.name, + description: roleData.description, + permissions: newPermissions, + }, + }, + }); - navigate(URLS.ROLES); + enqueueSnackbar(formatMessage('editRole.successfullyUpdated'), { + variant: 'success', + }); + + navigate(URLS.ROLES); + } catch (error) { + throw new Error('Failed while updating!'); + } }; if (roleLoading || !role) return ; @@ -82,7 +93,10 @@ export default function EditRole(): React.ReactElement { fullWidth /> - + ({ label, value })); } -// TODO: introduce interaction feedback upon deletion (successful + failure) // TODO: introduce loading bar export default function EditUser(): React.ReactElement { const formatMessage = useFormatMessage(); @@ -34,20 +35,32 @@ export default function EditUser(): React.ReactElement { const { userId } = useParams(); const { user, loading: userLoading } = useUser(userId); const { roles, loading: rolesLoading } = useRoles(); + const { enqueueSnackbar } = useSnackbar(); + const navigate = useNavigate(); - const handleUserUpdate = (userDataToUpdate: Partial) => { - updateUser({ - variables: { - input: { - id: userId, - fullName: userDataToUpdate.fullName, - email: userDataToUpdate.email, - role: { - id: userDataToUpdate.role?.id - } - } - } - }); + const handleUserUpdate = async (userDataToUpdate: Partial) => { + try { + await updateUser({ + variables: { + input: { + id: userId, + fullName: userDataToUpdate.fullName, + email: userDataToUpdate.email, + role: { + id: userDataToUpdate.role?.id, + }, + }, + }, + }); + + enqueueSnackbar(formatMessage('editUser.successfullyUpdated'), { + variant: 'success', + }); + + navigate(URLS.USERS); + } catch (error) { + throw new Error('Failed while updating!'); + } }; if (userLoading) return ; @@ -76,14 +89,19 @@ export default function EditUser(): React.ReactElement { fullWidth /> - + } + renderInput={(params) => ( + + )} loading={rolesLoading} />