Fix sign up and accept invite

Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
Émile Ré
2026-01-02 18:15:10 +01:00
committed by Bryan Frimin
parent c3d4573091
commit 3564bd5495
14 changed files with 516 additions and 117 deletions

View File

@@ -1,114 +0,0 @@
import { Link, useNavigate } from "react-router";
import { Button, Field, useToast } from "@probo/ui";
import { useTranslate } from "@probo/i18n";
import { z } from "zod";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { usePageTitle } from "@probo/hooks";
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
fullName: z.string().min(2),
});
export default function RegisterPage() {
const { __ } = useTranslate();
const navigate = useNavigate();
const { toast } = useToast();
const { register, handleSubmit, formState } = useFormWithSchema(schema, {
defaultValues: {
email: "",
password: "",
fullName: "",
},
});
const onSubmit = handleSubmit(async (data) => {
const response = await fetch("/connect/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify(data),
});
// Registration failed
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
toast({
title: __("Registration failed"),
description: errorData.message || __("Registration failed"),
variant: "error",
});
return;
}
toast({
title: __("Success"),
description: __("Account created successfully"),
variant: "success",
});
navigate("/", { replace: true });
});
usePageTitle(__("Sign up"));
return (
<div className="space-y-6 w-full max-w-md mx-auto">
<div className="space-y-2 text-center">
<h1 className="text-3xl font-bold">{__("Sign up")}</h1>
<p className="text-txt-tertiary">
{__("Enter your information to create an account")}
</p>
</div>
<form onSubmit={onSubmit} className="space-y-4">
<Field
label={__("Full Name")}
type="text"
placeholder={__("John Doe")}
{...register("fullName")}
required
error={formState.errors.fullName?.message}
/>
<Field
label={__("Email")}
type="email"
placeholder={__("name@example.com")}
{...register("email")}
required
error={formState.errors.email?.message}
/>
<Field
label={__("Password")}
type="password"
placeholder="••••••••"
{...register("password")}
required
error={formState.errors.password?.message}
/>
<Button type="submit" className="w-full" disabled={formState.isLoading}>
{formState.isLoading
? __("Creating account...")
: __("Sign up with email")}
</Button>
</form>
<div className="text-center">
<p className="text-sm text-txt-tertiary">
{__("Already have an account?")}{" "}
<Link
to="/auth/login"
className="underline text-txt-primary hover:text-txt-secondary"
>
{__("Log in here")}
</Link>
</p>
</div>
</div>
);
}

View File

@@ -1,135 +0,0 @@
import { Link, useNavigate, useSearchParams } from "react-router";
import { Button, Field, useToast } from "@probo/ui";
import { useTranslate } from "@probo/i18n";
import { z } from "zod";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { usePageTitle } from "@probo/hooks";
import { useEffect } from "react";
const schema = z.object({
fullName: z.string().min(2),
password: z.string().min(8),
});
export default function SignupFromInvitationPage() {
const { __ } = useTranslate();
const navigate = useNavigate();
const { toast } = useToast();
const [searchParams] = useSearchParams();
const { register, handleSubmit, formState, reset } = useFormWithSchema(
schema,
{
defaultValues: {
fullName: "",
password: "",
},
}
);
useEffect(() => {
const fullNameFromParams = searchParams.get("fullName") || "";
if (fullNameFromParams) {
reset({
fullName: fullNameFromParams,
password: "",
});
}
}, [searchParams, reset]);
const onSubmit = handleSubmit(async (data) => {
const token = searchParams.get("token");
if (!token) {
toast({
title: __("Signup failed"),
description: __("Invalid or missing invitation token"),
variant: "error",
});
return;
}
const response = await fetch("/connect/signup-from-invitation", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify({
token: token,
password: data.password,
fullName: data.fullName,
}),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
toast({
title: __("Signup failed"),
description: errorData.message || __("Signup failed"),
variant: "error",
});
return;
}
toast({
title: __("Success"),
description: __(
"Account created successfully. Please accept your invitation to join the organization."
),
variant: "success",
});
navigate("/", { replace: true });
});
usePageTitle(__("Create your account"));
return (
<div className="space-y-6 w-full max-w-md mx-auto">
<div className="space-y-2 text-center">
<h1 className="text-3xl font-bold">{__("Create your account")}</h1>
<p className="text-txt-tertiary">
{__("Set your password to join the organization")}
</p>
</div>
<form onSubmit={onSubmit} className="space-y-4">
<Field
label={__("Full Name")}
type="text"
placeholder={__("John Doe")}
{...register("fullName")}
required
error={formState.errors.fullName?.message}
/>
<Field
label={__("Password")}
type="password"
placeholder="••••••••"
{...register("password")}
required
error={formState.errors.password?.message}
/>
<Button type="submit" className="w-full" disabled={formState.isLoading}>
{formState.isLoading
? __("Creating account...")
: __("Create account")}
</Button>
</form>
<div className="text-center">
<p className="text-sm text-txt-tertiary">
{__("Already have an account?")}{" "}
<Link
to="/auth/login"
className="underline text-txt-primary hover:text-txt-secondary"
>
{__("Log in here")}
</Link>
</p>
</div>
</div>
);
}