/* |-------------------------------------------------------------------------- | 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 `${th}${body}
`; } 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`); }