envio de sms ao agendar consulta

This commit is contained in:
Lucas Deiró Rodrigues 2025-11-11 00:53:04 -03:00
parent 0fcc7ae97b
commit 866e15df9e
4 changed files with 113 additions and 27 deletions

View File

@ -14,8 +14,10 @@ import { Textarea } from "@/components/ui/textarea";
import { Calendar as CalendarShadcn } from "@/components/ui/calendar";
import { format, addDays } from "date-fns";
import { User, StickyNote, Calendar } from "lucide-react";
import {smsService } from "@/services/Sms.mjs"
import { toast } from "@/hooks/use-toast";
export default function ScheduleForm() {
// Estado do usuário e role
const [role, setRole] = useState<string>("paciente");
@ -244,41 +246,56 @@ const handleSubmit = async (e: React.FormEvent) => {
}.`,
});
// 📞 busca o telefone corretamente
let phoneNumber = "+5511999999999"; // fallback
let phoneNumber = "+5511999999999"; // fallback
try {
if (isSecretaryLike) {
// se for secretária/admin → usa paciente selecionado
const patient = patients.find((p: any) => p.id === patientId);
if (patient?.phone_number) phoneNumber = patient.phone_number;
} else {
// se for paciente → usa o service do próprio user
const me = await usersService.getMe();
if (me?.profile?.phone) phoneNumber = me.profile.phone;
}
try {
if (isSecretaryLike) {
// Secretária/admin → telefone do paciente selecionado
const patient = patients.find((p: any) => p.id === patientId);
// Pacientes criados no sistema podem ter phone ou phone_mobile
const rawPhone = patient?.phone || patient?.phone_mobile || null;
if (rawPhone) phoneNumber = rawPhone;
} else {
// Paciente → telefone vem do perfil do próprio usuário logado
const me = await usersService.getMe();
const rawPhone =
me?.profile?.phone ||
(typeof me?.profile === "object" && "phone_mobile" in me.profile ? (me.profile as any).phone_mobile : null) ||
(typeof me === "object" && "user_metadata" in me ? (me as any).user_metadata?.phone : null) ||
null;
if (rawPhone) phoneNumber = rawPhone;
}
// 🔹 Normaliza para formato internacional (+55)
if (phoneNumber) {
phoneNumber = phoneNumber.replace(/\D/g, "");
if (!phoneNumber.startsWith("55")) phoneNumber = `55${phoneNumber}`;
phoneNumber = `+${phoneNumber}`;
}
console.log("📞 Telefone usado:", phoneNumber);
} catch (err) {
console.warn("⚠️ Não foi possível obter telefone do paciente:", err);
}
// padroniza número para formato internacional (+55)
if (phoneNumber) {
phoneNumber = phoneNumber.replace(/\D/g, ""); // remove caracteres não numéricos
if (!phoneNumber.startsWith("55")) phoneNumber = `55${phoneNumber}`;
phoneNumber = `+${phoneNumber}`;
}
} catch (err) {
console.warn("Não foi possível obter telefone do paciente:", err);
}
// 💬 envia o SMS de confirmação
// 💬 Envia o SMS de lembrete (sem mostrar nada ao paciente)
// 💬 Envia o SMS de lembrete (somente loga no console, não mostra no sistema)
try {
const smsRes = await appointmentsService.send_sms({
const smsRes = await smsService.sendSms({
phone_number: phoneNumber,
message: `Lembrete: sua consulta é em ${dateFormatted} às ${selectedTime} na Clínica MediConnect.`,
patient_id: patientId,
});
if (smsRes?.success) {
console.log("✅ SMS enviado com sucesso:", smsRes.message_sid || smsRes.sid || "(sem SID retornado)");
console.log("✅ SMS enviado com sucesso:", smsRes.message_sid);
} else {
console.warn("⚠️ Falha no envio do SMS:", smsRes);
}
@ -287,6 +304,8 @@ try {
}
// 🧹 limpa os campos
setSelectedDoctor("");
setSelectedDate("");

58
services/Sms.mjs Normal file
View File

@ -0,0 +1,58 @@
/**
* Serviço de SMS via Supabase Edge Function (sem backend)
* Usa o token JWT salvo no localStorage (chave: "token")
*/
const SUPABASE_FUNCTION_URL =
"https://yuanqfswhberkoevtmfr.supabase.co/functions/v1/send-sms";
export const smsService = {
/**
* Envia um SMS de lembrete via Twilio
* @param {Object} params
* @param {string} params.phone_number - Ex: +5511999999999
* @param {string} params.message - Mensagem de texto
* @param {string} [params.patient_id] - ID opcional do paciente
*/
async sendSms({ phone_number, message, patient_id }) {
try {
// 🔹 Busca o token salvo pelo login
const token = localStorage.getItem("token");
if (!token) {
console.error("❌ Nenhum token JWT encontrado no localStorage (chave: 'token').");
return { success: false, error: "Token JWT não encontrado." };
}
const body = JSON.stringify({
phone_number,
message,
patient_id,
});
console.log("[smsService] Enviando SMS para:", phone_number);
const response = await fetch(SUPABASE_FUNCTION_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`, // 🔑 autenticação Supabase
},
body,
});
const result = await response.json();
if (!response.ok) {
console.error("❌ Falha no envio do SMS:", result);
return { success: false, error: result };
}
console.log("✅ SMS enviado com sucesso:", result);
return result;
} catch (err) {
console.error("❌ Erro inesperado ao enviar SMS:", err);
return { success: false, error: err.message };
}
},
};

View File

@ -89,11 +89,20 @@ async function request(endpoint, options = {}) {
// --- CORREÇÃO 1: PARA O SUBMIT DO AGENDAMENTO ---
// Se a resposta for um sucesso de criação (201) ou sem conteúdo (204), não quebra.
if (response.status === 201 || response.status === 204) {
return null;
// --- CORREÇÃO: funções do Supabase retornam 200 ou 201, nunca queremos perder o body ---
if (response.status === 204) {
return null;
}
const text = await response.text();
try {
return JSON.parse(text);
} catch {
return text || null;
}
return response.json();
}
// Exportamos o objeto 'api' com os métodos que os componentes vão usar.

View File

@ -46,6 +46,6 @@ export const appointmentsService = {
*/
delete: (id) => api.delete(`/rest/v1/appointments?id=eq.${id}`),
send_sms: (data) => api.post("/functions/v1/send-sms", data)
};