190 lines
4.7 KiB
TypeScript
190 lines
4.7 KiB
TypeScript
import { useState } from "react";
|
|
import { type RefinementCtx, z } from "zod/v4";
|
|
import { useTranslation } from "~/providers/I18nProvider";
|
|
|
|
export const zStrongPassword = z.string().superRefine((password, ctx) => {
|
|
const test = [/.{8,}/, /[0-9]/, /[a-z]/, /[A-Z]/].every((regex) =>
|
|
regex.test(password),
|
|
);
|
|
if (!test) {
|
|
ctx.addIssue({
|
|
code: "custom",
|
|
message:
|
|
"La password deve contenere almeno 8 caratteri, un numero, una lettera minuscola e una maiuscola.",
|
|
});
|
|
}
|
|
});
|
|
|
|
type PasswordManager = {
|
|
getStrengthColor: (score: number) => string;
|
|
getStrengthTxt: (score: number) => string;
|
|
isVisible: boolean;
|
|
passwordRefine: ({
|
|
password,
|
|
refinementContext,
|
|
path,
|
|
}: {
|
|
password: string;
|
|
refinementContext: RefinementCtx;
|
|
path: string;
|
|
}) => void;
|
|
strengthScore: number;
|
|
toggleVisibility: () => void;
|
|
maxStrength: number;
|
|
};
|
|
|
|
export const useStrongPassword = (): PasswordManager => {
|
|
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.lenght8 },
|
|
{ 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) {
|
|
refinementContext.issues.push({
|
|
code: "custom",
|
|
input: "",
|
|
message: complexity
|
|
.filter((req) => !req.met)
|
|
.map((req) => req.text)
|
|
.join("\n"),
|
|
path: [path],
|
|
});
|
|
}
|
|
setStrengthScore(complexity.filter((req) => req.met).length);
|
|
};
|
|
|
|
return {
|
|
getStrengthColor,
|
|
getStrengthTxt,
|
|
isVisible,
|
|
passwordRefine,
|
|
strengthScore,
|
|
toggleVisibility,
|
|
maxStrength: 4,
|
|
};
|
|
};
|
|
|
|
export const zMildPassword = z.string().superRefine((password, ctx) => {
|
|
const test = [/.{6,}/, /[a-z]/, /[A-Z]/].every((regex) =>
|
|
regex.test(password),
|
|
);
|
|
if (!test) {
|
|
ctx.addIssue({
|
|
code: "custom",
|
|
message:
|
|
"La password deve contenere almeno 6 caratteri, una lettera minuscola e una maiuscola.",
|
|
});
|
|
}
|
|
});
|
|
|
|
export const useMildPassword = (): PasswordManager => {
|
|
const { t } = useTranslation();
|
|
|
|
const [isVisible, setIsVisible] = useState<boolean>(false);
|
|
const [strengthScore, setStrengthScore] = useState(0);
|
|
|
|
const checkStrength = (pass: string) => {
|
|
const requirements = [
|
|
{ regex: /.{6,}/, text: t.auth.signup.pw.lenght6 },
|
|
{ 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-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-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) {
|
|
refinementContext.issues.push({
|
|
code: "custom",
|
|
input: "",
|
|
message: complexity
|
|
.filter((req) => !req.met)
|
|
.map((req) => req.text)
|
|
.join("\n"),
|
|
path: [path],
|
|
});
|
|
}
|
|
setStrengthScore(complexity.filter((req) => req.met).length);
|
|
};
|
|
|
|
return {
|
|
getStrengthColor,
|
|
getStrengthTxt,
|
|
isVisible,
|
|
passwordRefine,
|
|
strengthScore,
|
|
toggleVisibility,
|
|
maxStrength: 3,
|
|
};
|
|
};
|