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 { useToast } from 'primevue/usetoast'
|
||||||
import { useDocumentGenerate } from '../composables/useDocumentGenerate'
|
import { useDocumentGenerate } from '../composables/useDocumentGenerate'
|
||||||
import { useDocumentTemplates } from '../composables/useDocumentTemplates'
|
import { useDocumentTemplates } from '../composables/useDocumentTemplates'
|
||||||
|
import { loadGeneratedFromDocId } from '@/services/DocumentGenerate.service'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: { type: Boolean, default: false },
|
visible: { type: Boolean, default: false },
|
||||||
patientId: { type: String, default: null },
|
patientId: { type: String, default: null },
|
||||||
patientName: { type: String, default: '' },
|
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'])
|
const emit = defineEmits(['update:visible', 'generated'])
|
||||||
@@ -52,13 +59,48 @@ const {
|
|||||||
// ── Reset ao abrir ──────────────────────────────────────────
|
// ── Reset ao abrir ──────────────────────────────────────────
|
||||||
|
|
||||||
watch(() => props.visible, async (v) => {
|
watch(() => props.visible, async (v) => {
|
||||||
if (v) {
|
if (!v) return;
|
||||||
step.value = 'select'
|
step.value = 'select'
|
||||||
reset()
|
reset()
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fetchTemplates(),
|
fetchTemplates(),
|
||||||
props.patientId ? loadVariables(props.patientId, props.agendaEventoId) : Promise.resolve()
|
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() {
|
async function onGenerate() {
|
||||||
try {
|
try {
|
||||||
const result = await generateAndSave(props.patientId)
|
const result = await generateAndSave(props.patientId, props.editingDocId || null)
|
||||||
toast.add({ severity: 'success', summary: 'Documento salvo', detail: 'Disponível nos documentos do paciente.', life: 3000 })
|
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)
|
emit('generated', result)
|
||||||
close()
|
close()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -153,10 +202,10 @@ function close() {
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span class="flex items-center justify-center w-8 h-8 rounded-lg bg-green-500/10">
|
<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>
|
</span>
|
||||||
<div>
|
<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)]">
|
<div class="text-xs text-[var(--text-color-secondary)]">
|
||||||
<template v-if="step === 'select'">Selecione um template</template>
|
<template v-if="step === 'select'">Selecione um template</template>
|
||||||
<template v-else-if="step === 'edit'">{{ selectedTemplate?.nome_template }} — {{ patientName }}</template>
|
<template v-else-if="step === 'edit'">{{ selectedTemplate?.nome_template }} — {{ patientName }}</template>
|
||||||
@@ -299,7 +348,7 @@ function close() {
|
|||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
v-if="step === 'preview'"
|
v-if="step === 'preview'"
|
||||||
label="Salvar documento"
|
:label="editingDocId ? 'Substituir documento' : 'Salvar documento'"
|
||||||
icon="pi pi-check"
|
icon="pi pi-check"
|
||||||
@click="onGenerate"
|
@click="onGenerate"
|
||||||
:loading="generating"
|
:loading="generating"
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ const signatureDlg = ref(false);
|
|||||||
const shareDlg = ref(false);
|
const shareDlg = ref(false);
|
||||||
const selectedDoc = ref(null);
|
const selectedDoc = ref(null);
|
||||||
const previewUrl = ref('');
|
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) ────────────────
|
// ── Tipo selecionado (filtro pela sidebar) ────────────────
|
||||||
// null = todos os tipos
|
// null = todos os tipos
|
||||||
@@ -123,9 +127,15 @@ async function onPreview(doc) {
|
|||||||
function onDownload(doc) { download(doc); }
|
function onDownload(doc) { download(doc); }
|
||||||
|
|
||||||
function onEdit(doc) {
|
function onEdit(doc) {
|
||||||
selectedDoc.value = doc;
|
// Abre o DocumentGenerateDialog em modo edicao (editingDocId passado).
|
||||||
// Reusa preview dialog em modo "ver" — edit completo só via DocumentsListPage
|
// Dialog busca template + dados_preenchidos do document_generated e
|
||||||
onPreview(doc);
|
// 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) {
|
function onDelete(doc) {
|
||||||
@@ -380,13 +390,24 @@ onBeforeUnmount(() => {
|
|||||||
:doc="selectedDoc"
|
:doc="selectedDoc"
|
||||||
:preview-url="previewUrl"
|
:preview-url="previewUrl"
|
||||||
@updated="fetchDocuments"
|
@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
|
<DocumentGenerateDialog
|
||||||
v-if="patientId"
|
v-if="patientId"
|
||||||
v-model:visible="generateDlg"
|
:visible="generateDlg"
|
||||||
:patient-id="patientId"
|
:patient-id="patientId"
|
||||||
:patient-name="patientName"
|
:patient-name="patientName"
|
||||||
|
:editing-doc-id="editingDoc?.id || null"
|
||||||
@generated="onGenerated"
|
@generated="onGenerated"
|
||||||
|
@update:visible="(v) => { generateDlg = v; if (!v) editingDoc = null; }"
|
||||||
/>
|
/>
|
||||||
<DocumentSignatureDialog
|
<DocumentSignatureDialog
|
||||||
:visible="signatureDlg"
|
:visible="signatureDlg"
|
||||||
|
|||||||
Reference in New Issue
Block a user