From e9dadffb0d819d520ee801ce611b7fe60ad25ba3 Mon Sep 17 00:00:00 2001 From: Max Pinheiro Date: Wed, 1 Nov 2023 11:06:04 -0400 Subject: [PATCH 1/3] merit onboarding --- .../AccountSetup/ProfessorInfoForm.tsx | 59 +++++++------------ src/components/AccountSetup/RoleSetup.tsx | 28 ++++----- src/components/AccountSetup/StepIndicator.tsx | 4 +- src/components/AccountSetup/StepWrapper.tsx | 15 ++--- src/components/AccountSetup/UserInfoForm.tsx | 57 ++++++++++++------ src/shared/components/DropdownInput.tsx | 2 +- src/shared/utils/misc.util.ts | 21 +++++++ src/styles/utilities.css | 1 + 8 files changed, 107 insertions(+), 80 deletions(-) diff --git a/src/components/AccountSetup/ProfessorInfoForm.tsx b/src/components/AccountSetup/ProfessorInfoForm.tsx index 8d2d5d7..0e92a25 100644 --- a/src/components/AccountSetup/ProfessorInfoForm.tsx +++ b/src/components/AccountSetup/ProfessorInfoForm.tsx @@ -4,7 +4,6 @@ import TextAreaInput from '@/shared/components/TextAreaInput'; import { SabbaticalOption } from '@prisma/client'; import React, { useState } from 'react'; import PercentageInfo from '../Profile/PercentageInfo'; -import { ResponseStatus } from '@/client/activities.client'; import { updateProfessorInfoForUser } from '@/client/professorInfo.client'; import { createUser } from '@/client/users.client'; import { CreateProfessorInfoDto } from '@/models/professorInfo.model'; @@ -12,18 +11,10 @@ import { useRouter } from 'next/router'; import { useDispatch, useSelector } from 'react-redux'; import { selectUserInfo, setStep } from '@/store/accountSetup.store'; import StepWrapper from './StepWrapper'; - -interface ProfessorInfoFormProps { - // submit: ( - // position: string, - // teachingPercent: number, - // researchPercent: number, - // servicePercent: number, - // sabbatical: SabbaticalOption, - // teachingReleaseExplanation?: string, - // ) => void; - // back: () => void; -} +import { + isErrorResponse, + responseStatusMessage, +} from '@/shared/utils/misc.util'; const positionOptions: Option[] = [ { label: 'Non-Tenure Track', value: 'Non-Tenure Track' }, @@ -36,12 +27,7 @@ const sabbaticalOptions: Option[] = [ { label: 'Sabbatical: Semester', value: SabbaticalOption.SEMESTER }, ]; -const ProfessorInfoForm: React.FC = ( - { - // submit, - // back, - }, -) => { +const ProfessorInfoForm: React.FC = () => { const userInfo = useSelector(selectUserInfo); const [position, setPosition] = useState(''); const [teachingPercent, setTeachingPercent] = useState(0); @@ -132,27 +118,21 @@ const ProfessorInfoForm: React.FC = ( const newUser = await createUser(userInfo); - if (newUser === ResponseStatus.UnknownError) setPageError('Unknown Error'); - else { - let newProfessorInfo: CreateProfessorInfoDto = { - userId: newUser.id, - position, - teachingPercent, - researchPercent, - servicePercent, - sabbatical, - teachingReleaseExplanation: teachingReleaseExplanation || null, - }; + if (isErrorResponse(newUser)) + return setPageError(responseStatusMessage[newUser]); + let newProfessorInfo: CreateProfessorInfoDto = { + userId: newUser.id, + position, + teachingPercent, + researchPercent, + servicePercent, + sabbatical, + teachingReleaseExplanation: teachingReleaseExplanation || null, + }; - const res = await updateProfessorInfoForUser(newProfessorInfo); - if (res === ResponseStatus.Unauthorized) setPageError('Unauthorized'); - else if (res === ResponseStatus.BadRequest) setPageError('Bad request'); - else if (res === ResponseStatus.UnknownError) - setPageError('Unknown error'); - else { - router.push('/profile'); - } - } + const res = await updateProfessorInfoForUser(newProfessorInfo); + if (isErrorResponse(res)) return setPageError(responseStatusMessage[res]); + router.push('/profile'); }; return ( @@ -186,6 +166,7 @@ const ProfessorInfoForm: React.FC = ( withMarginY incomplete={!!distributionError} incompleteMessage={distributionError} + infoMessage="This represents the standard workloads for the faculty level selected above. The weighting can be adjusted based on your circumstances." required >
diff --git a/src/components/AccountSetup/RoleSetup.tsx b/src/components/AccountSetup/RoleSetup.tsx index 6f087fe..069953d 100644 --- a/src/components/AccountSetup/RoleSetup.tsx +++ b/src/components/AccountSetup/RoleSetup.tsx @@ -1,6 +1,5 @@ import { obtainRoleForAccessCode } from '@/client/accessCodes.client'; import { ResponseStatus } from '@/client/activities.client'; -import { Role } from '@prisma/client'; import React, { useState } from 'react'; import InputContainer from '@/shared/components/InputContainer'; @@ -8,6 +7,10 @@ import TextInput from '@/shared/components/TextInput'; import StepWrapper from './StepWrapper'; import { useDispatch } from 'react-redux'; import { setRole, setStep } from '@/store/accountSetup.store'; +import { + isErrorResponse, + responseStatusMessage, +} from '@/shared/utils/misc.util'; interface RoleSetupProps {} @@ -19,21 +22,17 @@ const RoleSetup: React.FC = () => { const [error, setError] = useState(null); const dispatch = useDispatch(); - const submitCode = () => { - console.log(codeInput); + const submitCode = async () => { if (!codeInput) return setAccessCodeError('Please enter your access code.'); - obtainRoleForAccessCode(codeInput).then((res) => { - if (res === ResponseStatus.NotFound) - setAccessCodeError('Incorrect access code.'); - else if (res === ResponseStatus.Unauthorized) setError('Unauthorized'); - else if (res === ResponseStatus.BadRequest) setError('Bad request'); - else if (res === ResponseStatus.UnknownError) setError('Unknown error'); - else { - dispatch(setRole(res)); - dispatch(setStep('user info')); - } - }); + const res = await obtainRoleForAccessCode(codeInput); + if (res === ResponseStatus.NotFound) + setAccessCodeError('Incorrect access code.'); + else if (isErrorResponse(res)) return setError(responseStatusMessage[res]); + else { + dispatch(setRole(res)); + dispatch(setStep('user info')); + } }; const onChange = (value: string) => { @@ -46,6 +45,7 @@ const RoleSetup: React.FC = () => { {}} diff --git a/src/components/AccountSetup/StepIndicator.tsx b/src/components/AccountSetup/StepIndicator.tsx index 8563bce..31a48ab 100644 --- a/src/components/AccountSetup/StepIndicator.tsx +++ b/src/components/AccountSetup/StepIndicator.tsx @@ -39,12 +39,12 @@ const StepLabel: React.FC<{ step: number; state: CompletionState }> = ({ state, }) => (

- {step + 1} + {step}

); const idempotentArray = (length: number): number[] => - Array.apply(null, Array(length)).map((_, idx) => idx); + Array.apply(null, Array(length)).map((_, idx) => idx + 1); const StepIndicator: React.FC = ({ currentStep, diff --git a/src/components/AccountSetup/StepWrapper.tsx b/src/components/AccountSetup/StepWrapper.tsx index 1909a56..c795c64 100644 --- a/src/components/AccountSetup/StepWrapper.tsx +++ b/src/components/AccountSetup/StepWrapper.tsx @@ -7,6 +7,7 @@ import { selectFieldsIncomplete } from '@/store/accountSetup.store'; interface StepWrapperProps { currentStep: number; numSteps?: number; + hideProgressBar?: boolean; title: string; subtitle: string; next: () => void; @@ -16,7 +17,8 @@ interface StepWrapperProps { const StepWrapper: React.FC = ({ currentStep, - numSteps = 3, + hideProgressBar = false, + numSteps = 2, title, subtitle, next, @@ -27,18 +29,17 @@ const StepWrapper: React.FC = ({ return (
- -
+ {!hideProgressBar && ( + + )} +

{title}

{subtitle}

{children}
{currentStep > 0 && ( - )} diff --git a/src/components/AccountSetup/UserInfoForm.tsx b/src/components/AccountSetup/UserInfoForm.tsx index 4a03799..30738f9 100644 --- a/src/components/AccountSetup/UserInfoForm.tsx +++ b/src/components/AccountSetup/UserInfoForm.tsx @@ -11,22 +11,17 @@ import { setStep, } from '@/store/accountSetup.store'; import { CreateUserDto } from '@/models/user.model'; +import { Role, SabbaticalOption } from '@prisma/client'; +import { createUser } from '@/client/users.client'; +import { + isErrorResponse, + responseStatusMessage, +} from '@/shared/utils/misc.util'; +import { CreateProfessorInfoDto } from '@/models/professorInfo.model'; +import { updateProfessorInfoForUser } from '@/client/professorInfo.client'; +import { useRouter } from 'next/router'; -interface UserInfoFormProps { - // initialName: string; - // initialPreferredName?: string; - // submit: (firstName: string, lastName: string, preferredName?: string) => void; - // back: () => void; -} - -const UserInfoForm: React.FC = ( - { - // initialName, - // initialPreferredName, - // submit, - // back, - }, -) => { +const UserInfoForm: React.FC = () => { const initialName = useSelector(selectName); const email = useSelector(selectEmail); const role = useSelector(selectRole); @@ -34,6 +29,7 @@ const UserInfoForm: React.FC = ( const [firstName, setFirstName] = useState(parsedName[0] || ''); const [lastName, setLastName] = useState(parsedName[1] || ''); const [preferredName, setPreferredName] = useState(''); + const [pageError, setPageError] = useState(null); const [firstNameError, setFirstNameError] = useState( undefined, ); @@ -41,8 +37,29 @@ const UserInfoForm: React.FC = ( undefined, ); + const router = useRouter(); const dispatch = useDispatch(); + const createMeritUser = async (userInfo: CreateUserDto) => { + const newUser = await createUser(userInfo); + if (isErrorResponse(newUser)) + return setPageError(responseStatusMessage[newUser]); + + let newProfessorInfo: CreateProfessorInfoDto = { + userId: newUser.id, + position: '', + teachingPercent: 0, + researchPercent: 0, + servicePercent: 0, + sabbatical: SabbaticalOption.NO, + teachingReleaseExplanation: null, + }; + + const res = await updateProfessorInfoForUser(newProfessorInfo); + if (isErrorResponse(res)) return setPageError(responseStatusMessage[res]); + router.push('/profile'); + }; + const submit = () => { if (!firstName || !lastName) { if (!firstName) setFirstNameError('Please enter a First Name.'); @@ -59,8 +76,13 @@ const UserInfoForm: React.FC = ( role, dateModified: BigInt(Date.now()), }; - dispatch(setUserInfo(newUser)); - dispatch(setStep('professor info')); + // onlf faculty members are presented with third screen + if (role === Role.FACULTY) { + dispatch(setUserInfo(newUser)); + dispatch(setStep('professor info')); + } else { + createMeritUser(newUser); + } }; const onFirstNameChange = (value: string) => { @@ -78,6 +100,7 @@ const UserInfoForm: React.FC = ( dispatch(setStep('role'))} diff --git a/src/shared/components/DropdownInput.tsx b/src/shared/components/DropdownInput.tsx index da28cda..e9ab1b9 100644 --- a/src/shared/components/DropdownInput.tsx +++ b/src/shared/components/DropdownInput.tsx @@ -35,7 +35,7 @@ const DropdownInput = ({ : options; return ( -
+
{ return str .split(' ') @@ -30,3 +32,22 @@ export const shortenDescription = (description: string): string => { return description.substring(0, 99) + '...'; }; + +export const isErrorResponse = ( + status: ResponseStatus | any, +): status is ResponseStatus => { + return [ + ResponseStatus.BadRequest, + ResponseStatus.NotFound, + ResponseStatus.Unauthorized, + ResponseStatus.UnknownError, + ].includes(status); +}; + +export const responseStatusMessage: Record = { + [ResponseStatus.Success]: 'Success', + [ResponseStatus.UnknownError]: 'Unknown Error', + [ResponseStatus.NotFound]: 'Not Found', + [ResponseStatus.Unauthorized]: 'Unauthorized', + [ResponseStatus.BadRequest]: 'Bad Request', +}; diff --git a/src/styles/utilities.css b/src/styles/utilities.css index ba5fc11..9a56ef1 100644 --- a/src/styles/utilities.css +++ b/src/styles/utilities.css @@ -59,6 +59,7 @@ top: 150%; left: 50%; margin-left: -70px; + pointer-events: none; } .infoTooltip .infoTooltipText::before, From e73a5aae7df0237a43b9b11b795ee5dcd88036bd Mon Sep 17 00:00:00 2001 From: Max Pinheiro Date: Wed, 1 Nov 2023 11:15:42 -0400 Subject: [PATCH 2/3] cleanup if chain --- src/components/AccountSetup/RoleSetup.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/AccountSetup/RoleSetup.tsx b/src/components/AccountSetup/RoleSetup.tsx index 069953d..d1585c0 100644 --- a/src/components/AccountSetup/RoleSetup.tsx +++ b/src/components/AccountSetup/RoleSetup.tsx @@ -26,10 +26,11 @@ const RoleSetup: React.FC = () => { if (!codeInput) return setAccessCodeError('Please enter your access code.'); const res = await obtainRoleForAccessCode(codeInput); - if (res === ResponseStatus.NotFound) + if (res === ResponseStatus.NotFound) { setAccessCodeError('Incorrect access code.'); - else if (isErrorResponse(res)) return setError(responseStatusMessage[res]); - else { + } else if (isErrorResponse(res)) { + setError(responseStatusMessage[res]); + } else { dispatch(setRole(res)); dispatch(setStep('user info')); } From 99040b54b79aed5846d69a52dd22c320c4789812 Mon Sep 17 00:00:00 2001 From: Max Pinheiro Date: Wed, 1 Nov 2023 11:18:13 -0400 Subject: [PATCH 3/3] fix dropdown z-index --- src/shared/components/DropdownInput.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/shared/components/DropdownInput.tsx b/src/shared/components/DropdownInput.tsx index e9ab1b9..d926066 100644 --- a/src/shared/components/DropdownInput.tsx +++ b/src/shared/components/DropdownInput.tsx @@ -35,7 +35,12 @@ const DropdownInput = ({ : options; return ( -
+