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>;
 |