mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
feat(onboarding): replace tabs with single form and multiple buttons for auth choice
This commit is contained in:
@@ -10,15 +10,16 @@ import { generatePasskeyContextAction } from "@/features/onboarding/actions/gene
|
||||
import { WithPasswordSchema } from "@/features/onboarding/schemas/account.schema";
|
||||
import type { OnboardingMeta } from "@/features/onboarding/types";
|
||||
|
||||
type AccountInput = z.infer<typeof WithPasswordSchema>;
|
||||
type AccountInput = z.infer<typeof WithPasswordSchema> & { method?: "passkey" | "password" };
|
||||
|
||||
export const useUpdateAccount = (refetchSession: () => Promise<any>, selectedMethod: "passkey" | "password" = "password") => {
|
||||
export const useUpdateAccount = (refetchSession: () => Promise<any>) => {
|
||||
const { state, updateContext, next } = useOnboarding();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (values: AccountInput) => {
|
||||
const existingAccount = state?.context.flowData.account;
|
||||
const isUpdateMode = !!existingAccount;
|
||||
const selectedMethod = values.method ?? "password";
|
||||
|
||||
if (isUpdateMode) {
|
||||
const result = await updateAccountAction({
|
||||
|
||||
@@ -33,22 +33,41 @@ export const StepAccountInfo = () => {
|
||||
const passkeyEnabled = meta?.passkeyEnabled ?? false;
|
||||
const emailPasswordEnabled = meta?.emailPasswordEnabled ?? false;
|
||||
|
||||
const [selectedMethod, setSelectedMethod] = useState<"passkey" | "password">(
|
||||
passkeyEnabled && !emailPasswordEnabled ? "passkey" : "password"
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (session?.user && !isUpdateMode) {
|
||||
next();
|
||||
}
|
||||
}, [session?.user?.id, next, session?.user, isUpdateMode]);
|
||||
|
||||
const schema = selectedMethod === "password" && !isUpdateMode ? WithPasswordSchema : BaseSchema;
|
||||
const schema = isUpdateMode
|
||||
? BaseSchema
|
||||
: z.object({
|
||||
...BaseSchema.shape,
|
||||
password: z.string().optional(),
|
||||
});
|
||||
|
||||
const form = useZodForm({ schema }) as unknown as ReturnType<
|
||||
typeof useZodForm<typeof WithPasswordSchema>
|
||||
>;
|
||||
|
||||
const mutation = useUpdateAccount(refetchSession, selectedMethod);
|
||||
const mutation = useUpdateAccount(refetchSession);
|
||||
|
||||
const onSubmitPassword = async () => {
|
||||
const valid = await form.trigger();
|
||||
if (!valid) return;
|
||||
const values = form.getValues();
|
||||
if (!values.password || values.password.length < 8) {
|
||||
form.setError("password", { message: "Min. 8 characters" });
|
||||
return;
|
||||
}
|
||||
mutation.mutateAsync({ ...(values as any), method: "password" });
|
||||
};
|
||||
|
||||
const onSubmitPasskey = async () => {
|
||||
const valid = await form.trigger(["firstName", "lastName", "email"] as any);
|
||||
if (!valid) return;
|
||||
mutation.mutateAsync({ ...(form.getValues() as any), method: "passkey" });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
@@ -63,20 +82,20 @@ export const StepAccountInfo = () => {
|
||||
<Form
|
||||
form={form}
|
||||
className="flex flex-col gap-4"
|
||||
onSubmit={async (values) => mutation.mutateAsync(values as any)}
|
||||
onSubmit={async (values) => {
|
||||
if (isUpdateMode) {
|
||||
mutation.mutateAsync({ ...(values as any) });
|
||||
} else if (!emailPasswordEnabled) {
|
||||
mutation.mutateAsync({ ...(values as any), method: "passkey" });
|
||||
} else {
|
||||
if (!values.password || (values.password as string).length < 8) {
|
||||
form.setError("password", { message: "Min. 8 characters" });
|
||||
return;
|
||||
}
|
||||
mutation.mutateAsync({ ...(values as any), method: "password" });
|
||||
}
|
||||
}}
|
||||
>
|
||||
{!isUpdateMode && passkeyEnabled && emailPasswordEnabled && (
|
||||
<Tabs
|
||||
value={selectedMethod}
|
||||
onValueChange={(v) => setSelectedMethod(v as any)}
|
||||
className="w-full"
|
||||
>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="password">Password</TabsTrigger>
|
||||
<TabsTrigger value="passkey">Passkey</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
)}
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -121,7 +140,7 @@ export const StepAccountInfo = () => {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{!isUpdateMode && selectedMethod === "password" && (
|
||||
{!isUpdateMode && emailPasswordEnabled && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="password"
|
||||
@@ -137,13 +156,31 @@ export const StepAccountInfo = () => {
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Button type="submit" disabled={mutation.isPending}>
|
||||
{isUpdateMode
|
||||
? "Update account"
|
||||
: selectedMethod === "passkey"
|
||||
? "Create account with passkey"
|
||||
: "Create account"}
|
||||
</Button>
|
||||
<div className="flex flex-col gap-2 mt-2">
|
||||
{isUpdateMode ? (
|
||||
<Button type="submit" disabled={mutation.isPending}>
|
||||
Update account
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
{emailPasswordEnabled && (
|
||||
<Button type="button" onClick={onSubmitPassword} disabled={mutation.isPending}>
|
||||
Create account
|
||||
</Button>
|
||||
)}
|
||||
{passkeyEnabled && (
|
||||
<Button
|
||||
type="button"
|
||||
variant={emailPasswordEnabled ? "outline" : "default"}
|
||||
onClick={onSubmitPasskey}
|
||||
disabled={mutation.isPending}
|
||||
>
|
||||
Create account with passkey
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user