Update login, update api requests, add schedule view for supervisors

This commit is contained in:
adam-skowronek 2022-11-17 21:36:36 +01:00
parent 8dcb7528f9
commit 2120ab9690
15 changed files with 544 additions and 101 deletions

View File

@ -19,6 +19,7 @@ import Schedule from './views/coordinator/Schedule'
import SupervisorSchedules from './views/supervisor/SupervisorSchedules' import SupervisorSchedules from './views/supervisor/SupervisorSchedules'
import StudentSchedules from './views/student/StudentSchedules' import StudentSchedules from './views/student/StudentSchedules'
import StudentSchedule from './views/student/StudentSchedule' import StudentSchedule from './views/student/StudentSchedule'
import SupervisorSchedule from './views/supervisor/SupervisorSchedule'
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {
@ -55,6 +56,7 @@ function App() {
<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 path="schedule" element={<SupervisorSchedules />} />
<Route path="schedule/:id" element={<SupervisorSchedule />} />
</Route> </Route>
</Routes> </Routes>
</QueryClientProvider> </QueryClientProvider>

View File

@ -14,7 +14,6 @@ export interface Leader {
email: string email: string
limit_group: number limit_group: number
count_groups: number count_groups: number
mode: number
} }
export const getLeaders = ( export const getLeaders = (
@ -32,7 +31,7 @@ export const getLeaders = (
{ params }, { params },
) )
export const createLeader = (payload: Leader) => export const createLeader = (payload: Partial<Leader>) =>
axiosInstance.post('coordinator/project_supervisor/', payload) axiosInstance.post('coordinator/project_supervisor/', payload)
export const deleteLeader = (id: number) => export const deleteLeader = (id: number) =>

View File

@ -1,34 +1,45 @@
import axiosInstance from './axiosInstance' import axiosInstance from './axiosInstance'
interface TermOfDefences {
id: number
start_date: string
end_date: string
title: string
members_of_committee: {
members: { first_name: string; last_name: string }[]
}
group: { name: string }
}
export const getTermsOfDefences = (scheduleId: number) => { export const getTermsOfDefences = (scheduleId: number) => {
return axiosInstance.get<{ return axiosInstance.get<{
term_of_defences: { term_of_defences: TermOfDefences[]
id: number
start_date: string
end_date: string
title: string
members_of_committee: {
members: { first_name: string; last_name: string }[]
}
group: { name: string }
}[]
}>(`coordinator/enrollments/${scheduleId}/term-of-defences/`) }>(`coordinator/enrollments/${scheduleId}/term-of-defences/`)
} }
export const getStudentsTermsOfDefences = (scheduleId: number) => { export const getStudentsTermsOfDefences = (
scheduleId: number,
studentIndex: number,
) => {
return axiosInstance.get<{ return axiosInstance.get<{
term_of_defences: { term_of_defences: TermOfDefences[]
id: number }>(
start_date: string `students/examination-schedule/${scheduleId}/enrollments?student_index=${studentIndex}`,
end_date: string )
title: string
members_of_committee: {
members: { first_name: string; last_name: string }[]
}
group: { name: string }
}[]
}>(`students/examination-schedule/${scheduleId}/enrollments/`)
} }
export const getSupervisorTermsOfDefences = (scheduleId: number) => {
return axiosInstance.get<{
term_of_defences: TermOfDefences[]
}>(`project_supervisor/${scheduleId}/term-of-defences?id=1`) //fix hardcode id
}
export const getAvailabilityForSupervisor = (scheduleId: number) => {
return axiosInstance.get<{
free_times: { id: number; start_date: string; end_date: string }[]
}>(`project_supervisor/${scheduleId}/temporary-availabilities?id=1`) //fix hardcode id
}
export const getSchedules = (year_group_id: number = 1) => { export const getSchedules = (year_group_id: number = 1) => {
return axiosInstance.get<{ return axiosInstance.get<{
examination_schedules: { examination_schedules: {
@ -125,3 +136,34 @@ export const downloadSchedule = (scheduleId: number) =>
responseType: 'blob', responseType: 'blob',
}, },
) )
export const addAvailability = ({
start_date,
end_date,
scheduleId,
project_supervisor_id,
}: {
start_date: string
end_date: string
scheduleId: number
project_supervisor_id: number
}) => {
return axiosInstance.post(`project_supervisor/${scheduleId}/enrollments/`, {
start_date,
end_date,
project_supervisor_id,
})
}
export const setDateOfExaminationSchedule = (
scheduleId: number,
payload: {
start_date_for_enrollment_students: string
end_date_for_enrollment_students: string
},
) => {
return axiosInstance.put(
`coordinator/examination_schedule/${scheduleId}/date`,
payload,
)
}

View File

@ -0,0 +1,25 @@
import axiosInstance from './axiosInstance'
export interface CreateYearGroup {
name: string
mode: string
}
export interface YearGroup {
id: number
mode: string
name: string
}
export const getYearGroups = (
params: Partial<{
page: number
per_page: number
}>,
) =>
axiosInstance.get<{ max_pages: number; year_groups: YearGroup[] }>(
`coordinator/year-group`,
{
params,
},
)

View File

@ -1,10 +1,71 @@
import { NavLink } from 'react-router-dom' import { useState } from 'react'
import { useQuery } from 'react-query'
import { NavLink, useNavigate } from 'react-router-dom'
import { InputActionMeta } from 'react-select'
import Select from 'react-select'
import useLocalStorageState from 'use-local-storage-state' import useLocalStorageState from 'use-local-storage-state'
import { getStudents } from '../api/students'
import { getYearGroups } from '../api/yearGroups'
type SelectValue = {
value: string | number
label: string
}
const Login = () => { const Login = () => {
const navigate = useNavigate()
const [yearGroupId, setYearGroupId] = useLocalStorageState('yearGroupId', { const [yearGroupId, setYearGroupId] = useLocalStorageState('yearGroupId', {
defaultValue: 1, defaultValue: 1,
}) })
const [studentId, setStudentId] = useLocalStorageState('studentId')
const [supervisorId, setSupervisorId] = useLocalStorageState('supervisorId', {
defaultValue: 1,
})
const [studentOptions, setStudentOptions] = useState<SelectValue[]>([])
const [yearGroupOptions, setYearGroupOptions] = useState<SelectValue[]>([])
const [selectedYear, setSelectedYear] = useState<any>()
const [selectedRole, setSelectedRole] = useState<any>()
useQuery(
'students',
() => getStudents({ year_group_id: yearGroupId, per_page: 1000 }),
{
onSuccess: (data) => {
setStudentOptions(
data?.data.students.map(({ first_name, last_name, index }) => {
return {
value: index,
label: `${first_name} ${last_name} (${index})`,
}
}),
)
},
},
)
useQuery('year_groups', () => getYearGroups({ per_page: 100 }), {
onSuccess: (data) => {
setYearGroupOptions(
data?.data.year_groups.map(({ name, id }) => {
return {
value: id,
label: name,
}
}),
)
},
})
const onStudentChange = (v: any) => {
setSelectedRole(v?.value)
setStudentId(v.value)
}
const onYearChange = (v: any) => {
setSelectedYear(v?.value)
setYearGroupId(v?.value)
}
return ( return (
<> <>
@ -12,7 +73,36 @@ const Login = () => {
<h1 className="text-xl font-bold mx-auto">System PRI</h1> <h1 className="text-xl font-bold mx-auto">System PRI</h1>
</div> </div>
<div className="w-full lg:w-1/4 flex flex-col mx-auto mt-20 px-10 py-5 bg-gray-300 rounded-lg shadow"> <div className="w-full lg:w-1/4 flex flex-col mx-auto mt-20 px-10 py-5 bg-gray-300 rounded-lg shadow">
<div className="form-control"> <label className="label">Rok</label>
<Select
closeMenuOnSelect={true}
options={yearGroupOptions}
placeholder="Wybierz rok"
onChange={onYearChange}
styles={{
control: (styles) => ({
...styles,
padding: '0.3rem',
borderRadius: '0.5rem',
}),
}}
/>
<label className="label">Student</label>
<Select
closeMenuOnSelect={true}
options={studentOptions}
placeholder="Wybierz studenta"
onChange={onStudentChange}
styles={{
control: (styles) => ({
...styles,
padding: '0.3rem',
borderRadius: '0.5rem',
}),
}}
/>
{/* <div className="form-control">
<label className="label" htmlFor="login"> <label className="label" htmlFor="login">
Login Login
</label> </label>
@ -23,11 +113,17 @@ const Login = () => {
Hasło Hasło
</label> </label>
<input className="input input-bordered" id="password" type="text" /> <input className="input input-bordered" id="password" type="text" />
</div> </div> */}
<button className="btn mt-5 text-lg">Zaloguj</button> <button
className="btn mt-5 text-lg"
disabled={!selectedRole || !selectedYear}
onClick={() => navigate('/student')}
>
Zaloguj
</button>
<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> */}
<NavLink to="/supervisor">Opiekun</NavLink> <NavLink to="/supervisor">Opiekun</NavLink>
</div> </div>
</div> </div>

View File

@ -28,7 +28,7 @@ const AddGroup = () => {
const { isLoading: areStudentsLoading } = useQuery( const { isLoading: areStudentsLoading } = useQuery(
'students', 'students',
() => getStudents({ per_page: 1000 }), () => getStudents({ year_group_id: Number(yearGroupId), per_page: 1000 }),
{ {
onSuccess: (data) => { onSuccess: (data) => {
setStudentOptions( setStudentOptions(
@ -46,7 +46,7 @@ const AddGroup = () => {
) )
const { isLoading: areLeadersLoading } = useQuery( const { isLoading: areLeadersLoading } = useQuery(
'leaders', 'leaders',
() => getLeaders({ per_page: 1000 }), () => getLeaders({ year_group_id: Number(yearGroupId), per_page: 1000 }),
{ {
onSuccess: (data) => { onSuccess: (data) => {
setSupervisorOptions( setSupervisorOptions(

View File

@ -83,7 +83,7 @@ const AddLeader = () => {
<InputError>Email jest wymagany</InputError> <InputError>Email jest wymagany</InputError>
)} )}
</div> </div>
<div className="form-control"> {/* <div className="form-control">
<label className="label" htmlFor="limit_group"> <label className="label" htmlFor="limit_group">
Limit grup Limit grup
</label> </label>
@ -99,49 +99,7 @@ const AddLeader = () => {
{errors.limit_group?.type === 'pattern' && ( {errors.limit_group?.type === 'pattern' && (
<InputError>Limit grup musi być liczbą dodatnią</InputError> <InputError>Limit grup musi być liczbą dodatnią</InputError>
)} )}
</div> </div> */}
<div className="form-control gap-2">
<label className="label">Tryb studiów</label>
<div className="flex gap-2">
<input
className="radio"
id="mode-1"
type="radio"
{...register('mode', {
required: true,
})}
value="0"
/>
<label htmlFor="mode-1">Stacjonarny</label>
</div>
<div className="flex gap-2">
<input
className="radio"
id="mode-0"
type="radio"
{...register('mode', {
required: true,
})}
value="1"
/>
<label htmlFor="mode-0">Niestacjonarny</label>
</div>
<div className="flex gap-2">
<input
className="radio"
id="mode-2"
type="radio"
{...register('mode', {
required: true,
})}
value="2"
/>
<label htmlFor="mode-2">Oba</label>
</div>
{errors.mode?.type === 'required' && (
<InputError>Wybierz tryb studiów</InputError>
)}
</div>
<button className="btn btn-success mt-4">Dodaj opiekuna</button> <button className="btn btn-success mt-4">Dodaj opiekuna</button>
</form> </form>
) )

View File

@ -73,11 +73,11 @@ const EditSchedule = ({
{DateTime.fromJSDate(eventData.start).toFormat('yyyy-LL-dd HH:mm:ss')}{' '} {DateTime.fromJSDate(eventData.start).toFormat('yyyy-LL-dd HH:mm:ss')}{' '}
- {DateTime.fromJSDate(eventData.end).toFormat('yyyy-LL-dd HH:mm:ss')} - {DateTime.fromJSDate(eventData.end).toFormat('yyyy-LL-dd HH:mm:ss')}
</p> </p>
{eventData.resource.committee.members.length ? ( {eventData.resource?.members_of_committee?.length ? (
<> <>
Komisja:{' '} Komisja:{' '}
<ul className="list-disc"> <ul className="list-disc">
{eventData.resource.committee.members.map((member: any) => ( {eventData.resource.members_of_committee.map((member: any) => (
<li <li
key={`${member.first_name} ${member.last_name}`} key={`${member.first_name} ${member.last_name}`}
className="ml-4" className="ml-4"

View File

@ -85,7 +85,7 @@ const Leaders = () => {
<th>Email</th> <th>Email</th>
<th>Limit grup</th> <th>Limit grup</th>
<th>Liczba grup</th> <th>Liczba grup</th>
<th>Tryb</th> {/* <th>Tryb</th> */}
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -98,7 +98,6 @@ const Leaders = () => {
email, email,
limit_group, limit_group,
count_groups, count_groups,
mode,
}) => ( }) => (
<tr key={id}> <tr key={id}>
<td>{first_name}</td> <td>{first_name}</td>
@ -106,13 +105,13 @@ const Leaders = () => {
<td>{email}</td> <td>{email}</td>
<td>{limit_group}</td> <td>{limit_group}</td>
<td>{count_groups}</td> <td>{count_groups}</td>
<td> {/* <td>
{mode === 0 {mode === 0
? 'Stacjonarny' ? 'Stacjonarny'
: mode === 1 : mode === 1
? 'Niestacjonarny' ? 'Niestacjonarny'
: 'Nie/stacjonarny'} : 'Nie/stacjonarny'}
</td> </td> */}
<td> <td>
<button onClick={() => mutateDelete(id)}> <button onClick={() => mutateDelete(id)}>
<IconRemove /> <IconRemove />

View File

@ -1,11 +1,12 @@
import { Calendar, luxonLocalizer, Views } from 'react-big-calendar' import { Calendar, luxonLocalizer, Views } from 'react-big-calendar'
import { DateTime, Settings } from 'luxon' import { DateTime, Settings } from 'luxon'
import { useCallback, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useMutation, useQuery } from 'react-query' import { useMutation, useQuery } from 'react-query'
import { import {
createEvent, createEvent,
downloadSchedule, downloadSchedule,
getTermsOfDefences, getTermsOfDefences,
setDateOfExaminationSchedule,
} from '../../api/schedule' } 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'
@ -15,6 +16,7 @@ import Select from 'react-select'
import { getLeaders } from '../../api/leaders' import { getLeaders } from '../../api/leaders'
import useLocalStorageState from 'use-local-storage-state' import useLocalStorageState from 'use-local-storage-state'
import bigCalendarTranslations from '../../utils/bigCalendarTranslations' import bigCalendarTranslations from '../../utils/bigCalendarTranslations'
import DatePicker from 'react-date-picker'
const customStyles = { const customStyles = {
content: { content: {
@ -66,6 +68,12 @@ const Schedule = () => {
project_supervisors: NestedValue<any[]> project_supervisors: NestedValue<any[]>
}>({ mode: 'onBlur' }) }>({ mode: 'onBlur' })
const [committeeOptions, setCommitteeOptions] = useState<SelectValue[]>([]) const [committeeOptions, setCommitteeOptions] = useState<SelectValue[]>([])
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( const { isLoading: areLeadersLoading } = useQuery(
'leaders', 'leaders',
@ -194,6 +202,30 @@ 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) {
setStartDate(new Date(scheduleData.start_date_for_enrollment_students))
setEndDate(new Date(scheduleData.end_date_for_enrollment_students))
}
}, [scheduleData])
const eventGetter = (event: any) => { const eventGetter = (event: any) => {
return event?.resource?.group return event?.resource?.group
? { ? {
@ -210,6 +242,32 @@ const Schedule = () => {
return ( return (
<div className="flex flex-col"> <div className="flex flex-col">
<div>
Start zapisów dla studentów: <label htmlFor="end_date">Od </label>
<DatePicker
onChange={setStartDate}
value={startDate}
format={'yyyy-MM-dd'}
/>
<label htmlFor="end_date">Do </label>
<DatePicker
onChange={setEndDate}
value={endDate}
format={'yyyy-MM-dd'}
/>
<button
className="btn btn-success ml-2"
onClick={() =>
mutateSetDateOfExaminationSchedule({
start_date_for_enrollment_students: startDate.toISOString(),
end_date_for_enrollment_students: endDate.toISOString(),
})
}
>
ZAPISZ
</button>
</div>
<button <button
className="btn btn-success btn-xs md:btn-md self-end mb-4" className="btn btn-success btn-xs md:btn-md self-end mb-4"
onClick={() => mutateDownload(Number(id))} onClick={() => mutateDownload(Number(id))}

View File

@ -13,6 +13,7 @@ const Schedules = () => {
mode: 'onBlur', mode: 'onBlur',
}) })
const [yearGroupId] = useLocalStorageState('yearGroupId') const [yearGroupId] = useLocalStorageState('yearGroupId')
const [scheduleData, setScheduleData] = useLocalStorageState('scheduleData')
const [startDate, setStartDate] = useState(new Date()) const [startDate, setStartDate] = useState(new Date())
const [endDate, setEndDate] = useState(new Date()) const [endDate, setEndDate] = useState(new Date())
@ -59,13 +60,21 @@ const Schedules = () => {
Od Od
</label> </label>
<DatePicker onChange={setStartDate} value={startDate} /> <DatePicker
onChange={setStartDate}
value={startDate}
format={'yyyy-MM-dd'}
/>
<label className="label" htmlFor="end_date"> <label className="label" htmlFor="end_date">
Do Do
</label> </label>
<DatePicker onChange={setEndDate} value={endDate} /> <DatePicker
onChange={setEndDate}
value={endDate}
format={'yyyy-MM-dd'}
/>
</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>
@ -74,9 +83,15 @@ const Schedules = () => {
schedules?.data?.examination_schedules.map((schedule) => ( schedules?.data?.examination_schedules.map((schedule) => (
<h3 className="text-xl " key={schedule.title}> <h3 className="text-xl " key={schedule.title}>
-{' '} -{' '}
<Link to={`/coordinator/schedule/${schedule.id}`}> <Link
to={`/coordinator/schedule/${schedule.id}`}
onClick={() => {
setScheduleData(schedule)
}}
>
{schedule.title} {schedule.title}
</Link> </Link>
1
</h3> </h3>
))} ))}
</div> </div>

View File

@ -2,6 +2,7 @@ import { DateTime } from 'luxon'
import React, { useState } from 'react' import React, { useState } from 'react'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import { useMutation } from 'react-query' import { useMutation } from 'react-query'
import useLocalStorageState from 'use-local-storage-state'
import { assignGroup } from '../../api/schedule' import { assignGroup } from '../../api/schedule'
const ScheduleAddGroup = ({ const ScheduleAddGroup = ({
@ -20,6 +21,7 @@ const ScheduleAddGroup = ({
const { register, handleSubmit, reset, control } = useForm<{ const { register, handleSubmit, reset, control } = useForm<{
student_index: number student_index: number
}>({ mode: 'onBlur' }) }>({ mode: 'onBlur' })
const [studentId] = useLocalStorageState('studentId')
const { mutate: mutateAssignGroup } = useMutation( const { mutate: mutateAssignGroup } = useMutation(
['assignGroup'], ['assignGroup'],
@ -34,7 +36,7 @@ const ScheduleAddGroup = ({
mutateAssignGroup({ mutateAssignGroup({
scheduleId: Number(scheduleId), scheduleId: Number(scheduleId),
enrollmentId: eventData.id, enrollmentId: eventData.id,
studentIndex: data.student_index, studentIndex: Number(studentId),
}) })
} }
@ -46,11 +48,11 @@ const ScheduleAddGroup = ({
{DateTime.fromJSDate(eventData.start).toFormat('yyyy-LL-dd HH:mm:ss')}{' '} {DateTime.fromJSDate(eventData.start).toFormat('yyyy-LL-dd HH:mm:ss')}{' '}
- {DateTime.fromJSDate(eventData.end).toFormat('yyyy-LL-dd HH:mm:ss')} - {DateTime.fromJSDate(eventData.end).toFormat('yyyy-LL-dd HH:mm:ss')}
</h3> </h3>
{eventData.resource.committee.members.length > 0 && ( {eventData.resource?.members_of_committee?.length > 0 && (
<> <>
Komisja:{' '} Komisja:{' '}
<ul className="list-disc"> <ul className="list-disc">
{eventData.resource.committee.members.map((member: any) => ( {eventData.resource.members_of_committee.map((member: any) => (
<li <li
key={`${member.first_name} ${member.last_name}`} key={`${member.first_name} ${member.last_name}`}
className="ml-4" className="ml-4"
@ -66,17 +68,6 @@ const ScheduleAddGroup = ({
)} )}
{!eventData.resource.group && ( {!eventData.resource.group && (
<> <>
<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> <button className="btn btn-success mt-4">ZAPISZ</button>
</> </>
)} )}

View File

@ -8,6 +8,7 @@ import Modal from 'react-modal'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import ScheduleAddGroup from './ScheduleAddGroup' import ScheduleAddGroup from './ScheduleAddGroup'
import bigCalendarTranslations from '../../utils/bigCalendarTranslations' import bigCalendarTranslations from '../../utils/bigCalendarTranslations'
import useLocalStorageState from 'use-local-storage-state'
const customStyles = { const customStyles = {
content: { content: {
@ -24,6 +25,7 @@ const StudentSchedule = () => {
Settings.defaultZone = DateTime.local().zoneName Settings.defaultZone = DateTime.local().zoneName
Settings.defaultLocale = 'pl' Settings.defaultLocale = 'pl'
const [studentId] = useLocalStorageState('studentId')
const { id } = useParams<{ id: string }>() const { id } = useParams<{ id: string }>()
const [events, setEvents] = useState< const [events, setEvents] = useState<
{ {
@ -55,7 +57,7 @@ const StudentSchedule = () => {
const { refetch } = useQuery( const { refetch } = useQuery(
['studentSchedules'], ['studentSchedules'],
() => getStudentsTermsOfDefences(Number(id)), () => getStudentsTermsOfDefences(Number(id), Number(studentId)),
{ {
onSuccess: (data) => { onSuccess: (data) => {
setEvents( setEvents(

View File

@ -0,0 +1,256 @@
import { Calendar, luxonLocalizer, Views } from 'react-big-calendar'
import { DateTime, Settings } from 'luxon'
import { useCallback, useState } from 'react'
import { useMutation, useQuery } from 'react-query'
import {
addAvailability,
getAvailabilityForSupervisor,
getSupervisorTermsOfDefences,
} from '../../api/schedule'
import { useParams } from 'react-router-dom'
import Modal from 'react-modal'
import { useForm } from 'react-hook-form'
import EditSchedule from '../coordinator/EditSchedule'
import useLocalStorageState from 'use-local-storage-state'
import bigCalendarTranslations from '../../utils/bigCalendarTranslations'
const customStyles = {
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
},
}
type SelectValue = {
value: string | number
label: string
}
const SupervisorSchedule = () => {
Settings.defaultZone = DateTime.local().zoneName
Settings.defaultLocale = 'pl'
const { id } = useParams<{ id: string }>()
const [yearGroupId] = useLocalStorageState('yearGroupId')
const [supervisorId] = useLocalStorageState('supervisorId')
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.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<{
from: string
to: string
}>({ mode: 'onBlur' })
const { refetch, isSuccess } = useQuery(
['schedules'],
() => getSupervisorTermsOfDefences(Number(id)),
{
onSuccess: (data) => {
setEvents(
data.data.term_of_defences.map(
({
id,
start_date,
end_date,
title = 'Obrona',
members_of_committee,
group,
}) => {
return {
id,
title: `${group?.name ?? '-'}`,
start: new Date(start_date),
end: new Date(end_date),
resource: {
members_of_committee,
},
}
},
),
)
},
},
)
const { refetch: refetchAvailability } = useQuery(
['availability'],
() => getAvailabilityForSupervisor(Number(id)),
{
onSuccess: (data) => {
setEvents([
...events,
...data.data.free_times.map(({ id, start_date, end_date }) => {
return {
id,
title: 'Moja dostępność',
start: new Date(start_date),
end: new Date(end_date),
resource: {},
}
}),
])
},
enabled: isSuccess,
},
)
const { mutateAsync: mutateAddAvailability } = useMutation(
['createEvent'],
(data: {
project_supervisor_id: number
start_date: string
end_date: string
scheduleId: number
}) => addAvailability(data),
)
const handleSelectSlot = async (event: any) => {
setSelectedDate(event)
if (view === Views.MONTH) {
setIsModalOpen(true)
}
}
const handleSelectEvent = useCallback(
(event: {
id: number
title: string
start: Date
end: Date
resource: any
}) => {
setSelectedDate(event)
setIsEditModalOpen(true)
},
[],
)
function closeModal() {
setIsModalOpen(false)
}
const onSubmit = async (data: any) => {
if (selectedDate && view === Views.MONTH) {
const from = data.from.split(':')
const to = data.to.split(':')
await mutateAddAvailability({
start_date: DateTime.fromJSDate(selectedDate.start)
.set({ hour: from[0], minute: from[1] })
.toFormat('yyyy-LL-dd HH:mm:ss'),
end_date: DateTime.fromJSDate(selectedDate.start)
.set({ hour: to[0], minute: to[1] })
.toFormat('yyyy-LL-dd HH:mm:ss'),
scheduleId: Number(id),
project_supervisor_id: Number(supervisorId),
})
setEvents([])
refetch()
refetchAvailability()
reset()
closeModal()
}
}
const eventGetter = (event: any) => {
return event?.title === '-'
? {
style: {
backgroundColor: '#3174ad',
},
}
: {
style: {
backgroundColor: '#329f32',
},
}
}
return (
<div className="flex flex-col">
<Calendar
localizer={luxonLocalizer(DateTime)}
startAccessor="start"
endAccessor="end"
selectable
style={{ height: '80vh' }}
onSelectEvent={handleSelectEvent}
onSelectSlot={handleSelectSlot}
events={events}
onView={onView}
view={view}
eventPropGetter={eventGetter}
min={DateTime.fromObject({ hour: 8, minute: 0 }).toJSDate()}
max={DateTime.fromObject({ hour: 16, minute: 0 }).toJSDate()}
messages={bigCalendarTranslations}
/>
<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 dostępność</button>
</form>
</Modal>
<Modal
isOpen={isEditModalOpen}
onRequestClose={() => setIsEditModalOpen(false)}
contentLabel="modal"
style={customStyles}
>
{selectedDate && id ? (
<EditSchedule eventData={selectedDate} scheduleId={id} />
) : null}
</Modal>
</div>
)
}
export default SupervisorSchedule

View File

@ -12,7 +12,7 @@ const SupervisorSchedules = () => {
schedules?.data?.examination_schedules.map((schedule) => ( schedules?.data?.examination_schedules.map((schedule) => (
<h3 className="text-xl" key={schedule.title}> <h3 className="text-xl" key={schedule.title}>
-{' '} -{' '}
<Link to={`/coordinator/schedule/${schedule.id}`}> <Link to={`/supervisor/schedule/${schedule.id}`}>
{schedule.title} {schedule.title}
</Link> </Link>
</h3> </h3>