1
0
Fork 0
freesewing/sites/shared/components/susi/sign-in.mjs

227 lines
7.3 KiB
JavaScript
Raw Normal View History

2023-07-29 21:01:26 +02:00
// Hooks
2023-08-20 18:35:19 +02:00
import { useState, useEffect } from 'react'
2023-07-29 21:01:26 +02:00
import { useAccount } from 'shared/hooks/use-account.mjs'
import { useTranslation } from 'next-i18next'
import { useBackend } from 'shared/hooks/use-backend.mjs'
import { useRouter } from 'next/router'
import { useLoadingStatus } from 'shared/hooks/use-loading-status.mjs'
import { horFlexClasses, horFlexClassesNoSm } from 'shared/utils.mjs'
2023-07-29 21:01:26 +02:00
// Components
import Link from 'next/link'
import {
EmailIcon,
KeyIcon,
LockIcon,
WarningIcon,
GoogleIcon,
GitHubIcon,
FreeSewingIcon,
2023-08-28 14:10:43 +02:00
SettingsIcon,
UserIcon,
} from 'shared/components/icons.mjs'
import { StringInput, PasswordInput } from 'shared/components/inputs.mjs'
2023-07-29 21:01:26 +02:00
2023-08-28 14:10:43 +02:00
export const ns = ['susi', 'errors', 'status']
2023-07-31 07:59:21 +02:00
export const SignIn = () => {
2023-07-29 21:01:26 +02:00
const { setAccount, setToken, seenUser, setSeenUser } = useAccount()
2023-08-28 14:10:43 +02:00
const { t } = useTranslation(ns)
2023-07-29 21:01:26 +02:00
const backend = useBackend()
const router = useRouter()
const { setLoadingStatus, LoadingStatus } = useLoadingStatus()
2023-07-29 21:01:26 +02:00
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [magicLink, setMagicLink] = useState(true)
const [signInFailed, setSignInFailed] = useState(false)
const [magicLinkSent, setMagicLinkSent] = useState(false)
const [seenBefore, setSeenBefore] = useState(false)
useEffect(() => {
if (typeof window !== 'undefined' && signInFailed) {
window.setTimeout(() => setSignInFailed(false), 1750)
}
}, [signInFailed])
// Avoid SSR rendering mismatch by setting this in effect
useEffect(() => {
if (seenUser) {
setSeenBefore(seenUser)
setUsername(seenUser)
} else {
setSeenBefore(false)
setUsername('')
}
}, [seenUser])
const signinHandler = async (evt) => {
evt.preventDefault()
setLoadingStatus([true, 'processingUpdate'])
2023-07-29 21:01:26 +02:00
const result = magicLink
? await backend.signIn({ username, password: false })
: await backend.signIn({ username, password })
// Sign-in succeeded
if (result.success) {
if (magicLink) {
2023-08-28 14:10:43 +02:00
setLoadingStatus([true, t('susi:emailSent'), true, true])
2023-07-29 21:01:26 +02:00
setMagicLinkSent(true)
} else {
setAccount(result.data.account)
setToken(result.data.token)
setSeenUser(result.data.account.username)
setLoadingStatus([
true,
2023-08-28 14:10:43 +02:00
t('susi:welcomeBackName', { name: result.data.account.username }),
true,
true,
])
2023-07-29 21:01:26 +02:00
router.push('/account')
}
}
// Sign-in failed
if (result.response?.response?.status === 401) {
2023-08-28 14:10:43 +02:00
const msg = magicLink ? t('susi:notFound') : t('susi:signInFailed')
2023-07-29 21:01:26 +02:00
setSignInFailed(msg)
setLoadingStatus([true, msg, true, false])
2023-07-29 21:01:26 +02:00
}
// Bad request
if (result.status === 400) {
let msg
2023-08-28 14:10:43 +02:00
if (result.data.error === 'usernameMissing') msg = t('susi:usernameMissing')
else if (result.data.error === 'passwordMissing') msg = t('susi:passwordMissing')
2023-07-29 21:01:26 +02:00
setSignInFailed(msg)
setLoadingStatus([true, msg, true, false])
2023-07-29 21:01:26 +02:00
}
}
const btnClasses = `btn capitalize w-full mt-4 ${
signInFailed ? 'btn-warning' : 'btn-primary'
} transition-colors ease-in-out duration-300 ${horFlexClasses}`
2023-07-29 21:01:26 +02:00
const noBueno = (
<>
2023-07-29 21:01:26 +02:00
<WarningIcon />
<span className="pl-2">{signInFailed}</span>
<WarningIcon />
</>
2023-07-29 21:01:26 +02:00
)
if (magicLinkSent)
return (
<>
<LoadingStatus />
2023-07-29 21:01:26 +02:00
<h1 className="text-inherit text-3xl lg:text-5xl mb-4 pb-0 text-center">
2023-08-28 14:10:43 +02:00
{t('susi:emailSent')}
2023-07-29 21:01:26 +02:00
</h1>
<p className="text-inherit text-lg text-center">
2023-08-28 14:10:43 +02:00
{t('susi:checkYourInbox')} <b>FreeSewing.org</b>
2023-07-29 21:01:26 +02:00
</p>
2023-08-28 14:10:43 +02:00
<p className="text-inherit text-lg text-center">{t('susi:clickSigninLink')}</p>
2023-07-29 21:01:26 +02:00
<div className="flex flex-row gap-4 items-center justify-center p-8">
<button className="btn btn-ghost" onClick={() => setMagicLinkSent(false)}>
2023-08-28 14:10:43 +02:00
{t('susi:back')}
2023-07-29 21:01:26 +02:00
</button>
<Link href="/support" className="btn btn-ghost">
2023-08-28 14:10:43 +02:00
{t('susi:contact')}
2023-07-29 21:01:26 +02:00
</Link>
</div>
</>
)
return (
<>
<LoadingStatus />
2023-08-28 14:10:43 +02:00
<h2>{seenBefore ? t('susi:welcomeBackName', { name: seenUser }) : t('susi:welcome')}</h2>
<p>{t('susi:signInToThing', { thing: 'FreeSewing' })}:</p>
{!seenBefore && (
<StringInput
2023-08-28 14:10:43 +02:00
label={t('susi:emailUsernameId')}
update={setUsername}
2023-08-28 14:10:43 +02:00
placeholder={t('susi:emailUsernameId')}
value={username}
valid={(val) => val.length > 1}
/>
)}
2023-07-29 21:01:26 +02:00
{magicLink ? (
<button
className={`${btnClasses} btn-lg`}
tabIndex="-1"
role="button"
onClick={signinHandler}
>
2023-07-29 21:01:26 +02:00
{signInFailed ? (
noBueno
) : (
<>
<span className="hidden lg:block">
<EmailIcon />
</span>
2023-08-28 14:10:43 +02:00
<span className="pl-2">{t('susi:emailSigninLink')}</span>
<span className="hidden lg:block">
<EmailIcon />
</span>
</>
2023-07-29 21:01:26 +02:00
)}
</button>
) : (
<>
<PasswordInput
2023-08-28 14:10:43 +02:00
label={t('susi:password')}
update={setPassword}
current={password}
valid={(val) => val.length > 0}
/>
2023-07-29 21:01:26 +02:00
<button className={btnClasses} tabIndex="-1" role="button" onClick={signinHandler}>
{signInFailed ? (
noBueno
) : (
<>
<span className="hidden lg:block">
<KeyIcon />
</span>
2023-08-28 14:10:43 +02:00
<span className="pl-2">{t('susi:signIn')}</span>
<span className="hidden lg:block">
<LockIcon />
</span>
</>
2023-07-29 21:01:26 +02:00
)}
</button>
</>
)}
<button
className={`block md:flex md:flex-row md:justify-between md:items-center btn btn-primary btn-outline w-full mt-8`}
onClick={() => setMagicLink(!magicLink)}
>
<span className="hidden lg:block">{magicLink ? <LockIcon /> : <EmailIcon />}</span>
2023-08-28 14:10:43 +02:00
{magicLink ? t('susi:usePassword') : t('susi:emailSigninLink')}
<span className="hidden lg:block">{magicLink ? <KeyIcon /> : <EmailIcon />}</span>
</button>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 items-center mt-2">
{['Google', 'Github'].map((provider) => (
2023-08-28 17:10:03 +02:00
<button key={provider} id={provider} className={`${horFlexClasses} btn btn-secondary`}>
{provider === 'Google' ? <GoogleIcon stroke={0} /> : <GitHubIcon />}
2023-08-28 14:10:43 +02:00
<span>{t('susi:signInWithProvider', { provider })}</span>
2023-07-29 21:01:26 +02:00
</button>
))}
</div>
{seenBefore ? (
<button
className={`${horFlexClassesNoSm} btn btn-neutral btn-outline mt-2 w-full`}
onClick={() => setSeenUser(false)}
>
<UserIcon />
{t('susi:signInAsOtherUser')}
</button>
) : (
<Link className={`${horFlexClasses} btn btn-lg btn-neutral mt-2`} href="/signup">
<FreeSewingIcon className="h-10 w-10" />
2023-08-28 14:10:43 +02:00
{t('susi:signUpHere')}
</Link>
)}
2023-08-28 14:10:43 +02:00
<Link className={`${horFlexClasses} btn btn-neutral btn-outline mt-2`} href="/migrate">
<SettingsIcon />
{t('susi:migrateV2Account')}
</Link>
2023-07-29 21:01:26 +02:00
</>
)
}