Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merit Committee Onboarding #83

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 20 additions & 39 deletions src/components/AccountSetup/ProfessorInfoForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,17 @@ 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';
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' },
Expand All @@ -36,12 +27,7 @@ const sabbaticalOptions: Option<SabbaticalOption>[] = [
{ label: 'Sabbatical: Semester', value: SabbaticalOption.SEMESTER },
];

const ProfessorInfoForm: React.FC<ProfessorInfoFormProps> = (
{
// submit,
// back,
},
) => {
const ProfessorInfoForm: React.FC = () => {
const userInfo = useSelector(selectUserInfo);
const [position, setPosition] = useState('');
const [teachingPercent, setTeachingPercent] = useState(0);
Expand Down Expand Up @@ -132,27 +118,21 @@ const ProfessorInfoForm: React.FC<ProfessorInfoFormProps> = (

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 (
Expand Down Expand Up @@ -186,6 +166,7 @@ const ProfessorInfoForm: React.FC<ProfessorInfoFormProps> = (
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
>
<div className="w-full px-5">
Expand Down
29 changes: 15 additions & 14 deletions src/components/AccountSetup/RoleSetup.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
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';
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 {}

Expand All @@ -19,21 +22,18 @@ const RoleSetup: React.FC<RoleSetupProps> = () => {
const [error, setError] = useState<string | null>(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)) {
setError(responseStatusMessage[res]);
} else {
dispatch(setRole(res));
dispatch(setStep('user info'));
}
};

const onChange = (value: string) => {
Expand All @@ -46,6 +46,7 @@ const RoleSetup: React.FC<RoleSetupProps> = () => {
<StepWrapper
title="Welcome!"
subtitle="Please enter your access code."
hideProgressBar
currentStep={0}
next={submitCode}
back={() => {}}
Expand Down
4 changes: 2 additions & 2 deletions src/components/AccountSetup/StepIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ const StepLabel: React.FC<{ step: number; state: CompletionState }> = ({
state,
}) => (
<p className={`text-sm font-semibold ${textCompletionClass[state]}`}>
{step + 1}
{step}
</p>
);

const idempotentArray = (length: number): number[] =>
Array.apply(null, Array(length)).map((_, idx) => idx);
Array.apply(null, Array(length)).map((_, idx) => idx + 1);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shifting index so that RoleSetup is not the first step


const StepIndicator: React.FC<StepIndicatorProps> = ({
currentStep,
Expand Down
15 changes: 8 additions & 7 deletions src/components/AccountSetup/StepWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { selectFieldsIncomplete } from '@/store/accountSetup.store';
interface StepWrapperProps {
currentStep: number;
numSteps?: number;
hideProgressBar?: boolean;
title: string;
subtitle: string;
next: () => void;
Expand All @@ -16,7 +17,8 @@ interface StepWrapperProps {

const StepWrapper: React.FC<StepWrapperProps> = ({
currentStep,
numSteps = 3,
hideProgressBar = false,
numSteps = 2,
title,
subtitle,
next,
Expand All @@ -27,18 +29,17 @@ const StepWrapper: React.FC<StepWrapperProps> = ({

return (
<div className="flex flex-col w-full items-center rounded-lg bg-gray-100 shadow-lg pt-6 px-20 pb-11">
<StepIndicator currentStep={currentStep} numSteps={numSteps} />
<div className="my-8 flex flex-col items-center">
{!hideProgressBar && (
<StepIndicator currentStep={currentStep} numSteps={numSteps} />
)}
<div className="mt-6 mb-4 flex flex-col items-center">
<p className="text-2xl font-bold">{title}</p>
<p className="text-sm font-medium">{subtitle}</p>
</div>
{children}
<div className="flex justify-between mt-12 w-full" id="buttons">
{currentStep > 0 && (
<Button
variant='secondary'
onClick={back}
>
<Button variant="secondary" onClick={back}>
<p className="text-xs">Back</p>
</Button>
)}
Expand Down
57 changes: 40 additions & 17 deletions src/components/AccountSetup/UserInfoForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,55 @@ 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<UserInfoFormProps> = (
{
// initialName,
// initialPreferredName,
// submit,
// back,
},
) => {
const UserInfoForm: React.FC = () => {
const initialName = useSelector(selectName);
const email = useSelector(selectEmail);
const role = useSelector(selectRole);
const parsedName = initialName.split(' ');
const [firstName, setFirstName] = useState(parsedName[0] || '');
const [lastName, setLastName] = useState(parsedName[1] || '');
const [preferredName, setPreferredName] = useState('');
const [pageError, setPageError] = useState<string | null>(null);
const [firstNameError, setFirstNameError] = useState<string | undefined>(
undefined,
);
const [lastNameError, setLastNameError] = useState<string | undefined>(
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.');
Expand All @@ -59,8 +76,13 @@ const UserInfoForm: React.FC<UserInfoFormProps> = (
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) => {
Expand All @@ -78,6 +100,7 @@ const UserInfoForm: React.FC<UserInfoFormProps> = (
<StepWrapper
title="Create your account"
subtitle="Please provide your full name."
hideProgressBar={role !== Role.FACULTY}
currentStep={1}
next={submit}
back={() => dispatch(setStep('role'))}
Expand Down
7 changes: 6 additions & 1 deletion src/shared/components/DropdownInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ const DropdownInput = <T extends unknown>({
: options;

return (
<div className="relative w-full min-h-[40px]">
<div
className={clsx([
'relative w-full min-h-[40px]',
absoluteDropdown && 'z-[2]',
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ensures that absolute dropdowns are above info icons

])}
>
<div
className={clsx([
'flex flex-col bg-white border-[0.5px] border-gray-500 rounded-lg cursor-pointer',
Expand Down
21 changes: 21 additions & 0 deletions src/shared/utils/misc.util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ResponseStatus } from '@/client/activities.client';

export const toTitleCase = (str: string): string => {
return str
.split(' ')
Expand Down Expand Up @@ -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, string> = {
[ResponseStatus.Success]: 'Success',
[ResponseStatus.UnknownError]: 'Unknown Error',
[ResponseStatus.NotFound]: 'Not Found',
[ResponseStatus.Unauthorized]: 'Unauthorized',
[ResponseStatus.BadRequest]: 'Bad Request',
};
1 change: 1 addition & 0 deletions src/styles/utilities.css
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
top: 150%;
left: 50%;
margin-left: -70px;
pointer-events: none;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixes bug where tooltip appears when hovering on hidden tooltip text (since we set opacity and not visibility, tooltip text is still hoverable when hidden)

}

.infoTooltip .infoTooltipText::before,
Expand Down