Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
179 changes: 80 additions & 99 deletions client/package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,8 @@ export const StrikeDetailsModal = ({
<Modal
open={isOpen}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
slots={{ backdrop: Backdrop }}
slotProps={{ backdrop: { timeout: 500 } }}
>
<Fade in={isOpen}>
<Box
Expand All @@ -123,7 +121,7 @@ export const StrikeDetailsModal = ({
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
bgcolor: 'background.paper',
backgroundColor: 'background.paper',
boxShadow: 24,
p: 4,
}}
Expand All @@ -140,7 +138,7 @@ export const StrikeDetailsModal = ({
label="Date"
type="date"
value={formatDate(strikeFields.date)}
InputLabelProps={{ shrink: true }}
slotProps={{ inputLabel: { shrink: true } }}
onChange={handleStrikeChange}
fullWidth
/>
Expand Down Expand Up @@ -187,7 +185,7 @@ export const StrikeDetailsModal = ({
error={commentsRequiredError}
helperText={commentsRequiredError ? 'Comments are required' : ''}
value={strikeFields.comments}
InputLabelProps={{ shrink: true }}
slotProps={{ inputLabel: { shrink: true} }}
onChange={onChangeComments}
fullWidth
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import EditIcon from '@mui/icons-material/Edit';
import MarkdownText from '../../components/MarkdownText';
import { Strike } from '../../../../data/types/Trainee';
import { formatDateForDisplay } from '../../utils/dateHelper';
import React from 'react';

interface StrikesListProps {
strikes: Strike[];
Expand Down Expand Up @@ -33,7 +34,7 @@ export const StrikesList: React.FC<StrikesListProps> = ({ strikes, onClickEdit,

const renderActions = (id: string) => {
return (
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', paddingRight: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', pr: 1 }}>
<IconButton aria-label="edit" onClick={() => handleEdit(id)}>
<EditIcon />
</IconButton>
Expand All @@ -48,7 +49,7 @@ export const StrikesList: React.FC<StrikesListProps> = ({ strikes, onClickEdit,
<List
sx={{
width: '100%',
bgcolor: 'background.paper',
backgroundColor: 'background.paper',
maxHeight: 300,
overflow: 'auto',
scrollbarWidth: 'thin',
Expand All @@ -66,16 +67,15 @@ export const StrikesList: React.FC<StrikesListProps> = ({ strikes, onClickEdit,
alignItems="flex-start"
disablePadding
sx={{
bgcolor: index % 2 === 0 ? 'background.paperAlt' : 'background.paper',
backgroundColor: index % 2 === 0 ? 'background.paperAlt' : 'background.paper',
}}
>
<ListItemAvatar
sx={{
display: 'flex',
alignIntems: 'center',
paddingLeft: 2,
paddingRight: 2,
paddingTop: 1,
alignItems: 'center',
px: 2,
pt: 1,
}}
>
<AvatarWithTooltip imageUrl={strike.reporter.imageUrl} name={strike.reporter.name} />
Expand All @@ -87,11 +87,10 @@ export const StrikesList: React.FC<StrikesListProps> = ({ strikes, onClickEdit,
flexDirection="row"
justifyContent="space-between"
width="100%"
paddingTop={1}
paddingBottom={1}
py={1}
>
{formatStrikeReason(strike.reason || '')}
<Typography sx={{ paddingRight: 2 }}>{formatDateForDisplay(strike.date)}</Typography>
<Typography sx={{ pr: 2 }}>{formatDateForDisplay(strike.date)}</Typography>
</Box>
}
secondary={
Expand Down
97 changes: 30 additions & 67 deletions client/src/features/trainee-profile/employment/EmploymentInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import {
Box,
Divider,
FormControl,
InputAdornment,
InputLabel,
Link,
List,
ListItem,
ListItemText,
MenuItem,
Select,
TextField,
Typography,
} from '@mui/material';
import { createSelectChangeHandler, createTextChangeHandler } from '../utils/formHelper';

import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import { JobPathSelect } from '../profile/components/JobPathSelect';
import LinkIcon from '@mui/icons-material/Link';
import React from 'react';
import { formatDate } from '../utils/dateHelper';
import { useTraineeProfileContext } from '../context/useTraineeProfileContext';
import { EmploymentHistoryGroup } from './history/EmploymentHistoryGroup';

const NoIcon = () => null;

Expand All @@ -32,9 +25,11 @@ const NoIcon = () => null;
export const EmploymentInfo = () => {
const { trainee, setTrainee, isEditMode: isEditing } = useTraineeProfileContext();
const { employmentInfo: editedFields } = trainee;

const handleTextChange = createTextChangeHandler(setTrainee, 'employmentInfo');
const handleSelectChange = createSelectChangeHandler(setTrainee, 'employmentInfo');


return (
<Box display="flex" flexDirection="row" flexWrap="wrap" gap={4} padding="24px">
<div style={{ width: '100%' }}>
Expand All @@ -48,21 +43,22 @@ export const EmploymentInfo = () => {
name="cvURL"
label="CV"
type="url"
placeholder="https://cv.example.com"
value={editedFields?.cvURL || ''}
InputProps={{
readOnly: isEditing ? false : true,
endAdornment: (
<InputAdornment position="start">
{!isEditing && editedFields?.cvURL && (
<Link href={editedFields?.cvURL} target="_blank">
<LinkIcon sx={{ color: 'action.active' }} />
</Link>
)}
</InputAdornment>
),
}}
InputLabelProps={{
shrink: true,
slotProps={{
input: {
readOnly: !isEditing,
endAdornment: (
<InputAdornment position="start">
{!isEditing && editedFields?.cvURL && (
<Link href={editedFields?.cvURL} target="_blank">
<LinkIcon sx={{ color: 'action.active' }} />
</Link>
)}
</InputAdornment>
),
},
inputLabel: { shrink: true },
}}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
Expand All @@ -78,9 +74,9 @@ export const EmploymentInfo = () => {
name="availability"
label="Availability"
type="text"
placeholder="From next month, fulltime"
value={editedFields?.availability || ''}
InputProps={{ readOnly: isEditing ? false : true }}
InputLabelProps={{ shrink: true }}
slotProps={{ input: { readOnly: !isEditing }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
Expand All @@ -95,9 +91,9 @@ export const EmploymentInfo = () => {
name="preferredRole"
label="Preferred role"
type="text"
placeholder="Backend"
value={editedFields?.preferredRole || ''}
InputProps={{ readOnly: isEditing ? false : true }}
InputLabelProps={{ shrink: true }}
slotProps={{ input: { readOnly: !isEditing }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
Expand All @@ -110,9 +106,9 @@ export const EmploymentInfo = () => {
name="preferredLocation"
label="Preferred location"
type="text"
placeholder="Randstad, Utrecht"
value={editedFields?.preferredLocation || ''}
InputProps={{ readOnly: isEditing ? false : true }}
InputLabelProps={{ shrink: true }}
slotProps={{ input: { readOnly: !isEditing }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
Expand All @@ -126,7 +122,7 @@ export const EmploymentInfo = () => {
id="drivingLicense"
label="Driving license"
value={editedFields?.drivingLicense == null ? '' : editedFields?.drivingLicense}
inputProps={{ readOnly: isEditing ? false : true }}
slotProps={{ input: { readOnly: !isEditing } }}
IconComponent={isEditing ? ArrowDropDownIcon : NoIcon}
startAdornment=" "
onChange={handleSelectChange}
Expand All @@ -145,49 +141,17 @@ export const EmploymentInfo = () => {
name="extraTechnologies"
label="Extra technologies"
type="text"
placeholder="C#, C++, Vue.js"
value={editedFields?.extraTechnologies || ''}
InputProps={{ readOnly: isEditing ? false : true }}
InputLabelProps={{ shrink: true }}
slotProps={{ input: { readOnly: !isEditing }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
</FormControl>
</div>

<div style={{ width: '100%' }}>
{/* Employment history */}
<Box display="flex" flexDirection="row" alignItems="center" justifyContent="space-between">
<Typography variant="h6" padding="16px">
Employment history ({editedFields?.employmentHistory.length || 0})
</Typography>
</Box>

<List
sx={{
width: '100%',
bgcolor: 'background.paper',
}}
>
{editedFields?.employmentHistory.map((employmentHistory, index) => (
<React.Fragment key={employmentHistory.id}>
<ListItem
alignItems="flex-start"
secondaryAction={formatDate(employmentHistory.startDate)}
disablePadding
sx={{
paddingBottom: '16px',
}}
>
<ListItemText
primary={`${employmentHistory.role} at ${employmentHistory.companyName} (${employmentHistory.type})`}
secondary={employmentHistory.comments}
/>
</ListItem>
{index < editedFields?.employmentHistory.length - 1 && <Divider sx={{ color: 'black' }} component="li" />}
</React.Fragment>
))}
</List>
</div>
{/* Employment history */}
<EmploymentHistoryGroup />

<div style={{ width: '100%' }}>
{/* Comments */}
Expand All @@ -199,8 +163,7 @@ export const EmploymentInfo = () => {
type="text"
multiline
value={editedFields?.comments || ''}
InputProps={{ readOnly: isEditing ? false : true }}
InputLabelProps={{ shrink: true }}
slotProps={{ input: { readOnly: !isEditing }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
Expand Down
19 changes: 19 additions & 0 deletions client/src/features/trainee-profile/employment/api/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import axios from 'axios';
import { EmploymentHistory } from '../../../../data/types/Trainee';

export const getEmployments = async (traineeId: string) => {
const { data } = await axios.get<EmploymentHistory[]>(`/api/trainees/${traineeId}/employment-history`);
return data;
};

export const addEmployment = async (traineeId: string, employment: EmploymentHistory) => {
await axios.post(`/api/trainees/${traineeId}/employment-history`, employment);
};

export const deleteEmployment = async (traineeId: string, employmentId: string) => {
await axios.delete(`/api/trainees/${traineeId}/employment-history/${employmentId}`);
};

export const editEmployment = async (traineeId: string, employment: EmploymentHistory) => {
await axios.put(`/api/trainees/${traineeId}/employment-history/${employment.id}`, employment);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useQuery } from '@tanstack/react-query';
import { EmploymentHistory } from '../../../../data/types/Trainee';
import { getEmployments } from '../api/api';

export const employmentHistoryKeys = {
all: ['employmentHistory'] as const, // for broad invalidation
byQuery: (traineeId: string) => ['employmentHistory', traineeId] as const,
// per-term cache
};

/**
* Hook to get employments of a trainee.
* @param {string} traineeId the id of the trainee to get the employments from.
* @returns {UseQueryResult<EmploymentHistory[], Error>} the employments of the trainee.
*/
export const useGetEmploymentHistory = (traineeId: string) => {
return useQuery({
queryKey: employmentHistoryKeys.byQuery(traineeId),
queryFn: async () => {
const data = await getEmployments(traineeId);
return orderEmploymentHistoryByDateDesc(data);
},
enabled: !!traineeId,
refetchOnWindowFocus: false,
});
};

const orderEmploymentHistoryByDateDesc = (data: EmploymentHistory[]): EmploymentHistory[] => {
return data.sort((a, b) => new Date(b.startDate).getTime() - new Date(a.startDate).getTime());
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useMutation } from '@tanstack/react-query';
import { EmploymentHistory } from '../../../../data/types/Trainee'
import { addEmployment, deleteEmployment, editEmployment } from '../api/api';

/**
* Hook to add employment to a trainee.
* @param {string} traineeId the id of the trainee to add the employment to.
* @param {EmploymentHistory} employment the employment to add.
*/
export const useAddEmploymentHistory = (traineeId: string) => {
return useMutation({
mutationFn: (employment: EmploymentHistory) => addEmployment(traineeId, employment),
});
};

/**
* Hook to delete employment from a trainee.
* @param {string} traineeId the id of the trainee to delete the employment from.
* @param {string} employmentId the id of the employment to delete.
* */

export const useDeleteEmploymentHistory = (traineeId: string) => {
return useMutation({
mutationFn: (employmentId: string) => deleteEmployment(traineeId, employmentId),
});
};

/**
* Hook to edit employment of a trainee.
* @param {string} traineeId the id of the trainee to edit the employment of.
*/
export const useEditEmploymentHistory = (traineeId: string) => {
return useMutation({
mutationFn: (employment: EmploymentHistory) => editEmployment(traineeId, employment),
});
};
Loading