From 5aa8bfee45591d7cc64c357df294724b31055998 Mon Sep 17 00:00:00 2001 From: adam-skowronek Date: Mon, 16 Jan 2023 17:23:45 +0100 Subject: [PATCH] Update studentId, update opening enrollments --- backend/app/coordinator/schemas/students.py | 2 +- frontend/src/api/leaders.ts | 11 +- frontend/src/api/schedule.ts | 26 ++- frontend/src/api/students.ts | 7 +- frontend/src/views/Login.tsx | 9 +- frontend/src/views/coordinator/AddGroup.tsx | 4 +- frontend/src/views/coordinator/AddLeader.tsx | 23 +-- .../coordinator/AvailabilitySchedule.tsx | 4 +- .../views/coordinator/CoordinatorGroups.tsx | 2 +- .../src/views/coordinator/EditSchedule.tsx | 7 + frontend/src/views/coordinator/Group.tsx | 22 +- frontend/src/views/coordinator/Leaders.tsx | 2 +- frontend/src/views/coordinator/Schedule.tsx | 194 ++++++++++-------- frontend/src/views/coordinator/Students.tsx | 6 +- .../src/views/student/ScheduleAddGroup.tsx | 13 +- .../src/views/student/StudentSchedule.tsx | 7 +- .../src/views/supervisor/SupervisorGroups.tsx | 2 +- 17 files changed, 196 insertions(+), 145 deletions(-) diff --git a/backend/app/coordinator/schemas/students.py b/backend/app/coordinator/schemas/students.py index a01993a..4a6fb46 100644 --- a/backend/app/coordinator/schemas/students.py +++ b/backend/app/coordinator/schemas/students.py @@ -14,7 +14,7 @@ class GroupSchema(ma.SQLAlchemyAutoSchema): class StudentSchema(ma.SQLAlchemyAutoSchema): - group = fields.Nested(GroupSchema) + groups = fields.List(fields.Nested(GroupSchema)) class Meta: model = Student diff --git a/frontend/src/api/leaders.ts b/frontend/src/api/leaders.ts index c35af71..3aeae5b 100644 --- a/frontend/src/api/leaders.ts +++ b/frontend/src/api/leaders.ts @@ -14,6 +14,7 @@ export interface Leader { email: string limit_group?: number count_groups: number + year_group_id: number } export const getLeaders = ( @@ -31,8 +32,14 @@ export const getLeaders = ( { params }, ) -export const createLeader = (payload: Partial) => - axiosInstance.post('coordinator/project_supervisor/', payload) +export const createLeader = (payload: Partial) => { + const p = { ...payload } + delete p.year_group_id + return axiosInstance.post( + `coordinator/project_supervisor/${payload.year_group_id}/`, + p, + ) +} export const deleteLeader = (id: number) => axiosInstance.delete(`coordinator/project_supervisor/${id}/`) diff --git a/frontend/src/api/schedule.ts b/frontend/src/api/schedule.ts index 3c3ec3a..fec6147 100644 --- a/frontend/src/api/schedule.ts +++ b/frontend/src/api/schedule.ts @@ -12,6 +12,7 @@ interface TermOfDefences { last_name: string }[] group: { name: string; students: Student[] } + chairman_of_committee: number } export const getTermsOfDefences = (scheduleId: number) => { @@ -29,12 +30,12 @@ export const getTermsOfDefencesWithGroups = (scheduleId: number) => { export const getStudentsTermsOfDefences = ( scheduleId: number, - studentIndex: number, + studentId: number, ) => { return axiosInstance.get<{ term_of_defences: TermOfDefences[] }>( - `students/examination-schedule/${scheduleId}/enrollments?student_index=${studentIndex}`, + `students/examination-schedule/${scheduleId}/enrollments?student_id=${studentId}`, ) } @@ -78,11 +79,13 @@ export const createEvent = ({ end_date, scheduleId, project_supervisors, + chairman_of_committee, }: { start_date: string end_date: string scheduleId: number project_supervisors: number[] + chairman_of_committee: number }) => { return axiosInstance.post( `coordinator/enrollments/${scheduleId}/add-term-of-defences/`, @@ -90,6 +93,7 @@ export const createEvent = ({ start_date, end_date, project_supervisors, + chairman_of_committee, }, ) } @@ -122,16 +126,16 @@ export const setEventDate = ({ export const assignGroup = ({ scheduleId, enrollmentId, - studentIndex, + studentId, }: { scheduleId: number enrollmentId: number - studentIndex: number + studentId: number }) => { return axiosInstance.post( `students/${scheduleId}/enrollments/${enrollmentId}/`, { - student_index: studentIndex, + student_id: studentId, }, ) } @@ -192,6 +196,18 @@ export const setDateOfExaminationSchedule = ( ) } +export const openEnrollments = (scheduleId: number) => { + return axiosInstance.put( + `coordinator/examination_schedule/${scheduleId}/open-enrollments/`, + ) +} + +export const closeEnrollments = (scheduleId: number) => { + return axiosInstance.put( + `coordinator/examination_schedule/${scheduleId}/close-enrollments/`, + ) +} + export const generateTermsOfDefence = (scheduleId: number) => { return axiosInstance.post(`coordinator/enrollments/${scheduleId}/generate`) } diff --git a/frontend/src/api/students.ts b/frontend/src/api/students.ts index df89c94..d7f907d 100644 --- a/frontend/src/api/students.ts +++ b/frontend/src/api/students.ts @@ -8,6 +8,7 @@ interface StudentResponse { } export interface Student { + id: number first_name: string last_name: string index: number @@ -37,12 +38,12 @@ export const createStudent = (payload: Student & { year_group_id: number }) => export const uploadStudents = (payload: FormData, year_group_id: number) => axiosInstance.post( - `coordinator/students/upload/?id=${year_group_id}`, + `coordinator/students/upload/?year_group_id=${year_group_id}`, payload, ) -export const deleteStudent = (index: number) => - axiosInstance.delete(`coordinator/students/${index}/`) +export const deleteStudent = (id: number) => + axiosInstance.delete(`coordinator/students/${id}/`) export const downloadStudents = (mode: boolean, year_group_id: number) => axiosInstance.post( diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx index 40b05de..82c8830 100644 --- a/frontend/src/views/Login.tsx +++ b/frontend/src/views/Login.tsx @@ -22,8 +22,6 @@ const Login = () => { const [userId, setUserId] = useLocalStorageState('userId', { defaultValue: 0, }) - const [studentId, setStudentId] = useLocalStorageState('studentId') - const [userType, setUserType] = useLocalStorageState('userType', { defaultValue: 'coordinator', }) @@ -54,9 +52,9 @@ const Login = () => { { onSuccess: (data) => { setStudentOptions( - data?.data.students.map(({ first_name, last_name, index }) => { + data?.data.students.map(({ first_name, last_name, index, id }) => { return { - value: index, + value: id, label: `${first_name} ${last_name} (${index})`, } }), @@ -87,8 +85,7 @@ const Login = () => { ) const onStudentChange = (v: any) => { - setStudentId(v.value) - // setUserId(v.value) + setUserId(v.value) setUserType('student') } diff --git a/frontend/src/views/coordinator/AddGroup.tsx b/frontend/src/views/coordinator/AddGroup.tsx index 53a7826..686f70b 100644 --- a/frontend/src/views/coordinator/AddGroup.tsx +++ b/frontend/src/views/coordinator/AddGroup.tsx @@ -34,9 +34,9 @@ const AddGroup = () => { setStudentOptions( data?.data.students .filter(({ groups }) => !groups?.length) - .map(({ first_name, last_name, index }) => { + .map(({ first_name, last_name, index, id }) => { return { - value: index, + value: id, label: `${first_name} ${last_name} (${index})`, } }), diff --git a/frontend/src/views/coordinator/AddLeader.tsx b/frontend/src/views/coordinator/AddLeader.tsx index a7e0be7..61b2c63 100644 --- a/frontend/src/views/coordinator/AddLeader.tsx +++ b/frontend/src/views/coordinator/AddLeader.tsx @@ -19,31 +19,21 @@ const AddLeader = () => { const { mutate: mutateCreateLeader } = useMutation( 'createLeader', (payload: Leader) => { - delete payload.limit_group return createLeader(payload) }, { onSuccess: (data) => { setIsAlertVisible(true) - mutateAddToYearGroup({ - id: data?.data?.id, - year_group_id: Number(yearGroupId), - limit_group: getValues('limit_group') ?? 0, - }) reset() }, }, ) - const { mutate: mutateAddToYearGroup } = useMutation( - 'addLeaderToGroup', - (payload: { id: any; year_group_id: number; limit_group: number }) => - addLeaderToGroup(payload.id, payload.year_group_id, { - limit_group: payload.limit_group, - }), - ) const onSubmit = (data: Leader) => { - mutateCreateLeader(data) + mutateCreateLeader({ + ...data, + year_group_id: Number(yearGroupId), + }) } return ( @@ -110,10 +100,13 @@ const AddLeader = () => { id="limit_group" type="text" {...register('limit_group', { - required: false, + required: true, pattern: /^[1-9][0-9]*$/, })} /> + {errors.limit_group?.type === 'required' && ( + Limit jest wymagany + )} {errors.limit_group?.type === 'pattern' && ( Limit grup musi być liczbą dodatnią )} diff --git a/frontend/src/views/coordinator/AvailabilitySchedule.tsx b/frontend/src/views/coordinator/AvailabilitySchedule.tsx index 58e0dfe..87fdf11 100644 --- a/frontend/src/views/coordinator/AvailabilitySchedule.tsx +++ b/frontend/src/views/coordinator/AvailabilitySchedule.tsx @@ -54,12 +54,12 @@ const SupervisorSchedule = () => { return (
- + */} { const { mutate: mutateDelete } = useMutation( 'deleteGroup', - (index: number) => deleteGroup(index), + (id: number) => deleteGroup(id), { onSuccess: () => refetchGroups(), }, diff --git a/frontend/src/views/coordinator/EditSchedule.tsx b/frontend/src/views/coordinator/EditSchedule.tsx index 48b8420..7836e02 100644 --- a/frontend/src/views/coordinator/EditSchedule.tsx +++ b/frontend/src/views/coordinator/EditSchedule.tsx @@ -67,6 +67,10 @@ const EditSchedule = ({ }) } + const chairman = eventData.resource?.members_of_committee?.find( + (m: any) => Number(m.id) === eventData.resource?.chairman_of_committee, + ) + return (
@@ -75,6 +79,9 @@ const EditSchedule = ({ {dayjs(eventData.start).format('YYYY-MM-DD HH:mm')} -{' '} {dayjs(eventData.end).format('HH:mm')}

+

+ Przewodniczący: {chairman?.first_name} {chairman?.last_name} +

{eventData.resource?.members_of_committee?.length ? ( <> Komisja:{' '} diff --git a/frontend/src/views/coordinator/Group.tsx b/frontend/src/views/coordinator/Group.tsx index 2889b5b..b35741a 100644 --- a/frontend/src/views/coordinator/Group.tsx +++ b/frontend/src/views/coordinator/Group.tsx @@ -13,14 +13,14 @@ type SelectValue = { } const Group = () => { - const { id } = useParams<{ id: string }>() + const { id: groupId } = useParams<{ id: string }>() const location = useLocation() const { data: groups, refetch, isLoading: isGroupLoading, - } = useQuery(['getGroup', id], () => getGroup(Number(id))) + } = useQuery(['getGroup', groupId], () => getGroup(Number(groupId))) const { name, project_supervisor, students } = groups?.data || {} const [yearGroupId] = useLocalStorageState('yearGroupId') @@ -33,9 +33,9 @@ const Group = () => { setStudentOptions( data?.data.students .filter(({ groups }) => !groups?.length) - .map(({ first_name, last_name, index }) => { + .map(({ first_name, last_name, index, id }) => { return { - value: index, + value: id, label: `${first_name} ${last_name} (${index})`, } }), @@ -72,7 +72,7 @@ const Group = () => { location.pathname.includes('coordinator') ? 'coordinator' : 'supervisor' - }/groups/${id}/grade-card`} + }/groups/${groupId}/grade-card`} > @@ -88,7 +88,7 @@ const Group = () => { - {students?.map(({ first_name, last_name, index }) => ( + {students?.map(({ first_name, last_name, index, id }) => ( {first_name} {last_name} @@ -98,13 +98,13 @@ const Group = () => { onClick={() => { if (students && name && project_supervisor) mutateEditGroup({ - id: Number(id), + id: Number(groupId), payload: { name, project_supervisor_id: project_supervisor.id, students: students - .map((student) => student.index) - .filter((i) => index !== i), + .map((student) => student.id) + .filter((i) => id !== i), }, }) }} @@ -139,12 +139,12 @@ const Group = () => { onClick={() => { if (students && name && project_supervisor) mutateEditGroup({ - id: Number(id), + id: Number(groupId), payload: { name, project_supervisor_id: project_supervisor.id, students: [ - ...students.map((student) => student.index), + ...students.map((student) => student.id), newStudent, ], }, diff --git a/frontend/src/views/coordinator/Leaders.tsx b/frontend/src/views/coordinator/Leaders.tsx index 06f1520..ef7760d 100644 --- a/frontend/src/views/coordinator/Leaders.tsx +++ b/frontend/src/views/coordinator/Leaders.tsx @@ -41,7 +41,7 @@ const Leaders = () => { const { mutate: mutateDelete } = useMutation( 'deleteLeader', - (index: number) => deleteLeader(index), + (id: number) => deleteLeader(id), { onSuccess: () => refetchLeaders(), }, diff --git a/frontend/src/views/coordinator/Schedule.tsx b/frontend/src/views/coordinator/Schedule.tsx index 8aaacd3..1a2bce4 100644 --- a/frontend/src/views/coordinator/Schedule.tsx +++ b/frontend/src/views/coordinator/Schedule.tsx @@ -2,9 +2,11 @@ import { Calendar, Views, View } from 'react-big-calendar' import { useCallback, useEffect, useState } from 'react' import { useMutation, useQuery } from 'react-query' import { + closeEnrollments, createEvent, downloadSchedule, getTermsOfDefences, + openEnrollments, setDateOfExaminationSchedule, } from '../../api/schedule' import { Link, useParams } from 'react-router-dom' @@ -28,7 +30,7 @@ const customStyles = { marginRight: '-50%', transform: 'translate(-50%, -50%)', width: '400px', - height: '400px', + height: '500px', }, } type SelectValue = { @@ -57,6 +59,12 @@ const Schedule = () => { id: number resource: any }>() + const [scheduleData, setScheduleData] = useLocalStorageState<{ + start_date: string + end_date: string + title: string + open_enrollments: 'i' | 'o' | 'c' + }>('scheduleData') const [view, setView] = useState(Views.WEEK) const onView = useCallback((newView: any) => setView(newView), [setView]) @@ -68,14 +76,9 @@ const Schedule = () => { from: string to: string project_supervisors: NestedValue + chairman_of_committee: number }>({ mode: 'onBlur' }) const [committeeOptions, setCommitteeOptions] = useState([]) - const [startDate, setStartDate] = useState(new Date()) - const [endDate, setEndDate] = useState(new Date()) - const [scheduleData, setScheduleData] = useLocalStorageState<{ - start_date_for_enrollment_students: string - end_date_for_enrollment_students: string - }>('scheduleData') const { isLoading: areLeadersLoading } = useQuery( 'leaders', @@ -102,7 +105,14 @@ const Schedule = () => { if (data) { setEvents( data.data.term_of_defences.map( - ({ id, start_date, end_date, members_of_committee, group }) => { + ({ + id, + start_date, + end_date, + members_of_committee, + group, + chairman_of_committee, + }) => { let initials = '' let title = '' members_of_committee.forEach((member) => { @@ -129,6 +139,7 @@ const Schedule = () => { resource: { members_of_committee, group, + chairman_of_committee, }, } }, @@ -146,6 +157,7 @@ const Schedule = () => { start_date: string end_date: string scheduleId: number + chairman_of_committee: number }) => createEvent(data), ) @@ -164,6 +176,36 @@ const Schedule = () => { }, ) + const { mutate: mutateOpenEnrollments } = useMutation( + ['openEnrollments'], + () => openEnrollments(Number(id)), + { + onSuccess: () => { + if (scheduleData) { + setScheduleData({ + ...scheduleData, + open_enrollments: 'o', + }) + } + }, + }, + ) + + const { mutate: mutateCloseEnrollments } = useMutation( + ['openEnrollments'], + () => closeEnrollments(Number(id)), + { + onSuccess: () => { + if (scheduleData) { + setScheduleData({ + ...scheduleData, + open_enrollments: 'c', + }) + } + }, + }, + ) + const handleSelectSlot = async (event: any) => { setSelectedDate(event) setIsModalOpen(true) @@ -202,6 +244,7 @@ const Schedule = () => { .format('YYYY-MM-DD HH:mm:ss'), scheduleId: Number(id), project_supervisors: data?.project_supervisors, + chairman_of_committee: data?.chairman_of_committee, }) refetch() reset() @@ -209,33 +252,6 @@ const Schedule = () => { } } - const { mutate: mutateSetDateOfExaminationSchedule } = useMutation( - ['dateOfExaminationSchedule'], - (data: { - start_date_for_enrollment_students: string - end_date_for_enrollment_students: string - }) => setDateOfExaminationSchedule(Number(id), data), - { - onSuccess: () => { - setScheduleData({ - ...scheduleData, - start_date_for_enrollment_students: startDate.toISOString(), - end_date_for_enrollment_students: endDate.toISOString(), - }) - }, - }, - ) - - useEffect(() => { - if ( - scheduleData?.start_date_for_enrollment_students && - scheduleData?.end_date_for_enrollment_students - ) { - setStartDate(new Date(scheduleData.start_date_for_enrollment_students)) - setEndDate(new Date(scheduleData.end_date_for_enrollment_students)) - } - }, [scheduleData]) - const eventGetter = (event: any) => { return event?.resource?.group ? { @@ -252,54 +268,45 @@ const Schedule = () => { return (
-
- Start zapisów dla studentów:{' '} - - - - - -
-
- - Statystyki - - +
+
+ Zapisy dla studentów:{' '} + + {scheduleData?.open_enrollments === 'o' ? 'Otwarte' : 'Zamknięte'} + + + +
+
+ + Statystyki + + +
+ { /> )} /> + + ( +