feat: rework registration form
This commit is contained in:
parent
c7d7fd53cb
commit
3549281b73
4 changed files with 103 additions and 57 deletions
|
|
@ -22,7 +22,9 @@ export function Combobox({
|
|||
defaultText = "Select value...",
|
||||
placeholderText = "Search...",
|
||||
emptyText = "No value found.",
|
||||
current, setValue
|
||||
current,
|
||||
setValue,
|
||||
disabled = false,
|
||||
}: {
|
||||
values?: { value: string; label: string }[]
|
||||
emptyText?: string
|
||||
|
|
@ -30,17 +32,19 @@ export function Combobox({
|
|||
defaultText?: string
|
||||
current?: string
|
||||
setValue: (value: string) => void
|
||||
disabled?: boolean
|
||||
}) {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<Popover open={open && !disabled} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="w-full justify-between"
|
||||
disabled={disabled}
|
||||
>
|
||||
{current
|
||||
? values.find((value) => value.value === current)?.label!
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
import { Button } from "./components/ui/button";
|
||||
import { Link } from "react-router";
|
||||
|
||||
const SUPPORT_MAIL = "mailto:" + import.meta.env.VITE_SUPPORT_EMAIL;
|
||||
export const SUPPORT_MAIL = "mailto:" + import.meta.env.VITE_SUPPORT_EMAIL;
|
||||
|
||||
export function AuthLayout({
|
||||
children,
|
||||
|
|
|
|||
|
|
@ -70,18 +70,24 @@ export const getClasses = async () => {
|
|||
};
|
||||
|
||||
export const registerUser = async (
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
name: string,
|
||||
className: string,
|
||||
token: string
|
||||
) => {
|
||||
return makePostRequest(
|
||||
"/auth/register",
|
||||
{ firstName, lastName, className, token },
|
||||
{ name, className, token },
|
||||
"Échec de l'inscription"
|
||||
);
|
||||
};
|
||||
|
||||
export const getNames = async (className: string) => {
|
||||
return makeRequest(
|
||||
`/auth/autocomplete?className=${encodeURIComponent(className)}`,
|
||||
"Échec de la récupération des noms"
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* === USER API ===
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
import { Alert, AlertDescription } from "~/components/ui/alert";
|
||||
import { AlertCircleIcon, IdCardIcon, LoaderCircle, LogIn, MailIcon } from "lucide-react";
|
||||
import {
|
||||
AlertCircleIcon,
|
||||
ArrowRight,
|
||||
LoaderCircle,
|
||||
LogIn,
|
||||
MailIcon,
|
||||
} from "lucide-react";
|
||||
import { Label } from "~/components/ui/label";
|
||||
import { Input } from "~/components/ui/input";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { useEffect, useState } from "react";
|
||||
import { AuthLayout } from "~/layout";
|
||||
import { AuthLayout, SUPPORT_MAIL } from "~/layout";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { capitalizeFirstLetter } from "~/lib/utils";
|
||||
import { Combobox } from "~/components/combobox";
|
||||
import { getClasses, registerUser } from "~/lib/api";
|
||||
import { getClasses, getNames, registerUser } from "~/lib/api";
|
||||
|
||||
export function meta() {
|
||||
return [
|
||||
|
|
@ -23,9 +29,8 @@ export default function Register() {
|
|||
const [searchParams] = useSearchParams();
|
||||
const email = searchParams.get("email")!;
|
||||
const [emailFirst, emailLast] = getNameFromEmail(email);
|
||||
const [firstName, setFirstName] = useState(emailFirst);
|
||||
const [lastName, setLastName] = useState(emailLast);
|
||||
const [className, setClassName] = useState("");
|
||||
const [name, setName] = useState("");
|
||||
|
||||
const token = searchParams.get("token");
|
||||
useEffect(() => {
|
||||
|
|
@ -35,40 +40,69 @@ export default function Register() {
|
|||
}
|
||||
}, [email, token]);
|
||||
|
||||
const [classes, setClasses] = useState<{ value: string; label: string }[]>([]);
|
||||
const [classes, setClasses] = useState<{ value: string; label: string }[]>(
|
||||
[]
|
||||
);
|
||||
useEffect(() => {
|
||||
getClasses().then((data) => {
|
||||
setClasses(data);
|
||||
})
|
||||
});
|
||||
}, []);
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [names, setNames] = useState<{ value: string; label: string }[]>([]);
|
||||
const hasSubmitted = names.length > 0;
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
|
||||
if (!hasSubmitted) {
|
||||
// Validate inputs
|
||||
if (!firstName || !lastName) return setError("Veuillez compléter votre prénom et nom.");
|
||||
if (!className) return setError("Veuillez sélectionner votre classe.");
|
||||
setIsLoading(true);
|
||||
|
||||
await registerUser(firstName, lastName, className, token!)
|
||||
.then(() => {
|
||||
await getNames(className)
|
||||
.then((names) => {
|
||||
setNames(names);
|
||||
setIsLoading(false);
|
||||
navigate("/", {
|
||||
replace: true,
|
||||
})
|
||||
|
||||
// Try to default name from email
|
||||
const possibleName = names.find(
|
||||
(n: { label: string }) => n.label === `${emailFirst} ${emailLast}`
|
||||
);
|
||||
if (possibleName) {
|
||||
setName(possibleName.value);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setIsLoading(false);
|
||||
setError(err.message);
|
||||
});
|
||||
} else {
|
||||
// Register user
|
||||
if (!name) return setError("Veuillez compléter votre nom.");
|
||||
await registerUser(name, className, token!)
|
||||
.then(() => {
|
||||
setIsLoading(false);
|
||||
navigate("/", {
|
||||
replace: true,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setIsLoading(false);
|
||||
setError(err.message);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthLayout title="Inscription" description="Créez votre compte pour accéder à Khollisé.">
|
||||
<AuthLayout
|
||||
title="Inscription"
|
||||
description="Créez votre compte pour accéder à Khollisé."
|
||||
>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
|
|
@ -87,32 +121,6 @@ export default function Register() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 flex gap-4">
|
||||
<div>
|
||||
<Label htmlFor="password">Prénom</Label>
|
||||
<Input
|
||||
id="firstName"
|
||||
type="text"
|
||||
placeholder="Claire"
|
||||
value={firstName}
|
||||
onChange={(e) => setFirstName(capitalizeFirstLetter(e.target.value))}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Nom</Label>
|
||||
<Input
|
||||
id="lastName"
|
||||
type="text"
|
||||
placeholder="DUPONT"
|
||||
value={lastName}
|
||||
onChange={(e) => setLastName(e.target.value.toUpperCase())}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Classe</Label>
|
||||
<div className="block">
|
||||
|
|
@ -123,9 +131,33 @@ export default function Register() {
|
|||
placeholderText="Rechercher"
|
||||
current={className}
|
||||
setValue={setClassName}
|
||||
disabled={names.length > 0}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Si votre classe n'est pas dans la liste, contactez-nous par{" "}
|
||||
<a href={SUPPORT_MAIL} className="text-blue-500 hover:underline">
|
||||
mail
|
||||
</a>{" "}
|
||||
pour l'ajouter.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{hasSubmitted && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="names">Prénom et Nom</Label>
|
||||
<div className="block">
|
||||
<Combobox
|
||||
defaultText="Sélectionnez votre nom..."
|
||||
values={names}
|
||||
emptyText="Aucun nom trouvé"
|
||||
placeholderText="Rechercher"
|
||||
current={name}
|
||||
setValue={setName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<Alert variant="destructive" className="pb-2">
|
||||
|
|
@ -138,12 +170,16 @@ export default function Register() {
|
|||
{isLoading ? (
|
||||
<>
|
||||
<LoaderCircle className="h-4 w-4 animate-spin mr-2" />
|
||||
Inscription en cours...
|
||||
Validation en cours...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{hasSubmitted ? (
|
||||
<LogIn className="h-4 w-4 mr-2" />
|
||||
S'inscrire
|
||||
) : (
|
||||
<ArrowRight className="h-4 w-4 mr-2" />
|
||||
)}
|
||||
{hasSubmitted ? "S'inscrire" : "Continuer"}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue