import { z } from "zod"; import { onlyDigits } from "@/lib/crypto"; function hasSameDigits(value: string) { return /^(\d)\1+$/.test(value); } export function isValidCpf(value: string) { const cpf = onlyDigits(value); if (cpf.length !== 11 || hasSameDigits(cpf)) { return false; } const calcDigit = (base: string, factor: number) => { let total = 0; for (const digit of base) { total += Number(digit) * factor; factor -= 1; } const rest = (total * 10) % 11; return rest === 10 ? 0 : rest; }; const firstDigit = calcDigit(cpf.slice(0, 9), 10); const secondDigit = calcDigit(cpf.slice(0, 10), 11); return firstDigit === Number(cpf[9]) && secondDigit === Number(cpf[10]); } export function isValidCnpj(value: string) { const cnpj = onlyDigits(value); if (cnpj.length !== 14 || hasSameDigits(cnpj)) { return false; } const calcDigit = (base: string, weights: number[]) => { const total = base .split("") .reduce((sum, digit, index) => sum + Number(digit) * weights[index], 0); const rest = total % 11; return rest < 2 ? 0 : 11 - rest; }; const firstDigit = calcDigit(cnpj.slice(0, 12), [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]); const secondDigit = calcDigit(cnpj.slice(0, 13), [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]); return firstDigit === Number(cnpj[12]) && secondDigit === Number(cnpj[13]); } export function normalizeWhatsapp(value: string) { const digits = onlyDigits(value); const withoutCountry = digits.startsWith("55") && digits.length === 13 ? digits.slice(2) : digits; if (withoutCountry.length !== 11) { return null; } return `+55${withoutCountry}`; } export const cadastroSchema = z .object({ nomeCompleto: z.string().trim().min(3, "Informe seu nome completo."), cpf: z.string().refine(isValidCpf, "Informe um CPF valido."), whatsapp: z.string().refine((value) => normalizeWhatsapp(value) !== null, "Informe um WhatsApp com DDD."), email: z.string().trim().email("Informe um e-mail valido.").toLowerCase(), senha: z.string().min(8, "A senha precisa ter pelo menos 8 caracteres."), confirmarSenha: z.string(), tipo: z.enum(["PF", "PJ"]), razaoSocial: z.string().trim().optional(), cnpj: z.string().optional(), cep: z.string().optional(), logradouro: z.string().optional(), numero: z.string().optional(), complemento: z.string().optional(), bairro: z.string().optional(), cidade: z.string().optional(), uf: z.string().optional(), lgpdAceite: z.literal("on", { errorMap: () => ({ message: "Aceite a Politica de Privacidade para continuar." }) }) }) .superRefine((data, ctx) => { if (data.senha !== data.confirmarSenha) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["confirmarSenha"], message: "As senhas nao conferem." }); } if (data.tipo === "PJ") { if (!data.razaoSocial) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["razaoSocial"], message: "Informe a razao social." }); } if (!data.cnpj || !isValidCnpj(data.cnpj)) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["cnpj"], message: "Informe um CNPJ valido." }); } } }); export type CadastroInput = z.infer;