melissa/paciente-docs: wire-up preview actions + Editar abre dialog em modo edicao
DocumentPreviewDialog emitia @download/@edit/@share/@sign/@delete que o MelissaPatientDocuments nao ouvia — os 5 botoes da sidebar do preview caiam no vazio. Adicionado wire-up roteando pros mesmos handlers do card (onDownload, onEdit, onShare, onSign, onDelete). Share/sign/delete fecham o preview antes de abrir o proprio dialog pra UX limpa; download mantem preview aberto (acao instantanea). DocumentGenerateDialog ganha prop editing-doc-id. Quando setado: - Busca template_id + dados_preenchidos via loadGeneratedFromDocId - Pre-seleciona template, popula vars (sobrescreve auto-loaded vars com dados_preenchidos pra preservar customizacao anterior) - Pula direto pra step 'edit' - Save vira UPDATE in-place (preserva documents.id e audit trail) - Header muda pra "Editar documento" + icone pi-pencil amber - Botao final vira "Substituir documento" - Doc sem registro generated (legado): toast info + flow normal de select template; ao salvar, cria o registro generated linkado. MelissaPatientDocuments: - onEdit substituido (era shortcut pra onPreview): abre generate dialog com editing-doc-id setado. - Novo ref editingDoc dedicado (separado do selectedDoc que serve preview/share/sign/delete) pra evitar vazar "edit state" pro botao "Gerar" do header quando user so abre preview e fecha. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,12 +13,19 @@ import { ref, watch, computed } from 'vue'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import { useDocumentGenerate } from '../composables/useDocumentGenerate'
|
||||
import { useDocumentTemplates } from '../composables/useDocumentTemplates'
|
||||
import { loadGeneratedFromDocId } from '@/services/DocumentGenerate.service'
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean, default: false },
|
||||
patientId: { type: String, default: null },
|
||||
patientName: { type: String, default: '' },
|
||||
agendaEventoId: { type: String, default: null }
|
||||
agendaEventoId: { type: String, default: null },
|
||||
// Modo edicao: ID de um documents.id existente. Quando setado, o dialog
|
||||
// busca o template_id + dados_preenchidos do document_generated vinculado,
|
||||
// pre-seleciona o template e popula as variaveis. Save vira UPDATE
|
||||
// in-place (preserva documents.id e audit). Doc sem registro generated
|
||||
// (uploaded direto) cai no flow normal de "select template".
|
||||
editingDocId: { type: String, default: null }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:visible', 'generated'])
|
||||
@@ -52,13 +59,48 @@ const {
|
||||
// ── Reset ao abrir ──────────────────────────────────────────
|
||||
|
||||
watch(() => props.visible, async (v) => {
|
||||
if (v) {
|
||||
step.value = 'select'
|
||||
reset()
|
||||
await Promise.all([
|
||||
fetchTemplates(),
|
||||
props.patientId ? loadVariables(props.patientId, props.agendaEventoId) : Promise.resolve()
|
||||
])
|
||||
if (!v) return;
|
||||
step.value = 'select'
|
||||
reset()
|
||||
await Promise.all([
|
||||
fetchTemplates(),
|
||||
props.patientId ? loadVariables(props.patientId, props.agendaEventoId) : Promise.resolve()
|
||||
])
|
||||
|
||||
// Modo edicao: tenta carregar o registro do generated, pre-seleciona
|
||||
// template e popula vars com dados_preenchidos (sobrescreve auto-vars
|
||||
// — preserva customizacao anterior do user). Se nao houver linkage
|
||||
// (doc uploaded direto), continua no flow normal de "select template".
|
||||
if (props.editingDocId) {
|
||||
const gen = await loadGeneratedFromDocId(props.editingDocId)
|
||||
if (gen?.template_id) {
|
||||
try {
|
||||
await selectTemplate(gen.template_id)
|
||||
// Merge: dados_preenchidos override auto-loaded variables.
|
||||
// Mantemos as vars que o user nao tinha customizado da vez
|
||||
// anterior (pra caso o template tenha vars novas adicionadas
|
||||
// depois) — pegamos as keys auto + sobrescreve com generated.
|
||||
const saved = gen.dados_preenchidos || {}
|
||||
Object.entries(saved).forEach(([k, val]) => {
|
||||
setVariable(k, val == null ? '' : String(val))
|
||||
})
|
||||
step.value = 'edit'
|
||||
} catch (e) {
|
||||
toast.add({
|
||||
severity: 'warn',
|
||||
summary: 'Template original não encontrado',
|
||||
detail: 'Selecione um template para regenerar o documento.',
|
||||
life: 3500
|
||||
})
|
||||
}
|
||||
} else {
|
||||
toast.add({
|
||||
severity: 'info',
|
||||
summary: 'Documento legado',
|
||||
detail: 'Sem dados de edição. Selecione um template para regenerar.',
|
||||
life: 3500
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -109,8 +151,15 @@ function onVarChange(key, val) {
|
||||
|
||||
async function onGenerate() {
|
||||
try {
|
||||
const result = await generateAndSave(props.patientId)
|
||||
toast.add({ severity: 'success', summary: 'Documento salvo', detail: 'Disponível nos documentos do paciente.', life: 3000 })
|
||||
const result = await generateAndSave(props.patientId, props.editingDocId || null)
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: props.editingDocId ? 'Documento atualizado' : 'Documento salvo',
|
||||
detail: props.editingDocId
|
||||
? 'PDF substituído com os novos valores.'
|
||||
: 'Disponível nos documentos do paciente.',
|
||||
life: 3000
|
||||
})
|
||||
emit('generated', result)
|
||||
close()
|
||||
} catch (e) {
|
||||
@@ -153,10 +202,10 @@ function close() {
|
||||
<template #header>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="flex items-center justify-center w-8 h-8 rounded-lg bg-green-500/10">
|
||||
<i class="pi pi-file-pdf text-green-600" />
|
||||
<i :class="editingDocId ? 'pi pi-pencil text-amber-600' : 'pi pi-file-pdf text-green-600'" />
|
||||
</span>
|
||||
<div>
|
||||
<div class="text-base font-semibold">Gerar documento</div>
|
||||
<div class="text-base font-semibold">{{ editingDocId ? 'Editar documento' : 'Gerar documento' }}</div>
|
||||
<div class="text-xs text-[var(--text-color-secondary)]">
|
||||
<template v-if="step === 'select'">Selecione um template</template>
|
||||
<template v-else-if="step === 'edit'">{{ selectedTemplate?.nome_template }} — {{ patientName }}</template>
|
||||
@@ -299,7 +348,7 @@ function close() {
|
||||
/>
|
||||
<Button
|
||||
v-if="step === 'preview'"
|
||||
label="Salvar documento"
|
||||
:label="editingDocId ? 'Substituir documento' : 'Salvar documento'"
|
||||
icon="pi pi-check"
|
||||
@click="onGenerate"
|
||||
:loading="generating"
|
||||
|
||||
@@ -57,6 +57,10 @@ const signatureDlg = ref(false);
|
||||
const shareDlg = ref(false);
|
||||
const selectedDoc = ref(null);
|
||||
const previewUrl = ref('');
|
||||
// Ref dedicado pro modo edicao do generate dialog. Separado do selectedDoc
|
||||
// (que tambem alimenta preview/share/sign/delete) pra evitar vazar "edit
|
||||
// state" pro "Gerar" do header quando o user so abre preview e fecha.
|
||||
const editingDoc = ref(null);
|
||||
|
||||
// ── Tipo selecionado (filtro pela sidebar) ────────────────
|
||||
// null = todos os tipos
|
||||
@@ -123,9 +127,15 @@ async function onPreview(doc) {
|
||||
function onDownload(doc) { download(doc); }
|
||||
|
||||
function onEdit(doc) {
|
||||
selectedDoc.value = doc;
|
||||
// Reusa preview dialog em modo "ver" — edit completo só via DocumentsListPage
|
||||
onPreview(doc);
|
||||
// Abre o DocumentGenerateDialog em modo edicao (editingDocId passado).
|
||||
// Dialog busca template + dados_preenchidos do document_generated e
|
||||
// pre-popula tudo, pulando direto pra step 'edit'. Save substitui o PDF
|
||||
// in-place no Storage e atualiza documents (preserva id + audit trail).
|
||||
// Docs uploaded direto (sem registro generated) caem no flow normal de
|
||||
// "select template" com um toast info.
|
||||
editingDoc.value = doc;
|
||||
previewDlg.value = false; // fecha preview se estiver aberto
|
||||
generateDlg.value = true;
|
||||
}
|
||||
|
||||
function onDelete(doc) {
|
||||
@@ -380,13 +390,24 @@ onBeforeUnmount(() => {
|
||||
:doc="selectedDoc"
|
||||
:preview-url="previewUrl"
|
||||
@updated="fetchDocuments"
|
||||
@download="onDownload"
|
||||
@edit="onEdit"
|
||||
@share="(d) => { previewDlg = false; onShare(d); }"
|
||||
@sign="(d) => { previewDlg = false; onSign(d); }"
|
||||
@delete="(d) => { previewDlg = false; onDelete(d); }"
|
||||
/>
|
||||
<!-- editing-doc-id vem do ref editingDoc dedicado — so e setado
|
||||
via onEdit (botao Editar). "Gerar" no header usa generateDlg=true
|
||||
com editingDoc=null, abrindo limpo. Limpa editingDoc no
|
||||
fechamento pra nao vazar pro proximo Gerar. -->
|
||||
<DocumentGenerateDialog
|
||||
v-if="patientId"
|
||||
v-model:visible="generateDlg"
|
||||
:visible="generateDlg"
|
||||
:patient-id="patientId"
|
||||
:patient-name="patientName"
|
||||
:editing-doc-id="editingDoc?.id || null"
|
||||
@generated="onGenerated"
|
||||
@update:visible="(v) => { generateDlg = v; if (!v) editingDoc = null; }"
|
||||
/>
|
||||
<DocumentSignatureDialog
|
||||
:visible="signatureDlg"
|
||||
|
||||
Reference in New Issue
Block a user