refactor: improve login error handling and user feedback in FormLogin and auth.controller

This commit is contained in:
Marco Pedone 2025-11-24 10:51:51 +01:00
parent 70e24b5abe
commit 29c8453bad
4 changed files with 168 additions and 138 deletions

View file

@ -49,15 +49,25 @@ export const FormLogin = () => {
onError: ({ message }) => { onError: ({ message }) => {
toast.error(`${t.auth.login.fail}\n${message}`); toast.error(`${t.auth.login.fail}\n${message}`);
}, },
onSuccess: async () => { onSuccess: async (data) => {
await utils.auth.getSession.invalidate(); switch (data.status) {
setAlreadyRequested(true); case "not-found":
toast.success(t.auth.login.success); setUserNotFound(true);
await router.push(redirect ? (redirect as string) : "/"); toast.error(data.message);
//window.location.replace(redirect ? (redirect as string) : "/"); return;
case "fail":
toast.error(data.message);
return;
case "success":
await utils.auth.getSession.invalidate();
toast.success(t.auth.login.success);
await router.push(redirect ? (redirect as string) : "/");
//window.location.replace(redirect ? (redirect as string) : "/");
return;
}
}, },
}); });
const [altreadyRequested, setAlreadyRequested] = useState(false); const [userNotFound, setUserNotFound] = useState(false);
function onSubmit(fields: LoginFormValues) { function onSubmit(fields: LoginFormValues) {
mutate({ mutate({
email: fields.email, email: fields.email,
@ -89,123 +99,143 @@ export const FormLogin = () => {
TEMP DEV LOGIN TEMP DEV LOGIN
</button> </button>
)} )}
{userNotFound ? (
<Form {...form}> <div className="rounded-md bg-red-50 p-4">
<form <div className="flex">
autoComplete="on" <div className="ml-3">
className="space-y-8 px-0.5" <h3 className="font-medium text-lg text-red-800">
onSubmit={form.handleSubmit(onSubmit)} Account non trovato
> </h3>
<FormField <div className="mt-2 text-lg text-red-700">
control={form.control} <p>
name="email" Devi avere un account per effettuare il login. Se non hai un
render={({ field }) => ( account, contattaci per accedere al servizio.
<FormItem> </p>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="email">{t.auth.login.email}</FormLabel>{" "}
<FormMessage />
</div>
<FormControl>
<Input
placeholder="esempio@email.com"
{...field}
autoComplete="username"
id="email"
required
type="email"
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel
className="flex flex-1 justify-between"
htmlFor="password"
>
<div>{t.auth.login.password}</div>
<Link
className="text-primary"
href="/auth/new-password-reset"
>
{t.auth.login.password_forgotten}
</Link>
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
autoComplete="current-password"
id="password"
required
type="password"
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="checkbox"
render={({ field }) => (
<FormItem className="hidden">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="checkbox">Check Me</FormLabel>
<FormMessage />
</div>
<FormControl>
<Checkbox
checked={field.value}
id="checkbox"
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
<Button
className="w-full rounded-lg bg-indigo-600 px-4 py-2 font-medium text-white duration-150 hover:bg-indigo-500 active:bg-indigo-600"
disabled={isPending || altreadyRequested}
type="submit"
>
{isPending ? (
<div className="flex items-center justify-center">
<LoadingSpinner size={25} />
</div> </div>
) : ( </div>
t.auth.login.titolo </div>
)} </div>
</Button> ) : (
</form> <>
</Form> <Form {...form}>
<form
autoComplete="on"
className="space-y-8 px-0.5"
onSubmit={form.handleSubmit(onSubmit)}
>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="email">
{t.auth.login.email}
</FormLabel>{" "}
<FormMessage />
</div>
<br /> <FormControl>
<div className="px-2 pt-2 text-left text-sm"> <Input
<span>{t.auth.login.psa_1}</span> placeholder="esempio@email.com"
<Link {...field}
className="text-indigo-600 underline hover:text-indigo-500" autoComplete="username"
href="/termini-condizioni" id="email"
> required
{t.auth.login.psa_terms} type="email"
</Link> />
<span>{t.auth.login.psa_2}</span> </FormControl>
<Link </FormItem>
className="text-indigo-600 underline hover:text-indigo-500" )}
href="/privacy-policy" />
> <FormField
{t.auth.login.psa_privacy} control={form.control}
</Link> name="password"
. render={({ field }) => (
</div> <FormItem>
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel
className="flex flex-1 justify-between"
htmlFor="password"
>
<div>{t.auth.login.password}</div>
<Link
className="text-primary"
href="/auth/new-password-reset"
>
{t.auth.login.password_forgotten}
</Link>
</FormLabel>
<FormMessage />
</div>
<FormControl>
<Input
{...field}
autoComplete="current-password"
id="password"
required
type="password"
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="checkbox"
render={({ field }) => (
<FormItem className="hidden">
<div className="flex flex-wrap items-center gap-x-2">
<FormLabel htmlFor="checkbox">Check Me</FormLabel>
<FormMessage />
</div>
<FormControl>
<Checkbox
checked={field.value}
id="checkbox"
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
<Button
className="w-full rounded-lg bg-indigo-600 px-4 py-2 font-medium text-white duration-150 hover:bg-indigo-500 active:bg-indigo-600"
disabled={isPending}
type="submit"
>
{isPending ? (
<div className="flex items-center justify-center">
<LoadingSpinner size={25} />
</div>
) : (
t.auth.login.titolo
)}
</Button>
</form>
</Form>
<br />
<div className="px-2 pt-2 text-left text-sm">
<span>{t.auth.login.psa_1}</span>
<Link
className="text-indigo-600 underline hover:text-indigo-500"
href="/termini-condizioni"
>
{t.auth.login.psa_terms}
</Link>
<span>{t.auth.login.psa_2}</span>
<Link
className="text-indigo-600 underline hover:text-indigo-500"
href="/privacy-policy"
>
{t.auth.login.psa_privacy}
</Link>
.
</div>
</>
)}
</div> </div>
</> </>
); );

View file

@ -27,13 +27,13 @@ export const apisMiddleware = async (req: NextRequest) => {
)?.value; )?.value;
if (!accessToken) { if (!accessToken) {
console.log("No access token found"); console.log("No access token found, path:", pathname);
return new NextResponse("Unauthorized", { status: 401 }); return new NextResponse("Unauthorized", { status: 401 });
} }
const payload = await verifyToken(accessToken); const payload = await verifyToken(accessToken);
if (!payload) { if (!payload) {
console.log("Invalid token"); console.log("Invalid token, path:", pathname);
return new NextResponse("Unauthorized", { status: 401 }); return new NextResponse("Unauthorized", { status: 401 });
} }
} }

View file

@ -110,7 +110,7 @@ export const getAnnunciMetaByCod = async ({ cod }: { cod: string }) => {
} }
const ogImage = const ogImage =
annuncio.images && annuncio.images.length > 0 && annuncio.images[0] annuncio.images && annuncio.images.length > 0 && annuncio.images[0]
? `${env.BASE_URL}/storage-api/get/${annuncio.images[0]}?image=true` ? `${env.BASE_URL}/storage-api/get/${annuncio.images[0].img}?image=true`
: `${env.BASE_URL}/og.jpg`; : `${env.BASE_URL}/og.jpg`;
return { return {

View file

@ -84,19 +84,19 @@ export const logIn = async ({
const user = await findUser_byEmail({ email }); const user = await findUser_byEmail({ email });
if (!user) { if (!user) {
console.error("User not found in login for email:", email); return {
throw new TRPCError({ status: "not-found" as const,
code: "NOT_FOUND", message:
message: "Errore in Login: Utente non trovato", "Questo utente non esiste. Devi avere un account per effettuare il login.",
}); };
} }
//Check if user is blocked or unverified //Check if user is blocked or unverified
if (user.isBlocked) { if (user.isBlocked) {
throw new TRPCError({ return {
code: "FORBIDDEN", status: "fail" as const,
message: "Errore in Login: Utente bloccato", message: "Errore in Login, non è possibile effettuare il login.",
}); };
} }
// If Admin and password is "changeme", skip password check // If Admin and password is "changeme", skip password check
@ -110,10 +110,10 @@ export const logIn = async ({
}); });
if (!passworIsMatch) { if (!passworIsMatch) {
throw new TRPCError({ return {
code: "BAD_REQUEST", status: "fail" as const,
message: "Errore in Login: Invalid email or password", message: "Password errata, riprova.",
}); };
} }
} }