crabfit/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx
2021-05-05 22:21:27 +10:00

177 lines
5.7 KiB
TypeScript

import { useState, useEffect, Fragment } from 'react';
import dayjs from 'dayjs';
import localeData from 'dayjs/plugin/localeData';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import { useSettingsStore } from 'stores';
import { Legend, Center } from 'components';
import {
Wrapper,
Container,
Date,
Times,
DateLabel,
DayLabel,
Time,
Spacer,
Tooltip,
TooltipTitle,
TooltipDate,
TooltipContent,
TimeLabels,
TimeLabel,
TimeSpace,
People,
Person,
StyledMain,
} from './availabilityViewerStyle';
dayjs.extend(localeData);
dayjs.extend(customParseFormat);
const AvailabilityViewer = ({
times,
timeLabels,
dates,
isSpecificDates,
people = [],
min = 0,
max = 0,
...props
}) => {
const [tooltip, setTooltip] = useState(null);
const timeFormat = useSettingsStore(state => state.timeFormat);
const [filteredPeople, setFilteredPeople] = useState([]);
const [touched, setTouched] = useState(false);
const [tempFocus, setTempFocus] = useState(null);
const [focusCount, setFocusCount] = useState(null);
useEffect(() => {
setFilteredPeople(people.map(p => p.name));
setTouched(people.length <= 1);
}, [people]);
return (
<>
<StyledMain>
<Legend
min={Math.min(min, filteredPeople.length)}
max={Math.min(max, filteredPeople.length)}
total={people.filter(p => p.availability.length > 0).length}
onSegmentFocus={count => setFocusCount(count)}
/>
<Center>Hover or tap the calendar below to see who is available</Center>
{people.length > 1 && (
<>
<Center>Click the names below to view people individually</Center>
<People>
{people.map((person, i) =>
<Person
key={i}
filtered={filteredPeople.includes(person.name)}
onClick={() => {
setTempFocus(null);
if (filteredPeople.includes(person.name)) {
if (!touched) {
setTouched(true);
setFilteredPeople([person.name]);
} else {
setFilteredPeople(filteredPeople.filter(n => n !== person.name));
}
} else {
setFilteredPeople([...filteredPeople, person.name]);
}
}}
onMouseOver={() => setTempFocus(person.name)}
onMouseOut={() => setTempFocus(null)}
>{person.name}</Person>
)}
</People>
</>
)}
</StyledMain>
<Wrapper>
<Container>
<TimeLabels>
{!!timeLabels.length && timeLabels.map((label, i) =>
<TimeSpace key={i}>
{label.label?.length !== '' && <TimeLabel>{label.label}</TimeLabel>}
</TimeSpace>
)}
</TimeLabels>
{dates.map((date, i) => {
const parsedDate = isSpecificDates ? dayjs(date, 'DDMMYYYY') : dayjs().day(date);
const last = dates.length === i+1 || (isSpecificDates ? dayjs(dates[i+1], 'DDMMYYYY') : dayjs().day(dates[i+1])).diff(parsedDate, 'day') > 1;
return (
<Fragment key={i}>
<Date>
{isSpecificDates && <DateLabel>{parsedDate.format('MMM D')}</DateLabel>}
<DayLabel>{parsedDate.format('ddd')}</DayLabel>
<Times>
{timeLabels.map((timeLabel, i) => {
if (!timeLabel.time) return null;
if (!times.includes(`${timeLabel.time}-${date}`)) {
return (
<TimeSpace key={i} />
);
}
const time = `${timeLabel.time}-${date}`;
const peopleHere = tempFocus !== null
? people.filter(person => person.availability.includes(time) && tempFocus === person.name).map(person => person.name)
: people.filter(person => person.availability.includes(time) && filteredPeople.includes(person.name)).map(person => person.name);
return (
<Time
key={i}
time={time}
className="time"
peopleCount={focusCount !== null && focusCount !== peopleHere.length ? 0 : peopleHere.length}
aria-label={peopleHere.join(', ')}
maxPeople={tempFocus !== null ? 1 : Math.min(max, filteredPeople.length)}
minPeople={tempFocus !== null ? 0 : Math.min(min, filteredPeople.length)}
onMouseEnter={(e) => {
const cellBox = e.currentTarget.getBoundingClientRect();
const timeText = timeFormat === '12h' ? 'h:mma' : 'HH:mm';
setTooltip({
x: Math.round(cellBox.x + cellBox.width/2),
y: Math.round(cellBox.y + cellBox.height)+6,
available: `${peopleHere.length} / ${people.length} available`,
date: parsedDate.hour(time.slice(0, 2)).minute(time.slice(2, 4)).format(isSpecificDates ? `${timeText} ddd, D MMM YYYY` : `${timeText} ddd`),
people: peopleHere.join(', '),
});
}}
onMouseLeave={() => {
setTooltip(null);
}}
/>
);
})}
</Times>
</Date>
{last && dates.length !== i+1 && (
<Spacer />
)}
</Fragment>
);
})}
</Container>
{tooltip && (
<Tooltip
x={tooltip.x}
y={tooltip.y}
>
<TooltipTitle>{tooltip.available}</TooltipTitle>
<TooltipDate>{tooltip.date}</TooltipDate>
<TooltipContent>{tooltip.people}</TooltipContent>
</Tooltip>
)}
</Wrapper>
</>
);
};
export default AvailabilityViewer;