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

View file

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

View file

@ -69,6 +69,26 @@ export const fetchUserById = (id: string) => async (dispatch: Dispatch<any>) =>
dispatch(setUserModalLoading(false)); 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) => { export const updateUserById = (user: any) => async (dispatch: Dispatch<any>, getState: any) => {
dispatch(setUserModalLoading(true)); dispatch(setUserModalLoading(true));
@ -103,6 +123,34 @@ export const updateUserById = (user: any) => async (dispatch: Dispatch<any>, get
dispatch(setUserModalLoading(false)); 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>) => { export const createUser = (user: any) => async (dispatch: Dispatch<any>) => {
dispatch(setUserModalLoading(true)); dispatch(setUserModalLoading(true));