mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-11-02 11:50:31 +03:00
remove password strength meter and fix confirm password feedback
The strength meter still scored against the old 8 character and character class rules, so any password with a digit, like test1234, showed a full green "Very Strong" bar next to the failing 12 character requirement. The meter is gone and the length checklist is the only feedback. The password field's form message duplicated the checklist, and editing the password never revalidated the confirm field, so a fixed mismatch kept showing "Passwords do not match". Editing the password now revalidates the confirm field once it has a value, and both dialogs show a live match/mismatch indicator in place of the confirm form message.
This commit is contained in:
@@ -1086,13 +1086,6 @@
|
||||
"title": "Confirm Password",
|
||||
"placeholder": "Confirm Password"
|
||||
},
|
||||
"strength": {
|
||||
"title": "Password strength: ",
|
||||
"weak": "Weak",
|
||||
"medium": "Medium",
|
||||
"strong": "Strong",
|
||||
"veryStrong": "Very Strong"
|
||||
},
|
||||
"requirements": {
|
||||
"title": "Password requirements:",
|
||||
"length": "At least 12 characters"
|
||||
|
||||
@@ -37,12 +37,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { isDesktop, isMobile } from "react-device-detect";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import {
|
||||
calculatePasswordStrength,
|
||||
getPasswordRequirements,
|
||||
getPasswordStrengthLabel,
|
||||
getPasswordStrengthColor,
|
||||
} from "@/utils/passwordUtil";
|
||||
import { getPasswordRequirements } from "@/utils/passwordUtil";
|
||||
import {
|
||||
MobilePage,
|
||||
MobilePageContent,
|
||||
@@ -123,12 +118,6 @@ export default function CreateUserDialog({
|
||||
const passwordsMatch = password === confirmPassword;
|
||||
const showMatchIndicator = password && confirmPassword;
|
||||
|
||||
// Password strength calculation
|
||||
const passwordStrength = useMemo(
|
||||
() => calculatePasswordStrength(password),
|
||||
[password],
|
||||
);
|
||||
|
||||
const requirements = useMemo(
|
||||
() => getPasswordRequirements(password),
|
||||
[password],
|
||||
@@ -224,6 +213,12 @@ export default function CreateUserDialog({
|
||||
type={showPasswordVisible ? "text" : "password"}
|
||||
className="h-10 pr-10"
|
||||
{...field}
|
||||
onChange={(e) => {
|
||||
field.onChange(e);
|
||||
if (form.getValues("confirmPassword")) {
|
||||
form.trigger("confirmPassword");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -255,21 +250,6 @@ export default function CreateUserDialog({
|
||||
|
||||
{password && (
|
||||
<div className="mt-2 space-y-2">
|
||||
<div className="flex h-1.5 w-full overflow-hidden rounded-full bg-secondary-foreground">
|
||||
<div
|
||||
className={`${getPasswordStrengthColor(
|
||||
password,
|
||||
)} transition-all duration-300`}
|
||||
style={{ width: `${passwordStrength * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("users.dialog.form.password.strength.title")}
|
||||
<span className="font-medium">
|
||||
{getPasswordStrengthLabel(password, t)}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<div className="space-y-1 rounded-md bg-muted/50 p-2">
|
||||
<p className="text-xs font-medium text-muted-foreground">
|
||||
{t("users.dialog.form.password.requirements.title")}
|
||||
@@ -297,8 +277,6 @@ export default function CreateUserDialog({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
@@ -366,7 +344,6 @@ export default function CreateUserDialog({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -28,12 +28,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { useIsAdmin } from "@/hooks/use-is-admin";
|
||||
import {
|
||||
calculatePasswordStrength,
|
||||
getPasswordRequirements,
|
||||
getPasswordStrengthLabel,
|
||||
getPasswordStrengthColor,
|
||||
} from "@/utils/passwordUtil";
|
||||
import { getPasswordRequirements } from "@/utils/passwordUtil";
|
||||
|
||||
type SetPasswordProps = {
|
||||
show: boolean;
|
||||
@@ -124,12 +119,6 @@ export default function SetPasswordDialog({
|
||||
const password = form.watch("password");
|
||||
const confirmPassword = form.watch("confirmPassword");
|
||||
|
||||
// Password strength calculation
|
||||
const passwordStrength = useMemo(
|
||||
() => calculatePasswordStrength(password),
|
||||
[password],
|
||||
);
|
||||
|
||||
const requirements = useMemo(
|
||||
() => getPasswordRequirements(password),
|
||||
[password],
|
||||
@@ -303,6 +292,12 @@ export default function SetPasswordDialog({
|
||||
)}
|
||||
className="h-10 pr-10"
|
||||
autoFocus
|
||||
onChange={(e) => {
|
||||
field.onChange(e);
|
||||
if (form.getValues("confirmPassword")) {
|
||||
form.trigger("confirmPassword");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -334,21 +329,6 @@ export default function SetPasswordDialog({
|
||||
|
||||
{password && (
|
||||
<div className="mt-2 space-y-2">
|
||||
<div className="flex h-1.5 w-full overflow-hidden rounded-full bg-secondary-foreground">
|
||||
<div
|
||||
className={`${getPasswordStrengthColor(
|
||||
password,
|
||||
)} transition-all duration-300`}
|
||||
style={{ width: `${passwordStrength * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("users.dialog.form.password.strength.title")}
|
||||
<span className="font-medium">
|
||||
{getPasswordStrengthLabel(password, t)}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<div className="space-y-1 rounded-md bg-muted/50 p-2">
|
||||
<p className="text-xs font-medium text-muted-foreground">
|
||||
{t("users.dialog.form.password.requirements.title")}
|
||||
@@ -376,8 +356,6 @@ export default function SetPasswordDialog({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
@@ -428,18 +406,25 @@ export default function SetPasswordDialog({
|
||||
</div>
|
||||
</FormControl>
|
||||
|
||||
{password &&
|
||||
confirmPassword &&
|
||||
password === confirmPassword && (
|
||||
<div className="mt-1 flex items-center gap-1.5 text-xs">
|
||||
<LuCheck className="size-3.5 text-green-500" />
|
||||
<span className="text-green-600">
|
||||
{t("users.dialog.form.password.match")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormMessage />
|
||||
{password && confirmPassword && (
|
||||
<div className="mt-1 flex items-center gap-1.5 text-xs">
|
||||
{password === confirmPassword ? (
|
||||
<>
|
||||
<LuCheck className="size-3.5 text-green-500" />
|
||||
<span className="text-green-600">
|
||||
{t("users.dialog.form.password.match")}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<LuX className="size-3.5 text-red-500" />
|
||||
<span className="text-red-600">
|
||||
{t("users.dialog.form.password.notMatch")}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -1,41 +1,3 @@
|
||||
import { TFunction } from "i18next";
|
||||
|
||||
export const calculatePasswordStrength = (password: string): number => {
|
||||
if (!password) return 0;
|
||||
|
||||
let strength = 0;
|
||||
if (password.length >= 8) strength += 1;
|
||||
if (/\d/.test(password)) strength += 1;
|
||||
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) strength += 1;
|
||||
if (/[A-Z]/.test(password)) strength += 1;
|
||||
|
||||
return strength;
|
||||
};
|
||||
|
||||
export const getPasswordRequirements = (password: string) => ({
|
||||
length: password?.length >= 12,
|
||||
});
|
||||
|
||||
export const getPasswordStrengthLabel = (
|
||||
password: string,
|
||||
t: TFunction,
|
||||
): string => {
|
||||
const strength = calculatePasswordStrength(password);
|
||||
|
||||
if (!password) return "";
|
||||
if (strength < 1)
|
||||
return t("users.dialog.form.password.strength.weak", {
|
||||
ns: "views/settings",
|
||||
});
|
||||
return t("users.dialog.form.password.strength.veryStrong", {
|
||||
ns: "views/settings",
|
||||
});
|
||||
};
|
||||
|
||||
export const getPasswordStrengthColor = (password: string): string => {
|
||||
const strength = calculatePasswordStrength(password);
|
||||
|
||||
if (!password) return "bg-gray-200";
|
||||
if (strength === 0) return "bg-red-500";
|
||||
return "bg-green-500";
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user