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:
Leonardo
2026-03-30 14:08:19 -03:00
parent 0658e2e9bf
commit d088a89fb7
112 changed files with 115867 additions and 5266 deletions
@@ -378,6 +378,42 @@ function confirmRestoreTemplate(tpl) {
});
}
// ══════════════════════════════════════════════════════════════
// ABA 2 — Emojis rápidos para o guia de formatação
// ══════════════════════════════════════════════════════════════
const QUICK_EMOJIS = [
{ char: '📅', label: 'Calendário' },
{ char: '⏰', label: 'Relógio / Lembrete' },
{ char: '✅', label: 'Confirmado' },
{ char: '❌', label: 'Cancelado' },
{ char: '🔔', label: 'Notificação' },
{ char: '💬', label: 'Mensagem' },
{ char: '💙', label: 'Cuidado / Saúde' },
{ char: '🌿', label: 'Bem-estar' },
{ char: '🤝', label: 'Parceria / Encontro' },
{ char: '📋', label: 'Formulário / Triagem' },
{ char: '💰', label: 'Financeiro' },
{ char: '🔗', label: 'Link' },
{ char: '📍', label: 'Local' },
{ char: '📞', label: 'Telefone' },
{ char: '🏥', label: 'Clínica' },
{ char: '🧠', label: 'Psicologia' },
{ char: '👤', label: 'Paciente' },
{ char: '🌟', label: 'Destaque' },
{ char: '⚠️', label: 'Atenção' },
{ char: '➡️', label: 'Seta / Próximo passo' },
{ char: '🗓️', label: 'Agenda' },
{ char: '🕐', label: 'Hora' },
{ char: '📩', label: 'Recebido' },
{ char: '🔄', label: 'Reagendamento' }
];
function copyEmoji(char) {
navigator.clipboard?.writeText(char).catch(() => {});
toast.add({ severity: 'info', summary: `${char} copiado!`, life: 1500 });
}
// ══════════════════════════════════════════════════════════════
// ABA 3 — Logs de envio
// ══════════════════════════════════════════════════════════════
@@ -533,47 +569,152 @@ onBeforeUnmount(() => {
<!-- ABA 2 Templates -->
<TabPanel :value="1">
<div class="flex flex-col gap-3 pt-3">
<!-- Skeleton loading -->
<template v-if="templatesLoading">
<div v-for="n in 4" :key="n" class="border border-[var(--surface-border)] rounded-xl bg-[var(--surface-card)] p-4">
<div class="flex items-center gap-2 mb-3">
<Skeleton width="5rem" height="1.4rem" border-radius="999px" />
<Skeleton width="10rem" height="1rem" />
<div class="flex gap-4 pt-3 items-start">
<!-- Coluna esquerda: cards de templates (65%) -->
<div class="flex flex-col gap-3 min-w-0" style="flex: 0 0 65%;">
<!-- Skeleton loading -->
<template v-if="templatesLoading">
<div v-for="n in 4" :key="n" class="border border-[var(--surface-border)] rounded-xl bg-[var(--surface-card)] p-4">
<div class="flex items-center gap-2 mb-3">
<Skeleton width="5rem" height="1.4rem" border-radius="999px" />
<Skeleton width="10rem" height="1rem" />
</div>
<Skeleton width="100%" height="5rem" class="mb-2" />
<div class="flex gap-1">
<Skeleton v-for="i in 3" :key="i" width="6rem" height="1.6rem" border-radius="999px" />
</div>
</div>
<Skeleton width="100%" height="5rem" class="mb-2" />
<div class="flex gap-1">
<Skeleton v-for="i in 3" :key="i" width="6rem" height="1.6rem" border-radius="999px" />
</template>
<!-- Cards de templates -->
<div v-else v-for="tpl in templates" :key="tpl.key" class="border border-[var(--surface-border)] rounded-xl bg-[var(--surface-card)] p-4 flex flex-col gap-3">
<!-- Header do card -->
<div class="flex items-center gap-2 flex-wrap">
<span class="font-semibold text-sm">{{ tpl.label }}</span>
<Tag :value="tpl.type" :severity="tpl.type_severity" class="text-[0.65rem]" />
<Tag v-if="tpl.is_custom" value="Personalizado" severity="success" class="text-[0.65rem]" />
</div>
</div>
</template>
<!-- Cards de templates -->
<div v-else v-for="tpl in templates" :key="tpl.key" class="border border-[var(--surface-border)] rounded-xl bg-[var(--surface-card)] p-4 flex flex-col gap-3">
<!-- Header do card -->
<div class="flex items-center gap-2 flex-wrap">
<span class="font-semibold text-sm">{{ tpl.label }}</span>
<Tag :value="tpl.type" :severity="tpl.type_severity" class="text-[0.65rem]" />
<Tag v-if="tpl.is_custom" value="Personalizado" severity="success" class="text-[0.65rem]" />
</div>
<!-- Textarea editável -->
<Textarea :ref="(el) => setTextareaRef(tpl.key, el)" v-model="tpl.body_text" rows="5" auto-resize class="w-full text-sm font-mono" />
<!-- Textarea editável -->
<Textarea :ref="(el) => setTextareaRef(tpl.key, el)" v-model="tpl.body_text" rows="5" auto-resize class="w-full text-sm font-mono" />
<!-- Variáveis clicáveis -->
<div class="flex flex-col gap-1.5">
<span class="text-xs text-[var(--text-color-secondary)]">Inserir variável no cursor:</span>
<div class="flex flex-wrap gap-1.5">
<Button v-for="v in tpl.variables" :key="v" :label="`{{${v}}}`" size="small" severity="secondary" outlined class="font-mono !text-[0.68rem] !py-1 !px-2" @click="insertVariable(tpl.key, v)" />
<!-- Variáveis clicáveis -->
<div class="flex flex-col gap-1.5">
<span class="text-xs text-[var(--text-color-secondary)]">Inserir variável no cursor:</span>
<div class="flex flex-wrap gap-1.5">
<Button v-for="v in tpl.variables" :key="v" :label="`{{${v}}}`" size="small" severity="secondary" outlined class="font-mono !text-[0.68rem] !py-1 !px-2" @click="insertVariable(tpl.key, v)" />
</div>
</div>
</div>
<!-- Ações -->
<div class="flex items-center gap-2 justify-end">
<Button v-if="isTemplateModified(tpl)" label="Restaurar original" icon="pi pi-undo" size="small" severity="secondary" outlined @click="confirmRestoreTemplate(tpl)" />
<Button label="Salvar" icon="pi pi-check" size="small" :loading="templateSaving[tpl.key]" :disabled="templateSaving[tpl.key]" @click="saveTemplate(tpl)" />
<!-- Ações -->
<div class="flex items-center gap-2 justify-end">
<Button v-if="isTemplateModified(tpl)" label="Restaurar original" icon="pi pi-undo" size="small" severity="secondary" outlined @click="confirmRestoreTemplate(tpl)" />
<Button label="Salvar" icon="pi pi-check" size="small" :loading="templateSaving[tpl.key]" :disabled="templateSaving[tpl.key]" @click="saveTemplate(tpl)" />
</div>
</div>
</div>
<!-- Coluna direita: guia de formatação (35%) -->
<div class="flex flex-col gap-3 sticky top-4" style="flex: 0 0 35%;">
<div class="border border-[var(--surface-border)] rounded-xl bg-[var(--surface-card)] p-4 flex flex-col gap-4">
<div class="flex items-center gap-2">
<i class="pi pi-book text-[var(--primary-color)]" />
<span class="font-semibold text-sm">Guia de formatação</span>
</div>
<!-- Formatação oficial -->
<div class="flex flex-col gap-2">
<div class="flex items-center gap-1.5 mb-1">
<span class="text-[0.7rem] font-semibold uppercase tracking-wider text-[var(--text-color-secondary)]">Formatação oficial</span>
<div class="flex-1 h-px bg-[var(--surface-border)]" />
</div>
<div class="flex flex-col gap-1.5">
<div class="flex items-center justify-between gap-2">
<span class="font-mono text-xs bg-[var(--surface-ground)] px-2 py-0.5 rounded text-[var(--text-color-secondary)]">*texto*</span>
<span class="text-xs font-bold">Negrito</span>
</div>
<div class="flex items-center justify-between gap-2">
<span class="font-mono text-xs bg-[var(--surface-ground)] px-2 py-0.5 rounded text-[var(--text-color-secondary)]">_texto_</span>
<span class="text-xs italic">Itálico</span>
</div>
<div class="flex items-center justify-between gap-2">
<span class="font-mono text-xs bg-[var(--surface-ground)] px-2 py-0.5 rounded text-[var(--text-color-secondary)]">~texto~</span>
<span class="text-xs line-through">Tachado</span>
</div>
<div class="flex items-center justify-between gap-2">
<span class="font-mono text-xs bg-[var(--surface-ground)] px-2 py-0.5 rounded text-[var(--text-color-secondary)]">`texto`</span>
<span class="text-xs font-mono bg-[var(--surface-ground)] px-1 rounded">Monoespaçado</span>
</div>
</div>
</div>
<!-- Efeitos extras Unicode -->
<div class="flex flex-col gap-2">
<div class="flex items-center gap-1.5 mb-1">
<span class="text-[0.7rem] font-semibold uppercase tracking-wider text-[var(--text-color-secondary)]">Efeitos extras (Unicode)</span>
<div class="flex-1 h-px bg-[var(--surface-border)]" />
</div>
<div class="flex flex-col gap-2">
<div class="flex flex-col gap-0.5">
<div class="flex items-center justify-between gap-2">
<span class="text-xs text-[var(--text-color-secondary)]">Negrito Unicode</span>
<span class="text-xs">𝙝𝙤𝙡𝙖</span>
</div>
<span class="text-[0.65rem] text-[var(--text-color-secondary)] opacity-70">Copie de sites de "font generator"</span>
</div>
<div class="flex flex-col gap-0.5">
<div class="flex items-center justify-between gap-2">
<span class="text-xs text-[var(--text-color-secondary)]">Cursiva Unicode</span>
<span class="text-xs">𝓽𝓮𝔁𝓽𝓸</span>
</div>
<span class="text-[0.65rem] text-[var(--text-color-secondary)] opacity-70">Cada letra é um caractere diferente</span>
</div>
<div class="flex flex-col gap-0.5">
<div class="flex items-center justify-between gap-2">
<span class="text-xs text-[var(--text-color-secondary)]">Small Caps</span>
<span class="text-xs">ᴛᴇxᴛᴏ</span>
</div>
<span class="text-[0.65rem] text-[var(--text-color-secondary)] opacity-70">Bom para títulos curtos</span>
</div>
<div class="flex flex-col gap-0.5">
<div class="flex items-center justify-between gap-2">
<span class="text-xs text-[var(--text-color-secondary)]">Sublinhado</span>
<span class="text-xs">t̲e̲x̲t̲o̲</span>
</div>
<span class="text-[0.65rem] text-[var(--text-color-secondary)] opacity-70">U+0332 após cada letra</span>
</div>
</div>
</div>
<!-- Emojis mais usados -->
<div class="flex flex-col gap-2">
<div class="flex items-center gap-1.5 mb-1">
<span class="text-[0.7rem] font-semibold uppercase tracking-wider text-[var(--text-color-secondary)]">Emojis mais usados</span>
<div class="flex-1 h-px bg-[var(--surface-border)]" />
</div>
<div class="flex flex-wrap gap-1">
<button
v-for="emoji in QUICK_EMOJIS"
:key="emoji.char"
v-tooltip.top="emoji.label"
class="text-base leading-none p-1 rounded hover:bg-[var(--surface-hover)] transition-colors cursor-pointer border-0 bg-transparent"
@click="copyEmoji(emoji.char)"
>{{ emoji.char }}</button>
</div>
<span class="text-[0.65rem] text-[var(--text-color-secondary)] opacity-70">Clique para copiar</span>
</div>
<!-- Dica -->
<div class="flex items-start gap-2 px-3 py-2.5 rounded-lg bg-[var(--surface-ground)] border border-[var(--surface-border)]">
<i class="pi pi-lightbulb text-amber-500 text-xs mt-0.5 shrink-0" />
<p class="text-[0.68rem] text-[var(--text-color-secondary)] m-0 leading-relaxed">
Use <strong>*negrito*</strong> para destacar horários e datas. Evite excesso de formatação mensagens simples têm maior taxa de leitura.
</p>
</div>
</div>
</div>
</div>
</TabPanel>