'use client'; import React, { useEffect, useMemo, useState } from 'react'; import { Button, Divider, Input, Result } from 'antd'; import { User } from '@supabase/auth-js'; import { inputHasText } from '@/utils'; import { useKeyPress, useMemoizedFn } from 'ahooks'; import Link from 'next/link'; import { BusterRoutes, createBusterRoute } from '@/routes/busterRoutes'; import { BsGithub, BsGoogle, BsMicrosoft } from 'react-icons/bs'; import { createStyles } from 'antd-style'; import { Text } from '@/components/text/Text'; import { Title } from '@/components/text/Title'; import Cookies from 'js-cookie'; import { useBusterSupabaseAuthMethods } from '@/hooks/useBusterSupabaseAuthMethods'; import { PolicyCheck } from './PolicyCheck'; import { rustErrorHandler } from '@/api/buster_rest/errors'; const DEFAULT_CREDENTIALS = { email: process.env.NEXT_PUBLIC_USER!, password: process.env.NEXT_PUBLIC_USER_PASSWORD! }; export const useStyles = createStyles(({ token, css }) => ({ link: { color: token.colorPrimary } })); export const LoginForm: React.FC<{ user: null | User; signInWithEmailAndPassword: ReturnType< typeof useBusterSupabaseAuthMethods >['signInWithEmailAndPassword']; signInWithGoogle: ReturnType['signInWithGoogle']; signUp: ReturnType['signUp']; signInWithGithub: ReturnType['signInWithGithub']; signInWithAzure: ReturnType['signInWithAzure']; }> = ({ user, signInWithEmailAndPassword, signInWithGoogle, signInWithGithub, signInWithAzure, signUp }) => { const hasSupabaseUser = !!user; const [loading, setLoading] = useState<'google' | 'github' | 'azure' | 'email' | null>(null); const [errorMessages, setErrorMessages] = useState([]); const [signUpFlow, setSignUpFlow] = useState(hasSupabaseUser); const [signUpSuccess, setSignUpSuccess] = useState(false); const hasUser = signUpFlow; const errorFallback = (error: any) => { const errorMessage = rustErrorHandler(error); if (errorMessage?.message) { setErrorMessages(['Invalid email or password']); } else { setErrorMessages(['An error occurred']); } }; const onSignInWithUsernameAndPassword = useMemoizedFn( async ({ email, password }: { email: string; password: string }) => { setLoading('email'); try { const res = await signInWithEmailAndPassword({ email, password }); if (res?.error) throw res.error; } catch (error: any) { errorFallback(error); } setLoading(null); } ); const onSignInWithGoogle = useMemoizedFn(async () => { setLoading('google'); try { const res = await signInWithGoogle(); if (res?.error) throw res.error; } catch (error: any) { errorFallback(error); } setLoading('google'); }); const onSignInWithGithub = useMemoizedFn(async () => { setLoading('github'); try { const res = await signInWithGithub(); if (res?.error) throw res.error; } catch (error: any) { errorFallback(error); } setLoading('github'); }); const onSignInWithAzure = useMemoizedFn(async () => { setLoading('azure'); try { const res = await signInWithAzure(); if (res?.error) throw res.error; } catch (error: any) { errorFallback(error); } setLoading('azure'); }); const onSignUp = useMemoizedFn(async (d: { email: string; password: string }) => { setLoading('email'); try { const res = await signUp(d); if (res?.error) throw res.error; setSignUpSuccess(true); } catch (error: any) { errorFallback(error); } setLoading(null); }); const onSubmitClick = (d: { email: string; password: string }) => { try { setErrorMessages([]); setLoading('email'); if (hasUser) onSignInWithUsernameAndPassword(d); else onSignUp(d); } catch (error: any) { const errorMessage = rustErrorHandler(error); if (errorMessage?.message == 'User already registered') { onSignInWithUsernameAndPassword(d); return; } if (errorMessage?.message) { setErrorMessages([errorMessage.message]); } else { setErrorMessages(['An error occurred']); } setLoading(null); } }; return (
{signUpSuccess ? ( ) : ( )}
); }; const LoginOptions: React.FC<{ hasUser: boolean; onSubmitClick: (d: { email: string; password: string }) => void; onSignInWithGoogle: () => void; onSignInWithGithub: () => void; onSignInWithAzure: () => void; setSignUpFlow: (value: boolean) => void; errorMessages: string[]; loading: 'google' | 'github' | 'azure' | 'email' | null; setErrorMessages: (value: string[]) => void; signUpFlow: boolean; }> = ({ hasUser, onSubmitClick, onSignInWithGoogle, onSignInWithGithub, onSignInWithAzure, setSignUpFlow, errorMessages, loading, setErrorMessages, signUpFlow }) => { const { styles, cx } = useStyles(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [password2, setPassword2] = useState(''); const [passwordCheck, setPasswordCheck] = useState(false); const disableSubmitButton = !inputHasText(password) || !inputHasText(password2) || password !== password2 || !passwordCheck; const clearAllCookies = useMemoizedFn(() => { Object.keys(Cookies.get()).forEach((cookieName) => { Cookies.remove(cookieName); }); }); const onSubmitClickPreflight = async (d: { email: string; password: string }) => { clearAllCookies(); onSubmitClick(d); }; useKeyPress(['meta.shift.b', 'shift.ctrl.b'], async () => { setSignUpFlow(false); onSubmitClickPreflight({ email: DEFAULT_CREDENTIALS.email, password: DEFAULT_CREDENTIALS.password }); }); return ( <>
{ v.preventDefault(); onSubmitClickPreflight({ email, password }); }}> or { setEmail(v.target.value); }} disabled={!!loading} autoComplete="email" />
{ setPassword(v.target.value); }} disabled={!!loading} id="password" type="password" name="password" placeholder="Password" autoComplete="new-password" />
{!hasUser && ( { setPassword2(v.target.value); }} disabled={!!loading} id="password2" type="password" name="password2" placeholder="Confirm password" autoComplete="new-password" /> )}
{errorMessages.map((message, index) => ( ))}
{!hasUser ? `Already have an account? ` : `Don’t already have an account? `} { setErrorMessages([]); setPassword2(''); setSignUpFlow(!signUpFlow); }}> {hasUser ? `Sign up` : `Sign in`} {hasUser && ( Reset password )}
); }; const SignUpSuccess: React.FC<{ setSignUpSuccess: (value: boolean) => void; setSignUpFlow: (value: boolean) => void; }> = ({ setSignUpSuccess, setSignUpFlow }) => { return ( { setSignUpSuccess(false); setSignUpFlow(true); }}> Go to Login ]} /> ); }; const WelcomeText: React.FC<{ hasUser: boolean; }> = ({ hasUser }) => { const text = hasUser ? `Sign in` : `Sign up for free`; return ( {text} ); }; const LoginAlertMessage: React.FC<{ message: string; }> = ({ message }) => { return ( {message} ); };