3.6 Add schedules view for students, coordinator can now assign to committees
This commit is contained in:
parent
c71531ca10
commit
0cc9482878
@ -46,9 +46,9 @@ def create_enrollments(examination_schedule_id: int, data: dict) -> dict:
|
|||||||
enrollments = []
|
enrollments = []
|
||||||
for i in range(amount):
|
for i in range(amount):
|
||||||
sd = start_date + datetime.timedelta(minutes=i * prt)
|
sd = start_date + datetime.timedelta(minutes=i * prt)
|
||||||
ed = start_date + datetime.timedelta(minutes=(i + 1) * prt)
|
ed = start_date + datetime.timedelta(minutes=(i + 1) * prt)
|
||||||
enrollment = Enrollment(start_date=sd, end_date=ed, examination_schedule_id=examination_schedule_id)
|
enrollment = Enrollment(start_date=sd, end_date=ed, examination_schedule_id=examination_schedule_id)
|
||||||
enrollments.append(enrollment)
|
enrollments.append(enrollment)
|
||||||
db.session.add_all(enrollments)
|
db.session.add_all(enrollments)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ class GroupSchema(Schema):
|
|||||||
|
|
||||||
|
|
||||||
class EnrollmentSchema(Schema):
|
class EnrollmentSchema(Schema):
|
||||||
|
id = fields.Integer()
|
||||||
start_date = fields.DateTime()
|
start_date = fields.DateTime()
|
||||||
end_date = fields.DateTime()
|
end_date = fields.DateTime()
|
||||||
committee = fields.Nested(CommitteeSchema)
|
committee = fields.Nested(CommitteeSchema)
|
||||||
|
@ -16,6 +16,9 @@ import Student from './views/student/Student'
|
|||||||
import Supervisor from './views/supervisor/Supervisor'
|
import Supervisor from './views/supervisor/Supervisor'
|
||||||
import Schedules from './views/coordinator/Schedules'
|
import Schedules from './views/coordinator/Schedules'
|
||||||
import Schedule from './views/coordinator/Schedule'
|
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({
|
const queryClient = new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
@ -45,10 +48,13 @@ function App() {
|
|||||||
<Route path="student" element={<Student />}>
|
<Route path="student" element={<Student />}>
|
||||||
<Route index element={<Navigate to="enrollment" />} />
|
<Route index element={<Navigate to="enrollment" />} />
|
||||||
<Route path="enrollment" element={<Enrollment />} />
|
<Route path="enrollment" element={<Enrollment />} />
|
||||||
|
<Route path="schedule" element={<StudentSchedules />} />
|
||||||
|
<Route path="schedule/:id" element={<StudentSchedule />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="supervisor" element={<Supervisor />}>
|
<Route path="supervisor" element={<Supervisor />}>
|
||||||
<Route index element={<Navigate to="groups" />} />
|
<Route index element={<Navigate to="groups" />} />
|
||||||
<Route path="groups" element={<Groups />} />
|
<Route path="groups" element={<Groups />} />
|
||||||
|
<Route path="schedule" element={<SupervisorSchedules />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
|
@ -7,12 +7,32 @@ export const getEvents = (scheduleId: number) => {
|
|||||||
start_date: string
|
start_date: string
|
||||||
end_date: string
|
end_date: string
|
||||||
title: 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`,
|
`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 = () => {
|
export const getSchedules = () => {
|
||||||
return axiosInstance.get<{
|
return axiosInstance.get<{
|
||||||
examination_schedules: {
|
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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -23,7 +23,7 @@ const Login = () => {
|
|||||||
<div className="flex flex-col mt-3">
|
<div className="flex flex-col mt-3">
|
||||||
<NavLink to="/coordinator">Koordynator</NavLink>
|
<NavLink to="/coordinator">Koordynator</NavLink>
|
||||||
<NavLink to="/student">Student</NavLink>
|
<NavLink to="/student">Student</NavLink>
|
||||||
<span>Opiekun</span>
|
<NavLink to="/supervisor">Opiekun</NavLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
108
frontend/src/views/coordinator/EditSchedule.tsx
Normal file
108
frontend/src/views/coordinator/EditSchedule.tsx
Normal file
@ -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<any[]>
|
||||||
|
}>({ mode: 'onBlur' })
|
||||||
|
const [committeeOptions, setCommitteeOptions] = useState<SelectValue[]>([])
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div style={{ width: '300px', height: '250px' }}>
|
||||||
|
<form className="w-full flex flex-col " onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<h3>Termin</h3>
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label" htmlFor="committee">
|
||||||
|
Komisja
|
||||||
|
</label>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="committee"
|
||||||
|
rules={{ required: true }}
|
||||||
|
render={({ field: { onChange, onBlur } }) => (
|
||||||
|
<Select
|
||||||
|
closeMenuOnSelect={false}
|
||||||
|
options={committeeOptions}
|
||||||
|
placeholder="Wybierz komisje"
|
||||||
|
isMulti
|
||||||
|
onChange={(values) =>
|
||||||
|
onChange(values.map((value) => value.value))
|
||||||
|
}
|
||||||
|
onBlur={onBlur}
|
||||||
|
styles={{
|
||||||
|
control: (styles) => ({
|
||||||
|
...styles,
|
||||||
|
padding: '0.3rem',
|
||||||
|
borderRadius: '0.5rem',
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button className="btn btn-success mt-10">ZAPISZ</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditSchedule
|
@ -6,6 +6,7 @@ import { createEvent, getEvents } from '../../api/schedule'
|
|||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import Modal from 'react-modal'
|
import Modal from 'react-modal'
|
||||||
import { useForm } from 'react-hook-form'
|
import { useForm } from 'react-hook-form'
|
||||||
|
import EditSchedule from './EditSchedule'
|
||||||
|
|
||||||
const customStyles = {
|
const customStyles = {
|
||||||
content: {
|
content: {
|
||||||
@ -29,6 +30,7 @@ const Schedule = () => {
|
|||||||
title: string
|
title: string
|
||||||
start: Date
|
start: Date
|
||||||
end: Date
|
end: Date
|
||||||
|
resource: any
|
||||||
}[]
|
}[]
|
||||||
>([])
|
>([])
|
||||||
const [selectedDate, setSelectedDate] = useState<{
|
const [selectedDate, setSelectedDate] = useState<{
|
||||||
@ -36,11 +38,13 @@ const Schedule = () => {
|
|||||||
end: Date
|
end: Date
|
||||||
title: string
|
title: string
|
||||||
id: number
|
id: number
|
||||||
|
resource: any
|
||||||
}>()
|
}>()
|
||||||
const [view, setView] = useState(Views.MONTH)
|
const [view, setView] = useState(Views.MONTH)
|
||||||
const onView = useCallback((newView: any) => setView(newView), [setView])
|
const onView = useCallback((newView: any) => setView(newView), [setView])
|
||||||
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||||
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false)
|
||||||
Modal.setAppElement('#root')
|
Modal.setAppElement('#root')
|
||||||
|
|
||||||
const { register, handleSubmit, reset } = useForm<{
|
const { register, handleSubmit, reset } = useForm<{
|
||||||
@ -52,12 +56,22 @@ const Schedule = () => {
|
|||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
setEvents(
|
setEvents(
|
||||||
data.data.enrollments.map(
|
data.data.enrollments.map(
|
||||||
({ id, start_date, end_date, title = 'Obrona' }) => {
|
({
|
||||||
|
id,
|
||||||
|
start_date,
|
||||||
|
end_date,
|
||||||
|
title = 'Obrona',
|
||||||
|
committee,
|
||||||
|
group,
|
||||||
|
}) => {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
title,
|
title: `Obrona ${group?.name ?? ''}`,
|
||||||
start: new Date(start_date),
|
start: new Date(start_date),
|
||||||
end: new Date(end_date),
|
end: new Date(end_date),
|
||||||
|
resource: {
|
||||||
|
committee,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -89,8 +103,16 @@ const Schedule = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleSelectEvent = useCallback(
|
const handleSelectEvent = useCallback(
|
||||||
(event: { id: number; title: string; start: Date; end: Date }) =>
|
(event: {
|
||||||
window.alert(event.title),
|
id: number
|
||||||
|
title: string
|
||||||
|
start: Date
|
||||||
|
end: Date
|
||||||
|
resource: any
|
||||||
|
}) => {
|
||||||
|
setSelectedDate(event)
|
||||||
|
setIsEditModalOpen(true)
|
||||||
|
},
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -165,6 +187,16 @@ const Schedule = () => {
|
|||||||
<button className="btn btn-success mt-4">Dodaj termin</button>
|
<button className="btn btn-success mt-4">Dodaj termin</button>
|
||||||
</form>
|
</form>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<Modal
|
||||||
|
isOpen={isEditModalOpen}
|
||||||
|
onRequestClose={() => setIsEditModalOpen(false)}
|
||||||
|
contentLabel="modal"
|
||||||
|
style={customStyles}
|
||||||
|
>
|
||||||
|
{selectedDate && id ? (
|
||||||
|
<EditSchedule eventData={selectedDate} scheduleId={id} />
|
||||||
|
) : null}
|
||||||
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -45,9 +45,11 @@ const Schedules = () => {
|
|||||||
</div>
|
</div>
|
||||||
<button className="btn btn-success mt-4">Stwórz zapisy</button>
|
<button className="btn btn-success mt-4">Stwórz zapisy</button>
|
||||||
</form>
|
</form>
|
||||||
|
<h2 className="text-2xl font-bold mb-2">Wybierz zapisy:</h2>
|
||||||
{schedules &&
|
{schedules &&
|
||||||
schedules?.data?.examination_schedules.map((schedule) => (
|
schedules?.data?.examination_schedules.map((schedule) => (
|
||||||
<h3 className="text-xl font-bold" key={schedule.title}>
|
<h3 className="text-xl " key={schedule.title}>
|
||||||
|
-{' '}
|
||||||
<Link to={`/coordinator/schedule/${schedule.id}`}>
|
<Link to={`/coordinator/schedule/${schedule.id}`}>
|
||||||
{schedule.title}
|
{schedule.title}
|
||||||
</Link>
|
</Link>
|
||||||
|
77
frontend/src/views/student/ScheduleAddGroup.tsx
Normal file
77
frontend/src/views/student/ScheduleAddGroup.tsx
Normal file
@ -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 (
|
||||||
|
<div>
|
||||||
|
<form className="w-full flex flex-col " onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<h3>
|
||||||
|
Termin{' '}
|
||||||
|
{DateTime.fromJSDate(eventData.start).toFormat('yyyy-LL-dd HH:mm:ss')}{' '}
|
||||||
|
- {DateTime.fromJSDate(eventData.end).toFormat('yyyy-LL-dd HH:mm:ss')}
|
||||||
|
</h3>
|
||||||
|
Komisja:{' '}
|
||||||
|
<ul className="list-disc">
|
||||||
|
{eventData.resource.committee.members.map((member: any) => (
|
||||||
|
<li
|
||||||
|
key={`${member.first_name} ${member.last_name}`}
|
||||||
|
className="ml-4"
|
||||||
|
>
|
||||||
|
{member.first_name} {member.last_name}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label" htmlFor="student_index">
|
||||||
|
Indeks
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="input input-bordered"
|
||||||
|
id="to"
|
||||||
|
type="text"
|
||||||
|
{...register('student_index', { required: true })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button className="btn btn-success mt-4">ZAPISZ</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ScheduleAddGroup
|
@ -4,7 +4,12 @@ import TopBar from '../../components/TopBar'
|
|||||||
const Student = () => {
|
const Student = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar routes={[{ name: 'Zapisy', path: '/student/enrollment' }]} />
|
<TopBar
|
||||||
|
routes={[
|
||||||
|
{ name: 'Zapisy', path: '/student/enrollment' },
|
||||||
|
{ name: 'Harmonogram', path: '/student/schedule' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
<div className="m-10">
|
<div className="m-10">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
175
frontend/src/views/student/StudentSchedule.tsx
Normal file
175
frontend/src/views/student/StudentSchedule.tsx
Normal file
@ -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 (
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold mb-2 text-center">
|
||||||
|
Wybierz i zatwierdź termin obrony dla swojej grupy
|
||||||
|
</h1>
|
||||||
|
<Calendar
|
||||||
|
localizer={luxonLocalizer(DateTime)}
|
||||||
|
startAccessor="start"
|
||||||
|
endAccessor="end"
|
||||||
|
style={{ height: '85vh' }}
|
||||||
|
onSelectEvent={handleSelectEvent}
|
||||||
|
onSelectSlot={handleSelectSlot}
|
||||||
|
events={events}
|
||||||
|
onView={onView}
|
||||||
|
view={view}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
isOpen={isModalOpen}
|
||||||
|
onRequestClose={closeModal}
|
||||||
|
contentLabel="modal"
|
||||||
|
style={customStyles}
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
className="w-full flex flex-col "
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
>
|
||||||
|
<h3>Dostępne godziny</h3>
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label" htmlFor="from">
|
||||||
|
Od
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="input input-bordered"
|
||||||
|
id="from"
|
||||||
|
type="text"
|
||||||
|
{...register('from', { required: true })}
|
||||||
|
/>
|
||||||
|
<label className="label" htmlFor="to">
|
||||||
|
Do
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="input input-bordered"
|
||||||
|
id="to"
|
||||||
|
type="text"
|
||||||
|
{...register('to', { required: true })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button className="btn btn-success mt-4">Dodaj termin</button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
<Modal
|
||||||
|
isOpen={isEditModalOpen}
|
||||||
|
onRequestClose={() => setIsEditModalOpen(false)}
|
||||||
|
contentLabel="modal"
|
||||||
|
style={customStyles}
|
||||||
|
>
|
||||||
|
{selectedDate && id ? (
|
||||||
|
<ScheduleAddGroup eventData={selectedDate} scheduleId={id} />
|
||||||
|
) : null}
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StudentSchedule
|
24
frontend/src/views/student/StudentSchedules.tsx
Normal file
24
frontend/src/views/student/StudentSchedules.tsx
Normal file
@ -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 (
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold mb-2">Wybierz zapisy:</h2>
|
||||||
|
{schedules &&
|
||||||
|
schedules?.data?.examination_schedules.map((schedule) => (
|
||||||
|
<h3 className="text-xl" key={schedule.title}>
|
||||||
|
-{' '}
|
||||||
|
<Link to={`/student/schedule/${schedule.id}`}>
|
||||||
|
{schedule.title}
|
||||||
|
</Link>
|
||||||
|
</h3>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StudentSchedules
|
@ -4,7 +4,12 @@ import TopBar from '../../components/TopBar'
|
|||||||
const Supervisor = () => {
|
const Supervisor = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopBar routes={[{ name: 'Grupy', path: '/supervisor/groups' }]} />
|
<TopBar
|
||||||
|
routes={[
|
||||||
|
{ name: 'Grupy', path: '/supervisor/groups' },
|
||||||
|
{ name: 'Harmonogram', path: '/supervisor/schedule' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
<div className="m-10">
|
<div className="m-10">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
24
frontend/src/views/supervisor/SupervisorSchedules.tsx
Normal file
24
frontend/src/views/supervisor/SupervisorSchedules.tsx
Normal file
@ -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 (
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold mb-2">Wybierz zapisy:</h2>
|
||||||
|
{schedules &&
|
||||||
|
schedules?.data?.examination_schedules.map((schedule) => (
|
||||||
|
<h3 className="text-xl" key={schedule.title}>
|
||||||
|
-{' '}
|
||||||
|
<Link to={`/coordinator/schedule/${schedule.id}`}>
|
||||||
|
{schedule.title}
|
||||||
|
</Link>
|
||||||
|
</h3>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SupervisorSchedules
|
Loading…
Reference in New Issue
Block a user