/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/utils/lgpdExportFormats.js
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import { htmlToPdfDownload } from '@/services/pdf.service';
function htmlEscape(s) {
if (s === null || s === undefined) return '';
return String(s).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
}
function fmtDate(iso) {
if (!iso) return '';
try {
return new Date(iso).toLocaleString('pt-BR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
} catch {
return String(iso);
}
}
function fmtBRL(cents) {
if (cents === null || cents === undefined) return '';
const n = typeof cents === 'number' ? cents / 100 : Number(cents) / 100;
if (!isFinite(n)) return '';
return n.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
}
function renderKV(label, value) {
if (value === null || value === undefined || value === '') return '';
return `
${htmlEscape(label)}: ${htmlEscape(value)}
`;
}
function renderSectionHeader(title, count) {
const badge = count != null ? `${count}` : '';
return `${htmlEscape(title)} ${badge}
`;
}
function renderTable(headers, rows) {
if (!rows || !rows.length) {
return 'Sem registros.
';
}
const th = headers.map((h) => `${htmlEscape(h.label)} | `).join('');
const body = rows
.map(
(r) =>
'' +
headers
.map((h) => {
const raw = h.get ? h.get(r) : r[h.key];
return `| ${htmlEscape(raw ?? '')} | `;
})
.join('') +
'
'
)
.join('');
return ``;
}
export function buildLgpdHTML(payload, tenantName) {
if (!payload) return '';
const meta = payload.export_metadata || {};
const p = payload.paciente || {};
const sections = [];
// ── Cabeçalho LGPD ─────────────────────────────────────────────────────
sections.push(`
Relatório de Dados Pessoais
Documento gerado em atendimento ao art. 18, II da LGPD
(portabilidade de dados do titular).
${renderKV('Controlador', tenantName || 'AgênciaPSI')}
${renderKV('Gerado em', fmtDate(meta.generated_at))}
${renderKV('Fundamento', meta.lgpd_basis)}
${renderKV('Versão do formato', meta.format_version)}
`);
// ── Dados pessoais ─────────────────────────────────────────────────────
sections.push(renderSectionHeader('1. Dados pessoais do titular'));
sections.push(`
${renderKV('Nome completo', p.nome_completo)}
${renderKV('Nome social', p.nome_social)}
${renderKV('Apelido', p.apelido)}
${renderKV('CPF', p.cpf)}
${renderKV('RG', p.rg)}
${renderKV('Data de nascimento', p.data_nascimento)}
${renderKV('Gênero', p.genero)}
${renderKV('Estado civil', p.estado_civil)}
${renderKV('Profissão', p.profissao)}
${renderKV('Escolaridade', p.escolaridade)}
${renderKV('Telefone', p.telefone)}
${renderKV('E-mail', p.email)}
${renderKV('Endereço', [p.endereco_logradouro, p.endereco_numero, p.endereco_bairro, p.endereco_cidade, p.endereco_uf, p.endereco_cep].filter(Boolean).join(', '))}
${renderKV('Status atual', p.status)}
${renderKV('Data de criação do cadastro', fmtDate(p.created_at))}
`);
// ── Contatos ───────────────────────────────────────────────────────────
const contatos = payload.contatos || [];
sections.push(renderSectionHeader('2. Contatos', contatos.length));
sections.push(
renderTable(
[
{ label: 'Nome', key: 'nome' },
{ label: 'Tipo', key: 'tipo' },
{ label: 'Relação', key: 'relacao' },
{ label: 'Telefone', key: 'telefone' },
{ label: 'E-mail', key: 'email' }
],
contatos
)
);
// ── Contatos de apoio ──────────────────────────────────────────────────
const apoio = payload.contatos_apoio || [];
sections.push(renderSectionHeader('3. Contatos de apoio', apoio.length));
sections.push(
renderTable(
[
{ label: 'Nome', key: 'nome' },
{ label: 'Vínculo', key: 'vinculo' },
{ label: 'Telefone', key: 'telefone' }
],
apoio
)
);
// ── Histórico de status ────────────────────────────────────────────────
const status = payload.historico_status || [];
sections.push(renderSectionHeader('4. Histórico de status', status.length));
sections.push(
renderTable(
[
{ label: 'Data', key: 'alterado_em', get: (r) => fmtDate(r.alterado_em) },
{ label: 'Anterior', key: 'status_anterior' },
{ label: 'Novo', key: 'status_novo' },
{ label: 'Motivo', key: 'motivo' }
],
status
)
);
// ── Eventos de agenda ──────────────────────────────────────────────────
const agenda = payload.eventos_agenda || [];
sections.push(renderSectionHeader('5. Eventos de agenda', agenda.length));
sections.push(
renderTable(
[
{ label: 'Início', key: 'inicio_em', get: (r) => fmtDate(r.inicio_em) },
{ label: 'Tipo', key: 'tipo' },
{ label: 'Status', key: 'status' },
{ label: 'Observações', key: 'observacoes' }
],
agenda
)
);
// ── Registros financeiros ──────────────────────────────────────────────
const financeiro = payload.registros_financeiros || [];
sections.push(renderSectionHeader('6. Registros financeiros', financeiro.length));
sections.push(
renderTable(
[
{ label: 'Criado em', key: 'created_at', get: (r) => fmtDate(r.created_at) },
{ label: 'Vencimento', key: 'due_date' },
{ label: 'Valor', key: 'amount', get: (r) => fmtBRL(r.amount) },
{ label: 'Valor final', key: 'final_amount', get: (r) => fmtBRL(r.final_amount) },
{ label: 'Status', key: 'status' },
{ label: 'Método', key: 'payment_method' }
],
financeiro
)
);
// ── Documentos ─────────────────────────────────────────────────────────
const docs = payload.documentos || [];
sections.push(renderSectionHeader('7. Documentos', docs.length));
sections.push(
renderTable(
[
{ label: 'Nome', key: 'nome_original' },
{ label: 'Tipo', key: 'tipo_documento' },
{ label: 'Categoria', key: 'categoria' },
{ label: 'Tamanho (bytes)', key: 'tamanho_bytes' },
{ label: 'Upload em', key: 'uploaded_at', get: (r) => fmtDate(r.uploaded_at) }
],
docs
)
);
// ── Notificações ───────────────────────────────────────────────────────
const notifs = payload.notificacoes_enviadas || [];
sections.push(renderSectionHeader('8. Notificações enviadas', notifs.length));
sections.push(
renderTable(
[
{ label: 'Data', key: 'created_at', get: (r) => fmtDate(r.created_at) },
{ label: 'Canal', key: 'channel' },
{ label: 'Destinatário', key: 'recipient_address' },
{ label: 'Status', key: 'status' }
],
notifs
)
);
// ── Audit trail ────────────────────────────────────────────────────────
const audit = payload.audit_trail || [];
sections.push(renderSectionHeader('9. Auditoria de alterações', audit.length));
sections.push(
renderTable(
[
{ label: 'Data', key: 'created_at', get: (r) => fmtDate(r.created_at) },
{ label: 'Ação', key: 'action' },
{ label: 'Campos alterados', key: 'changed_fields', get: (r) => (r.changed_fields || []).join(', ') }
],
audit
)
);
// ── Acessos a documentos ───────────────────────────────────────────────
const acessos = payload.acessos_a_documentos || [];
sections.push(renderSectionHeader('10. Acessos a documentos', acessos.length));
sections.push(
renderTable(
[
{ label: 'Data', key: 'acessado_em', get: (r) => fmtDate(r.acessado_em) },
{ label: 'Ação', key: 'acao' }
],
acessos
)
);
return `
Relatório LGPD - ${htmlEscape(p.nome_completo || 'paciente')}
${sections.join('\n')}
`;
}
export async function downloadLgpdPDF(payload, tenantName, filename) {
const html = buildLgpdHTML(payload, tenantName);
await htmlToPdfDownload(html, filename || `lgpd-export-${Date.now()}.pdf`);
}