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) => (
))}
);
};