Files
pfadi-bussle/components/wizard/calendar.tsx
2020-10-30 23:57:40 +01:00

132 lines
4.0 KiB
TypeScript

import React, { useContext } from 'react'
import classnames from 'classnames'
import {
isPast,
isSameDay,
isAfter,
isWithinInterval,
endOfDay,
} from 'date-fns'
import Calendar from 'react-calendar'
import { dateFormatBackend } from '../../helpers/date'
import { getNextBigger, getNextSmaller } from '../../helpers/array'
import { WizardContext } from './context/wizardStore'
export default function MyCalendar({ ...props }) {
const { onChange, state, daysBooked, daysBookedErrorMessage } = useContext(
WizardContext
)
const { startDate: start, endDate: end } = state.formData
const startDate = (start && new Date(start)) || null
const endDate = (end && new Date(end)) || null
const inSelection = !!start && !end
const prevBooked = inSelection && getNextSmaller(daysBooked, start)
const nextBooked = inSelection && getNextBigger(daysBooked, start)
function isDateSelected(date: Date) {
if (!startDate) {
return false
}
if (isSameDay(date, startDate)) {
return true
}
// if end is before start, it is not within
if (endDate && !isAfter(endDate, startDate)) {
return false
}
return isWithinInterval(date, {
start: startDate,
end: endDate || startDate,
})
}
function tileClassName({ date, view }) {
const isMonthView = view === 'month'
const isDaysBookedLoaded = !!daysBooked
const isInPast = isPast(endOfDay(date))
const isBooked = daysBooked?.includes(dateFormatBackend(date))
const isSelected = isDateSelected(date)
return classnames({
'react-calendar__tile--past': isMonthView && isInPast,
'react-calendar__tile--booked':
isMonthView && isDaysBookedLoaded && isBooked && !isInPast,
'react-calendar__tile--free':
isMonthView &&
isDaysBookedLoaded &&
!isBooked &&
!isInPast &&
!isSelected,
'react-calendar__tile--selected-start':
isMonthView && isSameDay(date, startDate),
'react-calendar__tile--selected-end':
isMonthView && isSameDay(date, endDate),
'react-calendar__tile--selected': isSelected,
'react-calendar__tile--unselectable':
inSelection &&
((prevBooked && date < new Date(prevBooked)) ||
(nextBooked && date > new Date(nextBooked))),
})
}
if (daysBookedErrorMessage) {
return (
<div>
Entschuldigen Sie, aber die Buchungszeiten konnten nicht geladen werden.
Versuchen Sie es später nochmals.
</div>
)
}
return (
<div {...props}>
<h2 className="text-xl">Belegungsplan</h2>
<Calendar
minDate={new Date()}
// @ts-ignore
onClickDay={(date: Date, event: React.MouseEvent<HTMLInputElement>) => {
event.preventDefault()
event.stopPropagation()
const targetClassList = event.currentTarget.classList
if (
targetClassList.contains('react-calendar__tile--past') ||
targetClassList.contains('react-calendar__tile--booked') ||
targetClassList.contains('react-calendar__tile--unselectable')
) {
return
}
if (!startDate || !!endDate) {
onChange({ startDate: dateFormatBackend(date), endDate: null })
return
}
// when startDate set, but end missing
if (isAfter(date, startDate)) {
onChange({ endDate: dateFormatBackend(date) })
} else {
onChange({ startDate: dateFormatBackend(date), endDate: start })
}
}}
tileClassName={tileClassName}
value={null}
/>
<div className="mt-3 flex justify-center">
<div>
<div className="inline-block w-5 h-5 bg-green-200 rounded align-text-bottom"></div>
<span className="ml-2">Frei</span>
</div>
<div>
<div className="ml-6 inline-block w-5 h-5 bg-red-200 rounded align-text-bottom"></div>
<span className="ml-2">Belegt</span>
</div>
</div>
</div>
)
}