Merge pull request #272 from GRA0007/feat/web-worker
Web worker for heatmap table calculation
This commit is contained in:
commit
08f6646339
|
|
@ -12,8 +12,10 @@ import SelectField from '/src/components/SelectField/SelectField'
|
||||||
import { EventResponse, getPeople, PersonResponse, updatePerson } from '/src/config/api'
|
import { EventResponse, getPeople, PersonResponse, updatePerson } from '/src/config/api'
|
||||||
import { useTranslation } from '/src/i18n/client'
|
import { useTranslation } from '/src/i18n/client'
|
||||||
import timezones from '/src/res/timezones.json'
|
import timezones from '/src/res/timezones.json'
|
||||||
|
import { useStore } from '/src/stores'
|
||||||
import useRecentsStore from '/src/stores/recentsStore'
|
import useRecentsStore from '/src/stores/recentsStore'
|
||||||
import { expandTimes, makeClass } from '/src/utils'
|
import useSettingsStore from '/src/stores/settingsStore'
|
||||||
|
import { calculateTable, expandTimes, makeClass } from '/src/utils'
|
||||||
|
|
||||||
import styles from './page.module.scss'
|
import styles from './page.module.scss'
|
||||||
|
|
||||||
|
|
@ -25,6 +27,8 @@ interface EventAvailabilitiesProps {
|
||||||
const EventAvailabilities = ({ event, ...data }: EventAvailabilitiesProps) => {
|
const EventAvailabilities = ({ event, ...data }: EventAvailabilitiesProps) => {
|
||||||
const { t, i18n } = useTranslation('event')
|
const { t, i18n } = useTranslation('event')
|
||||||
|
|
||||||
|
const timeFormat = useStore(useSettingsStore, state => state.timeFormat) ?? '12h'
|
||||||
|
|
||||||
const [people, setPeople] = useState(data.people)
|
const [people, setPeople] = useState(data.people)
|
||||||
const expandedTimes = useMemo(() => expandTimes(event.times), [event.times])
|
const expandedTimes = useMemo(() => expandTimes(event.times), [event.times])
|
||||||
|
|
||||||
|
|
@ -34,6 +38,28 @@ const EventAvailabilities = ({ event, ...data }: EventAvailabilitiesProps) => {
|
||||||
const [tab, setTab] = useState<'group' | 'you'>('group')
|
const [tab, setTab] = useState<'group' | 'you'>('group')
|
||||||
const [timezone, setTimezone] = useState(Intl.DateTimeFormat().resolvedOptions().timeZone)
|
const [timezone, setTimezone] = useState(Intl.DateTimeFormat().resolvedOptions().timeZone)
|
||||||
|
|
||||||
|
// Web worker for calculating the heatmap table
|
||||||
|
const tableWorker = useMemo(() => (typeof window !== undefined && window.Worker) ? new Worker(new URL('/src/workers/calculateTable', import.meta.url)) : undefined, [])
|
||||||
|
|
||||||
|
// Calculate table (using a web worker if available)
|
||||||
|
const [table, setTable] = useState<ReturnType<typeof calculateTable>>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const args = { times: expandedTimes, locale: i18n.language, timeFormat, timezone }
|
||||||
|
if (tableWorker) {
|
||||||
|
tableWorker.postMessage(args)
|
||||||
|
setTable(undefined)
|
||||||
|
} else {
|
||||||
|
setTable(calculateTable(args))
|
||||||
|
}
|
||||||
|
}, [tableWorker, expandedTimes, i18n.language, timeFormat, timezone])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tableWorker) {
|
||||||
|
tableWorker.onmessage = (e: MessageEvent<ReturnType<typeof calculateTable>>) => setTable(e.data)
|
||||||
|
}
|
||||||
|
}, [tableWorker])
|
||||||
|
|
||||||
// Add this event to recents
|
// Add this event to recents
|
||||||
const addRecent = useRecentsStore(state => state.addRecent)
|
const addRecent = useRecentsStore(state => state.addRecent)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -138,7 +164,7 @@ const EventAvailabilities = ({ event, ...data }: EventAvailabilitiesProps) => {
|
||||||
{tab === 'group' ? <AvailabilityViewer
|
{tab === 'group' ? <AvailabilityViewer
|
||||||
times={expandedTimes}
|
times={expandedTimes}
|
||||||
people={people}
|
people={people}
|
||||||
timezone={timezone}
|
table={table}
|
||||||
/> : user && <AvailabilityEditor
|
/> : user && <AvailabilityEditor
|
||||||
times={expandedTimes}
|
times={expandedTimes}
|
||||||
timezone={timezone}
|
timezone={timezone}
|
||||||
|
|
@ -152,6 +178,7 @@ const EventAvailabilities = ({ event, ...data }: EventAvailabilitiesProps) => {
|
||||||
setUser({ ...user, availability: oldAvailability })
|
setUser({ ...user, availability: oldAvailability })
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
table={table}
|
||||||
/>}
|
/>}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import Section from '/src/components/Section/Section'
|
||||||
import TimeRangeField from '/src/components/TimeRangeField/TimeRangeField'
|
import TimeRangeField from '/src/components/TimeRangeField/TimeRangeField'
|
||||||
import Video from '/src/components/Video/Video'
|
import Video from '/src/components/Video/Video'
|
||||||
import { useTranslation } from '/src/i18n/server'
|
import { useTranslation } from '/src/i18n/server'
|
||||||
import { getWeekdayNames } from '/src/utils'
|
import { calculateTable, getWeekdayNames } from '/src/utils'
|
||||||
|
|
||||||
import styles from './page.module.scss'
|
import styles from './page.module.scss'
|
||||||
|
|
||||||
|
|
@ -25,9 +25,13 @@ export const generateMetadata = async (): Promise<Metadata> => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const 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']
|
||||||
|
|
||||||
const Page = async () => {
|
const Page = async () => {
|
||||||
const { t, i18n } = await useTranslation(['common', 'help'])
|
const { t, i18n } = await useTranslation(['common', 'help'])
|
||||||
|
|
||||||
|
const table = calculateTable({ times, locale: i18n.language, timezone: 'UTC', timeFormat: '12h' })
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<Content>
|
<Content>
|
||||||
<Header />
|
<Header />
|
||||||
|
|
@ -53,9 +57,9 @@ const Page = async () => {
|
||||||
<P>{t('help:p6')}</P>
|
<P>{t('help:p6')}</P>
|
||||||
<P>{t('help:p7')}</P>
|
<P>{t('help:p7')}</P>
|
||||||
<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={times}
|
||||||
people={[{ name: 'Jenny', created_at: 1618232400, availability: ['1100-12042021', '1100-13042021', '1100-14042021', '1100-15042021', '1115-12042021', '1115-13042021', '1115-14042021', '1115-15042021', '1130-12042021', '1130-13042021', '1130-14042021', '1130-15042021', '1145-12042021', '1145-13042021', '1145-14042021', '1145-15042021', '1200-12042021', '1200-13042021', '1200-14042021', '1200-15042021', '1215-12042021', '1215-13042021', '1215-14042021', '1215-15042021', '1230-12042021', '1230-13042021', '1230-14042021', '1230-15042021', '1245-12042021', '1245-13042021', '1245-14042021', '1245-15042021', '1300-12042021', '1300-13042021', '1300-14042021', '1300-15042021', '1300-16042021', '1315-12042021', '1315-13042021', '1315-14042021', '1315-15042021', '1315-16042021', '1330-12042021', '1330-13042021', '1330-14042021', '1330-15042021', '1330-16042021', '1345-12042021', '1345-13042021', '1345-14042021', '1345-15042021', '1345-16042021', '1400-12042021', '1400-13042021', '1400-14042021', '1400-15042021', '1400-16042021', '1415-12042021', '1415-13042021', '1415-14042021', '1415-15042021', '1415-16042021', '1430-12042021', '1430-13042021', '1430-14042021', '1430-15042021', '1430-16042021', '1445-12042021', '1445-13042021', '1445-14042021', '1445-15042021', '1445-16042021', '1500-12042021', '1500-15042021', '1500-16042021', '1515-12042021', '1515-15042021', '1515-16042021', '1530-12042021', '1530-15042021', '1530-16042021', '1545-12042021', '1545-15042021', '1545-16042021', '1600-12042021', '1600-15042021', '1600-16042021', '1615-12042021', '1615-15042021', '1615-16042021', '1630-12042021', '1630-15042021', '1630-16042021', '1645-12042021', '1645-15042021', '1645-16042021'] }]}
|
people={[{ name: 'Jenny', created_at: 1618232400, availability: ['1100-12042021', '1100-13042021', '1100-14042021', '1100-15042021', '1115-12042021', '1115-13042021', '1115-14042021', '1115-15042021', '1130-12042021', '1130-13042021', '1130-14042021', '1130-15042021', '1145-12042021', '1145-13042021', '1145-14042021', '1145-15042021', '1200-12042021', '1200-13042021', '1200-14042021', '1200-15042021', '1215-12042021', '1215-13042021', '1215-14042021', '1215-15042021', '1230-12042021', '1230-13042021', '1230-14042021', '1230-15042021', '1245-12042021', '1245-13042021', '1245-14042021', '1245-15042021', '1300-12042021', '1300-13042021', '1300-14042021', '1300-15042021', '1300-16042021', '1315-12042021', '1315-13042021', '1315-14042021', '1315-15042021', '1315-16042021', '1330-12042021', '1330-13042021', '1330-14042021', '1330-15042021', '1330-16042021', '1345-12042021', '1345-13042021', '1345-14042021', '1345-15042021', '1345-16042021', '1400-12042021', '1400-13042021', '1400-14042021', '1400-15042021', '1400-16042021', '1415-12042021', '1415-13042021', '1415-14042021', '1415-15042021', '1415-16042021', '1430-12042021', '1430-13042021', '1430-14042021', '1430-15042021', '1430-16042021', '1445-12042021', '1445-13042021', '1445-14042021', '1445-15042021', '1445-16042021', '1500-12042021', '1500-15042021', '1500-16042021', '1515-12042021', '1515-15042021', '1515-16042021', '1530-12042021', '1530-15042021', '1530-16042021', '1545-12042021', '1545-15042021', '1545-16042021', '1600-12042021', '1600-15042021', '1600-16042021', '1615-12042021', '1615-15042021', '1615-16042021', '1630-12042021', '1630-15042021', '1630-16042021', '1645-12042021', '1645-15042021', '1645-16042021'] }]}
|
||||||
timezone="UTC"
|
table={table}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<h2 className={styles.step}>{t('help:s3')}</h2>
|
<h2 className={styles.step}>{t('help:s3')}</h2>
|
||||||
|
|
@ -63,7 +67,7 @@ const Page = async () => {
|
||||||
<P>{t('help:p9')}</P>
|
<P>{t('help:p9')}</P>
|
||||||
<P>{t('help:p10')}</P>
|
<P>{t('help:p10')}</P>
|
||||||
<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={times}
|
||||||
people={[
|
people={[
|
||||||
{ name: 'Jenny', created_at: 1618232400, availability: ['1100-12042021', '1100-13042021', '1100-14042021', '1100-15042021', '1115-12042021', '1115-13042021', '1115-14042021', '1115-15042021', '1130-12042021', '1130-13042021', '1130-14042021', '1130-15042021', '1145-12042021', '1145-13042021', '1145-14042021', '1145-15042021', '1200-12042021', '1200-13042021', '1200-14042021', '1200-15042021', '1215-12042021', '1215-13042021', '1215-14042021', '1215-15042021', '1230-12042021', '1230-13042021', '1230-14042021', '1230-15042021', '1245-12042021', '1245-13042021', '1245-14042021', '1245-15042021', '1300-12042021', '1300-13042021', '1300-14042021', '1300-15042021', '1300-16042021', '1315-12042021', '1315-13042021', '1315-14042021', '1315-15042021', '1315-16042021', '1330-12042021', '1330-13042021', '1330-14042021', '1330-15042021', '1330-16042021', '1345-12042021', '1345-13042021', '1345-14042021', '1345-15042021', '1345-16042021', '1400-12042021', '1400-13042021', '1400-14042021', '1400-15042021', '1400-16042021', '1415-12042021', '1415-13042021', '1415-14042021', '1415-15042021', '1415-16042021', '1430-12042021', '1430-13042021', '1430-14042021', '1430-15042021', '1430-16042021', '1445-12042021', '1445-13042021', '1445-14042021', '1445-15042021', '1445-16042021', '1500-12042021', '1500-15042021', '1500-16042021', '1515-12042021', '1515-15042021', '1515-16042021', '1530-12042021', '1530-15042021', '1530-16042021', '1545-12042021', '1545-15042021', '1545-16042021', '1600-12042021', '1600-15042021', '1600-16042021', '1615-12042021', '1615-15042021', '1615-16042021', '1630-12042021', '1630-15042021', '1630-16042021', '1645-12042021', '1645-15042021', '1645-16042021'] },
|
{ name: 'Jenny', created_at: 1618232400, availability: ['1100-12042021', '1100-13042021', '1100-14042021', '1100-15042021', '1115-12042021', '1115-13042021', '1115-14042021', '1115-15042021', '1130-12042021', '1130-13042021', '1130-14042021', '1130-15042021', '1145-12042021', '1145-13042021', '1145-14042021', '1145-15042021', '1200-12042021', '1200-13042021', '1200-14042021', '1200-15042021', '1215-12042021', '1215-13042021', '1215-14042021', '1215-15042021', '1230-12042021', '1230-13042021', '1230-14042021', '1230-15042021', '1245-12042021', '1245-13042021', '1245-14042021', '1245-15042021', '1300-12042021', '1300-13042021', '1300-14042021', '1300-15042021', '1300-16042021', '1315-12042021', '1315-13042021', '1315-14042021', '1315-15042021', '1315-16042021', '1330-12042021', '1330-13042021', '1330-14042021', '1330-15042021', '1330-16042021', '1345-12042021', '1345-13042021', '1345-14042021', '1345-15042021', '1345-16042021', '1400-12042021', '1400-13042021', '1400-14042021', '1400-15042021', '1400-16042021', '1415-12042021', '1415-13042021', '1415-14042021', '1415-15042021', '1415-16042021', '1430-12042021', '1430-13042021', '1430-14042021', '1430-15042021', '1430-16042021', '1445-12042021', '1445-13042021', '1445-14042021', '1445-15042021', '1445-16042021', '1500-12042021', '1500-15042021', '1500-16042021', '1515-12042021', '1515-15042021', '1515-16042021', '1530-12042021', '1530-15042021', '1530-16042021', '1545-12042021', '1545-15042021', '1545-16042021', '1600-12042021', '1600-15042021', '1600-16042021', '1615-12042021', '1615-15042021', '1615-16042021', '1630-12042021', '1630-15042021', '1630-16042021', '1645-12042021', '1645-15042021', '1645-16042021'] },
|
||||||
{ name: 'Dakota', created_at: 1618232400, availability: ['1300-14042021', '1300-15042021', '1300-16042021', '1315-13042021', '1315-14042021', '1315-15042021', '1315-16042021', '1330-13042021', '1330-14042021', '1330-15042021', '1330-16042021', '1345-13042021', '1345-14042021', '1345-15042021', '1345-16042021', '1400-13042021', '1400-14042021', '1400-15042021', '1400-16042021', '1415-13042021', '1415-14042021', '1415-15042021', '1415-16042021', '1430-13042021', '1430-14042021', '1430-15042021', '1430-16042021', '1445-13042021', '1445-14042021', '1445-15042021', '1445-16042021', '1300-13042021', '1100-12042021', '1100-13042021', '1115-12042021', '1115-13042021', '1130-12042021', '1130-13042021', '1145-12042021', '1145-13042021'] },
|
{ name: 'Dakota', created_at: 1618232400, availability: ['1300-14042021', '1300-15042021', '1300-16042021', '1315-13042021', '1315-14042021', '1315-15042021', '1315-16042021', '1330-13042021', '1330-14042021', '1330-15042021', '1330-16042021', '1345-13042021', '1345-14042021', '1345-15042021', '1345-16042021', '1400-13042021', '1400-14042021', '1400-15042021', '1400-16042021', '1415-13042021', '1415-14042021', '1415-15042021', '1415-16042021', '1430-13042021', '1430-14042021', '1430-15042021', '1430-16042021', '1445-13042021', '1445-14042021', '1445-15042021', '1445-16042021', '1300-13042021', '1100-12042021', '1100-13042021', '1115-12042021', '1115-13042021', '1130-12042021', '1130-13042021', '1145-12042021', '1145-13042021'] },
|
||||||
|
|
@ -71,7 +75,7 @@ const Page = async () => {
|
||||||
{ name: 'Mark', created_at: 1618232400, availability: ['1200-12042021', '1200-13042021', '1200-14042021', '1200-16042021', '1215-12042021', '1215-13042021', '1215-14042021', '1215-16042021', '1230-12042021', '1230-13042021', '1230-14042021', '1230-16042021', '1245-12042021', '1245-13042021', '1245-14042021', '1245-16042021', '1300-12042021', '1300-13042021', '1300-14042021', '1300-16042021', '1315-12042021', '1315-13042021', '1315-14042021', '1315-16042021', '1330-12042021', '1330-13042021', '1330-14042021', '1330-16042021', '1345-12042021', '1345-13042021', '1345-14042021', '1345-16042021', '1400-12042021', '1400-13042021', '1400-14042021', '1400-16042021', '1415-12042021', '1415-13042021', '1415-14042021', '1415-16042021', '1430-12042021', '1430-13042021', '1430-14042021', '1430-16042021', '1445-12042021', '1445-13042021', '1445-14042021', '1445-16042021', '1500-12042021', '1500-13042021', '1500-14042021', '1500-16042021', '1515-12042021', '1515-13042021', '1515-14042021', '1515-16042021', '1530-12042021', '1530-13042021', '1530-14042021', '1530-16042021', '1545-12042021', '1545-13042021', '1545-14042021', '1545-16042021'] },
|
{ name: 'Mark', created_at: 1618232400, availability: ['1200-12042021', '1200-13042021', '1200-14042021', '1200-16042021', '1215-12042021', '1215-13042021', '1215-14042021', '1215-16042021', '1230-12042021', '1230-13042021', '1230-14042021', '1230-16042021', '1245-12042021', '1245-13042021', '1245-14042021', '1245-16042021', '1300-12042021', '1300-13042021', '1300-14042021', '1300-16042021', '1315-12042021', '1315-13042021', '1315-14042021', '1315-16042021', '1330-12042021', '1330-13042021', '1330-14042021', '1330-16042021', '1345-12042021', '1345-13042021', '1345-14042021', '1345-16042021', '1400-12042021', '1400-13042021', '1400-14042021', '1400-16042021', '1415-12042021', '1415-13042021', '1415-14042021', '1415-16042021', '1430-12042021', '1430-13042021', '1430-14042021', '1430-16042021', '1445-12042021', '1445-13042021', '1445-14042021', '1445-16042021', '1500-12042021', '1500-13042021', '1500-14042021', '1500-16042021', '1515-12042021', '1515-13042021', '1515-14042021', '1515-16042021', '1530-12042021', '1530-13042021', '1530-14042021', '1530-16042021', '1545-12042021', '1545-13042021', '1545-14042021', '1545-16042021'] },
|
||||||
{ name: 'Alex', created_at: 1618232400, availability: ['1200-13042021', '1200-14042021', '1215-13042021', '1215-14042021', '1230-13042021', '1230-14042021', '1245-13042021', '1245-14042021', '1300-13042021', '1300-14042021', '1315-13042021', '1315-14042021', '1330-13042021', '1330-14042021', '1345-13042021', '1345-14042021', '1400-13042021', '1400-14042021', '1415-13042021', '1415-14042021', '1430-13042021', '1430-14042021', '1445-13042021', '1445-14042021', '1500-13042021', '1500-14042021', '1515-13042021', '1515-14042021', '1530-13042021', '1530-14042021', '1545-13042021', '1545-14042021', '1200-12042021', '1215-12042021', '1545-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', '1100-15042021', '1100-16042021', '1115-15042021', '1115-16042021', '1130-15042021', '1130-16042021', '1145-15042021', '1145-16042021', '1200-15042021', '1200-16042021', '1215-15042021', '1215-16042021', '1230-15042021', '1230-16042021', '1245-15042021', '1245-16042021', '1300-15042021', '1300-16042021', '1315-15042021', '1315-16042021', '1330-15042021', '1330-16042021', '1345-15042021', '1345-16042021', '1400-15042021', '1400-16042021', '1415-15042021', '1415-16042021', '1430-15042021', '1430-16042021', '1445-15042021', '1445-16042021', '1500-15042021', '1500-16042021', '1515-15042021', '1515-16042021', '1530-15042021', '1530-16042021', '1545-15042021', '1545-16042021', '1600-15042021', '1600-16042021', '1615-15042021', '1615-16042021', '1630-15042021', '1630-16042021', '1645-15042021', '1645-16042021'] },
|
{ name: 'Alex', created_at: 1618232400, availability: ['1200-13042021', '1200-14042021', '1215-13042021', '1215-14042021', '1230-13042021', '1230-14042021', '1245-13042021', '1245-14042021', '1300-13042021', '1300-14042021', '1315-13042021', '1315-14042021', '1330-13042021', '1330-14042021', '1345-13042021', '1345-14042021', '1400-13042021', '1400-14042021', '1415-13042021', '1415-14042021', '1430-13042021', '1430-14042021', '1445-13042021', '1445-14042021', '1500-13042021', '1500-14042021', '1515-13042021', '1515-14042021', '1530-13042021', '1530-14042021', '1545-13042021', '1545-14042021', '1200-12042021', '1215-12042021', '1545-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', '1100-15042021', '1100-16042021', '1115-15042021', '1115-16042021', '1130-15042021', '1130-16042021', '1145-15042021', '1145-16042021', '1200-15042021', '1200-16042021', '1215-15042021', '1215-16042021', '1230-15042021', '1230-16042021', '1245-15042021', '1245-16042021', '1300-15042021', '1300-16042021', '1315-15042021', '1315-16042021', '1330-15042021', '1330-16042021', '1345-15042021', '1345-16042021', '1400-15042021', '1400-16042021', '1415-15042021', '1415-16042021', '1430-15042021', '1430-16042021', '1445-15042021', '1445-16042021', '1500-15042021', '1500-16042021', '1515-15042021', '1515-16042021', '1530-15042021', '1530-16042021', '1545-15042021', '1545-16042021', '1600-15042021', '1600-16042021', '1615-15042021', '1615-16042021', '1630-15042021', '1630-16042021', '1645-15042021', '1645-16042021'] },
|
||||||
]}
|
]}
|
||||||
timezone="UTC"
|
table={table}
|
||||||
/>
|
/>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,24 @@
|
||||||
import { Fragment, useCallback, useMemo, useRef, useState } from 'react'
|
import { Fragment, useCallback, useRef, useState } from 'react'
|
||||||
|
|
||||||
import Content from '/src/components/Content/Content'
|
import Content from '/src/components/Content/Content'
|
||||||
import GoogleCalendar from '/src/components/GoogleCalendar/GoogleCalendar'
|
import GoogleCalendar from '/src/components/GoogleCalendar/GoogleCalendar'
|
||||||
import { usePalette } from '/src/hooks/usePalette'
|
import { usePalette } from '/src/hooks/usePalette'
|
||||||
import { useTranslation } from '/src/i18n/client'
|
import { useTranslation } from '/src/i18n/client'
|
||||||
import { useStore } from '/src/stores'
|
|
||||||
import useSettingsStore from '/src/stores/settingsStore'
|
|
||||||
import { calculateTable, makeClass, parseSpecificDate } from '/src/utils'
|
import { calculateTable, makeClass, parseSpecificDate } from '/src/utils'
|
||||||
|
|
||||||
import styles from '../AvailabilityViewer/AvailabilityViewer.module.scss'
|
import styles from '../AvailabilityViewer/AvailabilityViewer.module.scss'
|
||||||
|
import Skeleton from '../AvailabilityViewer/components/Skeleton/Skeleton'
|
||||||
|
|
||||||
interface AvailabilityEditorProps {
|
interface AvailabilityEditorProps {
|
||||||
times: string[]
|
times: string[]
|
||||||
timezone: string
|
timezone: string
|
||||||
value: string[]
|
value: string[]
|
||||||
onChange: (value: string[]) => void
|
onChange: (value: string[]) => void
|
||||||
|
table?: ReturnType<typeof calculateTable>
|
||||||
}
|
}
|
||||||
|
|
||||||
const AvailabilityEditor = ({
|
const AvailabilityEditor = ({ times, timezone, value = [], onChange, table }: AvailabilityEditorProps) => {
|
||||||
times,
|
const { t } = useTranslation('event')
|
||||||
timezone,
|
|
||||||
value = [],
|
|
||||||
onChange,
|
|
||||||
}: AvailabilityEditorProps) => {
|
|
||||||
const { t, i18n } = useTranslation('event')
|
|
||||||
|
|
||||||
const timeFormat = useStore(useSettingsStore, state => state.timeFormat) ?? '12h'
|
|
||||||
|
|
||||||
// Calculate table
|
|
||||||
const { rows, columns } = useMemo(() =>
|
|
||||||
calculateTable(times, i18n.language, timeFormat, timezone),
|
|
||||||
[times, i18n.language, timeFormat, timezone])
|
|
||||||
|
|
||||||
// Ref and state required to rerender but also access static version in callbacks
|
// Ref and state required to rerender but also access static version in callbacks
|
||||||
const selectingRef = useRef<string[]>([])
|
const selectingRef = useRef<string[]>([])
|
||||||
|
|
@ -64,24 +52,24 @@ const AvailabilityEditor = ({
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.heatmap}>
|
<div className={styles.heatmap}>
|
||||||
<div className={styles.timeLabels}>
|
<div className={styles.timeLabels}>
|
||||||
{rows.map((row, i) =>
|
{table?.rows.map((row, i) =>
|
||||||
<div className={styles.timeSpace} key={i}>
|
<div className={styles.timeSpace} key={i}>
|
||||||
{row && <label className={styles.timeLabel}>
|
{row && <label className={styles.timeLabel}>
|
||||||
{row.label}
|
{row.label}
|
||||||
</label>}
|
</label>}
|
||||||
</div>
|
</div>
|
||||||
)}
|
) ?? null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{columns.map((column, x) => <Fragment key={x}>
|
{table?.columns.map((column, x) => <Fragment key={x}>
|
||||||
{column ? <div className={styles.dateColumn}>
|
{column ? <div className={styles.dateColumn}>
|
||||||
{column.header.dateLabel && <label className={styles.dateLabel}>{column.header.dateLabel}</label>}
|
{column.header.dateLabel && <label className={styles.dateLabel}>{column.header.dateLabel}</label>}
|
||||||
<label className={styles.dayLabel}>{column.header.weekdayLabel}</label>
|
<label className={styles.dayLabel}>{column.header.weekdayLabel}</label>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={styles.times}
|
className={styles.times}
|
||||||
data-border-left={x === 0 || columns.at(x - 1) === null}
|
data-border-left={x === 0 || table.columns.at(x - 1) === null}
|
||||||
data-border-right={x === columns.length - 1 || columns.at(x + 1) === null}
|
data-border-right={x === table.columns.length - 1 || table.columns.at(x + 1) === null}
|
||||||
>
|
>
|
||||||
{column.cells.map((cell, y) => {
|
{column.cells.map((cell, y) => {
|
||||||
if (y === column.cells.length - 1) return null
|
if (y === column.cells.length - 1) return null
|
||||||
|
|
@ -132,7 +120,7 @@ const AvailabilityEditor = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setSelecting(found.flatMap(d => {
|
setSelecting(found.flatMap(d => {
|
||||||
const serialized = columns[d.x]?.cells[d.y]?.serialized
|
const serialized = table.columns[d.x]?.cells[d.y]?.serialized
|
||||||
if (serialized && times.includes(serialized)) {
|
if (serialized && times.includes(serialized)) {
|
||||||
return [serialized]
|
return [serialized]
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +132,7 @@ const AvailabilityEditor = ({
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div> : <div className={styles.columnSpacer} />}
|
</div> : <div className={styles.columnSpacer} />}
|
||||||
</Fragment>)}
|
</Fragment>) ?? <Skeleton isSpecificDates={times[0].length === 13} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,17 @@ import useSettingsStore from '/src/stores/settingsStore'
|
||||||
import { calculateAvailability, calculateTable, makeClass, relativeTimeFormat } from '/src/utils'
|
import { calculateAvailability, calculateTable, makeClass, relativeTimeFormat } from '/src/utils'
|
||||||
|
|
||||||
import styles from './AvailabilityViewer.module.scss'
|
import styles from './AvailabilityViewer.module.scss'
|
||||||
|
import Skeleton from './components/Skeleton/Skeleton'
|
||||||
|
|
||||||
interface AvailabilityViewerProps {
|
interface AvailabilityViewerProps {
|
||||||
times: string[]
|
times: string[]
|
||||||
timezone: string
|
|
||||||
people: PersonResponse[]
|
people: PersonResponse[]
|
||||||
|
table?: ReturnType<typeof calculateTable>
|
||||||
}
|
}
|
||||||
|
|
||||||
const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps) => {
|
const AvailabilityViewer = ({ times, people, table }: AvailabilityViewerProps) => {
|
||||||
const { t, i18n } = useTranslation('event')
|
const { t, i18n } = useTranslation('event')
|
||||||
|
|
||||||
const timeFormat = useStore(useSettingsStore, state => state.timeFormat) ?? '12h'
|
|
||||||
const highlight = useStore(useSettingsStore, state => state.highlight)
|
const highlight = useStore(useSettingsStore, state => state.highlight)
|
||||||
const [filteredPeople, setFilteredPeople] = useState(people.map(p => p.name))
|
const [filteredPeople, setFilteredPeople] = useState(people.map(p => p.name))
|
||||||
const [tempFocus, setTempFocus] = useState<string>()
|
const [tempFocus, setTempFocus] = useState<string>()
|
||||||
|
|
@ -38,11 +38,6 @@ const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps
|
||||||
people: string[]
|
people: string[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// Calculate table
|
|
||||||
const { rows, columns } = useMemo(() =>
|
|
||||||
calculateTable(times, i18n.language, timeFormat, timezone),
|
|
||||||
[times, i18n.language, timeFormat, timezone])
|
|
||||||
|
|
||||||
// Calculate availabilities
|
// Calculate availabilities
|
||||||
const { availabilities, min, max } = useMemo(() =>
|
const { availabilities, min, max } = useMemo(() =>
|
||||||
calculateAvailability(times, people.filter(p => filteredPeople.includes(p.name))),
|
calculateAvailability(times, people.filter(p => filteredPeople.includes(p.name))),
|
||||||
|
|
@ -56,15 +51,15 @@ const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps
|
||||||
setFilteredPeople(people.map(p => p.name))
|
setFilteredPeople(people.map(p => p.name))
|
||||||
}, [people.length])
|
}, [people.length])
|
||||||
|
|
||||||
const heatmap = useMemo(() => columns.map((column, x) => <Fragment key={x}>
|
const heatmap = useMemo(() => table?.columns.map((column, x) => <Fragment key={x}>
|
||||||
{column ? <div className={styles.dateColumn}>
|
{column ? <div className={styles.dateColumn}>
|
||||||
{column.header.dateLabel && <label className={styles.dateLabel}>{column.header.dateLabel}</label>}
|
{column.header.dateLabel && <label className={styles.dateLabel}>{column.header.dateLabel}</label>}
|
||||||
<label className={styles.dayLabel}>{column.header.weekdayLabel}</label>
|
<label className={styles.dayLabel}>{column.header.weekdayLabel}</label>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={styles.times}
|
className={styles.times}
|
||||||
data-border-left={x === 0 || columns.at(x - 1) === null}
|
data-border-left={x === 0 || table.columns.at(x - 1) === null}
|
||||||
data-border-right={x === columns.length - 1 || columns.at(x + 1) === null}
|
data-border-right={x === table.columns.length - 1 || table.columns.at(x + 1) === null}
|
||||||
>
|
>
|
||||||
{column.cells.map((cell, y) => {
|
{column.cells.map((cell, y) => {
|
||||||
if (y === column.cells.length - 1) return null
|
if (y === column.cells.length - 1) return null
|
||||||
|
|
@ -110,9 +105,9 @@ const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div> : <div className={styles.columnSpacer} />}
|
</div> : <div className={styles.columnSpacer} />}
|
||||||
</Fragment>), [
|
</Fragment>) ?? <Skeleton isSpecificDates={times[0].length === 13} />, [
|
||||||
availabilities,
|
availabilities,
|
||||||
columns,
|
table?.columns,
|
||||||
highlight,
|
highlight,
|
||||||
max,
|
max,
|
||||||
min,
|
min,
|
||||||
|
|
@ -167,14 +162,14 @@ const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.heatmap}>
|
<div className={styles.heatmap}>
|
||||||
{useMemo(() => <div className={styles.timeLabels}>
|
{useMemo(() => <div className={styles.timeLabels}>
|
||||||
{rows.map((row, i) =>
|
{table?.rows.map((row, i) =>
|
||||||
<div className={styles.timeSpace} key={i}>
|
<div className={styles.timeSpace} key={i}>
|
||||||
{row && <label className={styles.timeLabel}>
|
{row && <label className={styles.timeLabel}>
|
||||||
{row.label}
|
{row.label}
|
||||||
</label>}
|
</label>}
|
||||||
</div>
|
</div>
|
||||||
)}
|
) ?? null}
|
||||||
</div>, [rows])}
|
</div>, [table?.rows])}
|
||||||
|
|
||||||
{heatmap}
|
{heatmap}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
.skeleton {
|
||||||
|
opacity: .5;
|
||||||
|
|
||||||
|
& > div:last-of-type {
|
||||||
|
height: 382px;
|
||||||
|
width: 300px;
|
||||||
|
border: 2px solid currentColor;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-block: 2px 10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dayLabels {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
|
||||||
|
span {
|
||||||
|
height: .9em;
|
||||||
|
display: block;
|
||||||
|
width: 3ch;
|
||||||
|
background: currentColor;
|
||||||
|
border-radius: .2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dateLabels {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-block-end: 3px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 5ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { makeClass } from '/src/utils'
|
||||||
|
|
||||||
|
import styles from './Skeleton.module.scss'
|
||||||
|
|
||||||
|
interface SkeletonProps {
|
||||||
|
isSpecificDates?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const Skeleton = ({ isSpecificDates }: SkeletonProps) => <div className={styles.skeleton}>
|
||||||
|
{isSpecificDates ? <div className={makeClass(styles.dayLabels, styles.dateLabels)}>{Array.from({ length: 5 }).map((_, i) => <span key={i} />)}</div> : null}
|
||||||
|
<div className={styles.dayLabels}>{Array.from({ length: 5 }).map((_, i) => <span key={i} />)}</div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
export default Skeleton
|
||||||
|
|
@ -108,7 +108,7 @@ const Handle = ({ value, onChange, labelPadding }: HandleProps) => {
|
||||||
left: `calc(${value * 4.166}% - 11px)`,
|
left: `calc(${value * 4.166}% - 11px)`,
|
||||||
'--extra-padding': labelPadding,
|
'--extra-padding': labelPadding,
|
||||||
} as React.CSSProperties}
|
} as React.CSSProperties}
|
||||||
data-label={Temporal.PlainTime.from({ hour: Number(times[value] === '24' ? '00' : times[value]) }).toLocaleString(i18n.language, { hour: 'numeric', hour12: timeFormat === '12h' })}
|
data-label={Temporal.PlainTime.from({ hour: Number(times[value] === '24' ? '00' : times[value]) }).toLocaleString(i18n.language, { hour: 'numeric', hourCycle: timeFormat === '12h' ? 'h12' : 'h24' })}
|
||||||
onMouseDown={() => {
|
onMouseDown={() => {
|
||||||
document.addEventListener('mousemove', handleMouseMove)
|
document.addEventListener('mousemove', handleMouseMove)
|
||||||
isMoving.current = true
|
isMoving.current = true
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,24 @@ import { calculateRows } from '/src/utils/calculateRows'
|
||||||
import { convertTimesToDates } from '/src/utils/convertTimesToDates'
|
import { convertTimesToDates } from '/src/utils/convertTimesToDates'
|
||||||
import { serializeTime } from '/src/utils/serializeTime'
|
import { serializeTime } from '/src/utils/serializeTime'
|
||||||
|
|
||||||
|
export interface CalculateTableArgs {
|
||||||
|
/** As `HHmm-DDMMYYYY` or `HHmm-d` strings */
|
||||||
|
times: string[]
|
||||||
|
locale: string
|
||||||
|
timeFormat: '12h' | '24h'
|
||||||
|
timezone: string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take rows and columns and turn them into a data structure representing an availability table
|
* Take rows and columns and turn them into a data structure representing an availability table
|
||||||
*/
|
*/
|
||||||
export const calculateTable = (
|
export const calculateTable = ({
|
||||||
/** As `HHmm-DDMMYYYY` or `HHmm-d` strings */
|
/** As `HHmm-DDMMYYYY` or `HHmm-d` strings */
|
||||||
times: string[],
|
times,
|
||||||
locale: string,
|
locale,
|
||||||
timeFormat: '12h' | '24h',
|
timeFormat,
|
||||||
timezone: string,
|
timezone,
|
||||||
) => {
|
}: CalculateTableArgs) => {
|
||||||
const dates = convertTimesToDates(times, timezone)
|
const dates = convertTimesToDates(times, timezone)
|
||||||
const rows = calculateRows(dates)
|
const rows = calculateRows(dates)
|
||||||
const columns = calculateColumns(dates)
|
const columns = calculateColumns(dates)
|
||||||
|
|
@ -22,7 +30,7 @@ export const calculateTable = (
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: rows.map(row => row && row.minute === 0 ? {
|
rows: rows.map(row => row && row.minute === 0 ? {
|
||||||
label: row.toLocaleString(locale, { hour: 'numeric', hour12: timeFormat === '12h' }),
|
label: row.toLocaleString(locale, { hour: 'numeric', hourCycle: timeFormat === '12h' ? 'h12' : 'h24' }),
|
||||||
string: row.toString(),
|
string: row.toString(),
|
||||||
} : null),
|
} : null),
|
||||||
|
|
||||||
|
|
@ -44,8 +52,8 @@ export const calculateTable = (
|
||||||
serialized,
|
serialized,
|
||||||
minute: date.minute,
|
minute: date.minute,
|
||||||
label: isSpecificDates
|
label: isSpecificDates
|
||||||
? date.toLocaleString(locale, { dateStyle: 'long', timeStyle: 'short', hour12: timeFormat === '12h' })
|
? date.toLocaleString(locale, { dateStyle: 'long', timeStyle: 'short', hourCycle: timeFormat === '12h' ? 'h12' : 'h24' })
|
||||||
: `${date.toLocaleString(locale, { timeStyle: 'short', hour12: timeFormat === '12h' })}, ${date.toLocaleString(locale, { weekday: 'long' })}`,
|
: `${date.toLocaleString(locale, { timeStyle: 'short', hourCycle: timeFormat === '12h' ? 'h12' : 'h24' })}, ${date.toLocaleString(locale, { weekday: 'long' })}`,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} : null)
|
} : null)
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,7 @@ const parseWeekdayDate = (str: string): Temporal.ZonedDateTime => {
|
||||||
|
|
||||||
// Extract values
|
// Extract values
|
||||||
const [hour, minute] = [Number(str.substring(0, 2)), Number(str.substring(2, 4))]
|
const [hour, minute] = [Number(str.substring(0, 2)), Number(str.substring(2, 4))]
|
||||||
let dayOfWeek = Number(str.substring(5))
|
const dayOfWeek = Number(str.substring(5))
|
||||||
if (dayOfWeek === 0) {
|
|
||||||
dayOfWeek = 7 // Sunday is 7 in ISO8601
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct PlainDateTime from today
|
// Construct PlainDateTime from today
|
||||||
const today = Temporal.Now.zonedDateTimeISO('UTC').round('day')
|
const today = Temporal.Now.zonedDateTimeISO('UTC').round('day')
|
||||||
|
|
|
||||||
5
frontend/src/workers/calculateTable.ts
Normal file
5
frontend/src/workers/calculateTable.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { calculateTable, CalculateTableArgs } from '/src/utils'
|
||||||
|
|
||||||
|
self.onmessage = (e: MessageEvent<CalculateTableArgs>) => {
|
||||||
|
self.postMessage(calculateTable(e.data))
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue