From 01a8a26e0465adc4313b53cd2699f0aebdf46f44 Mon Sep 17 00:00:00 2001 From: Ben Grant Date: Wed, 5 May 2021 21:37:35 +1000 Subject: [PATCH 1/2] Highlight availability segments and choose people manually to view --- crabfit-backend/routes/createEvent.js | 2 + crabfit-backend/swagger.yaml | 4 + .../AvailabilityViewer/AvailabilityViewer.tsx | 203 +++++++++++------- .../availabilityViewerStyle.ts | 32 +++ .../src/components/Legend/Legend.tsx | 9 +- crabfit-frontend/src/pages/Create/Create.tsx | 1 + crabfit-frontend/src/pages/Event/Event.tsx | 9 - crabfit-frontend/src/pages/Help/Help.tsx | 5 - crabfit-frontend/src/pages/Home/Home.tsx | 1 + 9 files changed, 176 insertions(+), 90 deletions(-) diff --git a/crabfit-backend/routes/createEvent.js b/crabfit-backend/routes/createEvent.js index 4e54479..08562f3 100644 --- a/crabfit-backend/routes/createEvent.js +++ b/crabfit-backend/routes/createEvent.js @@ -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); diff --git a/crabfit-backend/swagger.yaml b/crabfit-backend/swagger.yaml index 750b507..f9656a6 100644 --- a/crabfit-backend/swagger.yaml +++ b/crabfit-backend/swagger.yaml @@ -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: diff --git a/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx b/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx index 37a6ac1..81dfabe 100644 --- a/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx +++ b/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx @@ -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,83 +42,134 @@ 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 ( - - - - {!!timeLabels.length && timeLabels.map((label, i) => - - {label.label?.length !== '' && {label.label}} - - )} - - {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 ( - - - {isSpecificDates && {parsedDate.format('MMM D')}} - {parsedDate.format('ddd')} + <> + + p.availability.length > 0).length} + onSegmentFocus={count => setFocusCount(count)} + /> +
Hover or tap the calendar below to see who is available
+ {!!people.length && ( + <> +
Click the names below to view people individually
+ + {people.map((person, i) => + { + 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} + )} + + + )} +
- - {timeLabels.map((timeLabel, i) => { - if (!timeLabel.time) return null; - if (!times.includes(`${timeLabel.time}-${date}`)) { - return ( - - ); - } - const time = `${timeLabel.time}-${date}`; - const peopleHere = people.filter(person => person.availability.includes(time)).map(person => person.name); + + + + {!!timeLabels.length && timeLabels.map((label, i) => + + {label.label?.length !== '' && {label.label}} + + )} + + {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 ( + + + {isSpecificDates && {parsedDate.format('MMM D')}} + {parsedDate.format('ddd')} - return ( - -
- {last && dates.length !== i+1 && ( - - )} -
- ); - })} -
- {tooltip && ( - - {tooltip.available} - {tooltip.date} - {tooltip.people} - - )} -
+ + {timeLabels.map((timeLabel, i) => { + if (!timeLabel.time) return null; + if (!times.includes(`${timeLabel.time}-${date}`)) { + return ( + + ); + } + 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 ( + + + {last && dates.length !== i+1 && ( + + )} + + ); + })} + + {tooltip && ( + + {tooltip.available} + {tooltip.date} + {tooltip.people} + + )} + + ); }; diff --git a/crabfit-frontend/src/components/AvailabilityViewer/availabilityViewerStyle.ts b/crabfit-frontend/src/components/AvailabilityViewer/availabilityViewerStyle.ts index 1abf22a..707488f 100644 --- a/crabfit-frontend/src/components/AvailabilityViewer/availabilityViewerStyle.ts +++ b/crabfit-frontend/src/components/AvailabilityViewer/availabilityViewerStyle.ts @@ -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}; + `} +`; diff --git a/crabfit-frontend/src/components/Legend/Legend.tsx b/crabfit-frontend/src/components/Legend/Legend.tsx index 91b8645..16a51e3 100644 --- a/crabfit-frontend/src/components/Legend/Legend.tsx +++ b/crabfit-frontend/src/components/Legend/Legend.tsx @@ -11,6 +11,7 @@ const Legend = ({ min, max, total, + onSegmentFocus, ...props }) => { const theme = useTheme(); @@ -19,9 +20,13 @@ const Legend = ({ - + onSegmentFocus(null)}> {[...Array(max-min+1).keys()].map(i => - + onSegmentFocus(i+min)} + /> )} diff --git a/crabfit-frontend/src/pages/Create/Create.tsx b/crabfit-frontend/src/pages/Create/Create.tsx index c5d600a..e221521 100644 --- a/crabfit-frontend/src/pages/Create/Create.tsx +++ b/crabfit-frontend/src/pages/Create/Create.tsx @@ -119,6 +119,7 @@ const Create = ({ offline }) => { event: { name: data.name, times: times, + timezone: data.timezone, }, }); setCreatedEvent(response.data); diff --git a/crabfit-frontend/src/pages/Event/Event.tsx b/crabfit-frontend/src/pages/Event/Event.tsx index 382ba33..2d4c8e8 100644 --- a/crabfit-frontend/src/pages/Event/Event.tsx +++ b/crabfit-frontend/src/pages/Event/Event.tsx @@ -13,7 +13,6 @@ import { TextField, SelectField, Button, - Legend, AvailabilityViewer, AvailabilityEditor, Error, @@ -398,14 +397,6 @@ const Event = (props) => { {tab === 'group' ? (
- - p.availability.length > 0).length} - /> -
Hover or tap the calendar below to see who is available
-
{

Send the link to everyone you want to come.

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!

In this example, 1pm to 3pm on Friday the 16th works for all Jenny's friends.

- { event: { name: data.name, times: times, + timezone: data.timezone, }, }); push(`/${response.data.id}`); From 6ad94724346042378237af84457aa576bffa0b8d Mon Sep 17 00:00:00 2001 From: Ben Grant Date: Wed, 5 May 2021 22:21:27 +1000 Subject: [PATCH 2/2] Donate options --- .../AvailabilityViewer/AvailabilityViewer.tsx | 2 +- .../src/components/Donate/Donate.tsx | 7 +++-- .../src/components/Footer/Footer.tsx | 28 +++++++++++++++++++ .../src/components/Footer/footerStyle.ts | 23 +++++++++++++++ crabfit-frontend/src/components/index.ts | 1 + crabfit-frontend/src/pages/Event/Event.tsx | 8 ++---- .../src/pages/Event/eventStyle.ts | 9 ------ crabfit-frontend/src/pages/Help/Help.tsx | 8 ++---- crabfit-frontend/src/pages/Help/helpStyle.ts | 9 ------ crabfit-frontend/src/pages/Home/Home.tsx | 12 +++----- crabfit-frontend/src/pages/Home/homeStyle.ts | 9 ------ .../src/pages/Privacy/Privacy.tsx | 8 ++---- 12 files changed, 68 insertions(+), 56 deletions(-) create mode 100644 crabfit-frontend/src/components/Footer/Footer.tsx create mode 100644 crabfit-frontend/src/components/Footer/footerStyle.ts diff --git a/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx b/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx index 81dfabe..fad5a09 100644 --- a/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx +++ b/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx @@ -62,7 +62,7 @@ const AvailabilityViewer = ({ onSegmentFocus={count => setFocusCount(count)} />
Hover or tap the calendar below to see who is available
- {!!people.length && ( + {people.length > 1 && ( <>
Click the names below to view people individually
diff --git a/crabfit-frontend/src/components/Donate/Donate.tsx b/crabfit-frontend/src/components/Donate/Donate.tsx index 5d0b194..db42fb3 100644 --- a/crabfit-frontend/src/components/Donate/Donate.tsx +++ b/crabfit-frontend/src/components/Donate/Donate.tsx @@ -5,7 +5,7 @@ import { useTWAStore } from 'stores'; const PAYMENT_METHOD = 'https://play.google.com/billing'; const SKU = 'crab_donation'; -const Donate = () => { +const Donate = ({ onDonate = null }) => { const store = useTWAStore(); useEffect(() => { @@ -80,9 +80,12 @@ const Donate = () => { alert('Cannot make donation through Google. Please try donating through the website crab.fit 🦀'); } } + } else if (onDonate !== null) { + event.preventDefault(); + onDonate(); } }} - href="https://www.paypal.com/donate?business=N89X6YXRT5HKW&item_name=Crab+Fit+Donation¤cy_code=AUD" + href="https://www.paypal.com/donate?business=N89X6YXRT5HKW&item_name=Crab+Fit+Donation¤cy_code=AUD&amount=5" target="_blank" rel="noreferrer" > diff --git a/crabfit-frontend/src/components/Footer/Footer.tsx b/crabfit-frontend/src/components/Footer/Footer.tsx new file mode 100644 index 0000000..388a484 --- /dev/null +++ b/crabfit-frontend/src/components/Footer/Footer.tsx @@ -0,0 +1,28 @@ +import { useState } from 'react'; + +import { Donate } from 'components'; +import { Wrapper, Link } from './footerStyle'; + +const Footer = () => { + const [donateMode, setDonateMode] = useState(false); + + return ( + + ); +}; + +export default Footer; diff --git a/crabfit-frontend/src/components/Footer/footerStyle.ts b/crabfit-frontend/src/components/Footer/footerStyle.ts new file mode 100644 index 0000000..b15a9e1 --- /dev/null +++ b/crabfit-frontend/src/components/Footer/footerStyle.ts @@ -0,0 +1,23 @@ +import styled from '@emotion/styled'; + +export const Wrapper = styled.footer` + width: 600px; + margin: 20px auto; + max-width: calc(100% - 60px); + display: flex; + align-items: center; + justify-content: space-between; + + ${props => props.donateMode && ` + flex-wrap: wrap; + `} +`; + +export const Link = styled.a` + padding: 11px 10px; + white-space: nowrap; + + & strong { + font-weight: 800; + } +`; diff --git a/crabfit-frontend/src/components/index.ts b/crabfit-frontend/src/components/index.ts index ada1a57..0d60028 100644 --- a/crabfit-frontend/src/components/index.ts +++ b/crabfit-frontend/src/components/index.ts @@ -15,3 +15,4 @@ export { default as Center } from './Center/Center'; export { default as Donate } from './Donate/Donate'; export { default as Settings } from './Settings/Settings'; export { default as Egg } from './Egg/Egg'; +export { default as Footer } from './Footer/Footer'; diff --git a/crabfit-frontend/src/pages/Event/Event.tsx b/crabfit-frontend/src/pages/Event/Event.tsx index 2d4c8e8..a0184e1 100644 --- a/crabfit-frontend/src/pages/Event/Event.tsx +++ b/crabfit-frontend/src/pages/Event/Event.tsx @@ -9,7 +9,7 @@ import customParseFormat from 'dayjs/plugin/customParseFormat'; import { Center, - Donate, + Footer, TextField, SelectField, Button, @@ -20,7 +20,6 @@ import { import { StyledMain, - Footer, Logo, Title, EventName, @@ -442,10 +441,7 @@ const Event = (props) => { )} -
- Thank you for using Crab Fit. If you like it, consider donating. - -
+