From 824f846d78b495b7c999a514900e5418d369c514 Mon Sep 17 00:00:00 2001 From: Vivian Liu <85718304+Stuart6557@users.noreply.github.com> Date: Sat, 18 May 2024 12:44:59 -0700 Subject: [PATCH] added page to manage user access (#137) * added page to manage user access * addressed feedback * removed ADMIN from manage user access dropdown * removed unnecessary placeholder property * fixed bugs after merge * fixed errors * fixed more errors * addressed feedback * Fix compatibility --------- Co-authored-by: Alex Zhang Co-authored-by: Alex Zhang --- .../profile/Preview/style.module.scss | 2 +- src/lib/api/AdminAPI.ts | 34 ++++++++ src/lib/api/index.ts | 1 + src/lib/config.ts | 2 + src/lib/managers/AdminUserManager.ts | 20 +++++ src/pages/admin/access/index.tsx | 84 +++++++++++++++++++ src/pages/admin/index.tsx | 5 +- 7 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/lib/api/AdminAPI.ts create mode 100644 src/lib/managers/AdminUserManager.ts create mode 100644 src/pages/admin/access/index.tsx diff --git a/src/components/profile/Preview/style.module.scss b/src/components/profile/Preview/style.module.scss index 0ad749a1..8cdf1ce6 100644 --- a/src/components/profile/Preview/style.module.scss +++ b/src/components/profile/Preview/style.module.scss @@ -2,7 +2,7 @@ align-items: center; display: flex; flex-direction: column; - + text-align: center; .pfp { diff --git a/src/lib/api/AdminAPI.ts b/src/lib/api/AdminAPI.ts new file mode 100644 index 00000000..5eea06c0 --- /dev/null +++ b/src/lib/api/AdminAPI.ts @@ -0,0 +1,34 @@ +import { config } from '@/lib'; +import type { ModifyUserAccessLevelResponse, PrivateProfile } from '@/lib/types/apiResponses'; +import axios from 'axios'; + +/** + * Update current user's access level + * @param token Authorization bearer token + * @param user Email of the user whose access is being updated + * @param accessType The user's updated access type + * @returns The updated user profile + */ +export const manageUserAccess = async ( + token: string, + user: string, + accessType: string +): Promise => { + const accessUpdates = [{ user: user, accessType: accessType }]; + + const requestUrl = `${config.api.baseUrl}${config.api.endpoints.admin.access}`; + + const response = await axios.patch( + requestUrl, + { accessUpdates: accessUpdates }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + + return response.data.updatedUsers; +}; + +export default manageUserAccess; diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 91b6d28e..1a40f283 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -1,3 +1,4 @@ +export * as AdminAPI from './AdminAPI'; export * as AuthAPI from './AuthAPI'; export * as EventAPI from './EventAPI'; export * as FeedbackAPI from './FeedbackAPI'; diff --git a/src/lib/config.ts b/src/lib/config.ts index 9878ba1f..be11eb3b 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -26,6 +26,7 @@ const config = { attendance: '/admin/attendance', bonus: '/admin/bonus', emails: '/admin/email', + access: '/admin/access', }, event: { event: '/event', @@ -106,6 +107,7 @@ const config = { grantPastAttendance: '/admin/attendance', awardMilestone: '/admin/milestone', viewResumes: '/admin/resumes', + manageUserAccess: '/admin/access', store: { items: '/admin/store/items', pickup: '/admin/store/pickup', diff --git a/src/lib/managers/AdminUserManager.ts b/src/lib/managers/AdminUserManager.ts new file mode 100644 index 00000000..121f8820 --- /dev/null +++ b/src/lib/managers/AdminUserManager.ts @@ -0,0 +1,20 @@ +import { AdminAPI } from '@/lib/api'; +import { CookieService } from '@/lib/services'; +import { APIHandlerProps } from '@/lib/types'; +import { UserAccessUpdates } from '@/lib/types/apiRequests'; +import { PrivateProfile } from '@/lib/types/apiResponses'; +import { CookieType } from '@/lib/types/enums'; + +const manageUserAccess = async (data: UserAccessUpdates & APIHandlerProps) => { + const { user, accessType, onSuccessCallback, onFailCallback } = data; + + try { + const token = CookieService.getClientCookie(CookieType.ACCESS_TOKEN); + const updatedUsers = await AdminAPI.manageUserAccess(token, user, accessType); + onSuccessCallback?.(updatedUsers); + } catch (e: any) { + onFailCallback?.(e); + } +}; + +export default manageUserAccess; diff --git a/src/pages/admin/access/index.tsx b/src/pages/admin/access/index.tsx new file mode 100644 index 00000000..c3e9aa93 --- /dev/null +++ b/src/pages/admin/access/index.tsx @@ -0,0 +1,84 @@ +import { + VerticalForm, + VerticalFormButton, + VerticalFormItem, + VerticalFormTitle, +} from '@/components/common'; +import { showToast } from '@/lib'; +import withAccessType from '@/lib/hoc/withAccessType'; +import manageUserAccess from '@/lib/managers/AdminUserManager'; +import { PermissionService, ValidationService } from '@/lib/services'; +import { UserAccessUpdates } from '@/lib/types/apiRequests'; +import { UserAccessType } from '@/lib/types/enums'; +import { reportError } from '@/lib/utils'; +import { GetServerSideProps } from 'next'; +import { SubmitHandler, useForm } from 'react-hook-form'; +import { AiOutlineMail } from 'react-icons/ai'; +import { BsPerson } from 'react-icons/bs'; + +const ManageUserAccessPage = () => { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm(); + + const onSubmit: SubmitHandler = async ({ user, accessType }) => { + manageUserAccess({ + user, + accessType, + onSuccessCallback: updatedUsers => { + showToast(`User access type updated for user ${updatedUsers[0]?.email}!`); + }, + onFailCallback: error => { + reportError('Failed to update user access type', error); + }, + }); + }; + + return ( + + + } + element="input" + name="user" + type="email" + placeholder="Email (user@ucsd.edu)" + formRegister={register('user', { + validate: email => { + const validation = ValidationService.isValidEmail(email); + return validation.valid || validation.error; + }, + })} + error={errors.user} + /> + } + element="select" + name="User Access" + options={Object.values(UserAccessType).filter(option => option !== 'ADMIN')} + formRegister={register('accessType')} + error={errors.user} + placeholder="" + /> + + + ); +}; + +export default ManageUserAccessPage; + +const getServerSidePropsFunc: GetServerSideProps = async () => ({ + props: {}, +}); + +export const getServerSideProps = withAccessType( + getServerSidePropsFunc, + PermissionService.canViewAdminPage +); diff --git a/src/pages/admin/index.tsx b/src/pages/admin/index.tsx index 1357a1e0..2ba6541b 100644 --- a/src/pages/admin/index.tsx +++ b/src/pages/admin/index.tsx @@ -96,7 +96,10 @@ const AdminPage = ({ user: { accessType }, preview }: AdminProps) => { }} > {PermissionService.canViewResumes.includes(accessType) ? ( - View User Resumes + <> + View User Resumes + Manage User Access + ) : ( 'Restricted Access' )}