mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 11:39:00 +00:00
Adds a My Account page, reachable from the sidebar, with the signed-in user's read-only details and a Change Password section. The new password is checked live against the shared password rules (6+ characters, an uppercase letter, a number and a special character), which the invite registration flow reuses. The current password is still verified by the server first, and its rejection is shown on the current password field.
53 lines
1.7 KiB
TypeScript
53 lines
1.7 KiB
TypeScript
import { z } from "zod";
|
|
|
|
/**
|
|
* The single password rule for every surface that sets a password. It mirrors the
|
|
* server's Identity options: the character classes are ASCII, matching how the
|
|
* server classifies uppercase letters, digits and non-alphanumeric characters.
|
|
*/
|
|
export const PASSWORD_MIN_LENGTH = 6;
|
|
|
|
export type PasswordRuleId = "length" | "uppercase" | "number" | "special";
|
|
|
|
export type PasswordRule = {
|
|
id: PasswordRuleId;
|
|
label: string;
|
|
test: (password: string) => boolean;
|
|
};
|
|
|
|
export type PasswordRuleResult = {
|
|
id: PasswordRuleId;
|
|
label: string;
|
|
met: boolean;
|
|
};
|
|
|
|
export const PASSWORD_RULES: readonly PasswordRule[] = [
|
|
{
|
|
id: "length",
|
|
label: `At least ${PASSWORD_MIN_LENGTH} characters`,
|
|
test: (password) => password.length >= PASSWORD_MIN_LENGTH,
|
|
},
|
|
{ id: "uppercase", label: "One uppercase letter", test: (password) => /[A-Z]/.test(password) },
|
|
{ id: "number", label: "One number", test: (password) => /[0-9]/.test(password) },
|
|
{
|
|
id: "special",
|
|
label: "One special character",
|
|
test: (password) => /[^A-Za-z0-9]/.test(password),
|
|
},
|
|
];
|
|
|
|
export const PASSWORD_REQUIREMENTS_MESSAGE = "Password must meet every requirement below";
|
|
|
|
export const PASSWORDS_DO_NOT_MATCH_MESSAGE = "Passwords don't match";
|
|
|
|
export function evaluatePasswordRules(password: string): PasswordRuleResult[] {
|
|
return PASSWORD_RULES.map(({ id, label, test }) => ({ id, label, met: test(password) }));
|
|
}
|
|
|
|
export function meetsPasswordPolicy(password: string): boolean {
|
|
return PASSWORD_RULES.every((rule) => rule.test(password));
|
|
}
|
|
|
|
export const passwordSchema = z.string().refine(meetsPasswordPolicy, {
|
|
message: PASSWORD_REQUIREMENTS_MESSAGE,
|
|
});
|