From a2ca13607eacab187c87387f3220aeed3d1960c4 Mon Sep 17 00:00:00 2001 From: M-Gabrielly Date: Fri, 24 Oct 2025 16:33:46 -0300 Subject: [PATCH] feat(profissional): add endpoint de sms --- susconecta/app/profissional/page.tsx | 206 ++++++++++++++++++++------- 1 file changed, 154 insertions(+), 52 deletions(-) diff --git a/susconecta/app/profissional/page.tsx b/susconecta/app/profissional/page.tsx index 91715b6..bce7020 100644 --- a/susconecta/app/profissional/page.tsx +++ b/susconecta/app/profissional/page.tsx @@ -39,6 +39,7 @@ import { import dynamic from "next/dynamic"; +import { ENV_CONFIG } from '@/lib/env-config'; import dayGridPlugin from "@fullcalendar/daygrid"; import timeGridPlugin from "@fullcalendar/timegrid"; import interactionPlugin from "@fullcalendar/interaction"; @@ -82,6 +83,20 @@ const colorsByType = { return p?.idade ?? p?.age ?? ''; }; + // Normaliza número de telefone para E.164 básico (prioriza +55 quando aplicável) + const normalizePhoneNumber = (raw?: string) => { + if (!raw || typeof raw !== 'string') return ''; + // Remover tudo que não for dígito + const digits = raw.replace(/\D+/g, ''); + if (!digits) return ''; + // Já tem código de país (começa com 55) + if (digits.startsWith('55') && digits.length >= 11) return '+' + digits; + // Se tiver 10 ou 11 dígitos (DDD + número), assume Brasil e prefixa +55 + if (digits.length === 10 || digits.length === 11) return '+55' + digits; + // Se tiver outros formatos pequenos, apenas prefixa + + return '+' + digits; + }; + // Helpers para normalizar campos do laudo/relatório const getReportPatientName = (r: any) => r?.paciente?.full_name ?? r?.paciente?.nome ?? r?.patient?.full_name ?? r?.patient?.nome ?? r?.patient_name ?? r?.patient_full_name ?? ''; const getReportPatientId = (r: any) => r?.paciente?.id ?? r?.patient?.id ?? r?.patient_id ?? r?.patientId ?? r?.patient_id_raw ?? r?.patient_id ?? r?.id ?? ''; @@ -101,7 +116,7 @@ const colorsByType = { }; const ProfissionalPage = () => { - const { logout, user } = useAuth(); + const { logout, user, token } = useAuth(); const [activeSection, setActiveSection] = useState('calendario'); const [pacienteSelecionado, setPacienteSelecionado] = useState(null); @@ -374,10 +389,98 @@ const ProfissionalPage = () => { const [selectedEvent, setSelectedEvent] = useState(null); const [currentCalendarDate, setCurrentCalendarDate] = useState(new Date()); - const handleSave = (event: React.MouseEvent) => { + const [commPhoneNumber, setCommPhoneNumber] = useState(''); + const [commMessage, setCommMessage] = useState(''); + const [commPatientId, setCommPatientId] = useState(null); + const [smsSending, setSmsSending] = useState(false); + + const handleSave = async (event: React.MouseEvent) => { event.preventDefault(); - console.log("Laudo salvo!"); - window.scrollTo({ top: 0, behavior: "smooth" }); + setSmsSending(true); + try { + // Validate required fields + if (!commPhoneNumber || !commPhoneNumber.trim()) throw new Error('O campo phone_number é obrigatório'); + if (!commMessage || !commMessage.trim()) throw new Error('O campo message é obrigatório'); + + const payload: any = { phone_number: commPhoneNumber.trim(), message: commMessage.trim() }; + if (commPatientId) payload.patient_id = commPatientId; + + const headers: Record = { 'Content-Type': 'application/json' }; + // include any default headers from ENV_CONFIG if present (e.g. apikey) + if ((ENV_CONFIG as any)?.DEFAULT_HEADERS) Object.assign(headers, (ENV_CONFIG as any).DEFAULT_HEADERS); + // include Authorization if we have a token (user session) + if (token) headers['Authorization'] = `Bearer ${token}`; + + // Ensure apikey is present (frontend only has ANON key in this project) + if (!headers.apikey && (ENV_CONFIG as any)?.SUPABASE_ANON_KEY) { + headers.apikey = (ENV_CONFIG as any).SUPABASE_ANON_KEY; + } + // Ensure Accept header + headers['Accept'] = 'application/json'; + + // Normalizar número antes de enviar (E.164 básico) + const normalized = normalizePhoneNumber(commPhoneNumber); + if (!normalized) throw new Error('Número inválido após normalização'); + payload.phone_number = normalized; + + // Debug: log payload and headers with secrets masked to help diagnose issues + try { + const masked = { ...headers } as Record; + if (masked.apikey && typeof masked.apikey === 'string') masked.apikey = `${masked.apikey.slice(0,4)}...${masked.apikey.slice(-4)}`; + if (masked.Authorization) masked.Authorization = 'Bearer <>'; + console.debug('[ProfissionalPage] Enviando SMS -> url:', `${(ENV_CONFIG as any).SUPABASE_URL}/functions/v1/send-sms`, 'payload:', payload, 'headers(masked):', masked); + } catch (e) { + // ignore logging errors + } + + const res = await fetch(`${(ENV_CONFIG as any).SUPABASE_URL}/functions/v1/send-sms`, { + method: 'POST', + headers, + body: JSON.stringify(payload), + }); + + const body = await res.json().catch(() => null); + if (!res.ok) { + // If server returned 5xx and we sent a patient_id, try a single retry without patient_id + if (res.status >= 500 && payload.patient_id) { + try { + const fallback = { phone_number: payload.phone_number, message: payload.message }; + console.debug('[ProfissionalPage] 5xx ao enviar com patient_id — tentando reenviar sem patient_id', { fallback }); + const retryRes = await fetch(`${(ENV_CONFIG as any).SUPABASE_URL}/functions/v1/send-sms`, { + method: 'POST', + headers, + body: JSON.stringify(fallback), + }); + const retryBody = await retryRes.json().catch(() => null); + if (retryRes.ok) { + alert('SMS enviado com sucesso (sem patient_id)'); + setCommPhoneNumber(''); + setCommMessage(''); + setCommPatientId(null); + return; + } else { + throw new Error(retryBody?.message || retryBody?.error || `Erro ao enviar SMS (retry ${retryRes.status})`); + } + } catch (retryErr) { + console.warn('[ProfissionalPage] Reenvio sem patient_id falhou', retryErr); + throw new Error(body?.message || body?.error || `Erro ao enviar SMS (${res.status})`); + } + } + throw new Error(body?.message || body?.error || `Erro ao enviar SMS (${res.status})`); + } + + // success feedback + alert('SMS enviado com sucesso'); + // clear fields + setCommPhoneNumber(''); + setCommMessage(''); + setCommPatientId(null); + } catch (err: any) { + alert(String(err?.message || err || 'Falha ao enviar SMS')); + } finally { + setSmsSending(false); + window.scrollTo({ top: 0, behavior: 'smooth' }); + } }; @@ -2480,61 +2583,60 @@ const ProfissionalPage = () => {

Comunicação com o Paciente

-
+
- - + +
+
- - + +
-
-
-
- -

03/09/2025

+ +
+ +