130 lines
3.9 KiB
TypeScript
130 lines
3.9 KiB
TypeScript
import z from "zod/v3";
|
|
import { validateFile } from "~/features/file";
|
|
import { CoreSchema } from "./core.schema";
|
|
|
|
class UsersSchema extends CoreSchema {
|
|
// Zod schema for validation
|
|
create = z
|
|
.object({
|
|
email: z.string().min(1, "Email là bắt buộc").email("Email không hợp lệ"),
|
|
username: z
|
|
.string()
|
|
.min(1, "Tên đăng nhập là bắt buộc")
|
|
.min(3, "Tên đăng nhập phải có ít nhất 3 ký tự")
|
|
.regex(
|
|
/^[a-zA-Z0-9_]+$/,
|
|
"Tên đăng nhập chỉ được chứa chữ cái, số và dấu gạch dưới"
|
|
),
|
|
first_name: z
|
|
.string()
|
|
.min(1, "Họ là bắt buộc")
|
|
.min(2, "Họ phải có ít nhất 2 ký tự"),
|
|
last_name: z
|
|
.string()
|
|
.min(1, "Tên là bắt buộc")
|
|
.min(2, "Tên phải có ít nhất 2 ký tự"),
|
|
phone: z
|
|
.string()
|
|
.optional()
|
|
.refine((val) => !val || /^[0-9+\-\s()]+$/.test(val), {
|
|
message: "Số điện thoại không hợp lệ",
|
|
}),
|
|
avatar: z
|
|
.any()
|
|
.optional()
|
|
.refine(
|
|
(file) => {
|
|
if (!file) return true;
|
|
if (!(file instanceof File)) return true;
|
|
const error = validateFile(file);
|
|
return error === null;
|
|
},
|
|
{
|
|
message: "File không hợp lệ. Chỉ chấp nhận ảnh dưới 2MB",
|
|
}
|
|
),
|
|
is_login_enabled: z.boolean().default(false),
|
|
active: z.boolean().default(true),
|
|
roles: z
|
|
.array(z.string())
|
|
// .min(1, "Phải chọn ít nhất một vai trò")
|
|
.default([]),
|
|
password: z.string().optional(),
|
|
})
|
|
.refine(
|
|
(data) => {
|
|
if (
|
|
data.is_login_enabled &&
|
|
(!data.password || data.password.length < 6)
|
|
) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
{
|
|
message: "Mật khẩu phải có ít nhất 6 ký tự khi cho phép đăng nhập",
|
|
path: ["password"],
|
|
}
|
|
);
|
|
|
|
update = z
|
|
.object({
|
|
email: z.string().email("Email không hợp lệ").optional(),
|
|
username: z
|
|
.string()
|
|
.min(3, "Tên đăng nhập phải có ít nhất 3 ký tự")
|
|
.regex(
|
|
/^[a-zA-Z0-9_]+$/,
|
|
"Tên đăng nhập chỉ được chứa chữ cái, số và dấu gạch dưới"
|
|
)
|
|
.optional(),
|
|
first_name: z.string().min(2, "Họ phải có ít nhất 2 ký tự").optional(),
|
|
last_name: z.string().min(2, "Tên phải có ít nhất 2 ký tự").optional(),
|
|
phone: z
|
|
.union([z.string({ message: "Số điện thoại không hợp lệ" }), z.null()])
|
|
.optional()
|
|
.refine((val) => !val || /^[0-9+\-\s()]+$/.test(val), {
|
|
message: "Số điện thoại không hợp lệ",
|
|
}),
|
|
avatar: z
|
|
.any()
|
|
.optional()
|
|
.refine(
|
|
(file) => {
|
|
if (!file) return true;
|
|
if (!(file instanceof File)) return true;
|
|
const error = validateFile(file);
|
|
return error === null;
|
|
},
|
|
{
|
|
message: "File không hợp lệ. Chỉ chấp nhận ảnh dưới 2MB",
|
|
}
|
|
),
|
|
is_login_enabled: z.boolean().optional(),
|
|
active: z.boolean().optional(),
|
|
roles: z.array(z.string()).optional(),
|
|
password: z.string().optional(),
|
|
})
|
|
.refine(
|
|
(data) => {
|
|
if (
|
|
data.is_login_enabled &&
|
|
data.password &&
|
|
data.password.length < 6
|
|
) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
{
|
|
message: "Mật khẩu phải có ít nhất 6 ký tự khi cho phép đăng nhập",
|
|
path: ["password"],
|
|
}
|
|
);
|
|
}
|
|
|
|
export const usersSchema = new UsersSchema();
|
|
|
|
// ✅ Cách đơn giản và đúng:
|
|
export type EmployeeFormData = z.infer<typeof usersSchema.create>;
|