Skip to content

Commit

Permalink
supervisor phone
Browse files Browse the repository at this point in the history
  • Loading branch information
chelsea-EYDS committed Jan 27, 2025
1 parent 1aad2f7 commit 86ac114
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 67 deletions.
56 changes: 45 additions & 11 deletions backend/src/personnel/dto/update-personnel-recommitment.dto.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,51 @@
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
IsEmail,
IsOptional,
IsString,
Length,
ValidateIf,
ValidateNested,
} from 'class-validator';
import { Program } from '../../auth/interface';
import { RecommitmentStatus } from '../../common/enums/recommitment-status.enum';
import { IsEmail, IsString } from 'class-validator';

export class SupervisorInformationDTO {
@ApiProperty()
@IsEmail()
email?: string;

@ApiProperty()
@ApiProperty({
description: "Name of personnel's supervisor",
example: 'River Cartwright',
})
@IsString()
@Length(2, 50)
firstName: string;

@ApiProperty()
@ApiProperty({
description: "Name of personnel's supervisor",
example: 'River Cartwright',
})
@IsString()
@Length(2, 50)
lastName: string;

@ApiProperty()
@IsString()
@ApiProperty({
description: 'Supervisor phone number',
example: '2503334444',
})
@Length(10, 10)
@IsOptional()
@ValidateIf((o) => o.phone !== '')
phone?: string;

@ApiProperty({
description: "Name of personnel's supervisor",
example: 'River Cartwright',
})
@IsEmail()
@ValidateIf((o) => o.email !== '')
@Length(2, 50)
@IsOptional()
email?: string;
}

export class UpdatePersonnelRecommitmentDTO {
Expand Down Expand Up @@ -72,24 +99,31 @@ export class UpdatePersonnelRecommitmentDTO {
supervisorDecisionDate?: Date;
}



export class PersonnelRecommitmentDTO {
@ApiProperty({
type: SupervisorInformationDTO,
required: false,
})
@IsOptional()
@ValidateNested()
@Type(() => SupervisorInformationDTO)
supervisorInformation?: SupervisorInformationDTO;

@ApiProperty({
type: UpdatePersonnelRecommitmentDTO,
required: false,
})
@IsOptional()
@ValidateNested()
@Type(() => UpdatePersonnelRecommitmentDTO)
bcws?: UpdatePersonnelRecommitmentDTO;

@ApiProperty({
type: UpdatePersonnelRecommitmentDTO,
required: false,
})
@IsOptional()
@ValidateNested()
@Type(() => UpdatePersonnelRecommitmentDTO)
emcr?: UpdatePersonnelRecommitmentDTO;
}
154 changes: 101 additions & 53 deletions frontend/src/components/recommitment/RecommitmentFormBase.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { ArrowTopRightOnSquareIcon, ChevronLeftIcon } from '@heroicons/react/24/solid';
import {
ArrowTopRightOnSquareIcon,
ChevronLeftIcon,
} from '@heroicons/react/24/solid';
import type { MemberProfile } from '@/common';
import { ButtonTypes, Program } from '@/common';
import { useEffect, useState } from 'react';
Expand All @@ -16,6 +19,9 @@ import { QUESTIONS as PARQ_FOLLOWUP_QUESTIONS } from './parq/ParQFollowUp';
import { fillInAndDownloadParQ } from '@/utils';
import { useRecommitmentCycle } from '@/hooks/useRecommitment';
import { RecommitmentStatus } from '@/common/enums/recommitment-status';
import { Transition } from '@headlessui/react';
import { Banner } from '../ui/Banner';
import { BannerType } from '@/common/enums/banner-enum';

interface StepIndicatorProps {
currentStep: number;
Expand Down Expand Up @@ -240,7 +246,7 @@ export const RecommitmentFormBase = ({
key="initial"
/>,
<UnableToJoin
program={program}
program={program ?? Program.ALL}
onUpdate={setUnableToJoinReasons}
key="unable"
/>,
Expand All @@ -259,7 +265,7 @@ export const RecommitmentFormBase = ({

const steps = getSteps();
const currentComponent = steps[currentStep];

const [errorMessage, setErrorMessage] = useState<string | null>(null);
useEffect(() => {
setNumSteps(getSteps().length);
}, [recommitmentAnswer]);

Check warning on line 271 in frontend/src/components/recommitment/RecommitmentFormBase.tsx

View workflow job for this annotation

GitHub Actions / lint

React Hook useEffect has a missing dependency: 'getSteps'. Either include it or remove the dependency array
Expand All @@ -268,14 +274,16 @@ export const RecommitmentFormBase = ({
const currentYear = new Date().getFullYear();

const getReasons = (program: Program) => {
const selectedReasons = (unableToJoinReasons[program]?.selectedReasons || []).map(
(r) => reasonDefinitions[r as keyof typeof reasonDefinitions],
).filter(r => !!r);
const selectedReasons = (unableToJoinReasons[program]?.selectedReasons || [])
.map((r) => reasonDefinitions[r as keyof typeof reasonDefinitions])
.filter((r) => !!r);
if (unableToJoinReasons[program]?.otherReason) {
return [...selectedReasons, unableToJoinReasons[program]?.otherReason].join('; ');
return [...selectedReasons, unableToJoinReasons[program]?.otherReason].join(
'; ',
);
}
return selectedReasons.join('; ');
}
};

const createDecision = (
status: RecommitmentStatus,
Expand Down Expand Up @@ -320,8 +328,15 @@ export const RecommitmentFormBase = ({
const decision =
decisionMap[recommitmentAnswer as keyof typeof decisionMap] || {};
console.log(decision);
await updateRecommitment(personnel.id, { ...decision, supervisorInformation });
onClose();
const res = await updateRecommitment(personnel.id, {
...decision,
supervisorInformation,
});
if (res.error) {
setErrorMessage(res.error.message);
} else {
onClose();
}
};
const [buttonLoading, setButtonLoading] = useState(false);
const handleNext = () => {
Expand All @@ -344,7 +359,7 @@ export const RecommitmentFormBase = ({

if (
currentComponentType === Assertions ||
currentComponentType === UnableToJoin && recommitmentAnswer === 'no'
(currentComponentType === UnableToJoin && recommitmentAnswer === 'no')
) {
setButtonLoading(true);
handleSubmitRecommitment();
Expand Down Expand Up @@ -397,11 +412,16 @@ export const RecommitmentFormBase = ({
return false;
}
for (const program of programs) {
if ((unableToJoinReasons[program as Program]?.selectedReasons || []).length === 0) {
if (
(unableToJoinReasons[program as Program]?.selectedReasons || []).length ===
0
) {
return false;
}
if (
(unableToJoinReasons[program as Program]?.selectedReasons || []).includes('other') &&
(unableToJoinReasons[program as Program]?.selectedReasons || []).includes(
'other',
) &&
!unableToJoinReasons[program as Program]?.otherReason?.trim()
) {
return false;
Expand Down Expand Up @@ -444,11 +464,11 @@ export const RecommitmentFormBase = ({

// For SupervisorForm, require first name, last name, and email
if (currentComponentType === SupervisorForm) {
const { firstName, lastName, email } = supervisorInformation;
const { firstName, lastName, email, phone } = supervisorInformation;
const isValidEmail = /^[^\s@]+@gov.bc.ca+$/.test(email);

const isValidPhone = phone && phone !== '' ? /(\d{3})(\d{3})(\d{4})/.test(phone) : true
return Boolean(
firstName?.trim() && lastName?.trim() && email?.trim() && isValidEmail,
firstName?.trim() && lastName?.trim() && email?.trim() && isValidEmail && isValidPhone,
);
}

Expand All @@ -464,7 +484,7 @@ export const RecommitmentFormBase = ({
const currentComponentType = currentComponent.type;
if (
currentComponentType === Assertions ||
currentComponentType === UnableToJoin && recommitmentAnswer === 'no'
(currentComponentType === UnableToJoin && recommitmentAnswer === 'no')
) {
return 'Submit Decision';
}
Expand All @@ -486,46 +506,74 @@ export const RecommitmentFormBase = ({
};

return (
<div className="w-full py-8 min-h-[500px] flex flex-col">
<StepIndicator currentStep={currentStep} totalSteps={numSteps} />
{currentStep > 0 && (
<button
onClick={handleBack}
className="flex items-center text-sm text-blue-800 hover:text-defaultGray underline px-8 mb-4"
>
<ChevronLeftIcon className="w-4 h-4" />
Previous
</button>
)}

{currentComponent}

<div className="flex flex-row space-x-6 pt-4 justify-end px-8 border-t-2 border-defaultGray">
<Button
variant={ButtonTypes.PRIMARY}
type="button"
onClick={onClose}
text="Cancel"
/>
{currentComponent.type === ParQBase && currentParQStep === 3 && (
<>
<div className="w-full py-8 min-h-[500px] flex flex-col">
<StepIndicator currentStep={currentStep} totalSteps={numSteps} />
{currentStep > 0 && (
<button
onClick={handleBack}
className="flex items-center text-sm text-blue-800 hover:text-defaultGray underline px-8 mb-4"
>
<ChevronLeftIcon className="w-4 h-4" />
Previous
</button>
)}

{currentComponent}
<Transition
show={errorMessage !== null}
appear={true}
enter="ease-out duration-100"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<div>
{errorMessage && (
<Banner
title={'Error'}
content={errorMessage}
type={BannerType.ERROR}
onClose={() => {
setErrorMessage(null);
onClose();
}}
/>
)}
</div>
</Transition>

<div className="flex flex-row space-x-6 pt-4 justify-end px-8 border-t-2 border-defaultGray">
<Button
variant={ButtonTypes.PRIMARY}
type="button"
onClick={downloadParQ}
text="Download Response"
disabled={!isDeclarationComplete()}
textIcon={<ArrowTopRightOnSquareIcon className="h-4 w-4" />}
onClick={onClose}
text="Cancel"
/>
)}
<Button
variant={ButtonTypes.TERTIARY}
loading={buttonLoading}
text={getButtonText()}
type="button"
onClick={handleNext}
disabled={!nextClickable()}
/>

{currentComponent.type === ParQBase && currentParQStep === 3 && (
<Button
variant={ButtonTypes.PRIMARY}
type="button"
onClick={downloadParQ}
text="Download Response"
disabled={!isDeclarationComplete()}
textIcon={<ArrowTopRightOnSquareIcon className="h-4 w-4" />}
/>
)}
<Button
variant={ButtonTypes.TERTIARY}
loading={buttonLoading}
text={getButtonText()}
type="button"
onClick={handleNext}
disabled={!nextClickable()}
/>
</div>
</div>
</div>

</>
);
};
6 changes: 5 additions & 1 deletion frontend/src/components/recommitment/SupervisorForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export const SupervisorForm = ({ initialData, onUpdate }: SupervisorFormProps) =

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
name === 'phone' && value.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3')

const newData = { ...formData, [name]: value };

setFormData(newData);
onUpdate(newData);
};
Expand Down Expand Up @@ -96,10 +99,11 @@ export const SupervisorForm = ({ initialData, onUpdate }: SupervisorFormProps) =
type="tel"
id="phone"
name="phone"
value={formData.phone}
value={formData.phone?.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3')}
onChange={handleChange}
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
/>
{formData?.phone && !(/(\d{3})(\d{3})(\d{4})/).test(formData?.phone) && <p className="text-xs text-error">Please enter 10 digit phone number</p>}
</div>
</div>
</div>
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/hooks/useRecommitment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ export const useRecommitmentCycle = () => {
`/recommitment/${personnelId}`,
decisions,
);
console.log(data);
} catch (e) {
return data
} catch (e: any) {
console.error(e);
return {error:{message: 'An error has occurred. Please check your submission and try again'}}
}
};

Expand Down

0 comments on commit 86ac114

Please sign in to comment.