import { useEffect, useState } from 'react'; import { useHistory, Link } from 'react-router-dom'; import { useForm } from 'react-hook-form'; import { useTranslation, Trans } from 'react-i18next'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; import timezone from 'dayjs/plugin/timezone'; import customParseFormat from 'dayjs/plugin/customParseFormat'; import { TextField, CalendarField, TimeRangeField, SelectField, Button, Center, Error, Footer, Recents, } from 'components'; import { StyledMain, CreateForm, TitleSmall, TitleLarge, Logo, Links, AboutSection, P, Stats, Stat, StatNumber, StatLabel, OfflineMessage, ButtonArea, } from './homeStyle'; import api from 'services'; import { detect_browser } from 'utils'; import logo from 'res/logo.svg'; import timezones from 'res/timezones.json'; dayjs.extend(utc); dayjs.extend(timezone); dayjs.extend(customParseFormat); const Home = ({ offline }) => { const { register, handleSubmit, setValue } = useForm({ defaultValues: { 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 [browser, setBrowser] = useState(undefined); const { push } = useHistory(); const { t } = useTranslation(['common', 'home']); useEffect(() => { const fetch = async () => { try { const response = await api.get('/stats'); setStats(response.data); } catch (e) { console.error(e); } }; fetch(); document.title = 'Crab Fit'; setBrowser(detect_browser()); }, []); const onSubmit = async data => { setIsLoading(true); setError(null); try { const { start, end } = JSON.parse(data.times); const dates = JSON.parse(data.dates); if (dates.length === 0) { return setError(t('home:form.errors.no_dates')); } const isSpecificDates = typeof dates[0] === 'string' && dates[0].length === 8; if (start === end) { return setError(t('home:form.errors.same_times')); } let times = dates.reduce((times, date) => { let day = []; for (let i = start; i < (start > end ? 24 : end); i++) { if (isSpecificDates) { day.push( dayjs.tz(date, 'DDMMYYYY', data.timezone) .hour(i).minute(0).utc().format('HHmm-DDMMYYYY') ); } else { day.push( dayjs().tz(data.timezone) .day(date).hour(i).minute(0).utc().format('HHmm-d') ); } } if (start > end) { for (let i = 0; i < end; i++) { if (isSpecificDates) { day.push( dayjs.tz(date, 'DDMMYYYY', data.timezone) .hour(i).minute(0).utc().format('HHmm-DDMMYYYY') ); } else { day.push( dayjs().tz(data.timezone) .day(date).hour(i).minute(0).utc().format('HHmm-d') ); } } } return [...times, ...day]; }, []); if (times.length === 0) { return setError(t('home:form.errors.no_time')); } const response = await api.post('/event', { event: { name: data.name, times: times, timezone: data.timezone, }, }); push(`/${response.data.id}`); gtag('event', 'create_event', { 'event_category': 'home', }); } catch (e) { setError(t('home:form.errors.unknown')); console.error(e); } finally { setIsLoading(false); } }; return ( <>
{t('home:create')} CRAB FIT {t('home:nav.about')} / {t('home:nav.donate')}
{offline ? (

🦀📵

{t('home:offline')}

) : ( setError(null)}>{error}
)}

{t('home:about.name')}

{stats.eventCount ?? '350+'} {t('home:about.events')} {stats.personCount ?? '550+'} {t('home:about.availabilities')}

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.
Learn more about how to Crab Fit.

{['chrome', 'firefox', 'safari'].includes(browser) && ( )}

Created by Ben Grant, Crab Fit is the modern-day solution to your group event planning debates.

The code for Crab Fit is open source, if you find any issues or want to contribute, you can visit the repository. By using Crab Fit you agree to the privacy policy.

Consider donating below if it helped you out so it can stay free for everyone. 🦀