Merge branch 'feat/protect-users-endpoint' into 'main'

Feat/protect users endpoint

Closes #72, #57, and dashboard-backend#38

See merge request stackspin/dashboard!45
This commit is contained in:
Maarten de Waard 2022-07-13 15:25:13 +00:00
commit 77f456fb07
3 changed files with 128 additions and 45 deletions

View file

@ -11,8 +11,19 @@ import { UserModalProps } from './types';
export const UserModal = ({ open, onClose, userId, setUserId }: UserModalProps) => {
const [deleteModal, setDeleteModal] = useState(false);
const { user, loadUser, editUserById, createNewUser, userModalLoading, deleteUserById, clearSelectedUser } =
useUsers();
const [isAdminRoleSelected, setAdminRoleSelected] = useState(true);
const [isPersonalModal, setPersonalModal] = useState(false);
const {
user,
loadUser,
loadPersonalInfo,
editUserById,
editPersonalInfo,
createNewUser,
userModalLoading,
deleteUserById,
clearSelectedUser,
} = useUsers();
const { currentUser, isAdmin } = useAuth();
const { control, reset, handleSubmit } = useForm<User>({
@ -26,8 +37,14 @@ export const UserModal = ({ open, onClose, userId, setUserId }: UserModalProps)
useEffect(() => {
if (userId) {
const currentUserId = currentUser?.id;
if (currentUserId === userId) {
setPersonalModal(true);
loadPersonalInfo();
} else {
loadUser(userId);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [userId, open]);
@ -47,7 +64,9 @@ export const UserModal = ({ open, onClose, userId, setUserId }: UserModalProps)
});
useEffect(() => {
if (dashboardRole === UserRole.Admin) {
const isAdminDashboardRoleSelected = dashboardRole === UserRole.Admin;
setAdminRoleSelected(isAdminDashboardRoleSelected);
if (isAdminDashboardRoleSelected) {
fields.forEach((field, index) => update(index, { name: field.name, role: UserRole.Admin }));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -55,7 +74,9 @@ export const UserModal = ({ open, onClose, userId, setUserId }: UserModalProps)
const handleSave = async () => {
try {
if (userId) {
if (isPersonalModal) {
await handleSubmit((data) => editPersonalInfo(data))();
} else if (userId) {
await handleSubmit((data) => editUserById(data))();
} else {
await handleSubmit((data) => createNewUser(data))();
@ -178,13 +199,13 @@ export const UserModal = ({ open, onClose, userId, setUserId }: UserModalProps)
</div>
</div>
{isAdmin && (
{isAdmin && !userModalLoading && (
<div>
<div className="mt-8">
<h3 className="text-lg leading-6 font-medium text-gray-900">App Access</h3>
</div>
{dashboardRole === UserRole.Admin && (
{isAdminRoleSelected && (
<div className="sm:col-span-6">
<Banner
title="Admin users automatically have admin-level access to all apps."
@ -193,9 +214,10 @@ export const UserModal = ({ open, onClose, userId, setUserId }: UserModalProps)
</div>
)}
{!isAdminRoleSelected && (
<div>
<div className="flow-root mt-6">
<ul className="-my-5 divide-y divide-gray-200 ">
<ul className="-my-5 divide-y divide-gray-200">
{fields.map((item, index) => {
if (item.name === 'dashboard') {
return null;
@ -219,7 +241,7 @@ export const UserModal = ({ open, onClose, userId, setUserId }: UserModalProps)
key={item.id}
control={control}
name={`app_roles.${index}.role`}
disabled={dashboardRole === UserRole.Admin}
disabled={isAdminRoleSelected}
options={[
{ value: UserRole.NoAccess, name: 'No Access' },
{ value: UserRole.User, name: 'User' },
@ -234,6 +256,7 @@ export const UserModal = ({ open, onClose, userId, setUserId }: UserModalProps)
</ul>
</div>
</div>
)}
</div>
)}
</div>

View file

@ -3,7 +3,9 @@ import {
getUsers,
fetchUsers,
fetchUserById,
fetchPersonalInfo,
updateUserById,
updatePersonalInfo,
createUser,
deleteUser,
clearCurrentUser,
@ -25,6 +27,10 @@ export function useUsers() {
return dispatch(fetchUserById(id));
}
function loadPersonalInfo() {
return dispatch(fetchPersonalInfo());
}
function clearSelectedUser() {
return dispatch(clearCurrentUser());
}
@ -33,6 +39,10 @@ export function useUsers() {
return dispatch(updateUserById(data));
}
function editPersonalInfo(data: any) {
return dispatch(updatePersonalInfo(data));
}
function createNewUser(data: any) {
return dispatch(createUser(data));
}
@ -46,7 +56,9 @@ export function useUsers() {
user,
loadUser,
loadUsers,
loadPersonalInfo,
editUserById,
editPersonalInfo,
userModalLoading,
userTableLoading,
createNewUser,

View file

@ -69,6 +69,26 @@ export const fetchUserById = (id: string) => async (dispatch: Dispatch<any>) =>
dispatch(setUserModalLoading(false));
};
export const fetchPersonalInfo = () => async (dispatch: Dispatch<any>) => {
dispatch(setUserModalLoading(true));
try {
const { data } = await performApiCall({
path: '/me',
method: 'GET',
});
dispatch({
type: UserActionTypes.FETCH_USER,
payload: transformUser(data),
});
} catch (err) {
console.error(err);
}
dispatch(setUserModalLoading(false));
};
export const updateUserById = (user: any) => async (dispatch: Dispatch<any>, getState: any) => {
dispatch(setUserModalLoading(true));
@ -103,6 +123,34 @@ export const updateUserById = (user: any) => async (dispatch: Dispatch<any>, get
dispatch(setUserModalLoading(false));
};
export const updatePersonalInfo = (user: any) => async (dispatch: Dispatch<any>) => {
dispatch(setUserModalLoading(true));
try {
const { data } = await performApiCall({
path: '/me',
method: 'PUT',
body: transformRequestUser(user),
});
dispatch({
type: UserActionTypes.UPDATE_USER,
payload: transformUser(data),
});
dispatch({
type: AuthActionTypes.UPDATE_AUTH_USER,
payload: transformUser(data),
});
showToast('Personal information updated successfully.', ToastType.Success);
} catch (err) {
console.error(err);
}
dispatch(setUserModalLoading(false));
};
export const createUser = (user: any) => async (dispatch: Dispatch<any>) => {
dispatch(setUserModalLoading(true));