From 19caa42f3b53c1b7ce239e29a126a300ece7cf27 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Thu, 21 May 2026 04:52:01 -0300 Subject: [PATCH] compliance CFP #7: dialog gera share_link junto com signature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DocumentSignatureDialog (terapeuta-side) ja existia com fluxo de add signatarios. Estendido pra: - Checkbox "Gerar link publico para assinatura" (default ON) - Select de validade (24h/3d/7d/30d, default 7d) - Apos submit: alem de createSignatureRequests chama createShareLink e exibe o URL gerado num bloco emerald com botao Copy - Dialog fica aberto se gerou link (terapeuta copia/envia); fecha se nao gerou Fluxo end-to-end agora funcional: terapeuta clica "Solicitar assinatura" no DocumentsListPage > preenche signatarios > submit gera signature requests + share_link > copia URL > envia via WA/ email > paciente abre /shared/document/:token > assina via fluxo publico (RPC sign_document_by_token capturando IP/UA server-side). Fecha ROADMAP #1.2 #6/#7 — Compliance basico BR completo (#5/#6/#7/ #8/#9 todos verdes, #6 com TCLE + Telehealth + TCLE menores + termo sigilo + LGPD + autorizacao gravacao). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../components/DocumentSignatureDialog.vue | 93 ++++++++++++++++++- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/src/features/documents/components/DocumentSignatureDialog.vue b/src/features/documents/components/DocumentSignatureDialog.vue index 1383638..430c5ee 100644 --- a/src/features/documents/components/DocumentSignatureDialog.vue +++ b/src/features/documents/components/DocumentSignatureDialog.vue @@ -17,6 +17,7 @@ import { listSignatures, getSignatureStatus } from '@/services/DocumentSignatures.service' +import { createShareLink, buildShareUrl } from '@/services/DocumentShareLinks.service' const props = defineProps({ visible: { type: Boolean, default: false }, @@ -42,6 +43,11 @@ const TIPOS_SIGNATARIO = [ const signatarios = ref([]) const patientEmails = ref([]) +// Geracao de share link p/ assinatura via portal/whatsapp +const generateLink = ref(true) +const linkExpiracaoHoras = ref(168) // 7 dias default +const generatedShareUrl = ref('') + function addSignatario() { signatarios.value.push({ tipo: 'paciente', nome: '', email: '' }) } @@ -81,6 +87,7 @@ function useEmail(email) { watch(() => props.visible, async (v) => { if (v && props.doc) { signatarios.value = [] + generatedShareUrl.value = '' loading.value = true try { const [sigs, status] = await Promise.all([ @@ -99,6 +106,13 @@ watch(() => props.visible, async (v) => { } }) +function copyShareUrl() { + if (!generatedShareUrl.value) return + navigator.clipboard.writeText(generatedShareUrl.value) + .then(() => toast.add({ severity: 'success', summary: 'Link copiado', life: 1800 })) + .catch(() => toast.add({ severity: 'warn', summary: 'Falha ao copiar', detail: 'Copie manualmente.', life: 2200 })) +} + // ── Status badge ──────────────────────────────────────────── const statusColor = computed(() => { @@ -146,9 +160,39 @@ async function submit() { saving.value = true try { const result = await createSignatureRequests(props.doc.id, signatarios.value) - toast.add({ severity: 'success', summary: 'Solicitação enviada', detail: `${result.length} signatário(s) adicionado(s).`, life: 3000 }) - emit('requested', result) - emit('update:visible', false) + + // Gera share link público quando habilitado — o paciente abre /shared/document/:token + // e assina via fluxo público (RPC sign_document_by_token captura IP/UA server-side). + if (generateLink.value) { + try { + const link = await createShareLink(props.doc.id, { + expiracaoHoras: Number(linkExpiracaoHoras.value) || 168, + usosMax: Math.max(signatarios.value.length * 3, 5) + }) + generatedShareUrl.value = buildShareUrl(link.token) + toast.add({ + severity: 'success', + summary: 'Solicitação criada', + detail: `${result.length} signatário(s). Link de assinatura gerado.`, + life: 3500 + }) + } catch (linkErr) { + toast.add({ + severity: 'warn', + summary: 'Signatários criados, mas falhou o link', + detail: linkErr?.message || 'Tente gerar o link na ação "Compartilhar".', + life: 4500 + }) + } + } else { + toast.add({ severity: 'success', summary: 'Solicitação enviada', detail: `${result.length} signatário(s) adicionado(s).`, life: 3000 }) + } + + emit('requested', { signatures: result, shareUrl: generatedShareUrl.value }) + + // Mantém dialog aberto se gerou link — pra terapeuta copiar. + // Fecha automaticamente se não gerou link. + if (!generatedShareUrl.value) emit('update:visible', false) } catch (e) { toast.add({ severity: 'error', summary: 'Erro', detail: e?.message || 'Falha ao solicitar assinatura.' }) } finally { @@ -263,6 +307,49 @@ function close() { + +
+ +
+ +
+ Cria um link em /shared/document/<token> pra enviar via WhatsApp, e-mail ou copiar. O paciente assina sem precisar logar (IP, navegador e timestamp são registrados server-side). +
+
+ +