diff --git a/susconecta/app/profissional/page.tsx b/susconecta/app/profissional/page.tsx
index e800161..85e6a64 100644
--- a/susconecta/app/profissional/page.tsx
+++ b/susconecta/app/profissional/page.tsx
@@ -13,6 +13,7 @@ import { buscarPacientes, listarPacientes, buscarPacientePorId, buscarPacientesP
import { ENV_CONFIG } from '@/lib/env-config';
import { useReports } from "@/hooks/useReports";
import { CreateReportData } from "@/types/report-types";
+import { createAndNotifyReport } from "@/lib/reportService";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -2588,6 +2589,9 @@ const ProfissionalPage = () => {
if (isNewLaudo) {
if (createNewReport) {
const created = await createNewReport(payload as any);
+ console.log('[LaudoEditor] Report criado:', { created, patient_id: payload.patient_id });
+ // ✅ Webhook agora é enviado automaticamente dentro de createNewReport() / criarRelatorio()
+
if (onSaved) onSaved(created);
}
} else {
diff --git a/susconecta/lib/laudo-exemplos.ts b/susconecta/lib/laudo-exemplos.ts
new file mode 100644
index 0000000..14f6b02
--- /dev/null
+++ b/susconecta/lib/laudo-exemplos.ts
@@ -0,0 +1,275 @@
+/**
+ * EXEMPLO DE USO: Automação n8n para Notificação de Laudos
+ *
+ * Este arquivo demonstra como usar a função criarLaudo com integração n8n
+ * para criar um laudo e notificar automaticamente o paciente.
+ */
+
+import { criarLaudo, CriarLaudoData } from '@/lib/reports';
+
+/**
+ * Exemplo 1: Uso básico - criar um laudo simples
+ */
+export async function exemploBasico() {
+ try {
+ const laudoData: CriarLaudoData = {
+ pacienteId: 'patient-uuid-123', // ID do paciente (obrigatório)
+ textoLaudo: 'Paciente apresenta boa saúde geral. Sem achados relevantes.',
+ };
+
+ const novoLaudo = await criarLaudo(laudoData);
+
+ console.log('✓ Laudo criado com sucesso!');
+ console.log('ID do laudo:', novoLaudo.id);
+ console.log('Mensagem:', novoLaudo.mensagem);
+
+ return novoLaudo;
+ } catch (erro) {
+ console.error('✗ Erro ao criar laudo:', erro);
+ throw erro;
+ }
+}
+
+/**
+ * Exemplo 2: Criar laudo com dados médicos completos
+ */
+export async function exemploCompleto() {
+ try {
+ const laudoData: CriarLaudoData = {
+ pacienteId: 'patient-uuid-789',
+ medicoId: 'doctor-uuid-456', // Opcional
+ textoLaudo: `
+ AVALIAÇÃO CLÍNICA COMPLETA
+
+ Queixa Principal: Dor de cabeça persistente
+
+ História Presente:
+ Paciente relata dor de cabeça tipo tensional há 2 semanas,
+ intensidade 5/10, sem irradiação.
+
+ Exame Físico:
+ - PA: 120/80 mmHg
+ - FC: 72 bpm
+ - Sem alterações neurológicas
+
+ Impressão Diagnóstica:
+ Cefaleia tensional
+
+ Conduta:
+ - Repouso adequado
+ - Analgésicos conforme necessidade
+ - Retorno em 2 semanas se persistir
+ `,
+ exame: 'Consulta Neurologia',
+ diagnostico: 'Cefaleia tensional',
+ conclusao: 'Prescrição: Dipirona 500mg 6/6h conforme necessidade',
+ cidCode: 'G44.2', // CID da cefaleia tensional
+ status: 'concluido',
+ };
+
+ const novoLaudo = await criarLaudo(laudoData);
+
+ console.log('✓ Laudo completo criado com sucesso!');
+ console.log('ID:', novoLaudo.id);
+ console.log('Status:', novoLaudo.status);
+ console.log('CID:', novoLaudo.cid_code);
+
+ return novoLaudo;
+ } catch (erro) {
+ console.error('✗ Erro:', erro);
+ throw erro;
+ }
+}
+
+/**
+ * Exemplo 3: Integração em um componente React
+ * Este exemplo mostra como usar a função em um formulário
+ *
+ * NOTA: Este código deve ser usado em um arquivo .tsx (não .ts)
+ * e com o import de React importado corretamente
+ */
+export async function exemploComponenteReact() {
+ // Este é apenas um exemplo de estrutura para o componente
+ // Copie o código abaixo para um arquivo .tsx:
+ /*
+ 'use client';
+
+ import React from 'react';
+ import { criarLaudo, CriarLaudoData } from '@/lib/reports';
+
+ export function ComponenteLaudoExemplo() {
+ const [carregando, setCarregando] = React.useState(false);
+ const [mensagem, setMensagem] = React.useState('');
+
+ const handleCriarLaudo = async (formData: any) => {
+ setCarregando(true);
+ setMensagem('');
+
+ try {
+ const laudoData: CriarLaudoData = {
+ pacienteId: formData.pacienteId,
+ medicoId: formData.medicoId,
+ textoLaudo: formData.texto,
+ exame: formData.exame,
+ diagnostico: formData.diagnostico,
+ conclusao: formData.conclusao,
+ cidCode: formData.cid,
+ status: 'concluido',
+ };
+
+ const resultado = await criarLaudo(laudoData);
+
+ setMensagem(`✓ ${resultado.mensagem}`);
+ console.log('Laudo criado:', resultado.id);
+
+ // Você pode fazer mais algo aqui, como:
+ // - Redirecionar para página do laudo
+ // - Atualizar lista de laudos
+ // - Limpar formulário
+
+ } catch (erro) {
+ setMensagem(`✗ Erro: ${erro instanceof Error ? erro.message : String(erro)}`);
+ } finally {
+ setCarregando(false);
+ }
+ };
+
+ return (
+
+
+
+ {mensagem &&
{mensagem}
}
+
+ );
+ }
+ */
+}
+
+/**
+ * Exemplo 4: Tratamento de erros específicos
+ */
+export async function exemploTratamentoErros() {
+ try {
+ const laudoData: CriarLaudoData = {
+ pacienteId: 'patient-id',
+ medicoId: 'doctor-id',
+ textoLaudo: 'Texto do laudo',
+ };
+
+ const resultado = await criarLaudo(laudoData);
+ console.log('Sucesso:', resultado);
+
+ } catch (erro) {
+ if (erro instanceof Error) {
+ // Trata diferentes tipos de erro
+ if (erro.message.includes('Paciente ID') || erro.message.includes('Médico ID')) {
+ console.error('Erro de validação: dados incompletos');
+ } else if (erro.message.includes('Supabase')) {
+ console.error('Erro de conexão com banco de dados');
+ } else if (erro.message.includes('n8n')) {
+ console.warn('Laudo criado, mas notificação falhou');
+ } else {
+ console.error('Erro desconhecido:', erro.message);
+ }
+ }
+ }
+}
+
+/**
+ * DOCUMENTAÇÃO DO FLUXO N8N
+ *
+ * A função criarLaudo executa o seguinte fluxo:
+ *
+ * 1. CRIAÇÃO NO SUPABASE
+ * - Salva o report na tabela 'reports' do Supabase
+ * - Status padrão: 'concluido'
+ * - Retorna o report criado com seu ID
+ *
+ * 2. NOTIFICAÇÃO N8N
+ * - Se o report foi criado com sucesso, faz um POST para:
+ * URL: https://joaogustavo.me/webhook/notificar-laudo
+ * - Envia payload com:
+ * - pacienteId: ID do paciente (patient_id)
+ * - reportId: ID do report criado
+ *
+ * 3. NO N8N
+ * O webhook deve estar configurado para:
+ * - Receber o payload JSON POST
+ * - Extrair pacienteId e reportId
+ * - Buscar informações do paciente
+ * - Enviar notificação (email, SMS, push, etc.)
+ * - Registrar log da notificação
+ *
+ * 4. COMPORTAMENTO EM CASO DE FALHA
+ * - Se a criação do report falhar: exceção é lançada
+ * - Se o envio para n8n falhar: report é mantido, erro é logado
+ * (não bloqueia a operação de criação)
+ *
+ * EXEMPLO DE USO:
+ *
+ * const novoReport = await criarLaudo({
+ * pacienteId: "3854866a-5476-48be-8313-77029ccdb70f",
+ * textoLaudo: "Texto do laudo aqui..."
+ * });
+ *
+ * // Depois disto, automaticamente:
+ * // 1. Report é salvo no Supabase
+ * // 2. n8n recebe: { pacienteId: "...", reportId: "..." }
+ * // 3. Paciente é notificado
+ */
+
+/**
+ * EXEMPLO DE WEBHOOK N8N (Configuração)
+ *
+ * No n8n, você deve:
+ * 1. Criar um novo workflow
+ * 2. Adicionar trigger: "Webhook"
+ * 3. Configurar:
+ * - HTTP Method: POST
+ * - Path: /notificar-laudo
+ * - Authentication: None (ou Bearer token se desejar)
+ * 4. Adicionar nós para:
+ * - Parse do payload JSON recebido
+ * - Query no banco de dados para buscar paciente
+ * - Enviar email/SMS/notificação push
+ * - Logging do resultado
+ *
+ * Exemplo de nó JavaScript no n8n:
+ *
+ * const { pacienteId, laudoId, pacienteName, pacienteEmail } = $input.first().json;
+ *
+ * return {
+ * pacienteId,
+ * laudoId,
+ * pacienteName,
+ * pacienteEmail,
+ * notificationType: 'laudo_criado',
+ * timestamp: new Date().toISOString(),
+ * message: `Novo laudo ${laudoId} disponível para ${pacienteName}`
+ * };
+ */
diff --git a/susconecta/lib/laudo-notification.ts b/susconecta/lib/laudo-notification.ts
new file mode 100644
index 0000000..03737e3
--- /dev/null
+++ b/susconecta/lib/laudo-notification.ts
@@ -0,0 +1,193 @@
+/**
+ * Módulo de notificação de laudos via n8n
+ * Integração com automação n8n para notificar pacientes quando laudos são criados
+ */
+
+import { ENV_CONFIG } from '@/lib/env-config';
+
+/**
+ * Configurações do webhook n8n
+ */
+const N8N_WEBHOOK_CONFIG = {
+ // URL do webhook configurado no n8n
+ webhookUrl: 'https://joaogustavo.me/webhook/notificar-laudo',
+ // Timeout para a requisição (em ms)
+ timeout: 30000,
+ // Tentativas de retry em caso de falha
+ maxRetries: 3,
+};
+
+/**
+ * Tipos de dados para notificação de laudo
+ */
+export interface NotificacaoLaudoPayload {
+ pacienteId: string;
+ laudoId: string;
+ pacienteName?: string;
+ pacienteEmail?: string;
+ medicalDetails?: {
+ examType?: string;
+ medico?: string;
+ dataEmissao?: string;
+ };
+}
+
+/**
+ * Resultado da notificação
+ */
+export interface NotificacaoLaudoResult {
+ sucesso: boolean;
+ mensagem: string;
+ n8nResponse?: any;
+ erro?: string;
+}
+
+/**
+ * Notifica o n8n sobre a criação de um novo laudo
+ * @param payload Dados do laudo e paciente para notificação
+ * @returns Resultado da notificação
+ */
+export async function notificarLaudoCriadoN8n(
+ payload: NotificacaoLaudoPayload
+): Promise {
+ try {
+ // Validação básica dos dados
+ if (!payload.pacienteId || !payload.laudoId) {
+ return {
+ sucesso: false,
+ mensagem: 'Dados de paciente ou laudo inválidos',
+ erro: 'pacienteId e laudoId são obrigatórios',
+ };
+ }
+
+ // Constrói o payload para o webhook
+ const webhookPayload = {
+ pacienteId: payload.pacienteId,
+ laudoId: payload.laudoId,
+ pacienteName: payload.pacienteName || '',
+ pacienteEmail: payload.pacienteEmail || '',
+ // Adiciona dados médicos se disponíveis
+ ...(payload.medicalDetails && {
+ examType: payload.medicalDetails.examType,
+ medico: payload.medicalDetails.medico,
+ dataEmissao: payload.medicalDetails.dataEmissao,
+ }),
+ // Timestamp da notificação
+ notificadoEm: new Date().toISOString(),
+ };
+
+ console.log('[n8n] Enviando notificação de laudo criado:', {
+ pacienteId: payload.pacienteId,
+ laudoId: payload.laudoId,
+ webhookUrl: N8N_WEBHOOK_CONFIG.webhookUrl,
+ });
+
+ // Tenta enviar o webhook com retry
+ let ultimoErro: any = null;
+
+ for (let tentativa = 1; tentativa <= N8N_WEBHOOK_CONFIG.maxRetries; tentativa++) {
+ try {
+ const controller = new AbortController();
+ const timeoutId = setTimeout(
+ () => controller.abort(),
+ N8N_WEBHOOK_CONFIG.timeout
+ );
+
+ const response = await fetch(N8N_WEBHOOK_CONFIG.webhookUrl, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ },
+ body: JSON.stringify(webhookPayload),
+ signal: controller.signal,
+ });
+
+ clearTimeout(timeoutId);
+
+ if (!response.ok) {
+ throw new Error(
+ `HTTP ${response.status}: ${response.statusText}`
+ );
+ }
+
+ const responseData = await response.json();
+
+ console.log('[n8n] Notificação enviada com sucesso:', {
+ status: response.status,
+ laudoId: payload.laudoId,
+ });
+
+ return {
+ sucesso: true,
+ mensagem: 'Paciente notificado com sucesso',
+ n8nResponse: responseData,
+ };
+ } catch (erro) {
+ ultimoErro = erro;
+ console.warn(
+ `[n8n] Tentativa ${tentativa}/${N8N_WEBHOOK_CONFIG.maxRetries} falhou:`,
+ erro instanceof Error ? erro.message : String(erro)
+ );
+
+ // Se não for a última tentativa, aguarda um pouco antes de tentar novamente
+ if (tentativa < N8N_WEBHOOK_CONFIG.maxRetries) {
+ await new Promise((resolve) =>
+ setTimeout(resolve, 1000 * tentativa) // Backoff exponencial
+ );
+ }
+ }
+ }
+
+ // Se chegou aqui, todas as tentativas falharam
+ console.error('[n8n] Todas as tentativas de notificação falharam:', ultimoErro);
+
+ return {
+ sucesso: false,
+ mensagem: 'Falha ao notificar paciente através do n8n',
+ erro: ultimoErro instanceof Error ? ultimoErro.message : String(ultimoErro),
+ };
+ } catch (erro) {
+ console.error('[notificarLaudoCriadoN8n] Erro inesperado:', erro);
+
+ return {
+ sucesso: false,
+ mensagem: 'Erro ao processar notificação de laudo',
+ erro: erro instanceof Error ? erro.message : String(erro),
+ };
+ }
+}
+
+/**
+ * Versão assíncrona que não bloqueia - envia notificação em background
+ * Útil para não aumentar o tempo de resposta da API
+ * @param payload Dados do laudo e paciente
+ */
+export function notificarLaudoAsyncBackground(
+ payload: NotificacaoLaudoPayload
+): void {
+ // Envia notificação em background sem aguardar
+ notificarLaudoCriadoN8n(payload)
+ .then((result) => {
+ if (!result.sucesso) {
+ console.warn('[n8n] Notificação de laudo falhou (background):', result.erro);
+ }
+ })
+ .catch((erro) => {
+ console.error('[n8n] Erro ao notificar laudo em background:', erro);
+ });
+}
+
+/**
+ * Determina se as notificações n8n estão habilitadas
+ * Pode ser controlado via variável de ambiente
+ */
+export function notificacoesHabilitadas(): boolean {
+ if (typeof window === 'undefined') {
+ // Server-side: verificar variável de ambiente
+ return process.env.NEXT_PUBLIC_N8N_ENABLED !== 'false';
+ }
+
+ // Client-side: sempre habilitado
+ return true;
+}
diff --git a/susconecta/lib/reportService.ts b/susconecta/lib/reportService.ts
new file mode 100644
index 0000000..c0ddcc6
--- /dev/null
+++ b/susconecta/lib/reportService.ts
@@ -0,0 +1,148 @@
+/**
+ * serviço para criar relatórios e notificar pacientes via n8n
+ *
+ * Este serviço encapsula a lógica de:
+ * 1. Criar um novo report no Supabase
+ * 2. Notificar o paciente via webhook n8n (que dispara SMS via Twilio)
+ */
+
+interface CreateReportData {
+ patientId: string; // UUID do paciente
+ requestedBy: string; // UUID de quem solicitou (médico)
+ exam: string;
+ diagnosis: string;
+ conclusion: string;
+ contentHtml: string;
+}
+
+interface CreateReportResult {
+ success: boolean;
+ report?: any;
+ error?: string;
+}
+
+/**
+ * Cria um novo report no Supabase e notifica o paciente via n8n
+ *
+ * Fluxo:
+ * 1. Insere um novo registro na tabela 'reports' com status 'draft'
+ * 2. Envia webhook para n8n com pacienteId e reportId
+ * 3. n8n recebe e dispara notificação SMS via Twilio
+ * 4. Retorna o report criado (mesmo que a notificação falhe)
+ *
+ * @param data Dados do report a ser criado
+ * @returns { success: true, report } ou { success: false, error }
+ */
+export const createAndNotifyReport = async (data: CreateReportData): Promise => {
+ try {
+ // Validação básica
+ if (!data.patientId || !data.exam || !data.conclusion) {
+ throw new Error('Faltam campos obrigatórios: patientId, exam, conclusion');
+ }
+
+ console.log('[reportService] Criando novo report para paciente:', data.patientId);
+
+ // 1. Criar report no Supabase
+ const BASE_API = 'https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/reports';
+
+ let token: string | undefined = undefined;
+ if (typeof window !== 'undefined') {
+ token =
+ localStorage.getItem('auth_token') ||
+ localStorage.getItem('token') ||
+ sessionStorage.getItem('auth_token') ||
+ sessionStorage.getItem('token') ||
+ undefined;
+ }
+
+ const headers: HeadersInit = {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ 'apikey': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ',
+ 'Prefer': 'return=representation',
+ };
+
+ if (token) {
+ headers['Authorization'] = `Bearer ${token}`;
+ }
+
+ const reportPayload = {
+ patient_id: data.patientId,
+ status: 'draft',
+ requested_by: data.requestedBy,
+ exam: data.exam,
+ diagnosis: data.diagnosis,
+ conclusion: data.conclusion,
+ content_html: data.contentHtml,
+ created_at: new Date().toISOString(),
+ };
+
+ const responseSupabase = await fetch(BASE_API, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify(reportPayload),
+ });
+
+ if (!responseSupabase.ok) {
+ const errorText = await responseSupabase.text();
+ console.error('[reportService] Erro ao criar report no Supabase:', errorText);
+ throw new Error(`Supabase error: ${responseSupabase.statusText}`);
+ }
+
+ const newReport = await responseSupabase.json();
+
+ // Supabase retorna array
+ const report = Array.isArray(newReport) ? newReport[0] : newReport;
+
+ if (!report || !report.id) {
+ throw new Error('Report criado mas sem ID retornado');
+ }
+
+ console.log('[reportService] Report criado com sucesso. ID:', report.id);
+
+ // 2. Notificar paciente via n8n → Twilio
+ try {
+ console.log('[reportService] Enviando notificação para n8n...');
+
+ const notificationResponse = await fetch('https://joaogustavo.me/webhook/notificar-laudo', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ pacienteId: report.patient_id, // UUID do paciente
+ reportId: report.id, // UUID do report
+ }),
+ });
+
+ if (!notificationResponse.ok) {
+ console.warn(
+ '[reportService] Erro ao enviar notificação SMS. Status:',
+ notificationResponse.status
+ );
+ // Não falha a criação do report se SMS falhar
+ } else {
+ console.log('[reportService] Notificação enviada com sucesso ao n8n');
+ }
+ } catch (erroNotificacao) {
+ console.warn('[reportService] Erro ao enviar notificação para n8n:', erroNotificacao);
+ // Não falha a criação do report se a notificação falhar
+ }
+
+ return {
+ success: true,
+ report,
+ };
+ } catch (error) {
+ console.error('[reportService] Erro ao criar report:', error);
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : String(error),
+ };
+ }
+};
+
+/**
+ * Interface exportada para uso em componentes
+ */
+export type { CreateReportData, CreateReportResult };
diff --git a/susconecta/lib/reports.ts b/susconecta/lib/reports.ts
index a54c0e0..0f7475d 100644
--- a/susconecta/lib/reports.ts
+++ b/susconecta/lib/reports.ts
@@ -47,6 +47,7 @@ import {
ReportsResponse,
ReportResponse
} from '@/types/report-types';
+import { buscarPacientePorId } from '@/lib/api';
// Definição local para ApiError
type ApiError = {
@@ -214,7 +215,52 @@ export async function criarRelatorio(dadosRelatorio: CreateReportData, token?: s
const resultado = await resposta.json();
// Supabase retorna array
if (Array.isArray(resultado) && resultado.length > 0) {
- return resultado[0];
+ const novoRelatorio = resultado[0];
+
+ // ✅ ENVIAR NOTIFICAÇÃO PARA N8N APÓS CRIAR RELATÓRIO
+ if (novoRelatorio && novoRelatorio.id && dadosRelatorio.patient_id) {
+ try {
+ console.log('[criarRelatorio] Enviando notificação para n8n webhook...');
+
+ // Buscar dados do paciente para incluir nome e telefone
+ const pacienteData = await buscarPacientePorId(dadosRelatorio.patient_id).catch(e => {
+ console.warn('[criarRelatorio] Erro ao buscar paciente:', e);
+ return null;
+ });
+
+ const pacienteNome = pacienteData?.full_name || '';
+ const pacienteCelular = pacienteData?.phone_mobile || '';
+
+ const payloadWebhook = {
+ pacienteId: dadosRelatorio.patient_id,
+ reportId: novoRelatorio.id,
+ pacienteNome: pacienteNome,
+ pacienteCelular: pacienteCelular
+ };
+
+ console.log('[criarRelatorio] Payload do webhook:', payloadWebhook);
+
+ const resNotificacao = await fetch('https://joaogustavo.me/webhook/notificar-laudo', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payloadWebhook)
+ }).catch(e => {
+ console.warn('[criarRelatorio] Erro de rede ao enviar webhook:', e);
+ return null;
+ });
+
+ if (resNotificacao?.ok) {
+ console.log('[criarRelatorio] ✅ Notificação enviada com sucesso ao n8n');
+ } else if (resNotificacao) {
+ console.warn('[criarRelatorio] ⚠️ Notificação ao n8n retornou status:', resNotificacao.status);
+ }
+ } catch (erroNotificacao) {
+ console.warn('[criarRelatorio] ❌ Erro ao enviar notificação para n8n:', erroNotificacao);
+ // Não falha a criação do relatório se a notificação falhar
+ }
+ }
+
+ return novoRelatorio;
}
throw new Error('Resposta inesperada da API Supabase');
}
@@ -384,4 +430,134 @@ export async function listarRelatoriosParaMedicoAtribuido(userId?: string): Prom
console.error('[listarRelatoriosParaMedicoAtribuido] erro:', err);
throw err;
}
+}
+
+/**
+ * Interface para dados necessários ao criar um laudo
+ */
+export interface CriarLaudoData {
+ pacienteId: string; // ID do paciente (obrigatório)
+ textoLaudo: string; // Texto do laudo (obrigatório)
+ medicoId?: string; // ID do médico que criou (opcional)
+ exame?: string; // Tipo de exame (opcional)
+ diagnostico?: string; // Diagnóstico (opcional)
+ conclusao?: string; // Conclusão (opcional)
+ cidCode?: string; // Código CID (opcional)
+ status?: 'rascunho' | 'concluido' | 'enviado'; // Status (opcional, padrão: 'concluido')
+ contentHtml?: string; // Conteúdo HTML (opcional)
+ contentJson?: any; // Conteúdo JSON (opcional)
+}
+
+/**
+ * Cria um novo laudo no Supabase e notifica o paciente via n8n
+ *
+ * Fluxo:
+ * 1. Salva o laudo no Supabase (tabela 'reports')
+ * 2. Envia notificação ao n8n com pacienteId e laudoId
+ * 3. Retorna o laudo criado
+ *
+ * @param laudoData Dados do laudo a criar
+ * @returns Laudo criado com ID
+ * @throws Erro se falhar ao criar o laudo
+ */
+export async function criarLaudo(laudoData: CriarLaudoData): Promise {
+ try {
+ // 1. Validação dos dados obrigatórios
+ if (!laudoData.pacienteId || !laudoData.textoLaudo) {
+ throw new Error('Paciente ID e Texto do Laudo são obrigatórios');
+ }
+
+ console.log('[criarLaudo] Criando laudo para paciente:', laudoData.pacienteId);
+
+ // 2. Monta o payload para Supabase
+ const payloadSupabase = {
+ patient_id: laudoData.pacienteId,
+ ...(laudoData.medicoId && { requested_by: laudoData.medicoId }),
+ ...(laudoData.exame && { exam: laudoData.exame }),
+ ...(laudoData.diagnostico && { diagnosis: laudoData.diagnostico }),
+ ...(laudoData.conclusao && { conclusion: laudoData.conclusao }),
+ ...(laudoData.cidCode && { cid_code: laudoData.cidCode }),
+ ...(laudoData.contentHtml && { content_html: laudoData.contentHtml }),
+ ...(laudoData.contentJson && { content_json: laudoData.contentJson }),
+ status: laudoData.status || 'concluido',
+ };
+
+ // 3. Salva o laudo no Supabase
+ const urlSupabase = 'https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/reports';
+
+ let tokenAuth: string | undefined = undefined;
+ if (typeof window !== 'undefined') {
+ tokenAuth =
+ localStorage.getItem('auth_token') ||
+ localStorage.getItem('token') ||
+ sessionStorage.getItem('auth_token') ||
+ sessionStorage.getItem('token') ||
+ undefined;
+ }
+
+ const headersSupabase: HeadersInit = {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ 'apikey': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ',
+ 'Prefer': 'return=representation',
+ };
+
+ if (tokenAuth) {
+ headersSupabase['Authorization'] = `Bearer ${tokenAuth}`;
+ }
+
+ const resSupabase = await fetch(urlSupabase, {
+ method: 'POST',
+ headers: headersSupabase,
+ body: JSON.stringify(payloadSupabase),
+ });
+
+ if (!resSupabase.ok) {
+ const errorText = await resSupabase.text();
+ console.error('[criarLaudo] Erro ao salvar laudo no Supabase:', errorText);
+ throw new Error(`Falha ao salvar laudo: ${resSupabase.statusText}`);
+ }
+
+ const novoLaudo = await resSupabase.json();
+ const laudoId = novoLaudo?.id;
+
+ if (!laudoId) {
+ throw new Error('Laudo criado mas sem ID retornado');
+ }
+
+ console.log('[criarLaudo] Laudo salvo com sucesso. ID:', laudoId);
+
+ // 4. CHAMAR O N8N para notificar o paciente
+ // Padrão simples: apenas pacienteId e reportId
+ try {
+ console.log('[criarLaudo] Enviando notificação para n8n...');
+
+ const resNotificacao = await fetch('https://joaogustavo.me/webhook/notificar-laudo', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ pacienteId: laudoData.pacienteId, // ← ID do paciente
+ reportId: laudoId // ← ID do report criado
+ })
+ });
+
+ if (resNotificacao.ok) {
+ console.log('[criarLaudo] Notificação enviada com sucesso ao n8n');
+ } else {
+ console.warn('[criarLaudo] Notificação ao n8n retornou status:', resNotificacao.status);
+ }
+ } catch (erroNotificacao) {
+ // Não falha a criação do laudo se a notificação falhar
+ console.warn('[criarLaudo] Erro ao enviar notificação para n8n:', erroNotificacao);
+ }
+
+ // 5. Retorna o laudo criado
+ return {
+ ...novoLaudo,
+ mensagem: 'Laudo criado e paciente notificado com sucesso!',
+ };
+ } catch (erro) {
+ console.error('[criarLaudo] Erro ao criar laudo:', erro);
+ throw erro;
+ }
}
\ No newline at end of file