Highlight availability segments and choose people manually to view
This commit is contained in:
parent
0cfa931fe1
commit
01a8a26e04
9 changed files with 176 additions and 90 deletions
|
|
@ -29,6 +29,7 @@ module.exports = async (req, res) => {
|
||||||
name: name,
|
name: name,
|
||||||
created: currentTime,
|
created: currentTime,
|
||||||
times: event.times,
|
times: event.times,
|
||||||
|
timezone: event.timezone,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -39,6 +40,7 @@ module.exports = async (req, res) => {
|
||||||
name: name,
|
name: name,
|
||||||
created: currentTime,
|
created: currentTime,
|
||||||
times: event.times,
|
times: event.times,
|
||||||
|
timezone: event.timezone,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ definitions:
|
||||||
type: "string"
|
type: "string"
|
||||||
name:
|
name:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
timezone:
|
||||||
|
type: "string"
|
||||||
created:
|
created:
|
||||||
type: "integer"
|
type: "integer"
|
||||||
times:
|
times:
|
||||||
|
|
@ -81,6 +83,8 @@ paths:
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
type: "string"
|
type: "string"
|
||||||
|
timezone:
|
||||||
|
type: "string"
|
||||||
times:
|
times:
|
||||||
type: "array"
|
type: "array"
|
||||||
items:
|
items:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { useState, Fragment } from 'react';
|
import { useState, useEffect, Fragment } from 'react';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import localeData from 'dayjs/plugin/localeData';
|
import localeData from 'dayjs/plugin/localeData';
|
||||||
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
||||||
|
|
||||||
import { useSettingsStore } from 'stores';
|
import { useSettingsStore } from 'stores';
|
||||||
|
|
||||||
|
import { Legend, Center } from 'components';
|
||||||
import {
|
import {
|
||||||
Wrapper,
|
Wrapper,
|
||||||
Container,
|
Container,
|
||||||
|
|
@ -21,6 +22,9 @@ import {
|
||||||
TimeLabels,
|
TimeLabels,
|
||||||
TimeLabel,
|
TimeLabel,
|
||||||
TimeSpace,
|
TimeSpace,
|
||||||
|
People,
|
||||||
|
Person,
|
||||||
|
StyledMain,
|
||||||
} from './availabilityViewerStyle';
|
} from './availabilityViewerStyle';
|
||||||
|
|
||||||
dayjs.extend(localeData);
|
dayjs.extend(localeData);
|
||||||
|
|
@ -38,8 +42,56 @@ const AvailabilityViewer = ({
|
||||||
}) => {
|
}) => {
|
||||||
const [tooltip, setTooltip] = useState(null);
|
const [tooltip, setTooltip] = useState(null);
|
||||||
const timeFormat = useSettingsStore(state => state.timeFormat);
|
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 (
|
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 && (
|
||||||
|
<>
|
||||||
|
<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>
|
<Wrapper>
|
||||||
<Container>
|
<Container>
|
||||||
<TimeLabels>
|
<TimeLabels>
|
||||||
|
|
@ -67,17 +119,19 @@ const AvailabilityViewer = ({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const time = `${timeLabel.time}-${date}`;
|
const time = `${timeLabel.time}-${date}`;
|
||||||
const peopleHere = people.filter(person => person.availability.includes(time)).map(person => person.name);
|
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 (
|
return (
|
||||||
<Time
|
<Time
|
||||||
key={i}
|
key={i}
|
||||||
time={time}
|
time={time}
|
||||||
className="time"
|
className="time"
|
||||||
peopleCount={peopleHere.length}
|
peopleCount={focusCount !== null && focusCount !== peopleHere.length ? 0 : peopleHere.length}
|
||||||
aria-label={peopleHere.join(', ')}
|
aria-label={peopleHere.join(', ')}
|
||||||
maxPeople={max}
|
maxPeople={tempFocus !== null ? 1 : Math.min(max, filteredPeople.length)}
|
||||||
minPeople={min}
|
minPeople={tempFocus !== null ? 0 : Math.min(min, filteredPeople.length)}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
const cellBox = e.currentTarget.getBoundingClientRect();
|
const cellBox = e.currentTarget.getBoundingClientRect();
|
||||||
const timeText = timeFormat === '12h' ? 'h:mma' : 'HH:mm';
|
const timeText = timeFormat === '12h' ? 'h:mma' : 'HH:mm';
|
||||||
|
|
@ -115,6 +169,7 @@ const AvailabilityViewer = ({
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,3 +127,35 @@ export const TimeLabel = styled.label`
|
||||||
user-select: none;
|
user-select: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const StyledMain = styled.div`
|
||||||
|
width: 600px;
|
||||||
|
margin: 20px auto;
|
||||||
|
max-width: calc(100% - 60px);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const People = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 5px;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 14px auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Person = styled.button`
|
||||||
|
font: inherit;
|
||||||
|
font-size: 15px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid ${props => props.theme.text};
|
||||||
|
color: ${props => props.theme.text};
|
||||||
|
font-weight: 500;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 8px;
|
||||||
|
|
||||||
|
${props => props.filtered && `
|
||||||
|
background: ${props.theme.primary};
|
||||||
|
color: ${props.theme.background};
|
||||||
|
border-color: ${props.theme.primary};
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ const Legend = ({
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
total,
|
total,
|
||||||
|
onSegmentFocus,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
@ -19,9 +20,13 @@ const Legend = ({
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Label>{min}/{total} available</Label>
|
<Label>{min}/{total} available</Label>
|
||||||
|
|
||||||
<Bar>
|
<Bar onMouseOut={() => onSegmentFocus(null)}>
|
||||||
{[...Array(max-min+1).keys()].map(i =>
|
{[...Array(max-min+1).keys()].map(i =>
|
||||||
<Grade key={i} color={`${theme.primary}${Math.round((i/(max-min))*255).toString(16)}`} />
|
<Grade
|
||||||
|
key={i}
|
||||||
|
color={`${theme.primary}${Math.round((i/(max-min))*255).toString(16)}`}
|
||||||
|
onMouseOver={() => onSegmentFocus(i+min)}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Bar>
|
</Bar>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ const Create = ({ offline }) => {
|
||||||
event: {
|
event: {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
times: times,
|
times: times,
|
||||||
|
timezone: data.timezone,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
setCreatedEvent(response.data);
|
setCreatedEvent(response.data);
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import {
|
||||||
TextField,
|
TextField,
|
||||||
SelectField,
|
SelectField,
|
||||||
Button,
|
Button,
|
||||||
Legend,
|
|
||||||
AvailabilityViewer,
|
AvailabilityViewer,
|
||||||
AvailabilityEditor,
|
AvailabilityEditor,
|
||||||
Error,
|
Error,
|
||||||
|
|
@ -398,14 +397,6 @@ const Event = (props) => {
|
||||||
|
|
||||||
{tab === 'group' ? (
|
{tab === 'group' ? (
|
||||||
<section id="group">
|
<section id="group">
|
||||||
<StyledMain>
|
|
||||||
<Legend
|
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
total={people.filter(p => p.availability.length > 0).length}
|
|
||||||
/>
|
|
||||||
<Center>Hover or tap the calendar below to see who is available</Center>
|
|
||||||
</StyledMain>
|
|
||||||
<AvailabilityViewer
|
<AvailabilityViewer
|
||||||
times={times}
|
times={times}
|
||||||
timeLabels={timeLabels}
|
timeLabels={timeLabels}
|
||||||
|
|
|
||||||
|
|
@ -77,11 +77,6 @@ const Help = () => {
|
||||||
<P>Send the link to everyone you want to come.</P>
|
<P>Send the link to everyone you want to come.</P>
|
||||||
<P>After Jenny has sent the link to her friends and waited for them to also fill out their availabilities, she can now easily see them all on the heatmap below and choose the darkest area for a time that suits everyone!</P>
|
<P>After Jenny has sent the link to her friends and waited for them to also fill out their availabilities, she can now easily see them all on the heatmap below and choose the darkest area for a time that suits everyone!</P>
|
||||||
<P>In this example, 1pm to 3pm on Friday the 16th works for all Jenny's friends.</P>
|
<P>In this example, 1pm to 3pm on Friday the 16th works for all Jenny's friends.</P>
|
||||||
<Legend
|
|
||||||
min={0}
|
|
||||||
max={5}
|
|
||||||
total={5}
|
|
||||||
/>
|
|
||||||
<AvailabilityViewer
|
<AvailabilityViewer
|
||||||
times={["1100-12042021","1115-12042021","1130-12042021","1145-12042021","1200-12042021","1215-12042021","1230-12042021","1245-12042021","1300-12042021","1315-12042021","1330-12042021","1345-12042021","1400-12042021","1415-12042021","1430-12042021","1445-12042021","1500-12042021","1515-12042021","1530-12042021","1545-12042021","1600-12042021","1615-12042021","1630-12042021","1645-12042021","1100-13042021","1115-13042021","1130-13042021","1145-13042021","1200-13042021","1215-13042021","1230-13042021","1245-13042021","1300-13042021","1315-13042021","1330-13042021","1345-13042021","1400-13042021","1415-13042021","1430-13042021","1445-13042021","1500-13042021","1515-13042021","1530-13042021","1545-13042021","1600-13042021","1615-13042021","1630-13042021","1645-13042021","1100-14042021","1115-14042021","1130-14042021","1145-14042021","1200-14042021","1215-14042021","1230-14042021","1245-14042021","1300-14042021","1315-14042021","1330-14042021","1345-14042021","1400-14042021","1415-14042021","1430-14042021","1445-14042021","1500-14042021","1515-14042021","1530-14042021","1545-14042021","1600-14042021","1615-14042021","1630-14042021","1645-14042021","1100-15042021","1115-15042021","1130-15042021","1145-15042021","1200-15042021","1215-15042021","1230-15042021","1245-15042021","1300-15042021","1315-15042021","1330-15042021","1345-15042021","1400-15042021","1415-15042021","1430-15042021","1445-15042021","1500-15042021","1515-15042021","1530-15042021","1545-15042021","1600-15042021","1615-15042021","1630-15042021","1645-15042021","1100-16042021","1115-16042021","1130-16042021","1145-16042021","1200-16042021","1215-16042021","1230-16042021","1245-16042021","1300-16042021","1315-16042021","1330-16042021","1345-16042021","1400-16042021","1415-16042021","1430-16042021","1445-16042021","1500-16042021","1515-16042021","1530-16042021","1545-16042021","1600-16042021","1615-16042021","1630-16042021","1645-16042021"]}
|
times={["1100-12042021","1115-12042021","1130-12042021","1145-12042021","1200-12042021","1215-12042021","1230-12042021","1245-12042021","1300-12042021","1315-12042021","1330-12042021","1345-12042021","1400-12042021","1415-12042021","1430-12042021","1445-12042021","1500-12042021","1515-12042021","1530-12042021","1545-12042021","1600-12042021","1615-12042021","1630-12042021","1645-12042021","1100-13042021","1115-13042021","1130-13042021","1145-13042021","1200-13042021","1215-13042021","1230-13042021","1245-13042021","1300-13042021","1315-13042021","1330-13042021","1345-13042021","1400-13042021","1415-13042021","1430-13042021","1445-13042021","1500-13042021","1515-13042021","1530-13042021","1545-13042021","1600-13042021","1615-13042021","1630-13042021","1645-13042021","1100-14042021","1115-14042021","1130-14042021","1145-14042021","1200-14042021","1215-14042021","1230-14042021","1245-14042021","1300-14042021","1315-14042021","1330-14042021","1345-14042021","1400-14042021","1415-14042021","1430-14042021","1445-14042021","1500-14042021","1515-14042021","1530-14042021","1545-14042021","1600-14042021","1615-14042021","1630-14042021","1645-14042021","1100-15042021","1115-15042021","1130-15042021","1145-15042021","1200-15042021","1215-15042021","1230-15042021","1245-15042021","1300-15042021","1315-15042021","1330-15042021","1345-15042021","1400-15042021","1415-15042021","1430-15042021","1445-15042021","1500-15042021","1515-15042021","1530-15042021","1545-15042021","1600-15042021","1615-15042021","1630-15042021","1645-15042021","1100-16042021","1115-16042021","1130-16042021","1145-16042021","1200-16042021","1215-16042021","1230-16042021","1245-16042021","1300-16042021","1315-16042021","1330-16042021","1345-16042021","1400-16042021","1415-16042021","1430-16042021","1445-16042021","1500-16042021","1515-16042021","1530-16042021","1545-16042021","1600-16042021","1615-16042021","1630-16042021","1645-16042021"]}
|
||||||
timeLabels={[{"label":"11 AM","time":"1100"},{"label":"","time":"1115"},{"label":"","time":"1130"},{"label":"","time":"1145"},{"label":"12 PM","time":"1200"},{"label":"","time":"1215"},{"label":"","time":"1230"},{"label":"","time":"1245"},{"label":"1 PM","time":"1300"},{"label":"","time":"1315"},{"label":"","time":"1330"},{"label":"","time":"1345"},{"label":"2 PM","time":"1400"},{"label":"","time":"1415"},{"label":"","time":"1430"},{"label":"","time":"1445"},{"label":"3 PM","time":"1500"},{"label":"","time":"1515"},{"label":"","time":"1530"},{"label":"","time":"1545"},{"label":"4 PM","time":"1600"},{"label":"","time":"1615"},{"label":"","time":"1630"},{"label":"","time":"1645"},{"label":"5 PM","time":null}]}
|
timeLabels={[{"label":"11 AM","time":"1100"},{"label":"","time":"1115"},{"label":"","time":"1130"},{"label":"","time":"1145"},{"label":"12 PM","time":"1200"},{"label":"","time":"1215"},{"label":"","time":"1230"},{"label":"","time":"1245"},{"label":"1 PM","time":"1300"},{"label":"","time":"1315"},{"label":"","time":"1330"},{"label":"","time":"1345"},{"label":"2 PM","time":"1400"},{"label":"","time":"1415"},{"label":"","time":"1430"},{"label":"","time":"1445"},{"label":"3 PM","time":"1500"},{"label":"","time":"1515"},{"label":"","time":"1530"},{"label":"","time":"1545"},{"label":"4 PM","time":"1600"},{"label":"","time":"1615"},{"label":"","time":"1630"},{"label":"","time":"1645"},{"label":"5 PM","time":null}]}
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ const Home = ({ offline }) => {
|
||||||
event: {
|
event: {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
times: times,
|
times: times,
|
||||||
|
timezone: data.timezone,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
push(`/${response.data.id}`);
|
push(`/${response.data.id}`);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue