2024-10-26 17:54:07 -07:00

251 lines
11 KiB
TypeScript

import React, { useState } from 'react'
import { useAccess } from './useAccess.hook'
import classes from './Access.module.css'
import { Select, Space, Title, Image, Button, Modal, PasswordInput, TextInput, PinInput } from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import left from '../images/login.png'
import { IconLock, IconUser, IconUsers, IconSettings, IconServer, IconKey } from '@tabler/icons-react'
import { modals } from '@mantine/modals'
export function Access() {
const { state, actions } = useAccess()
const [urlOpened, { open: urlOpen, close: urlClose }] = useDisclosure(false)
const [otpOpened, { open: otpOpen, close: otpClose }] = useDisclosure(false)
const [disabled, setDisabled] = useState(false)
const login = async () => {
if (!state.loading) {
actions.setLoading(true)
try {
if (state.mode === 'account') {
await actions.accountLogin()
} else if (state.mode === 'create') {
await actions.accountCreate()
} else if (state.mode === 'reset') {
await actions.accountAccess()
} else if (state.mode === 'admin') {
await actions.adminLogin()
}
otpClose()
} catch (err) {
const { message } = err as { message: string }
console.log(message)
if (message === '405' || message === '403' || message === '429') {
if (message === '429') {
setDisabled(true)
} else {
setDisabled(false)
}
otpOpen()
} else {
modals.openConfirmModal({
title: state.strings.operationFailed,
withCloseButton: true,
overlayProps: {
backgroundOpacity: 0.55,
blur: 3,
},
children: <div>{state.strings.tryAgain}</div>,
cancelProps: { display: 'none' },
confirmProps: { display: 'none' },
})
}
}
actions.setLoading(false)
}
}
return (
<div className={classes.split}>
{state.layout === 'large' && (
<div className={classes.left}>
<Image className={classes.splash} src={left} fit="contain" />
</div>
)}
{state.layout != null && (
<div className={classes.right}>
<div className={classes.frame}>
{state.mode !== 'admin' && <Button variant="transparent" className={classes.float} leftSection={<IconSettings size={28} />} onClick={() => actions.setMode('admin')} />}
{state.mode === 'admin' && <Button variant="transparent" className={classes.float} leftSection={<IconUser size={28} />} onClick={() => actions.setMode('account')} />}
<Title className={classes.title} order={1}>
Databag
</Title>
{state.mode === 'account' && (
<>
<Title order={3}>{state.strings.login}</Title>
<Button size="compact-sm" variant="transparent" onClick={urlOpen}>
{state.host}
</Button>
<Space h="md" />
<TextInput
className={classes.input}
size="md"
value={state.username}
leftSectionPointerEvents="none"
leftSection={<IconUser />}
placeholder={state.strings.username}
onChange={(event) => actions.setUsername(event.currentTarget.value)}
/>
<PasswordInput
className={classes.input}
size="md"
value={state.password}
leftSection={<IconLock />}
placeholder={state.strings.password}
onChange={(event) => actions.setPassword(event.currentTarget.value)}
onKeyDown={(ev) => {
if (ev.code === 'Enter' && state.password && state.username) login()
}}
/>
<Space h="md" />
<Button variant="filled" className={classes.submit} onClick={login} loading={state.loading} disabled={!state.username || !state.password}>
{state.strings.login}
</Button>
<Button size="compact-sm" variant="subtle" onClick={() => actions.setMode('create')}>
{state.strings.createAccount}
</Button>
<Button size="compact-sm" variant="subtle" onClick={() => actions.setMode('reset')}>
{state.strings.forgotPassword}
</Button>
</>
)}
{state.mode === 'reset' && (
<>
<Title order={3}>{state.strings.accessAccount}</Title>
<Button size="compact-sm" variant="transparent" onClick={urlOpen}>
{state.host}
</Button>
<Space h="md" />
<TextInput
className={classes.input}
size="md"
value={state.token}
leftSectionPointerEvents="none"
leftSection={<IconKey />}
placeholder={state.strings.accessCode}
onChange={(event) => actions.setToken(event.currentTarget.value)}
/>
<Space h="md" />
<Button variant="filled" className={classes.submit} disabled={!state.token} onClick={login} loading={state.loading}>
{state.strings.login}
</Button>
<Button size="compact-sm" variant="subtle" onClick={() => actions.setMode('account')}>
{state.strings.accountLogin}
</Button>
</>
)}
{state.mode === 'create' && (
<>
<Space h="md" />
<Title order={3}>{state.strings.createAccount}</Title>
<Button size="compact-sm" variant="transparent" onClick={urlOpen}>
{state.host}
</Button>
<Space h="md" />
<TextInput
className={state.available === 0 ? classes.input : classes.hidden}
size="md"
value={state.token}
leftSectionPointerEvents="none"
leftSection={<IconKey />}
placeholder={state.strings.accessCode}
onChange={(event) => actions.setToken(event.currentTarget.value)}
/>
<TextInput
className={classes.input}
size="md"
value={state.username}
leftSectionPointerEvents="none"
leftSection={<IconUser />}
rightSection={state.taken ? <IconUsers /> : null}
placeholder={state.strings.username}
onChange={(event) => actions.setUsername(event.currentTarget.value)}
error={state.taken ? true : false}
/>
<PasswordInput
className={classes.input}
size="md"
value={state.password}
leftSection={<IconLock />}
placeholder={state.strings.password}
onChange={(event) => actions.setPassword(event.currentTarget.value)}
/>
<PasswordInput
className={classes.input}
size="md"
value={state.confirm}
leftSection={<IconLock />}
placeholder={state.strings.confirmPassword}
onChange={(event) => actions.setConfirm(event.currentTarget.value)}
/>
<Space h="md" />
<Button variant="filled" className={classes.submit} onClick={login} disabled={state.taken || !state.username || !state.password || state.password !== state.confirm}>
{state.strings.create}
</Button>
<Button variant="subtle" onClick={() => actions.setMode('account')} size="compact-sm">
{state.strings.accountLogin}
</Button>
</>
)}
{state.mode === 'admin' && (
<>
<Title order={3}>{state.strings.admin}</Title>
<Button size="compact-sm" variant="transparent" onClick={urlOpen}>
{state.host}
</Button>
<Space h="md" />
<PasswordInput
className={classes.input}
size="md"
value={state.password}
leftSection={<IconLock />}
placeholder={state.strings.password}
onChange={(event) => actions.setPassword(event.currentTarget.value)}
/>
<Space h="md" />
<Button variant="filled" className={classes.submit} disabled={!state.password} onClick={login} loading={state.loading}>
{state.strings.login}
</Button>
</>
)}
<div className={classes.settings}>
<Select label={state.strings.theme} data={state.themes} value={state.scheme} onChange={(theme) => actions.setTheme(theme as string)} />
<Select label={state.strings.language} data={state.languages} value={state.language} onChange={(language) => actions.setLanguage(language as string)} />
</div>
</div>
</div>
)}
<Modal opened={urlOpened} onClose={urlClose} withCloseButton={false} overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} centered>
<TextInput
className={classes.urlInput}
size="md"
leftSectionPointerEvents="none"
leftSection={<IconServer />}
placeholder={state.strings.host}
value={state.host}
onKeyDown={(ev) => {
if (ev.code === 'Enter') urlClose()
}}
onChange={(event) => actions.setNode(event.currentTarget.value)}
/>
</Modal>
<Modal opened={otpOpened} onClose={otpClose} withCloseButton={false} overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} centered>
<div className={classes.mfa}>
<div className={classes.mfaTitle}>{state.strings.mfaTitle}</div>
<div className={classes.mfaDescription}>{state.strings.mfaEnter}</div>
<PinInput value={state.code} length={6} className={classes.mfaPin} onChange={(event) => actions.setCode(event)} />
<div className={classes.mfaDisabled}>{disabled ? state.strings.mfaDisabled : ''}</div>
<div className={classes.mfaControl}>
<Button variant="outline" onClick={otpClose}>
{state.strings.cancel}
</Button>
<Button variant="filled" onClick={login} loading={state.loading} disabled={state.code.length !== 6}>
{state.strings.login}
</Button>
</div>
</div>
</Modal>
</div>
)
}