validation

This commit is contained in:
Alex Holliday committed 2026-02-07 20:54:17 +00:00
1 parent af4c9d8f91
commit b6004241e7
8 files changed
+113 -10

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { passwordSchema, type PasswordFormData } from "@/Validation/password";
export const usePasswordForm = () => {
return {
resolver: zodResolver(passwordSchema),
defaults: {
currentPassword: "",
newPassword: "",
confirm: "",
} as PasswordFormData,
};
};
+44 -2
View File
@@ -1,5 +1,47 @@
import { Box } from "@mui/material";
import { Stack } from "@mui/material";
import { ConfigBox } from "@/Components/v2/design-elements";
import { TextField } from "@/Components/v2/inputs";
import { useTranslation } from "react-i18next";
import { useTheme } from "@mui/material";
export const TabPassword = () => {
return <Box>{/* TODO: Implement TabPassword */}</Box>;
const { t } = useTranslation();
const theme = useTheme();
return (
<Stack gap={theme.spacing(8)}>
<ConfigBox
title={t("pages.account.form.currentPassword.title")}
subtitle={t("pages.account.form.currentPassword.description")}
rightContent={
<TextField
type="password"
fieldLabel={t("pages.account.form.currentPassword.option.label")}
placeholder={t("pages.account.form.currentPassword.option.placeholder")}
/>
}
/>
<ConfigBox
title={t("pages.account.form.newPassword.title")}
subtitle={t("pages.account.form.newPassword.description")}
rightContent={
<Stack gap={theme.spacing(8)}>
<TextField
type="password"
fieldLabel={t("pages.account.form.newPassword.option.newPassword.label")}
placeholder={t(
"pages.account.form.newPassword.option.newPassword.placeholder"
)}
/>
<TextField
type="password"
fieldLabel={t("pages.account.form.newPassword.option.confirm.label")}
placeholder={t("pages.account.form.newPassword.option.confirm.placeholder")}
/>
</Stack>
}
/>
</Stack>
);
};
+1 -1
View File
@@ -1,4 +1,4 @@
import { Stack, Box } from "@mui/material";
import { Stack } from "@mui/material";
import { ConfigBox } from "@/Components/v2/design-elements";
import { TextField, Button } from "@/Components/v2/inputs";
import { ImageUpload } from "@/Components/v2/inputs";
+25
View File
@@ -0,0 +1,25 @@
import { z } from "zod";
import { specialCharPattern } from "@/Validation/patterns";
export const passwordSchema = z
.object({
currentPassword: z.string().min(1, "Current password is required"),
newPassword: z
.string()
.min(1, "New password is required")
.min(8, "Password must be at least 8 characters")
.refine((val) => /[A-Z]/.test(val), "Password must contain an uppercase letter")
.refine((val) => /[a-z]/.test(val), "Password must contain a lowercase letter")
.refine((val) => /\d/.test(val), "Password must contain a number")
.refine(
(val) => specialCharPattern.test(val),
"Password must contain a special character"
),
confirm: z.string().min(1, "Please confirm your new password"),
})
.refine((data) => data.newPassword === data.confirm, {
message: "Passwords do not match",
path: ["confirm"],
});
export type PasswordFormData = z.infer<typeof passwordSchema>;
+4
View File
@@ -0,0 +1,4 @@
export const specialCharPattern = /[!?@#$%^&*()\-_=+[\]{};:'",.<>~`|\\/]/;
// Unicode-aware name pattern (allows letters, marks, apostrophes, parentheses, hyphens, periods, spaces)
export const namePattern = /^[\p{L}\p{M}''()\-\. ]+$/u;
+3 -4
View File
@@ -1,18 +1,17 @@
import { z } from "zod";
const NAME_PATTERN = /^[A-Za-z'-]+$/;
import { namePattern } from "@/Validation/patterns";
export const profileSchema = z.object({
firstName: z
.string()
.min(1, "First name is required")
.max(50, "First name must be 50 characters or less")
.regex(NAME_PATTERN, "First name can only contain letters, hyphens, and apostrophes"),
.regex(namePattern, "First name contains invalid characters"),
lastName: z
.string()
.min(1, "Last name is required")
.max(50, "Last name must be 50 characters or less")
.regex(NAME_PATTERN, "Last name can only contain letters, hyphens, and apostrophes"),
.regex(namePattern, "Last name contains invalid characters"),
profileImage: z.instanceof(File).optional().nullable(),
deleteProfileImage: z.boolean().optional(),
});
+1 -3
View File
@@ -1,7 +1,5 @@
import { z } from "zod";
const namePattern = /^[\p{L}\p{M}''()\-\. ]+$/u;
const specialCharPattern = /[!?@#$%^&*()\-_=+[\]{};:'",.<>~`|\\/]/;
import { specialCharPattern, namePattern } from "@/Validation/patterns";
export const registerSchema = z
.object({
+22
View File
@@ -523,6 +523,28 @@
"title": "Profile photo",
"description": "Upload a profile picture"
},
"currentPassword": {
"title": "Current password",
"description": "Enter your current password to verify your identity",
"option": {
"label": "Current password",
"placeholder": "Enter current password"
}
},
"newPassword": {
"title": "New password",
"description": "Choose a strong password with at least 8 characters",
"option": {
"newPassword": {
"label": "New password",
"placeholder": "Enter new password"
},
"confirm": {
"label": "Confirm password",
"placeholder": "Confirm new password"
}
}
},
"deleteAccount": {
"title": "Delete account",
"description": "This action is permanent and cannot be undone"