diff --git a/client/src/features/trainee-profile/personal-info/PersonalInfo.tsx b/client/src/features/trainee-profile/personal-info/PersonalInfo.tsx
index df8dc04d..90e52265 100644
--- a/client/src/features/trainee-profile/personal-info/PersonalInfo.tsx
+++ b/client/src/features/trainee-profile/personal-info/PersonalInfo.tsx
@@ -1,15 +1,9 @@
-import {
- Background,
- EducationLevel,
- EnglishLevel,
- Gender,
- Pronouns,
- ResidencyStatus,
-} from '../../../data/types/Trainee';
+import { Background, EducationLevel, EnglishLevel, Pronouns, ResidencyStatus } from '../../../data/types/Trainee';
import { Box, FormControl, InputLabel, MenuItem, Select, TextField } from '@mui/material';
import { createSelectChangeHandler, createTextChangeHandler } from '../utils/formHelper';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
+import { GenderSelect } from '../profile/components/GenderSelect';
import { useTraineeProfileContext } from '../context/useTraineeProfileContext';
const NoIcon = () => null;
@@ -88,24 +82,12 @@ const PersonalInfo = () => {
{/* Gender */}
-
- Gender
-
-
+
{/* Pronouns */}
diff --git a/client/src/features/trainee-profile/personal-info/api/api.ts b/client/src/features/trainee-profile/personal-info/api/api.ts
index 801dbb8e..254c25f4 100644
--- a/client/src/features/trainee-profile/personal-info/api/api.ts
+++ b/client/src/features/trainee-profile/personal-info/api/api.ts
@@ -1,13 +1,13 @@
-import axios from 'axios';
import { Trainee } from '../../../../data/types/Trainee';
-import { SaveTraineeRequestData } from './types';
+import { UpdateTraineeRequestData } from './types';
+import axios from 'axios';
-export const getTraineeInfo = async (traineeId: string) => {
+export const getTrainee = async (traineeId: string) => {
const { data } = await axios.get(`/api/trainees/${traineeId}`);
return data;
};
-export const saveTraineeInfo = async (traineeId: string, dataToSave: SaveTraineeRequestData) => {
+export const updateTrainee = async (traineeId: string, dataToSave: UpdateTraineeRequestData) => {
const { data } = await axios.patch(`/api/trainees/${traineeId}`, dataToSave);
return data;
};
diff --git a/client/src/features/trainee-profile/personal-info/api/types.ts b/client/src/features/trainee-profile/personal-info/api/types.ts
index d568677a..b2271ab7 100644
--- a/client/src/features/trainee-profile/personal-info/api/types.ts
+++ b/client/src/features/trainee-profile/personal-info/api/types.ts
@@ -5,7 +5,7 @@ import {
TraineePersonalInfo,
} from '../../../../data/types/Trainee';
-export interface SaveTraineeRequestData {
+export interface UpdateTraineeRequestData {
personalInfo?: Partial;
contactInfo?: Partial;
educationInfo?: Partial;
diff --git a/client/src/features/trainee-profile/personal-info/data/useTraineeInfoData.tsx b/client/src/features/trainee-profile/personal-info/data/useTraineeInfoData.tsx
index dfcba073..db6cdde8 100644
--- a/client/src/features/trainee-profile/personal-info/data/useTraineeInfoData.tsx
+++ b/client/src/features/trainee-profile/personal-info/data/useTraineeInfoData.tsx
@@ -1,7 +1,8 @@
-import { Trainee } from '../../../../data/types/Trainee';
+import { getTrainee, updateTrainee } from '../api/api';
import { useMutation, useQuery } from '@tanstack/react-query';
-import { getTraineeInfo, saveTraineeInfo } from '../api/api';
-import { SaveTraineeRequestData } from '../api/types';
+
+import { Trainee } from '../../../../data/types/Trainee';
+import { UpdateTraineeRequestData } from '../api/types';
/**
* A React Query hook that fetches trainee information data form api.
@@ -11,7 +12,7 @@ import { SaveTraineeRequestData } from '../api/types';
export const useTraineeInfoData = (traineeId: string) => {
return useQuery({
queryKey: ['traineeInfo', traineeId],
- queryFn: () => getTraineeInfo(traineeId),
+ queryFn: () => getTrainee(traineeId),
enabled: !!traineeId,
//Added because it keeps rendering
refetchOnMount: false, // Prevent refetching on component mount
@@ -27,8 +28,8 @@ export const useTraineeInfoData = (traineeId: string) => {
*/
export const useSaveTraineeInfo = (traineeId: string) => {
return useMutation({
- mutationFn: (dataToSave: SaveTraineeRequestData) => saveTraineeInfo(traineeId, dataToSave),
+ mutationFn: (dataToSave: UpdateTraineeRequestData) => updateTrainee(traineeId, dataToSave),
});
};
-export type { SaveTraineeRequestData };
+export type { UpdateTraineeRequestData };
diff --git a/client/src/features/trainee-profile/profile/components/DropdownSelect.tsx b/client/src/features/trainee-profile/profile/components/DropdownSelect.tsx
new file mode 100644
index 00000000..2a7d0ed3
--- /dev/null
+++ b/client/src/features/trainee-profile/profile/components/DropdownSelect.tsx
@@ -0,0 +1,60 @@
+import { FormControl, FormHelperText, InputLabel, MenuItem, Select, SelectChangeEvent } from '@mui/material';
+
+import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
+
+type CustomSelectProps = {
+ inputLabel: string;
+ disabled?: boolean;
+ id: string;
+ label: string;
+ name: string;
+ value?: string; //currently selected value
+ options: MenuItemType[]; // array of values to show in the dropdown
+ isEditing?: boolean;
+ error?: string;
+ onChange: (event: SelectChangeEvent) => void;
+};
+
+export type MenuItemType = {
+ label: string; //To be displayed to the user
+ value: string;
+};
+export const DropdownSelect = ({
+ disabled = false,
+ inputLabel,
+ id,
+ label,
+ name,
+ value = '',
+ options,
+ isEditing = false,
+ error,
+ onChange = () => {},
+}: CustomSelectProps) => {
+ const NoIcon = () => null;
+
+ return (
+
+ {inputLabel}
+
+ {!!error && {error}}
+
+ );
+};
diff --git a/client/src/features/trainee-profile/profile/components/GenderSelect.tsx b/client/src/features/trainee-profile/profile/components/GenderSelect.tsx
new file mode 100644
index 00000000..2bfaa7fe
--- /dev/null
+++ b/client/src/features/trainee-profile/profile/components/GenderSelect.tsx
@@ -0,0 +1,24 @@
+import { DropdownSelect } from './DropdownSelect';
+import { Gender } from '../../../../data/types/Trainee';
+import { SelectChangeEvent } from '@mui/material';
+import { formatTextToFriendly } from '../../utils/formHelper';
+
+const genderOptions = Object.values(Gender).map((gender) => ({
+ label: formatTextToFriendly(gender),
+ value: gender,
+}));
+
+type GenderSelectProps = {
+ initialValue?: string;
+ disabled?: boolean;
+ isEditing: boolean;
+ value?: string;
+ error?: string;
+ onChange: (event: SelectChangeEvent) => void;
+};
+
+export const GenderSelect: React.FC = (props) => {
+ return (
+
+ );
+};
diff --git a/client/src/features/trainee-profile/profile/components/JobPathSelect.tsx b/client/src/features/trainee-profile/profile/components/JobPathSelect.tsx
new file mode 100644
index 00000000..dfc3ed4d
--- /dev/null
+++ b/client/src/features/trainee-profile/profile/components/JobPathSelect.tsx
@@ -0,0 +1,24 @@
+import { DropdownSelect } from './DropdownSelect';
+import { JobPath } from '../../../../data/types/Trainee';
+import { SelectChangeEvent } from '@mui/material';
+import { formatJobPathToLabel } from '../../utils/formHelper';
+
+const options = Object.values(JobPath).map((status) => ({
+ label: formatJobPathToLabel(status),
+ value: status,
+}));
+
+type JobPathSelectProps = {
+ initialValue?: string;
+ disabled?: boolean;
+ isEditing: boolean;
+ value?: string;
+ error?: string;
+ onChange: (event: SelectChangeEvent) => void;
+};
+
+export const JobPathSelect: React.FC = (props) => {
+ return (
+
+ );
+};
diff --git a/client/src/features/trainee-profile/profile/components/LearningStatusSelect.tsx b/client/src/features/trainee-profile/profile/components/LearningStatusSelect.tsx
new file mode 100644
index 00000000..8692090b
--- /dev/null
+++ b/client/src/features/trainee-profile/profile/components/LearningStatusSelect.tsx
@@ -0,0 +1,31 @@
+import { DropdownSelect } from './DropdownSelect';
+import { LearningStatus } from '../../../../data/types/Trainee';
+import { SelectChangeEvent } from '@mui/material';
+import { formatTextToFriendly } from '../../utils/formHelper';
+
+const options = Object.values(LearningStatus).map((status) => ({
+ label: formatTextToFriendly(status),
+ value: status,
+}));
+
+type LearningStatusSelectProps = {
+ initialValue?: string;
+ disabled?: boolean;
+ isEditing: boolean;
+ value?: string;
+ error?: string;
+ onChange: (event: SelectChangeEvent) => void;
+};
+
+export const LearningStatusSelect: React.FC = (props) => {
+ return (
+
+ );
+};
diff --git a/client/src/features/trainee-profile/profile/components/TraineeProfile.tsx b/client/src/features/trainee-profile/profile/components/TraineeProfile.tsx
index d85bd930..167c885f 100644
--- a/client/src/features/trainee-profile/profile/components/TraineeProfile.tsx
+++ b/client/src/features/trainee-profile/profile/components/TraineeProfile.tsx
@@ -1,6 +1,6 @@
import { Box, Snackbar } from '@mui/material';
import {
- SaveTraineeRequestData,
+ UpdateTraineeRequestData,
useSaveTraineeInfo,
useTraineeInfoData,
} from '../../personal-info/data/useTraineeInfoData';
@@ -62,7 +62,7 @@ const TraineeProfile = ({ id }: TraineeProfileProps) => {
* Shows a snackbar with the result of the save operation and refreshes the trainee data.
* @param editedFields
*/
- const saveTraineeData = async (editedFields: SaveTraineeRequestData) => {
+ const saveTraineeData = async (editedFields: UpdateTraineeRequestData) => {
mutate(editedFields, {
onSuccess: (data: Trainee) => {
setSnackbarSeverity('success');
@@ -91,7 +91,7 @@ const TraineeProfile = ({ id }: TraineeProfileProps) => {
return;
}
- const changedFields: SaveTraineeRequestData = getTraineeInfoChanges(traineeData!);
+ const changedFields: UpdateTraineeRequestData = getTraineeInfoChanges(traineeData!);
saveTraineeData(changedFields);
};
diff --git a/client/src/features/trainee-profile/utils/formHelper.ts b/client/src/features/trainee-profile/utils/formHelper.ts
index 0857e0ec..05ad14db 100644
--- a/client/src/features/trainee-profile/utils/formHelper.ts
+++ b/client/src/features/trainee-profile/utils/formHelper.ts
@@ -1,5 +1,5 @@
import { ChangeEvent, ReactNode } from 'react';
-import { Trainee, TraineeInfoType } from '../../../data/types/Trainee';
+import { JobPath, Trainee, TraineeInfoType } from '../../../data/types/Trainee';
import { SelectChangeEvent } from '@mui/material';
@@ -55,7 +55,13 @@ export const createTextChangeHandler = (
* @param value
* @returns
*/
+// TODO: rename this function
export const formatTextToFriendly = (value: string): string => {
// replace '-' with ' ' in the type string and capitilzie first letter
return value.replace(/-/g, ' ').replace(/^\w/, (char) => char.toUpperCase());
};
+
+export const formatJobPathToLabel = (jobPath: JobPath): string => {
+ if (jobPath === JobPath.NonTechJob) return 'Non-tech job';
+ return formatTextToFriendly(jobPath);
+};