Skip to content

Commit

Permalink
feat: Add Functionality for Mentors to Edit Feedback for Students (#2512
Browse files Browse the repository at this point in the history
)
  • Loading branch information
valerydluski authored Jul 14, 2024
1 parent fa105d8 commit 677142b
Show file tree
Hide file tree
Showing 10 changed files with 308 additions and 169 deletions.
17 changes: 2 additions & 15 deletions client/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3894,10 +3894,10 @@ export interface MentorStudentDto {
'rank': number;
/**
*
* @type {Array<StudentFeedback>}
* @type {Array<StudentFeedbackDto>}
* @memberof MentorStudentDto
*/
'feedbacks': Array<StudentFeedback>;
'feedbacks': Array<StudentFeedbackDto>;
/**
*
* @type {string}
Expand Down Expand Up @@ -5208,19 +5208,6 @@ export interface StudentDto {
*/
'rank': number;
}
/**
*
* @export
* @interface StudentFeedback
*/
export interface StudentFeedback {
/**
*
* @type {number}
* @memberof StudentFeedback
*/
'id': number;
}
/**
*
* @export
Expand Down
185 changes: 185 additions & 0 deletions client/src/modules/Feedback/components/FeedbackForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { Alert, Button, Col, Form, Input, message, Radio, Rate, Row, Typography } from 'antd';
import {
CreateStudentFeedbackDto,
CreateStudentFeedbackDtoEnglishLevelEnum as EnglishLevelEnum,
MentorStudentDto,
CreateStudentFeedbackDtoRecommendationEnum as RecommendationEnum,
SoftSkillEntryIdEnum,
} from 'api';
import { UserSearch } from 'components/UserSearch';
import { useEffect } from 'react';
import { convertSoftSkillValueToEnum, softSkills, softSkillValues } from '../data/softSkills';
import { useRouter } from 'next/router';

type FormValues = Record<SoftSkillEntryIdEnum, number> & {
studentId: number;
suggestions: string;
recommendation: RecommendationEnum;
recommendationComment: string;
englishLevel: EnglishLevelEnum;
};

const englishLevels = [
EnglishLevelEnum.A1,
EnglishLevelEnum.A2,
EnglishLevelEnum.B1,
EnglishLevelEnum.B2,
EnglishLevelEnum.C1,
EnglishLevelEnum.C2,
];

type FeedbackFormProps = {
studentId: number;
students?: MentorStudentDto[];
onSubmit: (studentId: number, payload: CreateStudentFeedbackDto, existingFeedbackId?: number) => Promise<void>;
};

const { TextArea } = Input;

const getInitialValues = (studentId: number, students: MentorStudentDto[] | undefined): FormValues | undefined => {
const selectedStudent = students?.find(student => student.id === studentId);
if (selectedStudent) {
const feedback = selectedStudent.feedbacks[0];
if (feedback) {
return {
studentId,
suggestions: feedback.content.suggestions,
recommendation: feedback.recommendation,
recommendationComment: feedback.content.recommendationComment,
englishLevel: feedback.englishLevel,
...feedback.content.softSkills.reduce(
(acc, { id, value }) => {
acc[id as SoftSkillEntryIdEnum] = softSkillValues[value];
return acc;
},
{} as Record<SoftSkillEntryIdEnum, number>,
),
};
}
}
return { studentId } as FormValues;
};

export const FeedbackForm = ({ studentId, onSubmit, students }: FeedbackFormProps) => {
const router = useRouter();
const [form] = Form.useForm<FormValues>();

useEffect(() => {
const initialValues = getInitialValues(studentId, students);
if (initialValues) {
form.setFieldsValue(initialValues);
}
}, [studentId, students, form]);

const handleStudentChange = (value: string) => {
const newStudentId = Number(value);
const initialValues = getInitialValues(newStudentId, students);
if (initialValues) {
form.resetFields();
form.setFieldsValue(initialValues);
} else {
form.resetFields();
form.setFieldsValue({ studentId: newStudentId });
}
router.push(
{
pathname: router.pathname,
query: { ...router.query, studentId: newStudentId },
},
undefined,
{ shallow: true },
);
};

const handleSubmit = async (values: FormValues) => {
try {
const { studentId, ...rest } = values;

const payload: CreateStudentFeedbackDto = {
recommendation: rest.recommendation,
content: {
suggestions: rest.suggestions ?? '',
recommendationComment: rest.recommendationComment,
softSkills: softSkills.map(({ id }) => ({ id, value: convertSoftSkillValueToEnum(rest[id]) })),
},
englishLevel: rest.englishLevel ? rest.englishLevel : EnglishLevelEnum.Unknown,
};

const selectedStudent = students?.find(student => student.id === studentId);
const existingFeedback = selectedStudent?.feedbacks[0];

await onSubmit(studentId, payload, existingFeedback ? existingFeedback.id : undefined);
} catch (e) {
message.error('Error occurred while creating feedback. Please try later.');
}
};

return (
<Form
style={{ margin: '24px 0' }}
onFinish={handleSubmit}
form={form}
layout="vertical"
initialValues={{ studentId }}
>
<Alert
showIcon
type="info"
message={
<>
<div>This feedback is very important for RS School process.</div>
<div>Please spend 5 minutes to complete it. Thank you!</div>
</>
}
/>
<Alert
style={{ marginTop: 8 }}
showIcon
type="warning"
message={
<div>If you recommend to "Hire", we will attach the feedback to student's CV and it will be public.</div>
}
/>
<Form.Item name="studentId" label="Student">
<UserSearch allowClear={false} defaultValues={students} keyField="id" onChange={handleStudentChange} />
</Form.Item>
<Typography.Title level={5}>Recommended To</Typography.Title>
<Form.Item name="recommendation" rules={[{ required: true, message: 'Required' }]}>
<Radio.Group>
<Radio.Button value={RecommendationEnum.Hire}>Hire</Radio.Button>
<Radio.Button value={RecommendationEnum.NotHire}>Not Hire</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item name="recommendationComment" rules={[{ required: true, message: 'Required' }]} label="What was good">
<TextArea rows={7} allowClear />
</Form.Item>
<Form.Item name="suggestions" label="What could be improved">
<TextArea rows={3} allowClear />
</Form.Item>
<Typography.Title level={5}>English</Typography.Title>
<Form.Item label="Approximate English level" name="englishLevel">
<Radio.Group>
{englishLevels.map(level => (
<Radio.Button key={level} value={level}>
{level.toUpperCase()}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>

<Typography.Title level={5}>Soft Skills</Typography.Title>
<Row wrap={true}>
{softSkills.map(({ id, name }) => (
<Col key={id} span={12}>
<Form.Item key={id} label={name} name={id}>
<Rate />
</Form.Item>
</Col>
))}
</Row>
<Button htmlType="submit" type="primary">
Submit
</Button>
</Form>
);
};
42 changes: 42 additions & 0 deletions client/src/modules/Feedback/data/softSkills.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { SoftSkillEntryIdEnum, SoftSkillEntryValueEnum } from 'api';

export const softSkills: { id: SoftSkillEntryIdEnum; name: string }[] = [
{
id: SoftSkillEntryIdEnum.Responsible,
name: 'Responsible',
},
{
id: SoftSkillEntryIdEnum.TeamPlayer,
name: 'Good team player',
},
{
id: SoftSkillEntryIdEnum.Communicable,
name: 'Communicable',
},
];

export const convertSoftSkillValueToEnum = (value: number | null) => {
switch (value) {
case 1:
return SoftSkillEntryValueEnum.Poor;
case 2:
return SoftSkillEntryValueEnum.Fair;
case 3:
return SoftSkillEntryValueEnum.Good;
case 4:
return SoftSkillEntryValueEnum.Great;
case 5:
return SoftSkillEntryValueEnum.Excellent;
default:
return SoftSkillEntryValueEnum.None;
}
};

export const softSkillValues: Record<SoftSkillEntryValueEnum, number> = {
[SoftSkillEntryValueEnum.None]: 0,
[SoftSkillEntryValueEnum.Poor]: 1,
[SoftSkillEntryValueEnum.Fair]: 2,
[SoftSkillEntryValueEnum.Good]: 3,
[SoftSkillEntryValueEnum.Great]: 4,
[SoftSkillEntryValueEnum.Excellent]: 5,
};
32 changes: 24 additions & 8 deletions client/src/modules/Mentor/hooks/useMentorStudents.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import { MentorsApi } from 'api';
import { useMemo } from 'react';
import { useAsync } from 'react-use';
import { message } from 'antd';
import { MentorsApi, MentorStudentDto } from 'api';
import { useMemo, useState, useEffect, useCallback } from 'react';

export function useMentorStudents(mentorId: number | null) {
const service = useMemo(() => new MentorsApi(), []);

const { value: students, loading } = useAsync(async () => {
const { data = [] } = mentorId ? await service.getMentorStudents(mentorId) : { data: [] };
return data;
}, []);
const [students, setStudents] = useState<MentorStudentDto[]>([]);
const [loading, setLoading] = useState<boolean>(false);

return [students, loading] as const;
const fetchStudents = useCallback(async () => {
if (mentorId) {
setLoading(true);
try {
const { data = [] } = await service.getMentorStudents(mentorId);
setStudents(data);
} catch (error) {
message.error('Failed to fetch students');
} finally {
setLoading(false);
}
}
}, [mentorId, service]);

useEffect(() => {
fetchStudents();
}, [fetchStudents]);

return { students, loading, reload: fetchStudents };
}
Loading

0 comments on commit 677142b

Please sign in to comment.