2
0
Files
bot/apps/builder/src/features/auth/components/SignInForm.tsx
Baptiste Arnaud e783bb4121 🧑‍💻 Improve sign up error message
2024-02-19 10:28:57 +01:00

187 lines
5.3 KiB
TypeScript

import {
Button,
HTMLChakraProps,
Input,
Stack,
HStack,
Text,
Spinner,
Alert,
Flex,
AlertIcon,
SlideFade,
} from '@chakra-ui/react'
import React, { ChangeEvent, FormEvent, useEffect } from 'react'
import { useState } from 'react'
import {
ClientSafeProvider,
getProviders,
LiteralUnion,
signIn,
useSession,
} from 'next-auth/react'
import { DividerWithText } from './DividerWithText'
import { SocialLoginButtons } from './SocialLoginButtons'
import { useRouter } from 'next/router'
import { BuiltInProviderType } from 'next-auth/providers'
import { useToast } from '@/hooks/useToast'
import { TextLink } from '@/components/TextLink'
import { SignInError } from './SignInError'
import { useTranslate } from '@tolgee/react'
type Props = {
defaultEmail?: string
}
export const SignInForm = ({
defaultEmail,
}: Props & HTMLChakraProps<'form'>) => {
const { t } = useTranslate()
const router = useRouter()
const { status } = useSession()
const [authLoading, setAuthLoading] = useState(false)
const [isLoadingProviders, setIsLoadingProviders] = useState(true)
const [emailValue, setEmailValue] = useState(defaultEmail ?? '')
const [isMagicLinkSent, setIsMagicLinkSent] = useState(false)
const { showToast } = useToast()
const [providers, setProviders] =
useState<
Record<LiteralUnion<BuiltInProviderType, string>, ClientSafeProvider>
>()
const hasNoAuthProvider =
!isLoadingProviders && Object.keys(providers ?? {}).length === 0
useEffect(() => {
if (status === 'authenticated') {
router.replace(router.query.redirectPath?.toString() ?? '/typebots')
return
}
;(async () => {
const providers = await getProviders()
setProviders(providers ?? undefined)
setIsLoadingProviders(false)
})()
}, [status, router])
useEffect(() => {
if (!router.isReady) return
if (router.query.error === 'ip-banned') {
showToast({
status: 'info',
description:
'Your account has suspicious activity and is being reviewed by our team. Feel free to contact us.',
})
}
}, [router.isReady, router.query.error, showToast])
const handleEmailChange = (e: ChangeEvent<HTMLInputElement>) =>
setEmailValue(e.target.value)
const handleEmailSubmit = async (e: FormEvent) => {
e.preventDefault()
if (isMagicLinkSent) return
setAuthLoading(true)
try {
const response = await signIn('email', {
email: emailValue,
redirect: false,
})
if (response?.error) {
if (response.error.includes('rate-limited'))
showToast({
status: 'info',
description: t('auth.signinErrorToast.tooManyRequests'),
})
else if (response.error.includes('sign-up-disabled'))
showToast({
title: t('auth.signinErrorToast.title'),
description: t('auth.signinErrorToast.description'),
})
else
showToast({
status: 'info',
description: t('errorMessage'),
details: {
content: 'Check server logs to see relevent error message.',
lang: 'json',
},
})
} else {
setIsMagicLinkSent(true)
}
} catch (e) {
showToast({
status: 'info',
description: 'An error occured while signing in',
})
}
setAuthLoading(false)
}
if (isLoadingProviders) return <Spinner />
if (hasNoAuthProvider)
return (
<Text>
{t('auth.noProvider.preLink')}{' '}
<TextLink
href="https://docs.typebot.io/self-hosting/configuration"
isExternal
>
{t('auth.noProvider.link')}
</TextLink>
</Text>
)
return (
<Stack spacing="4" w="330px">
{!isMagicLinkSent && (
<>
<SocialLoginButtons providers={providers} />
{providers?.email && (
<>
<DividerWithText mt="6">{t('auth.orEmailLabel')}</DividerWithText>
<HStack as="form" onSubmit={handleEmailSubmit}>
<Input
name="email"
type="email"
autoComplete="email"
placeholder="email@company.com"
required
value={emailValue}
onChange={handleEmailChange}
/>
<Button
type="submit"
isLoading={
['loading', 'authenticated'].includes(status) || authLoading
}
isDisabled={isMagicLinkSent}
>
{t('auth.emailSubmitButton.label')}
</Button>
</HStack>
</>
)}
</>
)}
{router.query.error && (
<SignInError error={router.query.error.toString()} />
)}
<SlideFade offsetY="20px" in={isMagicLinkSent} unmountOnExit>
<Flex>
<Alert status="success" w="100%">
<HStack>
<AlertIcon />
<Stack spacing={1}>
<Text fontWeight="semibold">{t('auth.magicLink.title')}</Text>
<Text fontSize="sm">{t('auth.magicLink.description')}</Text>
</Stack>
</HStack>
</Alert>
</Flex>
</SlideFade>
</Stack>
)
}