diff --git a/backend/app/coordinator/routes/enrollments.py b/backend/app/coordinator/routes/enrollments.py
index 8429c77..b8a1878 100644
--- a/backend/app/coordinator/routes/enrollments.py
+++ b/backend/app/coordinator/routes/enrollments.py
@@ -46,9 +46,9 @@ def create_enrollments(examination_schedule_id: int, data: dict) -> dict:
enrollments = []
for i in range(amount):
sd = start_date + datetime.timedelta(minutes=i * prt)
- ed = start_date + datetime.timedelta(minutes=(i + 1) * prt)
- enrollment = Enrollment(start_date=sd, end_date=ed, examination_schedule_id=examination_schedule_id)
- enrollments.append(enrollment)
+ ed = start_date + datetime.timedelta(minutes=(i + 1) * prt)
+ enrollment = Enrollment(start_date=sd, end_date=ed, examination_schedule_id=examination_schedule_id)
+ enrollments.append(enrollment)
db.session.add_all(enrollments)
db.session.commit()
diff --git a/backend/app/examination_schedule/schemas.py b/backend/app/examination_schedule/schemas.py
index ad69647..96a9dd9 100644
--- a/backend/app/examination_schedule/schemas.py
+++ b/backend/app/examination_schedule/schemas.py
@@ -15,6 +15,7 @@ class GroupSchema(Schema):
class EnrollmentSchema(Schema):
+ id = fields.Integer()
start_date = fields.DateTime()
end_date = fields.DateTime()
committee = fields.Nested(CommitteeSchema)
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 671091d..ba2683d 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -16,6 +16,9 @@ import Student from './views/student/Student'
import Supervisor from './views/supervisor/Supervisor'
import Schedules from './views/coordinator/Schedules'
import Schedule from './views/coordinator/Schedule'
+import SupervisorSchedules from './views/supervisor/SupervisorSchedules'
+import StudentSchedules from './views/student/StudentSchedules'
+import StudentSchedule from './views/student/StudentSchedule'
const queryClient = new QueryClient({
defaultOptions: {
@@ -45,10 +48,13 @@ function App() {
}>
} />
} />
+ } />
+ } />
}>
} />
} />
+ } />
diff --git a/frontend/src/api/schedule.ts b/frontend/src/api/schedule.ts
index bea7547..00845ab 100644
--- a/frontend/src/api/schedule.ts
+++ b/frontend/src/api/schedule.ts
@@ -7,12 +7,32 @@ export const getEvents = (scheduleId: number) => {
start_date: string
end_date: string
title: string
+ committee: {
+ members: { first_name: string; last_name: string }[]
+ }
+ group: { name: string }
}[]
}>(
`http://127.0.0.1:5000/api/examination_schedule/enrollments/${scheduleId}/coordinator-view/?per_page=10000`,
)
}
+export const getStudentsSchedule = (scheduleId: number) => {
+ return axiosInstance.get<{
+ enrollments: {
+ id: number
+ start_date: string
+ end_date: string
+ title: string
+ committee: {
+ members: { first_name: string; last_name: string }[]
+ }
+ group: { name: string }
+ }[]
+ }>(
+ `http://127.0.0.1:5000/api/examination_schedule/enrollments/${scheduleId}/student-view?per_page=10000`,
+ )
+}
export const getSchedules = () => {
return axiosInstance.get<{
examination_schedules: {
@@ -72,3 +92,37 @@ export const setEventDate = ({
},
)
}
+
+export const assignGroup = ({
+ scheduleId,
+ enrollmentId,
+ studentIndex,
+}: {
+ scheduleId: number
+ enrollmentId: number
+ studentIndex: number
+}) => {
+ return axiosInstance.post(
+ `http://127.0.0.1:5000/api/students/${scheduleId}/enrollments/${enrollmentId}/`,
+ {
+ student_index: studentIndex,
+ },
+ )
+}
+
+export const assignSupervisor = ({
+ scheduleId,
+ enrollmentId,
+ supervisorId,
+}: {
+ scheduleId: number
+ enrollmentId: number
+ supervisorId: number
+}) => {
+ return axiosInstance.post(
+ `http://127.0.0.1:5000/api/project_supervisor/${scheduleId}/enrollments/${enrollmentId}/`,
+ {
+ project_supervisor_id: supervisorId,
+ },
+ )
+}
diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx
index f126412..8bb6124 100644
--- a/frontend/src/views/Login.tsx
+++ b/frontend/src/views/Login.tsx
@@ -23,7 +23,7 @@ const Login = () => {
Koordynator
Student
- Opiekun
+ Opiekun
>
diff --git a/frontend/src/views/coordinator/EditSchedule.tsx b/frontend/src/views/coordinator/EditSchedule.tsx
new file mode 100644
index 0000000..99ac15e
--- /dev/null
+++ b/frontend/src/views/coordinator/EditSchedule.tsx
@@ -0,0 +1,108 @@
+import { useState } from 'react'
+import { Controller, NestedValue, useForm } from 'react-hook-form'
+import { useMutation, useQuery } from 'react-query'
+import Select from 'react-select'
+import { getLeaders } from '../../api/leaders'
+import { assignSupervisor } from '../../api/schedule'
+
+type SelectValue = {
+ value: string | number
+ label: string
+}
+
+const EditSchedule = ({
+ eventData,
+ scheduleId,
+}: {
+ eventData: {
+ start: Date
+ end: Date
+ title: string
+ id: number
+ resource: any
+ }
+ scheduleId: string
+}) => {
+ const { register, handleSubmit, reset, control } = useForm<{
+ committee: NestedValue
+ }>({ mode: 'onBlur' })
+ const [committeeOptions, setCommitteeOptions] = useState([])
+
+ const { isLoading: areLeadersLoading } = useQuery(
+ 'leaders',
+ () => getLeaders({ per_page: 1000 }),
+ {
+ onSuccess: (data) => {
+ setCommitteeOptions(
+ data?.data.project_supervisors
+ .filter((ld) => ld.count_groups < ld.limit_group)
+ .map(({ id, first_name, last_name }) => ({
+ value: id,
+ label: `${first_name} ${last_name}`,
+ })),
+ )
+ },
+ },
+ )
+
+ const { mutate: mutateAssignSupervisor } = useMutation(
+ ['assignSupervisor'],
+ (data: {
+ scheduleId: number
+ enrollmentId: number
+ supervisorId: number
+ }) => assignSupervisor(data),
+ )
+
+ const onSubmit = (data: any) => {
+ data?.committee?.forEach((id: number) => {
+ mutateAssignSupervisor({
+ scheduleId: Number(scheduleId),
+ enrollmentId: eventData.id,
+ supervisorId: id,
+ })
+ })
+ }
+
+ return (
+
+ )
+}
+
+export default EditSchedule
diff --git a/frontend/src/views/coordinator/Schedule.tsx b/frontend/src/views/coordinator/Schedule.tsx
index 237c836..d581d4a 100644
--- a/frontend/src/views/coordinator/Schedule.tsx
+++ b/frontend/src/views/coordinator/Schedule.tsx
@@ -6,6 +6,7 @@ import { createEvent, getEvents } from '../../api/schedule'
import { useParams } from 'react-router-dom'
import Modal from 'react-modal'
import { useForm } from 'react-hook-form'
+import EditSchedule from './EditSchedule'
const customStyles = {
content: {
@@ -29,6 +30,7 @@ const Schedule = () => {
title: string
start: Date
end: Date
+ resource: any
}[]
>([])
const [selectedDate, setSelectedDate] = useState<{
@@ -36,11 +38,13 @@ const Schedule = () => {
end: Date
title: string
id: number
+ resource: any
}>()
const [view, setView] = useState(Views.MONTH)
const onView = useCallback((newView: any) => setView(newView), [setView])
const [isModalOpen, setIsModalOpen] = useState(false)
+ const [isEditModalOpen, setIsEditModalOpen] = useState(false)
Modal.setAppElement('#root')
const { register, handleSubmit, reset } = useForm<{
@@ -52,12 +56,22 @@ const Schedule = () => {
onSuccess: (data) => {
setEvents(
data.data.enrollments.map(
- ({ id, start_date, end_date, title = 'Obrona' }) => {
+ ({
+ id,
+ start_date,
+ end_date,
+ title = 'Obrona',
+ committee,
+ group,
+ }) => {
return {
id,
- title,
+ title: `Obrona ${group?.name ?? ''}`,
start: new Date(start_date),
end: new Date(end_date),
+ resource: {
+ committee,
+ },
}
},
),
@@ -89,8 +103,16 @@ const Schedule = () => {
}
const handleSelectEvent = useCallback(
- (event: { id: number; title: string; start: Date; end: Date }) =>
- window.alert(event.title),
+ (event: {
+ id: number
+ title: string
+ start: Date
+ end: Date
+ resource: any
+ }) => {
+ setSelectedDate(event)
+ setIsEditModalOpen(true)
+ },
[],
)
@@ -165,6 +187,16 @@ const Schedule = () => {
+ setIsEditModalOpen(false)}
+ contentLabel="modal"
+ style={customStyles}
+ >
+ {selectedDate && id ? (
+
+ ) : null}
+
)
}
diff --git a/frontend/src/views/coordinator/Schedules.tsx b/frontend/src/views/coordinator/Schedules.tsx
index 2d669bb..0abaf4b 100644
--- a/frontend/src/views/coordinator/Schedules.tsx
+++ b/frontend/src/views/coordinator/Schedules.tsx
@@ -45,9 +45,11 @@ const Schedules = () => {
+ Wybierz zapisy:
{schedules &&
schedules?.data?.examination_schedules.map((schedule) => (
-
+
+ -{' '}
{schedule.title}
diff --git a/frontend/src/views/student/ScheduleAddGroup.tsx b/frontend/src/views/student/ScheduleAddGroup.tsx
new file mode 100644
index 0000000..ebda02f
--- /dev/null
+++ b/frontend/src/views/student/ScheduleAddGroup.tsx
@@ -0,0 +1,77 @@
+import { DateTime } from 'luxon'
+import React, { useState } from 'react'
+import { useForm } from 'react-hook-form'
+import { useMutation } from 'react-query'
+import { assignGroup } from '../../api/schedule'
+
+const ScheduleAddGroup = ({
+ eventData,
+ scheduleId,
+}: {
+ eventData: {
+ start: Date
+ end: Date
+ title: string
+ id: number
+ resource: any
+ }
+ scheduleId: string
+}) => {
+ const { register, handleSubmit, reset, control } = useForm<{
+ student_index: number
+ }>({ mode: 'onBlur' })
+
+ const { mutate: mutateAssignGroup } = useMutation(
+ ['assignGroup'],
+ (data: {
+ scheduleId: number
+ enrollmentId: number
+ studentIndex: number
+ }) => assignGroup(data),
+ )
+
+ const onSubmit = (data: any) => {
+ mutateAssignGroup({
+ scheduleId: Number(scheduleId),
+ enrollmentId: eventData.id,
+ studentIndex: data.student_index,
+ })
+ }
+
+ return (
+
+ )
+}
+
+export default ScheduleAddGroup
diff --git a/frontend/src/views/student/Student.tsx b/frontend/src/views/student/Student.tsx
index ebbcf38..c5ff6a5 100644
--- a/frontend/src/views/student/Student.tsx
+++ b/frontend/src/views/student/Student.tsx
@@ -4,7 +4,12 @@ import TopBar from '../../components/TopBar'
const Student = () => {
return (
<>
-
+
diff --git a/frontend/src/views/student/StudentSchedule.tsx b/frontend/src/views/student/StudentSchedule.tsx
new file mode 100644
index 0000000..490f8f6
--- /dev/null
+++ b/frontend/src/views/student/StudentSchedule.tsx
@@ -0,0 +1,175 @@
+import { Calendar, luxonLocalizer, Views } from 'react-big-calendar'
+import { DateTime, Settings } from 'luxon'
+import { useCallback, useState } from 'react'
+import { useQuery } from 'react-query'
+import { getStudentsSchedule } from '../../api/schedule'
+import { useParams } from 'react-router-dom'
+import Modal from 'react-modal'
+import { useForm } from 'react-hook-form'
+import ScheduleAddGroup from './ScheduleAddGroup'
+
+const customStyles = {
+ content: {
+ top: '50%',
+ left: '50%',
+ right: 'auto',
+ bottom: 'auto',
+ marginRight: '-50%',
+ transform: 'translate(-50%, -50%)',
+ },
+}
+
+const StudentSchedule = () => {
+ Settings.defaultZone = DateTime.local().zoneName
+ Settings.defaultLocale = 'pl'
+
+ const { id } = useParams<{ id: string }>()
+ const [events, setEvents] = useState<
+ {
+ id: number
+ title: string
+ start: Date
+ end: Date
+ resource: any
+ }[]
+ >([])
+ const [selectedDate, setSelectedDate] = useState<{
+ start: Date
+ end: Date
+ title: string
+ id: number
+ resource: any
+ }>()
+ const [view, setView] = useState(Views.WEEK)
+ const onView = useCallback((newView: any) => setView(newView), [setView])
+
+ const [isModalOpen, setIsModalOpen] = useState(false)
+ const [isEditModalOpen, setIsEditModalOpen] = useState(false)
+ Modal.setAppElement('#root')
+
+ const { register, handleSubmit, reset } = useForm<{
+ from: string
+ to: string
+ }>({ mode: 'onBlur' })
+
+ const { refetch } = useQuery(
+ ['studentSchedules'],
+ () => getStudentsSchedule(Number(id)),
+ {
+ onSuccess: (data) => {
+ setEvents(
+ data.data.enrollments.map(
+ ({
+ id,
+ start_date,
+ end_date,
+ title = 'Obrona',
+ committee,
+ group,
+ }) => {
+ return {
+ id,
+ title: `Obrona ${group?.name ?? ''}`,
+ start: new Date(start_date),
+ end: new Date(end_date),
+ resource: {
+ committee,
+ },
+ }
+ },
+ ),
+ )
+ },
+ },
+ )
+
+ const handleSelectSlot = async (event: any) => {
+ setSelectedDate(event)
+ }
+
+ const handleSelectEvent = useCallback(
+ (event: {
+ id: number
+ title: string
+ start: Date
+ end: Date
+ resource: any
+ }) => {
+ console.log(event)
+ setSelectedDate(event)
+ setIsEditModalOpen(true)
+ },
+ [],
+ )
+
+ function closeModal() {
+ setIsModalOpen(false)
+ }
+ const onSubmit = async (data: any) => {}
+
+ return (
+
+
+ Wybierz i zatwierdź termin obrony dla swojej grupy
+
+
+
+
+
+
+
setIsEditModalOpen(false)}
+ contentLabel="modal"
+ style={customStyles}
+ >
+ {selectedDate && id ? (
+
+ ) : null}
+
+
+ )
+}
+
+export default StudentSchedule
diff --git a/frontend/src/views/student/StudentSchedules.tsx b/frontend/src/views/student/StudentSchedules.tsx
new file mode 100644
index 0000000..3c5f262
--- /dev/null
+++ b/frontend/src/views/student/StudentSchedules.tsx
@@ -0,0 +1,24 @@
+import { useQuery } from 'react-query'
+import { getSchedules } from '../../api/schedule'
+import { Link } from 'react-router-dom'
+
+const StudentSchedules = () => {
+ const { data: schedules } = useQuery(['getSchedules'], () => getSchedules())
+
+ return (
+
+
Wybierz zapisy:
+ {schedules &&
+ schedules?.data?.examination_schedules.map((schedule) => (
+
+ -{' '}
+
+ {schedule.title}
+
+
+ ))}
+
+ )
+}
+
+export default StudentSchedules
diff --git a/frontend/src/views/supervisor/Supervisor.tsx b/frontend/src/views/supervisor/Supervisor.tsx
index be1b8a9..6540b2b 100644
--- a/frontend/src/views/supervisor/Supervisor.tsx
+++ b/frontend/src/views/supervisor/Supervisor.tsx
@@ -4,7 +4,12 @@ import TopBar from '../../components/TopBar'
const Supervisor = () => {
return (
<>
-
+
diff --git a/frontend/src/views/supervisor/SupervisorSchedules.tsx b/frontend/src/views/supervisor/SupervisorSchedules.tsx
new file mode 100644
index 0000000..79fdb90
--- /dev/null
+++ b/frontend/src/views/supervisor/SupervisorSchedules.tsx
@@ -0,0 +1,24 @@
+import { useQuery } from 'react-query'
+import { getSchedules } from '../../api/schedule'
+import { Link } from 'react-router-dom'
+
+const SupervisorSchedules = () => {
+ const { data: schedules } = useQuery(['getSchedules'], () => getSchedules())
+
+ return (
+
+
Wybierz zapisy:
+ {schedules &&
+ schedules?.data?.examination_schedules.map((schedule) => (
+
+ -{' '}
+
+ {schedule.title}
+
+
+ ))}
+
+ )
+}
+
+export default SupervisorSchedules