Skip to content

Commit

Permalink
added page to manage user access
Browse files Browse the repository at this point in the history
  • Loading branch information
Stuart6557 committed Jan 26, 2024
1 parent 3b0f64e commit 8db8449
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ yarn-error.log*
.pnpm-debug.log*

# local env files
.env
.env*.local
.env.development
.env.production
Expand Down
10 changes: 4 additions & 6 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@
"editor.formatOnSave": true,
"typescript.preferences.importModuleSpecifier": "non-relative",
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
},
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"stylelint.validate": [
"scss"
],
"stylelint.validate": ["scss"],
"files.associations": {
"*.env.development": "env",
"*.env.production": "env"
}
}
}
2 changes: 1 addition & 1 deletion src/components/layout/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const Navbar = ({ accessType }: NavbarProps) => {
<p aria-hidden>·</p>
<Link href={config.eventsRoute}>Events</Link>
<p aria-hidden>·</p>
<Link href="/leaderboard">Leaderboard</Link>
<Link href={config.leaderboardRoute}>Leaderboard</Link>
</nav>
<nav className={styles.iconLinks}>
<ThemeToggle />
Expand Down
34 changes: 34 additions & 0 deletions src/lib/api/UserAccessAPI.ts
Original file line number Diff line number Diff line change
@@ -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
*/
const manageUserAccess = async (
token: string,
user: string,
accessType: string
): Promise<PrivateProfile[]> => {
const accessUpdates = [{ user: user, accessType: accessType }];

const requestUrl = `${config.api.baseUrl}${config.api.endpoints.admin.access}`;

const response = await axios.patch<ModifyUserAccessLevelResponse>(
requestUrl,
{ accessUpdates: accessUpdates },
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);

return response.data.updatedUsers;
};

export default manageUserAccess;
1 change: 1 addition & 0 deletions src/lib/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * as LeaderboardAPI from './LeaderboardAPI';
export * as ResumeAPI from './ResumeAPI';
export * as StoreAPI from './StoreAPI';
export * as UserAPI from './UserAPI';
export * as UserAccessAPI from './UserAccessAPI';
2 changes: 2 additions & 0 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const config = {
attendance: '/admin/attendance',
bonus: '/admin/bonus',
emails: '/admin/email',
access: '/admin/access',
},
event: {
event: '/event',
Expand Down Expand Up @@ -98,6 +99,7 @@ const config = {
grantPastAttendance: '/admin/attendance',
awardMilestone: '/admin/milestone',
viewResumes: '/admin/resumes',
manageUserAccess: '/admin/manage-user-access',
store: {
items: '/admin/store/items',
pickupEvents: '/admin/store/pickupEvents',
Expand Down
5 changes: 4 additions & 1 deletion src/pages/admin/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ const AdminPage = ({ user: { accessType } }: AdminProps) => {
}}
>
{PermissionService.canViewResumes.includes(accessType) ? (
<LinkButton href={config.admin.viewResumes}>View User Resumes</LinkButton>
<>
<LinkButton href={config.admin.viewResumes}>View User Resumes</LinkButton>
<LinkButton href={config.admin.manageUserAccess}>Manage User Access</LinkButton>
</>
) : (
'Restricted Access'
)}
Expand Down
99 changes: 99 additions & 0 deletions src/pages/admin/manage-user-access/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { SignInButton, SignInFormItem, SignInTitle } from '@/components/auth';
import { VerticalForm } from '@/components/common';
import { config, showToast } from '@/lib';
import manageUserAccess from '@/lib/api/UserAccessAPI';
import withAccessType from '@/lib/hoc/withAccessType';
import { CookieService, PermissionService, ValidationService } from '@/lib/services';
import { UserAccessUpdates } from '@/lib/types/apiRequests';
import { CookieType } from '@/lib/types/enums';
import { getMessagesFromError } from '@/lib/utils';
import { AxiosError } from 'axios';
import { GetServerSideProps } from 'next';
import { SubmitHandler, useForm } from 'react-hook-form';
import { AiOutlineMail } from 'react-icons/ai';
import { BsPerson } from 'react-icons/bs';

function reportError(title: string, error: unknown) {
if (error instanceof AxiosError && error.response?.data?.error) {
showToast(title, getMessagesFromError(error.response.data.error).join('\n\n'));
} else if (error instanceof Error) {
showToast(title, error.message);
} else {
showToast(title, 'Unknown error');
}
}

const ManageUserAccessPage = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<UserAccessUpdates>();

const onSubmit: SubmitHandler<UserAccessUpdates> = async ({ user, accessType }) => {
try {
const token = CookieService.getClientCookie(CookieType.ACCESS_TOKEN);
const updatedUsers = await manageUserAccess(token, user, accessType);
if (updatedUsers && updatedUsers.length > 0 && updatedUsers[0]?.email) {
showToast(`User access type updated for user ${updatedUsers[0].email}!`);
}
} catch (error) {
reportError('Failed to update user access type', error);
}
};

return (
<VerticalForm onEnterPress={handleSubmit(onSubmit)}>
<SignInTitle text="Manage User Access" />
<SignInFormItem
icon={<AiOutlineMail />}
element="input"
name="user"
type="email"
placeholder="Email ([email protected])"
formRegister={register('user', {
validate: email => {
const validation = ValidationService.isValidEmail(email);
return validation.valid || validation.error;
},
})}
error={errors.user}
/>
<SignInFormItem
icon={<BsPerson />}
element="select"
name="user access"
options={[
'RESTRICTED',
'STANDARD',
'STAFF',
'ADMIN',
'MARKETING',
'MERCH_STORE_MANAGER',
'MERCH_STORE_DISTRIBUTOR',
]}
placeholder="User access"
formRegister={register('accessType')}
error={errors.user}
/>
<SignInButton
type="button"
display="button1"
text="Submit"
onClick={handleSubmit(onSubmit)}
/>
</VerticalForm>
);
};

export default ManageUserAccessPage;

const getServerSidePropsFunc: GetServerSideProps = async () => ({
props: {},
});

export const getServerSideProps = withAccessType(
getServerSidePropsFunc,
PermissionService.canViewAdminPage,
config.homeRoute
);

0 comments on commit 8db8449

Please sign in to comment.