Finish migration of Help page to Nextjs
This commit is contained in:
parent
756b71433c
commit
041256d5f8
|
|
@ -1,7 +1,7 @@
|
||||||
import { Trans } from 'react-i18next/TransWithoutContext'
|
import { Trans } from 'react-i18next/TransWithoutContext'
|
||||||
import { Metadata } from 'next'
|
import { Metadata } from 'next'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { range } from '@giraugh/tools'
|
import { range, rotateArray } from '@giraugh/tools'
|
||||||
|
|
||||||
import AvailabilityViewer from '/src/components/AvailabilityViewer/AvailabilityViewer'
|
import AvailabilityViewer from '/src/components/AvailabilityViewer/AvailabilityViewer'
|
||||||
import Button from '/src/components/Button/Button'
|
import Button from '/src/components/Button/Button'
|
||||||
|
|
@ -12,6 +12,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 styles from './page.module.scss'
|
import styles from './page.module.scss'
|
||||||
|
|
||||||
|
|
@ -42,7 +43,7 @@ const Page = async () => {
|
||||||
<P><Trans i18nKey="help:p3" t={t} i18n={i18n}>_<Link href="/">_</Link>_</Trans></P>
|
<P><Trans i18nKey="help:p3" t={t} i18n={i18n}>_<Link href="/">_</Link>_</Trans></P>
|
||||||
<P>{t('help:p4')}</P>
|
<P>{t('help:p4')}</P>
|
||||||
<div className={styles.fakeCalendar}>
|
<div className={styles.fakeCalendar}>
|
||||||
<div>{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(d => <span key={d}>{d}</span>)}</div>
|
<div>{rotateArray(getWeekdayNames(i18n.language, 'short')).map(d => <span key={d}>{d}</span>)}</div>
|
||||||
<div>{range(11, 17).map(d => <span key={d}>{d}</span>)}</div>
|
<div>{range(11, 17).map(d => <span key={d}>{d}</span>)}</div>
|
||||||
</div>
|
</div>
|
||||||
<P>{t('help:p5')}</P>
|
<P>{t('help:p5')}</P>
|
||||||
|
|
@ -61,6 +62,17 @@ const Page = async () => {
|
||||||
<P>{t('help:p8')}</P>
|
<P>{t('help:p8')}</P>
|
||||||
<P>{t('help:p9')}</P>
|
<P>{t('help:p9')}</P>
|
||||||
<P>{t('help:p10')}</P>
|
<P>{t('help:p10')}</P>
|
||||||
|
<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']}
|
||||||
|
people={[
|
||||||
|
{ name: 'Jenny', created_at: 0, 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: 0, 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: 'Samson', created_at: 0, availability: ['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'] },
|
||||||
|
{ name: 'Mark', created_at: 0, 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: 0, 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"
|
||||||
|
/>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
<Section>
|
<Section>
|
||||||
|
|
|
||||||
|
|
@ -167,3 +167,45 @@
|
||||||
width: 12px;
|
width: 12px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
position: absolute;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border: 1px solid var(--text);
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
background-color: var(--background);
|
||||||
|
max-width: 200px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 100;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 15px;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
& > span {
|
||||||
|
font-size: 13px;
|
||||||
|
display: block;
|
||||||
|
opacity: .8;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
& > div {
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 4px 0;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 2px;
|
||||||
|
padding: 1px 4px;
|
||||||
|
border: 1px solid var(--primary);
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
&[data-disabled=true] {
|
||||||
|
opacity: .5;
|
||||||
|
border-color: var(--text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import { styled } from 'goober'
|
|
||||||
|
|
||||||
export const Tooltip = styled('div')`
|
|
||||||
position: absolute;
|
|
||||||
top: ${props => props.$y}px;
|
|
||||||
left: ${props => props.$x}px;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
border: 1px solid var(--text);
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 4px 8px;
|
|
||||||
background-color: var(--background);
|
|
||||||
max-width: 200px;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 100;
|
|
||||||
user-select: none;
|
|
||||||
`
|
|
||||||
|
|
||||||
export const TooltipTitle = styled('span')`
|
|
||||||
font-size: 15px;
|
|
||||||
display: block;
|
|
||||||
font-weight: 700;
|
|
||||||
`
|
|
||||||
|
|
||||||
export const TooltipDate = styled('span')`
|
|
||||||
font-size: 13px;
|
|
||||||
display: block;
|
|
||||||
opacity: .8;
|
|
||||||
font-weight: 600;
|
|
||||||
`
|
|
||||||
|
|
||||||
export const TooltipContent = styled('div')`
|
|
||||||
font-size: 13px;
|
|
||||||
padding: 4px 0;
|
|
||||||
`
|
|
||||||
|
|
||||||
export const TooltipPerson = styled('span')`
|
|
||||||
display: inline-block;
|
|
||||||
margin: 2px;
|
|
||||||
padding: 1px 4px;
|
|
||||||
border: 1px solid var(--primary);
|
|
||||||
border-radius: 3px;
|
|
||||||
|
|
||||||
${props => props.disabled && `
|
|
||||||
opacity: .5;
|
|
||||||
border-color: var(--text);
|
|
||||||
`}
|
|
||||||
`
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { Fragment, useEffect, useMemo, useState } from 'react'
|
import { Fragment, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Temporal } from '@js-temporal/polyfill'
|
import { Temporal } from '@js-temporal/polyfill'
|
||||||
import { createPalette } from 'hue-map'
|
import { createPalette } from 'hue-map'
|
||||||
|
|
||||||
|
|
@ -23,15 +23,21 @@ interface AvailabilityViewerProps {
|
||||||
const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps) => {
|
const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps) => {
|
||||||
const { t, i18n } = useTranslation('event')
|
const { t, i18n } = useTranslation('event')
|
||||||
|
|
||||||
// const [tooltip, setTooltip] = useState(null)
|
|
||||||
const timeFormat = useStore(useSettingsStore, state => state.timeFormat)
|
const timeFormat = useStore(useSettingsStore, state => state.timeFormat)
|
||||||
const highlight = useStore(useSettingsStore, state => state.highlight)
|
const highlight = useStore(useSettingsStore, state => state.highlight)
|
||||||
const colormap = useStore(useSettingsStore, state => state.colormap)
|
const colormap = useStore(useSettingsStore, state => state.colormap)
|
||||||
const [filteredPeople, setFilteredPeople] = useState(people.map(p => p.name))
|
const [filteredPeople, setFilteredPeople] = useState(people.map(p => p.name))
|
||||||
// const [tempFocus, setTempFocus] = useState(null)
|
const [tempFocus, setTempFocus] = useState<string>()
|
||||||
// const [focusCount, setFocusCount] = useState(null)
|
const [focusCount, setFocusCount] = useState<number>()
|
||||||
|
|
||||||
// const wrapper = useRef()
|
const wrapperRef = useRef<HTMLDivElement>(null)
|
||||||
|
const [tooltip, setTooltip] = useState<{
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
available: string
|
||||||
|
date: string
|
||||||
|
people: string[]
|
||||||
|
}>()
|
||||||
|
|
||||||
// Calculate rows and columns
|
// Calculate rows and columns
|
||||||
const [dates, rows, columns] = useMemo(() => {
|
const [dates, rows, columns] = useMemo(() => {
|
||||||
|
|
@ -56,7 +62,7 @@ const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPalette(createPalette({
|
setPalette(createPalette({
|
||||||
map: colormap !== 'crabfit' ? colormap : [[0, [247, 158, 0, 0]], [1, [247, 158, 0, 255]]],
|
map: colormap !== 'crabfit' ? colormap : [[0, [247, 158, 0, 0]], [1, [247, 158, 0, 255]]],
|
||||||
steps: (max - min) + 1,
|
steps: Math.max((max - min) + 1, 2),
|
||||||
}).format())
|
}).format())
|
||||||
}, [min, max, colormap])
|
}, [min, max, colormap])
|
||||||
|
|
||||||
|
|
@ -94,35 +100,37 @@ const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps
|
||||||
}
|
}
|
||||||
|
|
||||||
const date = column.toZonedDateTime({ timeZone: timezone, plainTime: row })
|
const date = column.toZonedDateTime({ timeZone: timezone, plainTime: row })
|
||||||
const peopleHere = availabilities.find(a => a.date.equals(date))?.people ?? []
|
let peopleHere = availabilities.find(a => a.date.equals(date))?.people ?? []
|
||||||
|
if (tempFocus) {
|
||||||
|
peopleHere = peopleHere.filter(p => p === tempFocus)
|
||||||
|
}
|
||||||
|
|
||||||
return <div
|
return <div
|
||||||
key={i}
|
key={i}
|
||||||
className={makeClass(
|
className={makeClass(
|
||||||
styles.time,
|
styles.time,
|
||||||
highlight && peopleHere.length === max && peopleHere.length > 0 && styles.highlight,
|
(focusCount === undefined || focusCount === peopleHere.length) && highlight && (peopleHere.length === max || tempFocus) && peopleHere.length > 0 && styles.highlight,
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: palette[peopleHere.length],
|
backgroundColor: (focusCount === undefined || focusCount === peopleHere.length) ? palette[tempFocus && peopleHere.length ? max : peopleHere.length] : 'transparent',
|
||||||
...date.minute !== 0 && date.minute !== 30 && { borderTopColor: 'transparent' },
|
...date.minute !== 0 && date.minute !== 30 && { borderTopColor: 'transparent' },
|
||||||
...date.minute === 30 && { borderTopStyle: 'dotted' },
|
...date.minute === 30 && { borderTopStyle: 'dotted' },
|
||||||
}}
|
}}
|
||||||
aria-label={peopleHere.join(', ')}
|
aria-label={peopleHere.join(', ')}
|
||||||
// onMouseEnter={e => {
|
onMouseEnter={e => {
|
||||||
// const cellBox = e.currentTarget.getBoundingClientRect()
|
const cellBox = e.currentTarget.getBoundingClientRect()
|
||||||
// const wrapperBox = wrapper?.current?.getBoundingClientRect() ?? { x: 0, y: 0 }
|
const wrapperBox = wrapperRef.current?.getBoundingClientRect() ?? { x: 0, y: 0 }
|
||||||
// const timeText = timeFormat === '12h' ? `h${locales[locale]?.separator ?? ':'}mma` : `HH${locales[locale]?.separator ?? ':'}mm`
|
setTooltip({
|
||||||
// setTooltip({
|
x: Math.round(cellBox.x - wrapperBox.x + cellBox.width / 2),
|
||||||
// x: Math.round(cellBox.x - wrapperBox.x + cellBox.width / 2),
|
y: Math.round(cellBox.y - wrapperBox.y + cellBox.height) + 6,
|
||||||
// y: Math.round(cellBox.y - wrapperBox.y + cellBox.height) + 6,
|
available: `${peopleHere.length} / ${filteredPeople.length} ${t('available')}`,
|
||||||
// available: `${peopleHere.length} / ${filteredPeople.length} ${t('event:available')}`,
|
date: isSpecificDates
|
||||||
// date: parsedDate.hour(time.slice(0, 2)).minute(time.slice(2, 4)).format(isSpecificDates ? `${timeText} ddd, D MMM YYYY` : `${timeText} ddd`),
|
? date.toLocaleString(i18n.language, { dateStyle: 'long', timeStyle: 'short', hour12: timeFormat === '12h' })
|
||||||
// people: peopleHere,
|
: `${date.toLocaleString(i18n.language, { timeStyle: 'short', hour12: timeFormat === '12h' })}, ${date.toLocaleString(i18n.language, { weekday: 'long' })}`,
|
||||||
// })
|
people: peopleHere,
|
||||||
// }}
|
})
|
||||||
// onMouseLeave={() => {
|
}}
|
||||||
// setTooltip(null)
|
onMouseLeave={() => setTooltip(undefined)}
|
||||||
// }}
|
|
||||||
/>
|
/>
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -140,6 +148,11 @@ const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps
|
||||||
t,
|
t,
|
||||||
timeFormat,
|
timeFormat,
|
||||||
palette,
|
palette,
|
||||||
|
tempFocus,
|
||||||
|
focusCount,
|
||||||
|
filteredPeople,
|
||||||
|
i18n.language,
|
||||||
|
timezone,
|
||||||
])
|
])
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
|
|
@ -149,7 +162,7 @@ const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps
|
||||||
max={max}
|
max={max}
|
||||||
total={filteredPeople.length}
|
total={filteredPeople.length}
|
||||||
palette={palette}
|
palette={palette}
|
||||||
onSegmentFocus={console.log}
|
onSegmentFocus={setFocusCount}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span className={styles.info}>{t('group.info1')}</span>
|
<span className={styles.info}>{t('group.info1')}</span>
|
||||||
|
|
@ -165,21 +178,16 @@ const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps
|
||||||
filteredPeople.includes(person.name) && styles.personSelected,
|
filteredPeople.includes(person.name) && styles.personSelected,
|
||||||
)}
|
)}
|
||||||
key={person.name}
|
key={person.name}
|
||||||
// onClick={() => {
|
onClick={() => {
|
||||||
// setTempFocus(null)
|
setTempFocus(undefined)
|
||||||
// if (filteredPeople.includes(person.name)) {
|
if (filteredPeople.includes(person.name)) {
|
||||||
// if (!touched) {
|
setFilteredPeople(filteredPeople.filter(n => n !== person.name))
|
||||||
// setTouched(true)
|
} else {
|
||||||
// setFilteredPeople([person.name])
|
setFilteredPeople([...filteredPeople, person.name])
|
||||||
// } else {
|
}
|
||||||
// setFilteredPeople(filteredPeople.filter(n => n !== person.name))
|
}}
|
||||||
// }
|
onMouseOver={() => setTempFocus(person.name)}
|
||||||
// } else {
|
onMouseOut={() => setTempFocus(undefined)}
|
||||||
// setFilteredPeople([...filteredPeople, person.name])
|
|
||||||
// }
|
|
||||||
// }}
|
|
||||||
// onMouseOver={() => setTempFocus(person.name)}
|
|
||||||
// onMouseOut={() => setTempFocus(null)}
|
|
||||||
title={Temporal.Instant.fromEpochSeconds(person.created_at).until(Temporal.Now.instant()).toLocaleString()}
|
title={Temporal.Instant.fromEpochSeconds(person.created_at).until(Temporal.Now.instant()).toLocaleString()}
|
||||||
>{person.name}</button>
|
>{person.name}</button>
|
||||||
)}
|
)}
|
||||||
|
|
@ -187,29 +195,23 @@ const AvailabilityViewer = ({ times, timezone, people }: AvailabilityViewerProps
|
||||||
</>}
|
</>}
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper} ref={wrapperRef}>
|
||||||
<div>
|
<div>
|
||||||
{heatmap}
|
{heatmap}
|
||||||
|
|
||||||
{/* {tooltip && (
|
{tooltip && <div
|
||||||
<Tooltip
|
className={styles.tooltip}
|
||||||
$x={tooltip.x}
|
style={{ top: tooltip.y, left: tooltip.x }}
|
||||||
$y={tooltip.y}
|
>
|
||||||
>
|
<h3>{tooltip.available}</h3>
|
||||||
<TooltipTitle>{tooltip.available}</TooltipTitle>
|
<span>{tooltip.date}</span>
|
||||||
<TooltipDate>{tooltip.date}</TooltipDate>
|
{!!filteredPeople.length && <div>
|
||||||
{!!filteredPeople.length && (
|
{tooltip.people.map(person => <span key={person}>{person}</span>)}
|
||||||
<TooltipContent>
|
{filteredPeople.filter(p => !tooltip.people.includes(p)).map(person =>
|
||||||
{tooltip.people.map(person =>
|
<span key={person} data-disabled>{person}</span>
|
||||||
<TooltipPerson key={person}>{person}</TooltipPerson>
|
|
||||||
)}
|
|
||||||
{filteredPeople.filter(p => !tooltip.people.includes(p)).map(person =>
|
|
||||||
<TooltipPerson key={person} disabled>{person}</TooltipPerson>
|
|
||||||
)}
|
|
||||||
</TooltipContent>
|
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</div>}
|
||||||
)} */}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ interface LegendProps {
|
||||||
const Legend = ({ min, max, total, palette, onSegmentFocus }: LegendProps) => {
|
const Legend = ({ min, max, total, palette, onSegmentFocus }: LegendProps) => {
|
||||||
const { t } = useTranslation('event')
|
const { t } = useTranslation('event')
|
||||||
const highlight = useStore(useSettingsStore, state => state.highlight)
|
const highlight = useStore(useSettingsStore, state => state.highlight)
|
||||||
const setHighlight = useStore(useSettingsStore, state => state.setHighlight)
|
const setHighlight = useSettingsStore(state => state.setHighlight)
|
||||||
|
|
||||||
return <div className={styles.wrapper}>
|
return <div className={styles.wrapper}>
|
||||||
<label className={styles.label}>{min}/{total} {t('available')}</label>
|
<label className={styles.label}>{min}/{total} {t('available')}</label>
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,116 +0,0 @@
|
||||||
import { styled } from 'goober'
|
|
||||||
|
|
||||||
export const Step = styled('h2')`
|
|
||||||
text-decoration-color: var(--primary);
|
|
||||||
text-decoration-style: solid;
|
|
||||||
text-decoration-line: underline;
|
|
||||||
margin-top: 30px;
|
|
||||||
`
|
|
||||||
|
|
||||||
export const FakeCalendar = styled('div')`
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
& div {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(7, 1fr);
|
|
||||||
grid-gap: 2px;
|
|
||||||
}
|
|
||||||
& .days span {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 3px 0;
|
|
||||||
font-weight: bold;
|
|
||||||
user-select: none;
|
|
||||||
opacity: .7;
|
|
||||||
@media (max-width: 350px) {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& .dates span {
|
|
||||||
background-color: var(--surface);
|
|
||||||
border: 1px solid var(--primary);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
color: #FFF;
|
|
||||||
background-color: var(--primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& .dates span:first-of-type {
|
|
||||||
border-start-start-radius: 3px;
|
|
||||||
border-end-start-radius: 3px;
|
|
||||||
}
|
|
||||||
& .dates span:last-of-type {
|
|
||||||
border-end-end-radius: 3px;
|
|
||||||
border-start-end-radius: 3px;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export const FakeTimeRange = styled('div')`
|
|
||||||
user-select: none;
|
|
||||||
background-color: var(--surface);
|
|
||||||
border: 1px solid var(--primary);
|
|
||||||
border-radius: 3px;
|
|
||||||
height: 50px;
|
|
||||||
position: relative;
|
|
||||||
margin: 38px 6px 18px;
|
|
||||||
|
|
||||||
& div {
|
|
||||||
height: calc(100% + 20px);
|
|
||||||
width: 20px;
|
|
||||||
border: 1px solid var(--primary);
|
|
||||||
background-color: var(--highlight);
|
|
||||||
border-radius: 3px;
|
|
||||||
position: absolute;
|
|
||||||
top: -10px;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: '|||';
|
|
||||||
font-size: 8px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: attr(data-label);
|
|
||||||
position: absolute;
|
|
||||||
bottom: calc(100% + 8px);
|
|
||||||
text-align: center;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& .start {
|
|
||||||
left: calc(${11 * 4.166}% - 11px);
|
|
||||||
}
|
|
||||||
& .end {
|
|
||||||
left: calc(${17 * 4.166}% - 11px);
|
|
||||||
}
|
|
||||||
&:before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
left: ${11 * 4.166}%;
|
|
||||||
right: calc(100% - ${17 * 4.166}%);
|
|
||||||
top: 0;
|
|
||||||
background-color: var(--primary);
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export const ButtonArea = styled('div')`
|
|
||||||
@media print {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
@ -20,6 +20,6 @@ export const calculateColumns = (dates: Temporal.ZonedDateTime[]): (Temporal.Pla
|
||||||
return partitionedDates.reduce((columns, partition, i) => [
|
return partitionedDates.reduce((columns, partition, i) => [
|
||||||
...columns,
|
...columns,
|
||||||
...partition,
|
...partition,
|
||||||
...partitionedDates.length - 1 < i ? [null] : [], // Add spacer in between partitions
|
...i < partitionedDates.length - 1 ? [null] : [], // Add spacer in between partitions
|
||||||
], [] as (Temporal.PlainDate | null)[])
|
], [] as (Temporal.PlainDate | null)[])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,6 @@ export const calculateRows = (dates: Temporal.ZonedDateTime[]): (Temporal.PlainT
|
||||||
...rows,
|
...rows,
|
||||||
...partition,
|
...partition,
|
||||||
partition[partition.length - 1].add({ minutes: 15 }),
|
partition[partition.length - 1].add({ minutes: 15 }),
|
||||||
...partitionedDates.length - 1 < i ? [null, null] : [], // Add spacer in between partitions
|
...i < partitionedDates.length - 1 ? [null, null] : [], // Add spacer in between partitions
|
||||||
], [] as (Temporal.PlainTime | null)[])
|
], [] as (Temporal.PlainTime | null)[])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
frontend/src/utils/getWeekdayNames.ts
Normal file
13
frontend/src/utils/getWeekdayNames.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Temporal } from '@js-temporal/polyfill'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the days of the week in a chosen locale.
|
||||||
|
* Note, this is brittle and will not work for additional calendars.
|
||||||
|
*/
|
||||||
|
export const getWeekdayNames = (locale: string, fmt: Intl.DateTimeFormatOptions['weekday'] = 'long') => {
|
||||||
|
// In ISO8601 (proleptic gregorian)
|
||||||
|
const knownMonday = Temporal.PlainDate.from({ year: 2023, month: 1, day: 2 })
|
||||||
|
|
||||||
|
return Array.from({ length: 7 })
|
||||||
|
.map((_, i) => knownMonday.add({ days: i }).toLocaleString(locale, { weekday: fmt }))
|
||||||
|
}
|
||||||
|
|
@ -5,3 +5,4 @@ export * from './convertTimesToDates'
|
||||||
export * from './calculateAvailability'
|
export * from './calculateAvailability'
|
||||||
export * from './calculateRows'
|
export * from './calculateRows'
|
||||||
export * from './calculateColumns'
|
export * from './calculateColumns'
|
||||||
|
export * from './getWeekdayNames'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue