Create event with API and load event details

This commit is contained in:
Ben Grant 2021-03-03 15:37:27 +11:00
parent 855477570f
commit 8e5954e0ca
19 changed files with 349 additions and 58 deletions

View file

@ -1,6 +1,6 @@
import { Link } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import {
Center,
@ -27,18 +27,60 @@ import {
Tab,
} from './eventStyle';
import api from 'services';
import logo from 'res/logo.svg';
import timezones from 'res/timezones.json';
const Event = (props) => {
const { register, handleSubmit } = useForm();
const id = props.match.params.id;
const { id } = props.match.params;
const [timezone, setTimezone] = useState(Intl.DateTimeFormat().resolvedOptions().timeZone);
const [user, setUser] = useState({
name: 'Benji',
availability: [],
});
const [user, setUser] = useState(null);
const [password, setPassword] = useState(null);
const [tab, setTab] = useState(user ? 'you' : 'group');
const [isLoading, setIsLoading] = useState(true);
const [event, setEvent] = useState(null);
const [people, setPeople] = useState([]);
useEffect(() => {
const fetchEvent = async () => {
const response = await api.get(`/event/${id}`);
if (response.status === 200) {
let times = [];
for (let i = response.data.startTime; i < response.data.endTime; i++) {
let hour = `${i}`.padStart(2, '0');
times.push(
`${hour}00`,
`${hour}15`,
`${hour}30`,
`${hour}45`,
);
}
setEvent({
...response.data,
times,
});
setIsLoading(false);
} else {
console.error(response);
//TODO: 404
}
};
const fetchPeople = async () => {
const response = await api.get(`/event/${id}/people`);
if (response.status === 200) {
setPeople(response.data.people);
} else {
console.error(response);
}
};
fetchEvent();
fetchPeople();
}, [id]);
const onSubmit = data => console.log('submit', data);
@ -52,9 +94,13 @@ const Event = (props) => {
</Center>
</Link>
<EventName>Event name ({id})</EventName>
<ShareInfo>https://page.url</ShareInfo>
<ShareInfo>Copy the link to this page, or share via <a href="#test">Email</a> or <a href="#test">Facebook</a>.</ShareInfo>
<EventName isLoading={isLoading}>{event?.name}</EventName>
<ShareInfo isLoading={isLoading}>{!!event?.name && `https://crab.fit/${id}`}</ShareInfo>
<ShareInfo isLoading={isLoading}>
{!!event?.name &&
<>Copy the link to this page, or share via <a href={`mailto:?subject=${encodeURIComponent(`Scheduling ${event?.name}`)}&body=${encodeURIComponent(`Visit this link to enter your availabilities: https://crab.fit/${id}`)}`}>Email</a>.</>
}
</ShareInfo>
</StyledMain>
<LoginSection id="login">
@ -129,47 +175,13 @@ const Event = (props) => {
{tab === 'group' ? (
<section id="group">
<StyledMain>
<Legend min={0} max={3} />
<Legend min={0} max={people.length} />
<Center>Hover or tap the calendar below to see who is available</Center>
</StyledMain>
<AvailabilityViewer
dates={['03032021', '04032021', '05032021', '07032021', '08032021']}
times={['0900', '0915', '0930', '0945', '1000', '1015', '1030', '1045', '1100', '1115', '1130', '1145', '1200', '1215', '1230', '1245', '1300', '1315', '1330', '1345', '1400', '1415', '1430', '1445', '1500', '1515', '1530', '1545', '1600', '1615', '1630', '1645']}
people={[{
name: 'James',
availability: [
'0900-04032021',
'0915-04032021',
'0930-04032021',
'0945-04032021',
'1000-04032021',
'1500-04032021',
'1515-04032021',
'1230-07032021',
'1245-07032021',
'1300-07032021',
'1315-07032021',
'1400-08032021',
'1430-08032021',
],
},{
name: 'Phoebe',
availability: [
'1100-07032021',
'1115-07032021',
'1130-07032021',
'1145-07032021',
'1200-07032021',
'1215-07032021',
'1230-07032021',
'1245-07032021',
'1300-07032021',
'1315-07032021',
'1330-07032021',
'1345-07032021',
'1400-07032021',
],
},user]}
dates={event?.dates ?? []}
times={event?.times ?? []}
people={people}
/>
</section>
) : (
@ -178,10 +190,23 @@ const Event = (props) => {
<Center>Click and drag the calendar below to set your availabilities</Center>
</StyledMain>
<AvailabilityEditor
dates={['03032021', '04032021', '05032021', '07032021', '08032021']}
times={['0900', '0915', '0930', '0945', '1000', '1015', '1030', '1045', '1100', '1115', '1130', '1145', '1200', '1215', '1230', '1245', '1300', '1315', '1330', '1345', '1400', '1415', '1430', '1445', '1500', '1515', '1530', '1545', '1600', '1615', '1630', '1645']}
dates={event?.dates ?? []}
times={event?.times ?? []}
value={user.availability}
onChange={availability => setUser({ ...user, availability })}
onChange={async availability => {
const oldAvailability = [...user.availability];
setUser({ ...user, availability });
const response = await api.patch(`/event/${id}/people/${user.name}`, {
person: {
password,
availability,
},
});
if (response.status !== 200) {
console.log(response);
setUser({ ...user, oldAvailability });
}
}}
/>
</section>
)}

View file

@ -34,6 +34,18 @@ export const EventName = styled.h1`
text-align: center;
font-weight: 800;
margin: 20px 0 14px;
${props => props.isLoading && `
&:after {
content: '';
display: inline-block;
height: 1em;
width: 300px;
max-width: 100%;
background-color: ${props.theme.loading};
border-radius: 3px;
}
`}
`;
export const LoginForm = styled.form`
@ -65,6 +77,18 @@ export const ShareInfo = styled.p`
margin: 6px 0;
text-align: center;
font-size: 15px;
${props => props.isLoading && `
&:after {
content: '';
display: inline-block;
height: 1em;
width: 500px;
max-width: 100%;
background-color: ${props.theme.loading};
border-radius: 3px;
}
`}
`;
export const Tabs = styled.div`

View file

@ -1,3 +1,5 @@
import { useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import {
@ -8,6 +10,7 @@ import {
Button,
Center,
Donate,
Error,
} from 'components';
import {
@ -20,8 +23,14 @@ import {
AboutSection,
Footer,
P,
Stats,
Stat,
StatNumber,
StatLabel,
} from './homeStyle';
import api from 'services';
import logo from 'res/logo.svg';
import timezones from 'res/timezones.json';
@ -31,8 +40,55 @@ const Home = () => {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
},
});
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [stats, setStats] = useState({
eventCount: null,
personCount: null,
version: 'loading...',
});
const { push } = useHistory();
const onSubmit = data => console.log('submit', data);
useEffect(() => {
const fetch = async () => {
const response = await api.get('/stats');
if (response.status === 200) {
setStats(response.data);
}
};
fetch();
}, []);
const onSubmit = async data => {
setIsLoading(true);
setError(null);
try {
const times = JSON.parse(data.times);
const response = await api.post('/event', {
event: {
name: data.name,
timezone: data.timezone,
startTime: times.start,
endTime: times.end,
dates: JSON.parse(data.dates),
},
});
if (response.status === 201) {
// Success
push(`/${response.data.id}`);
} else {
setError('An error ocurred while creating the event. Please try again later.');
console.error(response.status);
}
} catch (e) {
setError('An error ocurred while creating the event. Please try again later.');
console.error(e);
} finally {
setIsLoading(false);
}
};
return (
<>
@ -83,8 +139,12 @@ const Home = () => {
required
/>
{error && (
<Error onClose={() => setError(null)}>{error}</Error>
)}
<Center>
<Button type="submit">Create</Button>
<Button type="submit" isLoading={isLoading} disabled={isLoading}>Create</Button>
</Center>
</CreateForm>
</StyledMain>
@ -92,6 +152,16 @@ const Home = () => {
<AboutSection id="about">
<StyledMain>
<h2>About Crab Fit</h2>
<Stats>
<Stat>
<StatNumber>{stats.eventCount ?? '10+'}</StatNumber>
<StatLabel>Events created</StatLabel>
</Stat>
<Stat>
<StatNumber>{stats.peopleCount ?? '10+'}</StatNumber>
<StatLabel>Availabilities entered</StatLabel>
</Stat>
</Stats>
<P>Crab Fit helps you fit your event around everyone's schedules. Simply create an event above and send the link to everyone that is participating. Results update live and you will be able to see a heat-map of when everyone is free.</P>
{/* eslint-disable-next-line */}
<P>Create by <a href="https://bengrant.dev" target="_blank">Ben Grant</a>, Crab Fit is the modern-day solution to your group event planning debates.</P>

View file

@ -63,3 +63,28 @@ export const P = styled.p`
font-weight: 500;
line-height: 1.6em;
`;
export const Stats = styled.div`
display: flex;
justify-content: space-around;
align-items: flex-start;
flex-wrap: wrap;
`;
export const Stat = styled.div`
text-align: center;
padding: 0 6px;
min-width: 160px;
margin: 10px 0;
`;
export const StatNumber = styled.span`
display: block;
font-weight: 900;
color: ${props => props.theme.primaryDark};
font-size: 2em;
`;
export const StatLabel = styled.span`
display: block;
`;