116 lines
3.3 KiB
TypeScript
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>;
|