871 lines
32 KiB
HTML
871 lines
32 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="pt-BR">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Plano de Implementacao — Modulo Documentos & Arquivos</title>
|
|
<style>
|
|
:root {
|
|
--bg: #0d1117;
|
|
--bg-card: #161b22;
|
|
--bg-card-hover: #1c2129;
|
|
--bg-table-head: #1c2129;
|
|
--bg-table-row: #161b22;
|
|
--bg-table-row-alt: #0d1117;
|
|
--border: #30363d;
|
|
--border-light: #21262d;
|
|
--text: #e6edf3;
|
|
--text-secondary: #8b949e;
|
|
--text-muted: #6e7681;
|
|
--accent: #58a6ff;
|
|
--accent-dim: #1f6feb33;
|
|
--green: #3fb950;
|
|
--green-dim: #23863633;
|
|
--orange: #d29922;
|
|
--orange-dim: #9e6a0333;
|
|
--purple: #bc8cff;
|
|
--purple-dim: #8957e533;
|
|
--red: #f85149;
|
|
--red-dim: #da363333;
|
|
--cyan: #39d2c0;
|
|
--cyan-dim: #1b7c6e33;
|
|
--pink: #f778ba;
|
|
--pink-dim: #db61a233;
|
|
--font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
--font-mono: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
|
|
--radius: 8px;
|
|
--radius-lg: 12px;
|
|
}
|
|
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
body {
|
|
font-family: var(--font);
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
line-height: 1.6;
|
|
padding: 32px 24px 64px;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
/* ── Header ─────────────────────────── */
|
|
.page-header {
|
|
margin-bottom: 40px;
|
|
padding-bottom: 24px;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.page-header h1 {
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: var(--text);
|
|
margin-bottom: 6px;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
.page-header .subtitle {
|
|
font-size: 14px;
|
|
color: var(--text-secondary);
|
|
}
|
|
.page-header .meta {
|
|
display: flex;
|
|
gap: 16px;
|
|
margin-top: 12px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.meta-tag {
|
|
font-size: 12px;
|
|
font-family: var(--font-mono);
|
|
padding: 3px 10px;
|
|
border-radius: 20px;
|
|
border: 1px solid var(--border);
|
|
color: var(--text-secondary);
|
|
background: var(--bg-card);
|
|
}
|
|
|
|
/* ── Summary cards ──────────────────── */
|
|
.summary-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
gap: 10px;
|
|
margin-bottom: 40px;
|
|
}
|
|
.summary-card {
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 14px 16px;
|
|
text-align: center;
|
|
}
|
|
.summary-card .number {
|
|
font-size: 26px;
|
|
font-weight: 700;
|
|
line-height: 1.2;
|
|
}
|
|
.summary-card .label {
|
|
font-size: 11px;
|
|
color: var(--text-secondary);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.06em;
|
|
margin-top: 2px;
|
|
}
|
|
.summary-card.c-blue .number { color: var(--accent); }
|
|
.summary-card.c-green .number { color: var(--green); }
|
|
.summary-card.c-orange .number { color: var(--orange); }
|
|
.summary-card.c-purple .number { color: var(--purple); }
|
|
.summary-card.c-cyan .number { color: var(--cyan); }
|
|
.summary-card.c-pink .number { color: var(--pink); }
|
|
|
|
/* ── Sections ───────────────────────── */
|
|
.section {
|
|
margin-bottom: 36px;
|
|
}
|
|
.section-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 14px;
|
|
padding-bottom: 8px;
|
|
border-bottom: 1px solid var(--border-light);
|
|
}
|
|
.section-icon {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 6px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 14px;
|
|
flex-shrink: 0;
|
|
}
|
|
.section-icon.blue { background: var(--accent-dim); color: var(--accent); }
|
|
.section-icon.green { background: var(--green-dim); color: var(--green); }
|
|
.section-icon.orange { background: var(--orange-dim); color: var(--orange); }
|
|
.section-icon.purple { background: var(--purple-dim); color: var(--purple); }
|
|
.section-icon.cyan { background: var(--cyan-dim); color: var(--cyan); }
|
|
.section-icon.pink { background: var(--pink-dim); color: var(--pink); }
|
|
.section-icon.red { background: var(--red-dim); color: var(--red); }
|
|
|
|
.section-title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
}
|
|
.section-desc {
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
margin-top: -6px;
|
|
margin-bottom: 14px;
|
|
padding-left: 38px;
|
|
}
|
|
|
|
/* ── Tables ─────────────────────────── */
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
font-size: 13px;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
overflow: hidden;
|
|
}
|
|
thead th {
|
|
background: var(--bg-table-head);
|
|
color: var(--text-secondary);
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
padding: 10px 14px;
|
|
text-align: left;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
tbody td {
|
|
padding: 10px 14px;
|
|
border-bottom: 1px solid var(--border-light);
|
|
vertical-align: top;
|
|
}
|
|
tbody tr:nth-child(odd) { background: var(--bg-table-row); }
|
|
tbody tr:nth-child(even) { background: var(--bg-table-row-alt); }
|
|
tbody tr:hover { background: var(--bg-card-hover); }
|
|
tbody tr:last-child td { border-bottom: none; }
|
|
|
|
.col-file {
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
color: var(--accent);
|
|
white-space: nowrap;
|
|
}
|
|
.col-table {
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
color: var(--green);
|
|
white-space: nowrap;
|
|
}
|
|
.col-route {
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
color: var(--orange);
|
|
}
|
|
.col-key {
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
color: var(--purple);
|
|
}
|
|
.col-bucket {
|
|
font-family: var(--font-mono);
|
|
font-size: 12px;
|
|
color: var(--cyan);
|
|
}
|
|
|
|
/* ── Field chips ────────────────────── */
|
|
.fields {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 4px;
|
|
margin-top: 6px;
|
|
}
|
|
.field {
|
|
font-size: 10px;
|
|
font-family: var(--font-mono);
|
|
padding: 2px 7px;
|
|
border-radius: 12px;
|
|
border: 1px solid var(--border);
|
|
color: var(--text-muted);
|
|
background: var(--bg);
|
|
}
|
|
|
|
/* ── Notes ──────────────────────────── */
|
|
.note {
|
|
background: var(--bg-card);
|
|
border: 1px solid var(--border);
|
|
border-left: 3px solid var(--accent);
|
|
border-radius: var(--radius);
|
|
padding: 12px 16px;
|
|
font-size: 13px;
|
|
color: var(--text-secondary);
|
|
margin-top: 12px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* ── Responsive ─────────────────────── */
|
|
@media (max-width: 700px) {
|
|
body { padding: 16px 12px 40px; }
|
|
.summary-grid { grid-template-columns: repeat(3, 1fr); }
|
|
table { font-size: 12px; }
|
|
thead th, tbody td { padding: 8px 10px; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- ════════════════════════════════════════ HEADER ════════════════════════════════════════ -->
|
|
<div class="page-header">
|
|
<h1>Plano de Implementacao — Documentos & Arquivos</h1>
|
|
<div class="subtitle">Modulo completo: upload, templates, geracao PDF, assinatura eletronica, portal do paciente, auditoria</div>
|
|
<div class="meta">
|
|
<span class="meta-tag">AgenciaPsi v5</span>
|
|
<span class="meta-tag">Vue 3 + Supabase</span>
|
|
<span class="meta-tag">2026-03-30</span>
|
|
<span class="meta-tag">Status: em andamento</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ RESUMO ═══════════════════════════════════════ -->
|
|
<div class="summary-grid">
|
|
<div class="summary-card c-blue">
|
|
<div class="number">6</div>
|
|
<div class="label">Tabelas</div>
|
|
</div>
|
|
<div class="summary-card c-cyan">
|
|
<div class="number">2</div>
|
|
<div class="label">Buckets</div>
|
|
</div>
|
|
<div class="summary-card c-green">
|
|
<div class="number">7</div>
|
|
<div class="label">Services</div>
|
|
</div>
|
|
<div class="summary-card c-orange">
|
|
<div class="number">3</div>
|
|
<div class="label">Composables</div>
|
|
</div>
|
|
<div class="summary-card c-purple">
|
|
<div class="number">~10</div>
|
|
<div class="label">Componentes</div>
|
|
</div>
|
|
<div class="summary-card c-pink">
|
|
<div class="number">5</div>
|
|
<div class="label">Feature flags</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ 1. BANCO ═════════════════════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon blue">1</div>
|
|
<div class="section-title">Banco de Dados — Migrations</div>
|
|
</div>
|
|
<div class="section-desc">Tabelas, RLS policies, indexes, triggers</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Migration</th>
|
|
<th>Tabela / Objeto</th>
|
|
<th>O que faz</th>
|
|
<th>Campos principais</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="col-file" rowspan="4">005_create_documents_tables.sql</td>
|
|
<td class="col-table">documents</td>
|
|
<td>Arquivo vinculado a paciente. Path no Supabase Storage, tipo/categoria, visibilidade, tags, soft delete com retencao LGPD. Tabela central do modulo. O campo storage_bucket indica qual bucket do Storage contem o arquivo (documents ou generated-docs), permitindo que PDFs gerados aparecam na mesma listagem.</td>
|
|
<td>
|
|
<div class="fields">
|
|
<span class="field">id</span>
|
|
<span class="field">patient_id</span>
|
|
<span class="field">tenant_id</span>
|
|
<span class="field">owner_id</span>
|
|
<span class="field">bucket_path</span>
|
|
<span class="field">storage_bucket</span>
|
|
<span class="field">nome_original</span>
|
|
<span class="field">mime_type</span>
|
|
<span class="field">tamanho_bytes</span>
|
|
<span class="field">tipo_documento</span>
|
|
<span class="field">categoria</span>
|
|
<span class="field">descricao</span>
|
|
<span class="field">tags[]</span>
|
|
<span class="field">visibilidade</span>
|
|
<span class="field">compartilhado_portal</span>
|
|
<span class="field">compartilhado_supervisor</span>
|
|
<span class="field">agenda_evento_id</span>
|
|
<span class="field">session_note_id</span>
|
|
<span class="field">enviado_pelo_paciente</span>
|
|
<span class="field">status_revisao</span>
|
|
<span class="field">revisado_por</span>
|
|
<span class="field">revisado_em</span>
|
|
<span class="field">uploaded_by</span>
|
|
<span class="field">uploaded_at</span>
|
|
<span class="field">deleted_at</span>
|
|
<span class="field">deleted_by</span>
|
|
<span class="field">retencao_ate</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-table">document_access_logs</td>
|
|
<td>Log imutavel de quem visualizou ou baixou cada arquivo. Conformidade CFP e LGPD. Sem UPDATE/DELETE — somente INSERT e SELECT.</td>
|
|
<td>
|
|
<div class="fields">
|
|
<span class="field">id</span>
|
|
<span class="field">documento_id</span>
|
|
<span class="field">acao</span>
|
|
<span class="field">user_id</span>
|
|
<span class="field">ip</span>
|
|
<span class="field">user_agent</span>
|
|
<span class="field">acessado_em</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-table">document_signatures</td>
|
|
<td>Assinaturas eletronicas. Cada signatario (paciente, responsavel, terapeuta) tem seu registro com IP, timestamp e hash do documento.</td>
|
|
<td>
|
|
<div class="fields">
|
|
<span class="field">id</span>
|
|
<span class="field">documento_id</span>
|
|
<span class="field">signatario_tipo</span>
|
|
<span class="field">signatario_id</span>
|
|
<span class="field">ordem</span>
|
|
<span class="field">status</span>
|
|
<span class="field">ip</span>
|
|
<span class="field">user_agent</span>
|
|
<span class="field">assinado_em</span>
|
|
<span class="field">hash_documento</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-table">document_share_links</td>
|
|
<td>Links temporarios assinados para compartilhar documento com profissional externo sem conta no sistema. Prazo e limite de usos.</td>
|
|
<td>
|
|
<div class="fields">
|
|
<span class="field">id</span>
|
|
<span class="field">documento_id</span>
|
|
<span class="field">token</span>
|
|
<span class="field">expira_em</span>
|
|
<span class="field">usos_max</span>
|
|
<span class="field">usos</span>
|
|
<span class="field">criado_por</span>
|
|
<span class="field">criado_em</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file" rowspan="2">006_create_document_templates.sql</td>
|
|
<td class="col-table">document_templates</td>
|
|
<td>Templates de documentos (declaracao de comparecimento, atestado, recibo etc.). Corpo HTML com variaveis. Templates globais do sistema + personalizados por tenant com logo/cabecalho.</td>
|
|
<td>
|
|
<div class="fields">
|
|
<span class="field">id</span>
|
|
<span class="field">tenant_id</span>
|
|
<span class="field">nome_template</span>
|
|
<span class="field">tipo</span>
|
|
<span class="field">corpo_html</span>
|
|
<span class="field">variaveis[]</span>
|
|
<span class="field">is_global</span>
|
|
<span class="field">owner_id</span>
|
|
<span class="field">logo_url</span>
|
|
<span class="field">cabecalho_html</span>
|
|
<span class="field">rodape_html</span>
|
|
<span class="field">ativo</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-table">document_generated</td>
|
|
<td>Cada PDF gerado a partir de um template. Guarda os dados usados no preenchimento e o path do PDF resultante no Storage.</td>
|
|
<td>
|
|
<div class="fields">
|
|
<span class="field">id</span>
|
|
<span class="field">template_id</span>
|
|
<span class="field">patient_id</span>
|
|
<span class="field">tenant_id</span>
|
|
<span class="field">dados_preenchidos</span>
|
|
<span class="field">pdf_path</span>
|
|
<span class="field">gerado_em</span>
|
|
<span class="field">gerado_por</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ 2. STORAGE ═══════════════════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon cyan">2</div>
|
|
<div class="section-title">Supabase Storage — Buckets</div>
|
|
</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Bucket</th>
|
|
<th>Uso</th>
|
|
<th>Path pattern</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="col-bucket">documents</td>
|
|
<td>Arquivos enviados por terapeuta ou paciente (PDF, imagem, DOCX, etc.)</td>
|
|
<td class="col-file">{tenant_id}/{patient_id}/{timestamp}-{filename}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-bucket">generated-docs</td>
|
|
<td>PDFs gerados pelo sistema a partir de templates. Referenciado tanto por document_generated (snapshot) quanto por documents (listagem do paciente) via campo storage_bucket.</td>
|
|
<td class="col-file">{tenant_id}/{patient_id}/{template_nome_sanitizado}_{timestamp}.pdf</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ 3. SERVICES ══════════════════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon green">3</div>
|
|
<div class="section-title">Services — Camada de dados</div>
|
|
</div>
|
|
<div class="section-desc">src/services/ — seguem o padrao Medicos.service.js (getOwnerId + getActiveTenantId + CRUD)</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Arquivo</th>
|
|
<th>O que faz</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="col-file">Documents.service.js</td>
|
|
<td>CRUD completo de documentos: upload ao Storage + insert no banco, listagem por paciente com filtros (tipo, categoria, tags), soft delete com retencao, restauracao, download com URL assinada</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentTemplates.service.js</td>
|
|
<td>CRUD de templates: criar/editar templates (globais e por tenant), listar variaveis disponiveis, duplicar template, ativar/desativar</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentGenerate.service.js</td>
|
|
<td>Gerar PDF a partir de template: preencher variaveis com dados do paciente/sessao, renderizar HTML para PDF via pdf.service.js (jsPDF + html2canvas-pro), salvar no bucket generated-docs, registrar em document_generated E automaticamente na tabela documents (para aparecer na listagem do paciente). Nomes de arquivo sanitizados (sem acentos) para compatibilidade com Supabase Storage.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">pdf.service.js</td>
|
|
<td>Servico de geracao de PDF client-side usando jsPDF + html2canvas-pro. Substitui pdfmake que apresenta incompatibilidade com Vite (UMD vs ESM — getBlob/getBuffer travam silenciosamente). Recebe HTML completo, renderiza em canvas oculto (scale 1.5, JPEG 85%), gera PDF A4 com paginacao automatica. Retorna Blob para upload/download.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentSignatures.service.js</td>
|
|
<td>Criar solicitacao de assinatura, registrar assinatura (IP, hash, timestamp, user_agent), consultar status de cada signatario, verificar integridade via hash</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentShareLinks.service.js</td>
|
|
<td>Gerar link temporario com token, validar token no acesso, registrar uso, expirar link</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentAuditLog.service.js</td>
|
|
<td>Registrar log de acesso (visualizacao/download) e consultar historico de acessos por documento</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ 4. COMPOSABLES ═══════════════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon orange">4</div>
|
|
<div class="section-title">Composables — Logica reativa</div>
|
|
</div>
|
|
<div class="section-desc">src/features/documents/composables/</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Arquivo</th>
|
|
<th>O que faz</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="col-file">useDocuments.js</td>
|
|
<td>State reativo: lista de documentos do paciente, loading, filtros ativos (tipo, categoria, tags), operacoes CRUD, refresh automatico apos upload/delete</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">useDocumentTemplates.js</td>
|
|
<td>State reativo: lista de templates disponiveis (globais + tenant), preview com dados ficticios, variaveis extraidas do corpo HTML</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">useDocumentGenerate.js</td>
|
|
<td>Logica de geracao: carregar dados do paciente/sessao, mapear variaveis, chamar servico de geracao, retornar URL do PDF</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ 5. PAGINAS & COMPONENTES ═════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon purple">5</div>
|
|
<div class="section-title">Paginas & Componentes Vue</div>
|
|
</div>
|
|
<div class="section-desc">src/features/documents/</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Arquivo</th>
|
|
<th>Tipo</th>
|
|
<th>O que faz</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="col-file">DocumentsListPage.vue</td>
|
|
<td>Pagina</td>
|
|
<td>Pagina principal — lista todos os documentos do paciente com DataTable, filtros (tipo, categoria, tags), botoes de upload, preview, download. Hero header sticky com stats rapidos.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentUploadDialog.vue</td>
|
|
<td>Dialog</td>
|
|
<td>Upload de arquivo — drag & drop ou seletor, campos: tipo do documento, categoria, descricao, tags, vinculo com sessao (opcional), visibilidade. Validacao de tamanho e tipo de arquivo.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentPreviewDialog.vue</td>
|
|
<td>Dialog</td>
|
|
<td>Preview inline — renderiza PDF/imagem no dialog. Botoes: download, compartilhar, solicitar assinatura, excluir. Exibe metadados (tipo, tags, quem enviou, data).</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentTemplatesPage.vue</td>
|
|
<td>Pagina</td>
|
|
<td>Gestao de templates — lista templates disponiveis (globais + do tenant), criar novo, editar, duplicar, ativar/desativar. Cards com preview do template.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentTemplateEditor.vue</td>
|
|
<td>Componente</td>
|
|
<td>Editor de template — edicao do corpo HTML (editor rich text), insercao de variaveis via dropdown, preview ao vivo com dados ficticios, config de cabecalho/rodape/logo.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentGenerateDialog.vue</td>
|
|
<td>Dialog</td>
|
|
<td>Gerar documento — selecionar template, campos preenchidos automaticamente com dados do paciente/sessao, edicao manual se necessario, preview final via iframe sandbox, botao "Salvar documento" (salva online, sem download automatico). Botao "So baixar" gera PDF local sem salvar no banco.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentSignatureDialog.vue</td>
|
|
<td>Dialog</td>
|
|
<td>Solicitar assinatura — adicionar signatarios (paciente, responsavel, terapeuta), definir ordem, enviar link por email/whatsapp, acompanhar status de cada signatario.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">DocumentShareDialog.vue</td>
|
|
<td>Dialog</td>
|
|
<td>Compartilhar — gerar link temporario com prazo (24h, 48h, 7d) e limite de usos, copiar link, enviar por email. Exibe links ja criados com status.</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">components/DocumentCard.vue</td>
|
|
<td>Componente</td>
|
|
<td>Card reutilizavel de documento — thumbnail (icone por tipo ou preview de imagem), nome, tipo, data, tags, menu de acoes (3 dots).</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">components/DocumentTagsInput.vue</td>
|
|
<td>Componente</td>
|
|
<td>Input de tags livres — chips editaveis com autocomplete baseado em tags ja usadas pelo terapeuta. Criacao de novas tags inline.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ 6. INTEGRACAO PRONTUARIO ═════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon pink">6</div>
|
|
<div class="section-title">Integracao com Prontuario (arquivo existente)</div>
|
|
</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Arquivo existente</th>
|
|
<th>Alteracao</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="col-file">src/features/patients/prontuario/PatientProntuario.vue</td>
|
|
<td>Adicionar aba/secao "Documentos" que renderiza DocumentsListPage filtrada pelo patient_id atual. Botao rapido de upload direto do prontuario.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ 7. ROTAS ═════════════════════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon orange">7</div>
|
|
<div class="section-title">Rotas</div>
|
|
</div>
|
|
<div class="section-desc">Adicionadas em routes.therapist.js e routes.clinic.js</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Rota</th>
|
|
<th>Pagina</th>
|
|
<th>Descricao</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="col-route">/therapist/documents</td>
|
|
<td class="col-file">DocumentsListPage.vue</td>
|
|
<td>Lista geral de documentos (todos os pacientes do terapeuta)</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-route">/therapist/documents/templates</td>
|
|
<td class="col-file">DocumentTemplatesPage.vue</td>
|
|
<td>Gestao de templates do terapeuta</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-route">/therapist/patients/:id/documents</td>
|
|
<td class="col-file">DocumentsListPage.vue</td>
|
|
<td>Documentos de um paciente especifico (via props)</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-route">/clinic/documents/templates</td>
|
|
<td class="col-file">DocumentTemplatesPage.vue</td>
|
|
<td>Templates da clinica (admin configura templates compartilhados)</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ 8. MENUS ═════════════════════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon green">8</div>
|
|
<div class="section-title">Menus de Navegacao</div>
|
|
</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Arquivo</th>
|
|
<th>Item adicionado</th>
|
|
<th>Onde no menu</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="col-file">therapist.menu.js</td>
|
|
<td>"Documentos" — icon: pi-file, to: /therapist/documents</td>
|
|
<td>Grupo "Pacientes", abaixo de "Tags"</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">therapist.menu.js</td>
|
|
<td>"Templates" — icon: pi-file-edit, to: /therapist/documents/templates</td>
|
|
<td>Sub-item de Documentos</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-file">clinic.menu.js</td>
|
|
<td>"Templates de Documentos" — icon: pi-file-edit, to: /clinic/documents/templates</td>
|
|
<td>Grupo "Configuracoes"</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ 9. SAAS FEATURES ═════════════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon red">9</div>
|
|
<div class="section-title">SaaS — Feature Flags</div>
|
|
</div>
|
|
<div class="section-desc">Inseridas em saas_features e vinculadas aos planos via plan_features</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Feature key</th>
|
|
<th>Descricao</th>
|
|
<th>Planos</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="col-key">documents.upload</td>
|
|
<td>Upload de arquivos a pacientes — funcionalidade base</td>
|
|
<td>Free + Pro</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-key">documents.templates</td>
|
|
<td>Templates de documentos (declaracao, atestado, recibo etc.)</td>
|
|
<td>Pro</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-key">documents.signatures</td>
|
|
<td>Assinatura eletronica (TCLE, consentimentos)</td>
|
|
<td>Pro</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-key">documents.share_links</td>
|
|
<td>Links temporarios para compartilhamento externo</td>
|
|
<td>Pro</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="col-key">documents.patient_portal</td>
|
|
<td>Paciente visualiza e envia documentos pelo portal</td>
|
|
<td>Pro</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ 10. SEED DATA ════════════════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon cyan">10</div>
|
|
<div class="section-title">Seed Data — Templates Padrao</div>
|
|
</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Arquivo</th>
|
|
<th>O que insere</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td class="col-file">seed_015_document_templates.sql</td>
|
|
<td>
|
|
4 templates globais (is_global = true) com corpo HTML e variaveis mapeadas:
|
|
<div class="fields" style="margin-top: 8px;">
|
|
<span class="field">Declaracao de Comparecimento</span>
|
|
<span class="field">Atestado Psicologico</span>
|
|
<span class="field">Relatorio de Acompanhamento</span>
|
|
<span class="field">Recibo de Pagamento</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="note">
|
|
<strong>Variaveis dos templates:</strong> {{paciente_nome}}, {{paciente_cpf}}, {{data_sessao}}, {{hora_inicio}}, {{hora_fim}}, {{terapeuta_nome}}, {{terapeuta_crp}}, {{clinica_nome}}, {{clinica_endereco}}, {{valor}}, {{data_atual}}, entre outras. Cada template define quais variaveis utiliza no campo variaveis[].
|
|
</div>
|
|
|
|
<div class="note" style="border-left-color: var(--orange); margin-top: 8px;">
|
|
<strong>Decisao tecnica — Motor PDF:</strong> pdfmake foi substituido por jsPDF + html2canvas-pro. O pdfmake (UMD) trava silenciosamente com Vite (ESM) — createPdf().getBlob()/getBuffer() nunca retornam, mesmo com optimizeDeps configurado. A solucao final usa html2canvas-pro (fork com suporte a cores oklch do PrimeVue/Tailwind) para renderizar o HTML preenchido em canvas, e jsPDF para converter em PDF A4 com paginacao. Resultado: ~200-400KB por documento (JPEG 85%, scale 1.5).
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ════════════════════════════════════════ ORDEM DE EXECUCAO ════════════════════════════ -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<div class="section-icon blue">!</div>
|
|
<div class="section-title">Ordem de Execucao Sugerida</div>
|
|
</div>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Fase</th>
|
|
<th>O que</th>
|
|
<th>Depende de</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><strong>1</strong></td>
|
|
<td>Migrations (tabelas, RLS, triggers, indexes)</td>
|
|
<td>—</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>2</strong></td>
|
|
<td>Buckets no Supabase Storage</td>
|
|
<td>Fase 1</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>3</strong></td>
|
|
<td>Services (camada de dados)</td>
|
|
<td>Fase 1 + 2</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>4</strong></td>
|
|
<td>Composables (logica reativa)</td>
|
|
<td>Fase 3</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>5</strong></td>
|
|
<td>Componentes e Paginas Vue</td>
|
|
<td>Fase 4</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>6</strong></td>
|
|
<td>Rotas, menus, feature flags</td>
|
|
<td>Fase 5</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>7</strong></td>
|
|
<td>Integracao com Prontuario</td>
|
|
<td>Fase 5</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>8</strong></td>
|
|
<td>Seed data (templates padrao)</td>
|
|
<td>Fase 1</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
</body>
|
|
</html>
|