envio de sms ao agendar consulta
This commit is contained in:
parent
0fcc7ae97b
commit
866e15df9e
@ -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
58
services/Sms.mjs
Normal 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 };
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -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.
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user