Rename main folders and write sql backend adaptor

This commit is contained in:
Ben Grant 2023-05-11 17:04:17 +10:00
parent 1d34f8e06d
commit fdc58b428b
212 changed files with 3577 additions and 4775 deletions

View file

@ -0,0 +1,167 @@
import { useState, useEffect } from 'react'
import { loadGapiInsideDOM } from 'gapi-script'
import { useTranslation } from 'react-i18next'
import { Button, Center } from '/src/components'
import { Loader } from '../Loading/Loading.styles'
import {
CalendarList,
CheckboxInput,
CheckboxLabel,
CalendarLabel,
Info,
Options,
Title,
Icon,
LinkButton,
} from './GoogleCalendar.styles'
import googleLogo from '/src/res/google.svg'
const signIn = () => window.gapi.auth2.getAuthInstance().signIn()
const signOut = () => window.gapi.auth2.getAuthInstance().signOut()
const GoogleCalendar = ({ timeZone, timeMin, timeMax, onImport }) => {
const [signedIn, setSignedIn] = useState(undefined)
const [calendars, setCalendars] = useState(undefined)
const [freeBusyLoading, setFreeBusyLoading] = useState(false)
const { t } = useTranslation('event')
const calendarLogin = async () => {
const gapi = await loadGapiInsideDOM()
gapi.load('client:auth2', () => {
window.gapi.client.init({
clientId: '276505195333-9kjl7e48m272dljbspkobctqrpet0n8m.apps.googleusercontent.com',
discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest'],
scope: 'https://www.googleapis.com/auth/calendar.readonly',
})
.then(() => {
// Listen for state changes
window.gapi.auth2.getAuthInstance().isSignedIn.listen(isSignedIn => setSignedIn(isSignedIn))
// Handle initial sign-in state
setSignedIn(window.gapi.auth2.getAuthInstance().isSignedIn.get())
})
.catch(e => {
console.error(e)
setSignedIn(false)
})
})
}
const importAvailability = () => {
setFreeBusyLoading(true)
gtag('event', 'google_cal_sync', {
'event_category': 'event',
})
window.gapi.client.calendar.freebusy.query({
timeMin,
timeMax,
timeZone,
items: calendars.filter(c => c.checked).map(c => ({id: c.id})),
})
.then(response => {
onImport(response.result.calendars ? Object.values(response.result.calendars).reduce((busy, c) => [...busy, ...c.busy], []) : [])
setFreeBusyLoading(false)
}, e => {
console.error(e)
setFreeBusyLoading(false)
})
}
useEffect(() => void calendarLogin(), [])
useEffect(() => {
if (signedIn) {
window.gapi.client.calendar.calendarList.list({
'minAccessRole': 'freeBusyReader'
})
.then(response => {
setCalendars(response.result.items.map(item => ({
'name': item.summary,
'description': item.description,
'id': item.id,
'color': item.backgroundColor,
'checked': item.primary === true,
})))
})
.catch(e => {
console.error(e)
signOut()
})
}
}, [signedIn])
return (
<>
{!signedIn ? (
<Center>
<Button
onClick={() => signIn()}
isLoading={signedIn === undefined}
primaryColor="#4286F5"
secondaryColor="#3367BD"
icon={<img aria-hidden="true" focusable="false" src={googleLogo} alt="" />}
>
{t('event:you.google_cal.login')}
</Button>
</Center>
) : (
<CalendarList>
<Title>
<Icon src={googleLogo} alt="" />
<strong>{t('event:you.google_cal.login')}</strong>
(<LinkButton type="button" onClick={e => {
e.preventDefault()
signOut()
}}>{t('event:you.google_cal.logout')}</LinkButton>)
</Title>
<Options>
{calendars !== undefined && !calendars.every(c => c.checked) && (
<LinkButton type="button" onClick={e => {
e.preventDefault()
setCalendars(calendars.map(c => ({...c, checked: true})))
}}>{t('event:you.google_cal.select_all')}</LinkButton>
)}
{calendars !== undefined && calendars.every(c => c.checked) && (
<LinkButton type="button" onClick={e => {
e.preventDefault()
setCalendars(calendars.map(c => ({...c, checked: false})))
}}>{t('event:you.google_cal.select_none')}</LinkButton>
)}
</Options>
{calendars !== undefined ? calendars.map(calendar => (
<div key={calendar.id}>
<CheckboxInput
type="checkbox"
role="checkbox"
id={calendar.id}
color={calendar.color}
checked={calendar.checked}
onChange={() => setCalendars(calendars.map(c => c.id === calendar.id ? {...c, checked: !c.checked} : c))}
/>
<CheckboxLabel htmlFor={calendar.id} color={calendar.color} />
<CalendarLabel htmlFor={calendar.id}>{calendar.name}</CalendarLabel>
</div>
)) : (
<Loader />
)}
{calendars !== undefined && (
<>
<Info>{t('event:you.google_cal.info')}</Info>
<Button
small
isLoading={freeBusyLoading}
disabled={freeBusyLoading}
onClick={() => importAvailability()}
>{t('event:you.google_cal.button')}</Button>
</>
)}
</CalendarList>
)}
</>
)
}
export default GoogleCalendar

View file

@ -0,0 +1,122 @@
import { styled } from 'goober'
export const CalendarList = styled('div')`
width: 100%;
& > div {
display: flex;
margin: 2px 0;
}
`
export const CheckboxInput = styled('input')`
height: 0px;
width: 0px;
margin: 0;
padding: 0;
border: 0;
background: 0;
font-size: 0;
transform: scale(0);
position: absolute;
&:checked + label::after {
opacity: 1;
transform: scale(1);
}
&[disabled] + label {
opacity: .6;
}
&[disabled] + label:after {
border: 2px solid var(--text);
background-color: var(--text);
}
`
export const CheckboxLabel = styled('label')`
display: inline-block;
height: 24px;
width: 24px;
min-width: 24px;
position: relative;
border-radius: 3px;
transition: background-color 0.2s, box-shadow 0.2s;
&::before {
content: '';
display: inline-block;
height: 14px;
width: 14px;
border: 2px solid var(--text);
border-radius: 2px;
position: absolute;
top: 3px;
left: 3px;
}
&::after {
content: '';
display: inline-block;
height: 14px;
width: 14px;
border: 2px solid ${props => props.color || 'var(--primary)'};
background-color: ${props => props.color || 'var(--primary)'};
border-radius: 2px;
position: absolute;
top: 3px;
left: 3px;
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNMjEsN0w5LDE5TDMuNSwxMy41TDQuOTEsMTIuMDlMOSwxNi4xN0wxOS41OSw1LjU5TDIxLDdaIiAvPjwvc3ZnPg==');
background-size: 16px;
background-position: center;
background-repeat: no-repeat;
opacity: 0;
transform: scale(.5);
transition: opacity 0.15s, transform 0.15s;
}
`
export const CalendarLabel = styled('label')`
margin-left: .6em;
font-size: 15px;
font-weight: 500;
line-height: 24px;
`
export const Info = styled('div')`
font-size: 14px;
opacity: .6;
font-weight: 500;
padding: 14px 0 10px;
`
export const Options = styled('div')`
font-size: 14px;
padding: 0 0 5px;
`
export const Title = styled('p')`
display: flex;
align-items: center;
& strong {
margin-right: 1ex;
}
`
export const Icon = styled('img')`
height: 24px;
width: 24px;
margin-right: 12px;
filter: invert(1);
`
export const LinkButton = styled('button')`
font: inherit;
color: var(--primary);
border: 0;
background: none;
text-decoration: underline;
padding: 0;
margin: 0;
display: inline;
cursor: pointer;
appearance: none;
`