Files

116 lines
3.3 KiB
TypeScript

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