Documentos Pacientes, Template Documentos Pacientes Saas, Documentos prontuários, Documentos Externos, Visualização Externa, Permissão de Visualização, Render Otimização
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* Validadores e formatadores centralizados — AgenciaPsi
|
||||
*
|
||||
* Nomenclatura alinhada ao schema do banco:
|
||||
* nome_completo, cpf, cpf_responsavel, telefone, telefone_alternativo,
|
||||
* telefone_parente, telefone_responsavel, email_principal, email_alternativo, cep
|
||||
*
|
||||
* Regra do banco: CPF é armazenado como 11 dígitos (sem máscara).
|
||||
* Telefones são armazenados como dígitos apenas.
|
||||
*/
|
||||
|
||||
// ─── Utilidade base ────────────────────────────────────────────────────────────
|
||||
|
||||
/** Remove tudo que não for dígito */
|
||||
export function digitsOnly(v) {
|
||||
return String(v ?? '').replace(/\D/g, '')
|
||||
}
|
||||
|
||||
// ─── CPF ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Valida CPF (com ou sem máscara).
|
||||
* Retorna false para sequências repetidas (111.111.111-11) e para dígitos inválidos.
|
||||
*/
|
||||
export function isValidCPF(v) {
|
||||
const d = digitsOnly(v)
|
||||
if (d.length !== 11) return false
|
||||
if (/^(\d)\1+$/.test(d)) return false // sequências iguais
|
||||
|
||||
const calcDV = (base) => {
|
||||
let sum = 0
|
||||
for (let i = 0; i < base.length; i++) sum += Number(base[i]) * (base.length + 1 - i)
|
||||
const mod = sum % 11
|
||||
return mod < 2 ? 0 : 11 - mod
|
||||
}
|
||||
|
||||
const dv1 = calcDV(d.slice(0, 9))
|
||||
if (Number(d[9]) !== dv1) return false
|
||||
|
||||
const dv2 = calcDV(d.slice(0, 10))
|
||||
if (Number(d[10]) !== dv2) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/** Formata CPF para exibição: 000.000.000-00 */
|
||||
export function fmtCPF(v) {
|
||||
const d = digitsOnly(v).slice(0, 11)
|
||||
if (!d) return ''
|
||||
return d
|
||||
.replace(/^(\d{3})(\d)/, '$1.$2')
|
||||
.replace(/^(\d{3})\.(\d{3})(\d)/, '$1.$2.$3')
|
||||
.replace(/\.(\d{3})(\d)/, '.$1-$2')
|
||||
}
|
||||
|
||||
/** Gera um CPF válido (útil para testes/seed) */
|
||||
export function generateCPF() {
|
||||
const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
|
||||
const n = Array.from({ length: 9 }, () => randInt(0, 9))
|
||||
|
||||
const calcDV = (base) => {
|
||||
let sum = 0
|
||||
for (let i = 0; i < base.length; i++) sum += base[i] * (base.length + 1 - i)
|
||||
const mod = sum % 11
|
||||
return mod < 2 ? 0 : 11 - mod
|
||||
}
|
||||
|
||||
const d1 = calcDV(n)
|
||||
const d2 = calcDV([...n, d1])
|
||||
const cpf = [...n, d1, d2].join('')
|
||||
|
||||
if (/^(\d)\1+$/.test(cpf)) return generateCPF()
|
||||
return cpf
|
||||
}
|
||||
|
||||
// ─── CNPJ ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Valida CNPJ (com ou sem máscara).
|
||||
* Rejeita sequências repetidas (00.000.000/0000-00).
|
||||
*/
|
||||
export function isValidCNPJ(v) {
|
||||
const d = digitsOnly(v)
|
||||
if (d.length !== 14) return false
|
||||
if (/^(\d)\1+$/.test(d)) return false
|
||||
|
||||
const calcDV = (base, weights) => {
|
||||
let sum = 0
|
||||
for (let i = 0; i < base.length; i++) sum += Number(base[i]) * weights[i]
|
||||
const mod = sum % 11
|
||||
return mod < 2 ? 0 : 11 - mod
|
||||
}
|
||||
|
||||
const w1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
|
||||
const w2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
|
||||
|
||||
if (Number(d[12]) !== calcDV(d.slice(0, 12), w1)) return false
|
||||
if (Number(d[13]) !== calcDV(d.slice(0, 13), w2)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/** Formata CNPJ para exibição: 00.000.000/0000-00 */
|
||||
export function fmtCNPJ(v) {
|
||||
const d = digitsOnly(v).slice(0, 14)
|
||||
if (!d) return ''
|
||||
return d
|
||||
.replace(/^(\d{2})(\d)/, '$1.$2')
|
||||
.replace(/^(\d{2})\.(\d{3})(\d)/, '$1.$2.$3')
|
||||
.replace(/\.(\d{3})(\d)/, '.$1/$2')
|
||||
.replace(/(\d{4})(\d)/, '$1-$2')
|
||||
}
|
||||
|
||||
// ─── RG ────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Formata RG para exibição: 00.000.000-0 */
|
||||
export function fmtRG(v) {
|
||||
if (!v) return ''
|
||||
const d = digitsOnly(v).slice(0, 9)
|
||||
if (!d) return ''
|
||||
return d
|
||||
.replace(/^(\d{2})(\d)/, '$1.$2')
|
||||
.replace(/^(\d{2})\.(\d{3})(\d)/, '$1.$2.$3')
|
||||
.replace(/\.(\d{3})(\d)/, '.$1-$2')
|
||||
}
|
||||
|
||||
// ─── Telefone ──────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Valida telefone brasileiro (com ou sem máscara, com ou sem DDD).
|
||||
* Aceita 10 dígitos (fixo) ou 11 dígitos (celular).
|
||||
*/
|
||||
export function isValidPhone(v) {
|
||||
const d = digitsOnly(v)
|
||||
return d.length === 10 || d.length === 11
|
||||
}
|
||||
|
||||
/**
|
||||
* Formata telefone para exibição.
|
||||
* 11 dígitos → (XX) XXXXX-XXXX (celular)
|
||||
* 10 dígitos → (XX) XXXX-XXXX (fixo)
|
||||
*/
|
||||
export function fmtPhone(v) {
|
||||
const d = digitsOnly(v)
|
||||
if (!d) return ''
|
||||
if (d.length === 11) return d.replace(/^(\d{2})(\d{5})(\d{4})$/, '($1) $2-$3')
|
||||
if (d.length === 10) return d.replace(/^(\d{2})(\d{4})(\d{4})$/, '($1) $2-$3')
|
||||
return d
|
||||
}
|
||||
|
||||
// ─── Email ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Valida email (formato básico) */
|
||||
export function isValidEmail(v) {
|
||||
const s = String(v ?? '').trim()
|
||||
if (!s) return false
|
||||
return /.+@.+\..+/.test(s)
|
||||
}
|
||||
|
||||
// ─── CEP ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Valida CEP brasileiro: 8 dígitos */
|
||||
export function isValidCEP(v) {
|
||||
const d = digitsOnly(v)
|
||||
return d.length === 8
|
||||
}
|
||||
|
||||
/** Formata CEP para exibição: 00000-000 */
|
||||
export function fmtCEP(v) {
|
||||
const d = digitsOnly(v).slice(0, 8)
|
||||
if (!d) return ''
|
||||
return d.replace(/^(\d{5})(\d)/, '$1-$2')
|
||||
}
|
||||
|
||||
// ─── Sanitização para o banco ──────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Converte valor formatado para apenas dígitos antes de salvar no banco.
|
||||
* Retorna null para valores vazios.
|
||||
*/
|
||||
export function sanitizeDigits(v) {
|
||||
const d = digitsOnly(v)
|
||||
return d || null
|
||||
}
|
||||
|
||||
/**
|
||||
* Converte data de DD/MM/YYYY ou DD-MM-YYYY para YYYY-MM-DD (formato ISO para o banco).
|
||||
* Retorna null se inválido.
|
||||
*/
|
||||
export function toISODate(v) {
|
||||
if (!v) return null
|
||||
const s = String(v).trim()
|
||||
const match = s.match(/^(\d{2})[/\-](\d{2})[/\-](\d{4})$/)
|
||||
if (!match) return null
|
||||
const [, dd, mm, yyyy] = match
|
||||
const date = new Date(`${yyyy}-${mm}-${dd}`)
|
||||
if (isNaN(date.getTime())) return null
|
||||
return `${yyyy}-${mm}-${dd}`
|
||||
}
|
||||
Reference in New Issue
Block a user