2025-08-04 17:45:44 +02:00
|
|
|
import { useState } from "react";
|
2025-08-07 14:50:40 +02:00
|
|
|
import { z, type RefinementCtx } from "zod/v4";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { useTranslation } from "~/providers/I18nProvider";
|
|
|
|
|
|
2025-08-07 14:50:40 +02:00
|
|
|
export const zStrongPassword = z.string().superRefine((password, ctx) => {
|
|
|
|
|
const test = [/.{8,}/, /[0-9]/, /[a-z]/, /[A-Z]/].every((regex) =>
|
2025-08-04 17:45:44 +02:00
|
|
|
regex.test(password),
|
|
|
|
|
);
|
2025-08-07 14:50:40 +02:00
|
|
|
if (!test) {
|
|
|
|
|
ctx.addIssue({
|
|
|
|
|
code: "custom",
|
|
|
|
|
message:
|
|
|
|
|
"La password deve contenere almeno 8 caratteri, un numero, una lettera minuscola e una maiuscola.",
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-08-04 17:45:44 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export const useStrongPassword = () => {
|
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
|
|
|
|
|
const [isVisible, setIsVisible] = useState<boolean>(false);
|
|
|
|
|
const [strengthScore, setStrengthScore] = useState(0);
|
|
|
|
|
|
|
|
|
|
const checkStrength = (pass: string) => {
|
|
|
|
|
const requirements = [
|
|
|
|
|
{ regex: /.{8,}/, text: t.auth.signup.pw.lenght },
|
|
|
|
|
{ regex: /[0-9]/, text: t.auth.signup.pw.number },
|
|
|
|
|
{ regex: /[a-z]/, text: t.auth.signup.pw.lowercase },
|
|
|
|
|
{ regex: /[A-Z]/, text: t.auth.signup.pw.uppercase },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return requirements.map((req) => ({
|
|
|
|
|
met: req.regex.test(pass),
|
|
|
|
|
text: req.text,
|
|
|
|
|
}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getStrengthColor = (score: number) => {
|
|
|
|
|
if (score === 0) return "bg-border";
|
|
|
|
|
if (score <= 1) return "bg-red-500";
|
|
|
|
|
if (score <= 2) return "bg-orange-500";
|
|
|
|
|
if (score === 3) return "bg-amber-500";
|
|
|
|
|
return "bg-emerald-500";
|
|
|
|
|
};
|
|
|
|
|
const getStrengthTxt = (score: number) => {
|
|
|
|
|
if (score === 0) return "text-border";
|
|
|
|
|
if (score <= 1) return "text-red-500";
|
|
|
|
|
if (score <= 2) return "text-orange-500";
|
|
|
|
|
if (score === 3) return "text-amber-500";
|
|
|
|
|
return "text-emerald-500";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const toggleVisibility = () => setIsVisible((prevState) => !prevState);
|
|
|
|
|
|
|
|
|
|
const passwordRefine = ({
|
|
|
|
|
password,
|
|
|
|
|
refinementContext,
|
|
|
|
|
path,
|
|
|
|
|
}: {
|
|
|
|
|
password: string;
|
|
|
|
|
refinementContext: RefinementCtx;
|
|
|
|
|
path: string;
|
|
|
|
|
}) => {
|
|
|
|
|
const complexity = checkStrength(password);
|
|
|
|
|
|
|
|
|
|
if (complexity.filter((req) => req.met).length !== complexity.length) {
|
2025-08-07 14:50:40 +02:00
|
|
|
refinementContext.issues.push({
|
|
|
|
|
code: "custom",
|
2025-08-04 17:45:44 +02:00
|
|
|
message: complexity
|
|
|
|
|
.filter((req) => !req.met)
|
|
|
|
|
.map((req) => req.text)
|
|
|
|
|
.join("\n"),
|
|
|
|
|
path: [path],
|
2025-08-07 14:50:40 +02:00
|
|
|
input: "",
|
2025-08-04 17:45:44 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
setStrengthScore(complexity.filter((req) => req.met).length);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
isVisible,
|
|
|
|
|
strengthScore,
|
|
|
|
|
getStrengthColor,
|
|
|
|
|
getStrengthTxt,
|
|
|
|
|
toggleVisibility,
|
|
|
|
|
passwordRefine,
|
|
|
|
|
};
|
|
|
|
|
};
|