import { Button } from '@affine/admin/components/ui/button'; import type { CarouselApi } from '@affine/admin/components/ui/carousel'; import { Carousel, CarouselContent, CarouselItem, } from '@affine/admin/components/ui/carousel'; import { validateEmailAndPassword } from '@affine/admin/utils'; import { useMutateQueryResource } from '@affine/core/hooks/use-mutation'; import { serverConfigQuery } from '@affine/graphql'; import { useCallback, useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; import { useServerConfig } from '../common'; import { CreateAdmin } from './create-admin'; export enum CarouselSteps { Welcome = 0, CreateAdmin, SettingsDone, } const Welcome = () => { return (

Welcome to AFFiNE

Configure your Self Host AFFiNE with a few simple settings.

); }; const SettingsDone = () => { return (

All Settings Done

AFFiNE is ready to use.

); }; const CarouselItemElements = { [CarouselSteps.Welcome]: Welcome, [CarouselSteps.CreateAdmin]: CreateAdmin, [CarouselSteps.SettingsDone]: SettingsDone, }; export const Form = () => { const [api, setApi] = useState(); const [current, setCurrent] = useState(0); const [count, setCount] = useState(0); const navigate = useNavigate(); const [nameValue, setNameValue] = useState(''); const [emailValue, setEmailValue] = useState(''); const [passwordValue, setPasswordValue] = useState(''); const [invalidEmail, setInvalidEmail] = useState(false); const [invalidPassword, setInvalidPassword] = useState(false); const serverConfig = useServerConfig(); const passwordLimits = serverConfig.credentialsRequirement.password; const isCreateAdminStep = current - 1 === CarouselSteps.CreateAdmin; const disableContinue = (!nameValue || !emailValue || !passwordValue) && isCreateAdminStep; const revalidate = useMutateQueryResource(); useEffect(() => { if (!api) { return; } setCount(api.scrollSnapList().length); setCurrent(api.selectedScrollSnap() + 1); api.on('select', () => { setCurrent(api.selectedScrollSnap() + 1); }); }, [api, serverConfig.initialized, navigate]); const createAdmin = useCallback(async () => { try { const createResponse = await fetch('/api/setup/create-admin-user', { method: 'POST', body: JSON.stringify({ email: emailValue, password: passwordValue, }), headers: { 'Content-Type': 'application/json', }, }); if (!createResponse.ok) { const errorData = await createResponse.json(); throw new Error(errorData.message || 'Failed to create admin'); } await createResponse.json(); await revalidate(serverConfigQuery); toast.success('Admin account created successfully.'); } catch (err) { toast.error((err as Error).message); console.error(err); throw err; } }, [emailValue, passwordValue, revalidate]); const onNext = useCallback(async () => { if (isCreateAdminStep) { if ( !validateEmailAndPassword( emailValue, passwordValue, passwordLimits, setInvalidEmail, setInvalidPassword ) ) { return; } else { try { await createAdmin(); setInvalidEmail(false); setInvalidPassword(false); } catch (e) { console.error(e); setInvalidEmail(true); setInvalidPassword(true); return; } } } if (current === count) { return navigate('/', { replace: true }); } api?.scrollNext(); }, [ api, count, createAdmin, current, emailValue, isCreateAdminStep, navigate, passwordLimits, passwordValue, ]); const onPrevious = useCallback(() => { if (current === count) { if (serverConfig.initialized === true) { return navigate('/admin', { replace: true }); } toast.error('Goto Admin Panel failed, please try again.'); return; } api?.scrollPrev(); }, [api, count, current, serverConfig.initialized, navigate]); return (
{Object.entries(CarouselItemElements).map(([key, Element]) => ( ))}
{current > 1 && ( )}
{Array.from({ length: count }).map((_, index) => ( ))}
); };