Highlight availability segments and choose people manually to view
This commit is contained in:
parent
0cfa931fe1
commit
01a8a26e04
|
|
@ -29,6 +29,7 @@ module.exports = async (req, res) => {
|
|||
name: name,
|
||||
created: currentTime,
|
||||
times: event.times,
|
||||
timezone: event.timezone,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ module.exports = async (req, res) => {
|
|||
name: name,
|
||||
created: currentTime,
|
||||
times: event.times,
|
||||
timezone: event.timezone,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ definitions:
|
|||
type: "string"
|
||||
name:
|
||||
type: "string"
|
||||
timezone:
|
||||
type: "string"
|
||||
created:
|
||||
type: "integer"
|
||||
times:
|
||||
|
|
@ -81,6 +83,8 @@ paths:
|
|||
properties:
|
||||
name:
|
||||
type: "string"
|
||||
timezone:
|
||||
type: "string"
|
||||
times:
|
||||
type: "array"
|
||||
items:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { useState, Fragment } from 'react';
|
||||
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,
|
||||
|
|
@ -21,6 +22,9 @@ import {
|
|||
TimeLabels,
|
||||
TimeLabel,
|
||||
TimeSpace,
|
||||
People,
|
||||
Person,
|
||||
StyledMain,
|
||||
} from './availabilityViewerStyle';
|
||||
|
||||
dayjs.extend(localeData);
|
||||
|
|
@ -38,8 +42,56 @@ const AvailabilityViewer = ({
|
|||
}) => {
|
||||
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 && (
|
||||
<>
|
||||
<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>
|
||||
|
|
@ -67,17 +119,19 @@ const AvailabilityViewer = ({
|
|||
);
|
||||
}
|
||||
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 (
|
||||
<Time
|
||||
key={i}
|
||||
time={time}
|
||||
className="time"
|
||||
peopleCount={peopleHere.length}
|
||||
peopleCount={focusCount !== null && focusCount !== peopleHere.length ? 0 : peopleHere.length}
|
||||
aria-label={peopleHere.join(', ')}
|
||||
maxPeople={max}
|
||||
minPeople={min}
|
||||
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';
|
||||
|
|
@ -115,6 +169,7 @@ const AvailabilityViewer = ({
|
|||
</Tooltip>
|
||||
)}
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -127,3 +127,35 @@ export const TimeLabel = styled.label`
|
|||
user-select: none;
|
||||
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,
|
||||
max,
|
||||
total,
|
||||
onSegmentFocus,
|
||||
...props
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
|
@ -19,9 +20,13 @@ const Legend = ({
|
|||
<Wrapper>
|
||||
<Label>{min}/{total} available</Label>
|
||||
|
||||
<Bar>
|
||||
<Bar onMouseOut={() => onSegmentFocus(null)}>
|
||||
{[...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>
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ const Create = ({ offline }) => {
|
|||
event: {
|
||||
name: data.name,
|
||||
times: times,
|
||||
timezone: data.timezone,
|
||||
},
|
||||
});
|
||||
setCreatedEvent(response.data);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import {
|
|||
TextField,
|
||||
SelectField,
|
||||
Button,
|
||||
Legend,
|
||||
AvailabilityViewer,
|
||||
AvailabilityEditor,
|
||||
Error,
|
||||
|
|
@ -398,14 +397,6 @@ const Event = (props) => {
|
|||
|
||||
{tab === '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
|
||||
times={times}
|
||||
timeLabels={timeLabels}
|
||||
|
|
|
|||
|
|
@ -77,11 +77,6 @@ const Help = () => {
|
|||
<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>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
|
||||
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}]}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ const Home = ({ offline }) => {
|
|||
event: {
|
||||
name: data.name,
|
||||
times: times,
|
||||
timezone: data.timezone,
|
||||
},
|
||||
});
|
||||
push(`/${response.data.id}`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue