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 { WithPasswordSchema } from "@/features/onboarding/schemas/account.schema";
|
||||||
import type { OnboardingMeta } from "@/features/onboarding/types";
|
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();
|
const { state, updateContext, next } = useOnboarding();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (values: AccountInput) => {
|
mutationFn: async (values: AccountInput) => {
|
||||||
const existingAccount = state?.context.flowData.account;
|
const existingAccount = state?.context.flowData.account;
|
||||||
const isUpdateMode = !!existingAccount;
|
const isUpdateMode = !!existingAccount;
|
||||||
|
const selectedMethod = values.method ?? "password";
|
||||||
|
|
||||||
if (isUpdateMode) {
|
if (isUpdateMode) {
|
||||||
const result = await updateAccountAction({
|
const result = await updateAccountAction({
|
||||||
|
|||||||
@@ -33,22 +33,41 @@ export const StepAccountInfo = () => {
|
|||||||
const passkeyEnabled = meta?.passkeyEnabled ?? false;
|
const passkeyEnabled = meta?.passkeyEnabled ?? false;
|
||||||
const emailPasswordEnabled = meta?.emailPasswordEnabled ?? false;
|
const emailPasswordEnabled = meta?.emailPasswordEnabled ?? false;
|
||||||
|
|
||||||
const [selectedMethod, setSelectedMethod] = useState<"passkey" | "password">(
|
|
||||||
passkeyEnabled && !emailPasswordEnabled ? "passkey" : "password"
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session?.user && !isUpdateMode) {
|
if (session?.user && !isUpdateMode) {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
}, [session?.user?.id, next, session?.user, isUpdateMode]);
|
}, [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<
|
const form = useZodForm({ schema }) as unknown as ReturnType<
|
||||||
typeof useZodForm<typeof WithPasswordSchema>
|
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 (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
@@ -63,20 +82,20 @@ export const StepAccountInfo = () => {
|
|||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
className="flex flex-col gap-4"
|
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">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
@@ -121,7 +140,7 @@ export const StepAccountInfo = () => {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{!isUpdateMode && selectedMethod === "password" && (
|
{!isUpdateMode && emailPasswordEnabled && (
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="password"
|
name="password"
|
||||||
@@ -137,13 +156,31 @@ export const StepAccountInfo = () => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<div className="flex flex-col gap-2 mt-2">
|
||||||
|
{isUpdateMode ? (
|
||||||
<Button type="submit" disabled={mutation.isPending}>
|
<Button type="submit" disabled={mutation.isPending}>
|
||||||
{isUpdateMode
|
Update account
|
||||||
? "Update account"
|
|
||||||
: selectedMethod === "passkey"
|
|
||||||
? "Create account with passkey"
|
|
||||||
: "Create account"}
|
|
||||||
</Button>
|
</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>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user