diff --git a/MEDICONNECT 2/add-fernando-patient.cjs b/MEDICONNECT 2/add-fernando-patient.cjs
new file mode 100644
index 000000000..8dfe8cf2f
--- /dev/null
+++ b/MEDICONNECT 2/add-fernando-patient.cjs
@@ -0,0 +1,76 @@
+const axios = require("axios");
+
+const ANON_KEY =
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
+const BASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
+
+(async () => {
+ try {
+ console.log("🔐 Fazendo login como admin...");
+ const loginRes = await axios.post(
+ `${BASE_URL}/auth/v1/token?grant_type=password`,
+ {
+ email: "riseup@popcode.com.br",
+ password: "riseup",
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ },
+ }
+ );
+
+ console.log("✅ Login admin bem-sucedido!\n");
+ const token = loginRes.data.access_token;
+
+ // Buscar o ID do Fernando no profiles
+ console.log("🔍 Buscando ID do Fernando...");
+ const profileRes = await axios.get(
+ `${BASE_URL}/rest/v1/profiles?email=eq.fernando.pirichowski@souunit.com.br&select=*`,
+ {
+ headers: {
+ apikey: ANON_KEY,
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ );
+
+ if (profileRes.data.length === 0) {
+ console.log("❌ Fernando não encontrado no profiles");
+ return;
+ }
+
+ const fernandoId = profileRes.data[0].id;
+ console.log("✅ Fernando encontrado! ID:", fernandoId);
+
+ // Criar entrada na tabela patients
+ console.log("\n📋 Criando entrada na tabela patients...");
+ const patientRes = await axios.post(
+ `${BASE_URL}/rest/v1/patients`,
+ {
+ id: fernandoId,
+ email: "fernando.pirichowski@souunit.com.br",
+ full_name: "Fernando Pirichowski",
+ phone_mobile: "51999999999",
+ cpf: "12345678909", // CPF válido fictício
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ Authorization: `Bearer ${token}`,
+ Prefer: "return=representation",
+ },
+ }
+ );
+
+ console.log("✅ Entrada na tabela patients criada!");
+ console.log("\n🎉 Usuário Fernando Pirichowski completo!");
+ console.log("📧 Email: fernando.pirichowski@souunit.com.br");
+ console.log("🔑 Senha: fernando123");
+ console.log("\n✨ Agora você pode testar a recuperação de senha!");
+ } catch (err) {
+ console.error("❌ Erro:", err.response?.data || err.message);
+ }
+})();
diff --git a/MEDICONNECT 2/check-fernando.js b/MEDICONNECT 2/check-fernando.js
new file mode 100644
index 000000000..cd3c4cac1
--- /dev/null
+++ b/MEDICONNECT 2/check-fernando.js
@@ -0,0 +1,58 @@
+const axios = require("axios");
+
+const ANON_KEY =
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
+const BASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
+
+(async () => {
+ try {
+ console.log("🔐 Fazendo login como admin...");
+ const loginRes = await axios.post(
+ `${BASE_URL}/auth/v1/token?grant_type=password`,
+ {
+ email: "riseup@popcode.com.br",
+ password: "riseup",
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ },
+ }
+ );
+
+ console.log("✅ Login admin bem-sucedido!");
+ const token = loginRes.data.access_token;
+
+ console.log("\n🔍 Buscando usuário fernando...");
+ const usersRes = await axios.get(`${BASE_URL}/rest/v1/profiles?select=*`, {
+ headers: {
+ apikey: ANON_KEY,
+ Authorization: `Bearer ${token}`,
+ },
+ });
+
+ console.log(`\n📊 Total de usuários: ${usersRes.data.length}`);
+
+ const fernando = usersRes.data.find(
+ (u) =>
+ u.email &&
+ (u.email.toLowerCase().includes("fernando") ||
+ u.full_name?.toLowerCase().includes("fernando"))
+ );
+
+ if (fernando) {
+ console.log("\n✅ Usuário Fernando encontrado:");
+ console.log(JSON.stringify(fernando, null, 2));
+ } else {
+ console.log("\n❌ Usuário Fernando NÃO encontrado na tabela profiles");
+ console.log("\n📧 Alguns emails cadastrados:");
+ usersRes.data.slice(0, 15).forEach((u) => {
+ if (u.email)
+ console.log(` - ${u.email} (${u.full_name || "sem nome"})`);
+ });
+ }
+ } catch (err) {
+ console.error("❌ Erro:", err.response?.data || err.message);
+ }
+})();
diff --git a/MEDICONNECT 2/create-fernando.cjs b/MEDICONNECT 2/create-fernando.cjs
new file mode 100644
index 000000000..83c5cfbef
--- /dev/null
+++ b/MEDICONNECT 2/create-fernando.cjs
@@ -0,0 +1,87 @@
+const axios = require("axios");
+
+const ANON_KEY =
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
+const BASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
+
+(async () => {
+ try {
+ console.log("🔐 Fazendo login como admin...");
+ const loginRes = await axios.post(
+ `${BASE_URL}/auth/v1/token?grant_type=password`,
+ {
+ email: "riseup@popcode.com.br",
+ password: "riseup",
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ },
+ }
+ );
+
+ console.log("✅ Login admin bem-sucedido!\n");
+ const token = loginRes.data.access_token;
+
+ console.log("👤 Criando usuário Fernando Pirichowski...");
+
+ // Criar usuário via signup
+ const signupRes = await axios.post(
+ `${BASE_URL}/auth/v1/signup`,
+ {
+ email: "fernando.pirichowski@souunit.com.br",
+ password: "fernando123", // Senha temporária
+ options: {
+ data: {
+ full_name: "Fernando Pirichowski",
+ phone: "51999999999",
+ },
+ },
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ },
+ }
+ );
+
+ console.log("✅ Usuário criado com sucesso!");
+ console.log("📧 Email:", signupRes.data.user.email);
+ console.log("🆔 ID:", signupRes.data.user.id);
+ console.log("🔑 Senha temporária: fernando123\n");
+
+ // Criar entrada na tabela patients
+ console.log("📋 Criando entrada na tabela patients...");
+ const patientRes = await axios.post(
+ `${BASE_URL}/rest/v1/patients`,
+ {
+ id: signupRes.data.user.id,
+ email: "fernando.pirichowski@souunit.com.br",
+ full_name: "Fernando Pirichowski",
+ phone_mobile: "51999999999",
+ cpf: "12345678909", // CPF válido fictício
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ Authorization: `Bearer ${token}`,
+ Prefer: "return=representation",
+ },
+ }
+ );
+
+ console.log("✅ Entrada na tabela patients criada!");
+ console.log("\n🎉 Usuário Fernando Pirichowski criado com sucesso!");
+ console.log("📧 Email: fernando.pirichowski@souunit.com.br");
+ console.log("🔑 Senha: fernando123");
+ console.log("\n💡 Agora você pode testar a recuperação de senha!");
+ } catch (err) {
+ console.error("❌ Erro:", err.response?.data || err.message);
+ if (err.response?.data?.msg) {
+ console.error("Mensagem:", err.response.data.msg);
+ }
+ }
+})();
diff --git a/MEDICONNECT 2/index.html b/MEDICONNECT 2/index.html
index deaa41ce4..df371ea8b 100644
--- a/MEDICONNECT 2/index.html
+++ b/MEDICONNECT 2/index.html
@@ -1,11 +1,42 @@
-
-
+
-
+
- MediConnect
+ MediConnect - Sistema de Agendamento Médico
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MEDICONNECT 2/netlify.toml b/MEDICONNECT 2/netlify.toml
deleted file mode 100644
index 7f2c6815c..000000000
--- a/MEDICONNECT 2/netlify.toml
+++ /dev/null
@@ -1,24 +0,0 @@
-[build]
- command = "pnpm build"
- publish = "dist"
-
-[functions]
- directory = "netlify/functions"
-
-[dev]
- command = "npm run dev"
- targetPort = 5173
- port = 8888
- autoLaunch = false
- framework = "#custom"
-
-[[redirects]]
- from = "/*"
- to = "/index.html"
- status = 200
-
-# Optional: control caching of static assets
-[[headers]]
- for = "/assets/*"
- [headers.values]
- Cache-Control = "public, max-age=31536000, immutable"
diff --git a/MEDICONNECT 2/netlify/functions/appointments.ts b/MEDICONNECT 2/netlify/functions/appointments.ts
deleted file mode 100644
index 0eaab64df..000000000
--- a/MEDICONNECT 2/netlify/functions/appointments.ts
+++ /dev/null
@@ -1,163 +0,0 @@
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return { statusCode: 200, headers, body: "" };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token n�o fornecido" }),
- };
- }
-
- const pathParts = event.path.split("/");
- const appointmentId =
- pathParts[pathParts.length - 1] !== "appointments"
- ? pathParts[pathParts.length - 1]
- : null;
-
- if (event.httpMethod === "GET") {
- let url = `${SUPABASE_URL}/rest/v1/appointments`;
- if (appointmentId && appointmentId !== "appointments") {
- url += `?id=eq.${appointmentId}&select=*`;
- } else if (event.queryStringParameters) {
- const params = new URLSearchParams(
- event.queryStringParameters as Record
- );
- url += `?${params.toString()}`;
- if (!params.has("select")) {
- url += url.includes("?") ? "&select=*" : "?select=*";
- }
- } else {
- url += "?select=*";
- }
-
- const response = await fetch(url, {
- method: "GET",
- headers: { apikey: SUPABASE_ANON_KEY, Authorization: authHeader },
- });
- let data = await response.json();
- if (
- appointmentId &&
- appointmentId !== "appointments" &&
- Array.isArray(data) &&
- data.length > 0
- ) {
- data = data[0];
- }
- return {
- statusCode: response.status,
- headers: { ...headers, "Content-Type": "application/json" },
- body: JSON.stringify(data),
- };
- }
-
- if (event.httpMethod === "POST") {
- const body = JSON.parse(event.body || "{}");
- if (!body.patient_id || !body.doctor_id || !body.scheduled_at) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campos obrigat�rios: patient_id, doctor_id, scheduled_at",
- }),
- };
- }
- const response = await fetch(`${SUPABASE_URL}/rest/v1/appointments`, {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- });
- let data = await response.json();
- if (Array.isArray(data) && data.length > 0) data = data[0];
- return {
- statusCode: response.status,
- headers: { ...headers, "Content-Type": "application/json" },
- body: JSON.stringify(data),
- };
- }
-
- if (event.httpMethod === "PATCH") {
- if (!appointmentId || appointmentId === "appointments") {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "ID do agendamento � obrigat�rio" }),
- };
- }
- const body = JSON.parse(event.body || "{}");
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/appointments?id=eq.${appointmentId}`,
- {
- method: "PATCH",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- }
- );
- let data = await response.json();
- if (Array.isArray(data) && data.length > 0) data = data[0];
- return {
- statusCode: response.status,
- headers: { ...headers, "Content-Type": "application/json" },
- body: JSON.stringify(data),
- };
- }
-
- if (event.httpMethod === "DELETE") {
- if (!appointmentId || appointmentId === "appointments") {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "ID do agendamento � obrigat�rio" }),
- };
- }
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/appointments?id=eq.${appointmentId}`,
- {
- method: "DELETE",
- headers: { apikey: SUPABASE_ANON_KEY, Authorization: authHeader },
- }
- );
- return { statusCode: response.status, headers, body: "" };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro:", error);
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({ error: "Erro interno" }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/assignments.ts b/MEDICONNECT 2/netlify/functions/assignments.ts
deleted file mode 100644
index 1869ebcbf..000000000
--- a/MEDICONNECT 2/netlify/functions/assignments.ts
+++ /dev/null
@@ -1,153 +0,0 @@
-/**
- * Netlify Function: Listar Atribuições
- * GET /rest/v1/patient_assignments
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- // GET - Listar atribuições
- if (event.httpMethod === "GET") {
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- // Monta URL com query params (se houver)
- const queryString = event.queryStringParameters
- ? "?" +
- new URLSearchParams(
- event.queryStringParameters as Record
- ).toString()
- : "";
-
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/patient_assignments${queryString}`,
- {
- method: "GET",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- }
- );
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- } catch (error) {
- console.error("Erro ao listar atribuições:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
- }
-
- // POST - Criar atribuição
- if (event.httpMethod === "POST") {
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- const body = JSON.parse(event.body || "{}");
-
- if (!body.patient_id || !body.user_id || !body.role) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "patient_id, user_id e role são obrigatórios",
- }),
- };
- }
-
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/patient_assignments`,
- {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- }
- );
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- } catch (error) {
- console.error("Erro ao criar atribuição:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
-};
diff --git a/MEDICONNECT 2/netlify/functions/auth-login.ts b/MEDICONNECT 2/netlify/functions/auth-login.ts
index b4f76a5ab..90293e6dc 100644
--- a/MEDICONNECT 2/netlify/functions/auth-login.ts
+++ b/MEDICONNECT 2/netlify/functions/auth-login.ts
@@ -46,6 +46,12 @@ export const handler: Handler = async (event: HandlerEvent) => {
const body: LoginRequest = JSON.parse(event.body || "{}");
if (!body.email || !body.password) {
+ // Log headers and raw body to help debugging malformed requests from frontend
+ console.error(
+ "[auth-login] Requisição inválida - falta email ou password. Headers:",
+ event.headers
+ );
+ console.error("[auth-login] Raw body:", event.body);
return {
statusCode: 400,
headers,
diff --git a/MEDICONNECT 2/netlify/functions/auth-logout.ts b/MEDICONNECT 2/netlify/functions/auth-logout.ts
deleted file mode 100644
index 1854837c9..000000000
--- a/MEDICONNECT 2/netlify/functions/auth-logout.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-/**
- * Netlify Function: Logout
- * Invalida a sessão do usuário no Supabase
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- if (event.httpMethod !== "POST") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- // Pega o Bearer token do header
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- // Faz logout no Supabase
- const response = await fetch(`${SUPABASE_URL}/auth/v1/logout`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- });
-
- // Logout retorna 204 No Content (sem body)
- if (response.status === 204) {
- return {
- statusCode: 204,
- headers,
- body: "",
- };
- }
-
- // Se não for 204, retorna o body da resposta
- const data = await response.text();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: data || "{}",
- };
- } catch (error) {
- console.error("Erro no logout:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/auth-magic-link.ts b/MEDICONNECT 2/netlify/functions/auth-magic-link.ts
deleted file mode 100644
index bf2d32bf4..000000000
--- a/MEDICONNECT 2/netlify/functions/auth-magic-link.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * Netlify Function: Magic Link
- * Envia link de autenticação sem senha por email
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-// Constantes da API (protegidas no backend)
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-interface MagicLinkRequest {
- email: string;
- redirect_url?: string;
-}
-
-export const handler: Handler = async (event: HandlerEvent) => {
- // CORS headers
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- // Handle preflight
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- // Apenas POST é permitido
- if (event.httpMethod !== "POST") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- // Parse body
- const body: MagicLinkRequest = JSON.parse(event.body || "{}");
-
- if (!body.email) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "Email é obrigatório" }),
- };
- }
-
- // Faz requisição para API Supabase COM a apikey protegida
- const response = await fetch(`${SUPABASE_URL}/auth/v1/otp`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- apikey: SUPABASE_ANON_KEY,
- },
- body: JSON.stringify({
- email: body.email,
- options: {
- emailRedirectTo:
- body.redirect_url ||
- "https://mediconnectbrasil.netlify.app/auth/callback",
- },
- }),
- });
-
- const data = await response.json();
-
- // Repassa a resposta para o frontend
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- } catch (error) {
- console.error("[auth-magic-link] Erro:", error);
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro ao enviar magic link",
- details: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/auth-refresh.ts b/MEDICONNECT 2/netlify/functions/auth-refresh.ts
deleted file mode 100644
index 84eca6a30..000000000
--- a/MEDICONNECT 2/netlify/functions/auth-refresh.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Netlify Function: Refresh Token
- * Renova o access token usando o refresh token
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-interface RefreshTokenRequest {
- refresh_token: string;
-}
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- if (event.httpMethod !== "POST") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- const body: RefreshTokenRequest = JSON.parse(event.body || "{}");
-
- if (!body.refresh_token) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "Refresh token é obrigatório" }),
- };
- }
-
- // Faz requisição para renovar token no Supabase
- const response = await fetch(
- `${SUPABASE_URL}/auth/v1/token?grant_type=refresh_token`,
- {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- apikey: SUPABASE_ANON_KEY,
- },
- body: JSON.stringify({
- refresh_token: body.refresh_token,
- }),
- }
- );
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- } catch (error) {
- console.error("Erro ao renovar token:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/auth-user.ts b/MEDICONNECT 2/netlify/functions/auth-user.ts
deleted file mode 100644
index 1203887e5..000000000
--- a/MEDICONNECT 2/netlify/functions/auth-user.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * Netlify Function: Auth User
- * GET /auth/v1/user - Retorna dados do usuário autenticado
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "GET, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- if (event.httpMethod === "GET") {
- const response = await fetch(`${SUPABASE_URL}/auth/v1/user`, {
- method: "GET",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- });
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro na API de auth user:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/avatars-delete.ts b/MEDICONNECT 2/netlify/functions/avatars-delete.ts
deleted file mode 100644
index 13e18cb94..000000000
--- a/MEDICONNECT 2/netlify/functions/avatars-delete.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-/**
- * Netlify Function: Delete Avatar
- * DELETE /storage/v1/object/avatars/{userId}/avatar
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "DELETE, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- if (event.httpMethod !== "DELETE") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- const userId = event.queryStringParameters?.userId;
-
- if (!userId) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "userId é obrigatório" }),
- };
- }
-
- const response = await fetch(
- `${SUPABASE_URL}/storage/v1/object/avatars/${userId}/avatar`,
- {
- method: "DELETE",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- }
- );
-
- // DELETE pode retornar 200 com body vazio
- const contentType = response.headers.get("content-type");
- const data = contentType?.includes("application/json")
- ? await response.json()
- : {};
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- } catch (error) {
- console.error("Erro ao deletar avatar:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/avatars-upload.ts b/MEDICONNECT 2/netlify/functions/avatars-upload.ts
deleted file mode 100644
index 854ee40cb..000000000
--- a/MEDICONNECT 2/netlify/functions/avatars-upload.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-/**
- * Netlify Function: Upload Avatar
- * POST /storage/v1/object/avatars/{userId}/avatar
- *
- * Aceita JSON com base64 para simplificar o upload via Netlify Functions
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- if (event.httpMethod !== "POST") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- // Extrai userId do query string
- const userId = event.queryStringParameters?.userId;
-
- if (!userId) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "userId é obrigatório" }),
- };
- }
-
- // Parse JSON body com base64
- let fileData: string;
- let contentType: string;
-
- try {
- const body = JSON.parse(event.body || "{}");
- fileData = body.fileData; // base64 string
- contentType = body.contentType || "image/jpeg";
-
- if (!fileData) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "fileData (base64) é obrigatório" }),
- };
- }
- } catch {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Body deve ser JSON válido com fileData em base64",
- }),
- };
- }
-
- // Converte base64 para Buffer
- const buffer = Buffer.from(fileData, "base64");
-
- // Upload para Supabase Storage
- const response = await fetch(
- `${SUPABASE_URL}/storage/v1/object/avatars/${userId}/avatar`,
- {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": contentType,
- "x-upsert": "true", // Sobrescreve se já existir
- },
- body: buffer,
- }
- );
-
- const data = await response.json();
-
- if (!response.ok) {
- console.error("Erro do Supabase:", data);
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- error: data.error || "Erro ao fazer upload no Supabase",
- details: data,
- }),
- };
- }
-
- return {
- statusCode: 200,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- message: "Upload realizado com sucesso",
- path: data.Key || data.path,
- fullPath: data.Key || data.path,
- }),
- };
- } catch (error) {
- console.error("Erro no upload do avatar:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/consultas.ts b/MEDICONNECT 2/netlify/functions/consultas.ts
deleted file mode 100644
index b22e39ab9..000000000
--- a/MEDICONNECT 2/netlify/functions/consultas.ts
+++ /dev/null
@@ -1,163 +0,0 @@
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return { statusCode: 200, headers, body: "" };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token n�o fornecido" }),
- };
- }
-
- const pathParts = event.path.split("/");
- const appointmentId =
- pathParts[pathParts.length - 1] !== "consultas"
- ? pathParts[pathParts.length - 1]
- : null;
-
- if (event.httpMethod === "GET") {
- let url = `${SUPABASE_URL}/rest/v1/appointments`;
- if (appointmentId && appointmentId !== "consultas") {
- url += `?id=eq.${appointmentId}&select=*`;
- } else if (event.queryStringParameters) {
- const params = new URLSearchParams(
- event.queryStringParameters as Record
- );
- url += `?${params.toString()}`;
- if (!params.has("select")) {
- url += url.includes("?") ? "&select=*" : "?select=*";
- }
- } else {
- url += "?select=*";
- }
-
- const response = await fetch(url, {
- method: "GET",
- headers: { apikey: SUPABASE_ANON_KEY, Authorization: authHeader },
- });
- let data = await response.json();
- if (
- appointmentId &&
- appointmentId !== "consultas" &&
- Array.isArray(data) &&
- data.length > 0
- ) {
- data = data[0];
- }
- return {
- statusCode: response.status,
- headers: { ...headers, "Content-Type": "application/json" },
- body: JSON.stringify(data),
- };
- }
-
- if (event.httpMethod === "POST") {
- const body = JSON.parse(event.body || "{}");
- if (!body.patient_id || !body.doctor_id || !body.scheduled_at) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campos obrigat�rios: patient_id, doctor_id, scheduled_at",
- }),
- };
- }
- const response = await fetch(`${SUPABASE_URL}/rest/v1/appointments`, {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- });
- let data = await response.json();
- if (Array.isArray(data) && data.length > 0) data = data[0];
- return {
- statusCode: response.status,
- headers: { ...headers, "Content-Type": "application/json" },
- body: JSON.stringify(data),
- };
- }
-
- if (event.httpMethod === "PATCH") {
- if (!appointmentId || appointmentId === "consultas") {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "ID do agendamento � obrigat�rio" }),
- };
- }
- const body = JSON.parse(event.body || "{}");
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/appointments?id=eq.${appointmentId}`,
- {
- method: "PATCH",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- }
- );
- let data = await response.json();
- if (Array.isArray(data) && data.length > 0) data = data[0];
- return {
- statusCode: response.status,
- headers: { ...headers, "Content-Type": "application/json" },
- body: JSON.stringify(data),
- };
- }
-
- if (event.httpMethod === "DELETE") {
- if (!appointmentId || appointmentId === "consultas") {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "ID do agendamento � obrigat�rio" }),
- };
- }
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/appointments?id=eq.${appointmentId}`,
- {
- method: "DELETE",
- headers: { apikey: SUPABASE_ANON_KEY, Authorization: authHeader },
- }
- );
- return { statusCode: response.status, headers, body: "" };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro:", error);
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({ error: "Erro interno" }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/create-doctor.ts b/MEDICONNECT 2/netlify/functions/create-doctor.ts
deleted file mode 100644
index a8ecad447..000000000
--- a/MEDICONNECT 2/netlify/functions/create-doctor.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * Netlify Function: Create Doctor
- * POST /create-doctor - Cria registro de médico com validações
- * Não cria auth user - apenas registro na tabela doctors
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- if (event.httpMethod !== "POST") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- const body = JSON.parse(event.body || "{}");
-
- // Validação dos campos obrigatórios
- if (
- !body.email ||
- !body.full_name ||
- !body.cpf ||
- !body.crm ||
- !body.crm_uf
- ) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campos obrigatórios: email, full_name, cpf, crm, crm_uf",
- }),
- };
- }
-
- // Chama a Edge Function do Supabase para criar médico
- const response = await fetch(`${SUPABASE_URL}/functions/v1/create-doctor`, {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(body),
- });
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- } catch (error) {
- console.error("Erro na API de create doctor:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/create-patient.ts b/MEDICONNECT 2/netlify/functions/create-patient.ts
deleted file mode 100644
index cc447906f..000000000
--- a/MEDICONNECT 2/netlify/functions/create-patient.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-/**
- * Netlify Function: Create Patient
- * POST /create-patient - Cria registro de paciente diretamente
- * Não cria auth user - apenas registro na tabela patients
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- if (event.httpMethod !== "POST") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- const body = JSON.parse(event.body || "{}");
-
- // Validação dos campos obrigatórios
- if (
- !body.full_name ||
- !body.cpf ||
- !body.email ||
- !body.phone_mobile ||
- !body.created_by
- ) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error:
- "Campos obrigatórios: full_name, cpf, email, phone_mobile, created_by",
- }),
- };
- }
-
- // Chama REST API do Supabase para criar paciente diretamente
- const response = await fetch(`${SUPABASE_URL}/rest/v1/patients`, {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- });
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- } catch (error) {
- console.error("Erro na API de create patient:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/create-user-with-password.ts b/MEDICONNECT 2/netlify/functions/create-user-with-password.ts
deleted file mode 100644
index 882d3ece2..000000000
--- a/MEDICONNECT 2/netlify/functions/create-user-with-password.ts
+++ /dev/null
@@ -1,223 +0,0 @@
-/**
- * Netlify Function: Create User With Password
- * POST /create-user-with-password - Cria usuário com senha
- * Usa Edge Function do Supabase (não Admin API)
- * Requer permissão de admin, gestor ou secretaria
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization, apikey",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- if (event.httpMethod !== "POST") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- console.error("[create-user-with-password] Token não fornecido!");
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({
- error: "Token de autenticação é obrigatório",
- }),
- };
- }
-
- const body = JSON.parse(event.body || "{}");
-
- console.log(
- "[create-user-with-password] Recebido:",
- JSON.stringify({ ...body, password: "***" }, null, 2)
- );
-
- // Validações
- if (!body.email || !body.password || !body.full_name) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campos obrigatórios: email, password, full_name",
- }),
- };
- }
-
- if (body.password.length < 6) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Senha deve ter no mínimo 6 caracteres",
- }),
- };
- }
-
- // 1. Criar usuário via Edge Function do Supabase
- console.log(
- "[create-user-with-password] Chamando Edge Function do Supabase..."
- );
- console.log(
- "[create-user-with-password] URL:",
- `${SUPABASE_URL}/functions/v1/create-user`
- );
- console.log("[create-user-with-password] Payload:", {
- email: body.email,
- has_password: !!body.password,
- full_name: body.full_name,
- });
-
- const createUserResponse = await fetch(
- `${SUPABASE_URL}/functions/v1/create-user`,
- {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- email: body.email,
- password: body.password,
- full_name: body.full_name,
- phone: body.phone || null,
- role: body.role || "user",
- }),
- }
- );
-
- console.log(
- "[create-user-with-password] Status da resposta:",
- createUserResponse.status
- );
- console.log(
- "[create-user-with-password] Status text:",
- createUserResponse.statusText
- );
-
- // Sempre tenta ler a resposta como JSON
- let responseData;
- try {
- responseData = await createUserResponse.json();
- console.log(
- "[create-user-with-password] Resposta JSON:",
- JSON.stringify(responseData, null, 2)
- );
- } catch (error) {
- const responseText = await createUserResponse.text();
- console.error(
- "[create-user-with-password] Resposta não é JSON:",
- responseText
- );
- console.error("[create-user-with-password] Erro ao parsear JSON:", error);
- return {
- statusCode: 500,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- error: "Erro ao processar resposta do Supabase",
- details: responseText,
- }),
- };
- }
-
- if (!createUserResponse.ok) {
- console.error(
- "[create-user-with-password] Erro ao criar usuário:",
- JSON.stringify(responseData, null, 2)
- );
- return {
- statusCode: createUserResponse.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- error:
- responseData.msg || responseData.message || "Erro ao criar usuário",
- details: responseData,
- }),
- };
- }
-
- // Verificar se a Edge Function retornou sucesso
- if (!responseData.success) {
- console.error(
- "[create-user-with-password] Edge Function retornou erro:",
- JSON.stringify(responseData, null, 2)
- );
- return {
- statusCode: 400,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- error: responseData.error || "Erro ao criar usuário",
- details: responseData,
- }),
- };
- }
-
- const userData = responseData.user;
- console.log(
- "[create-user-with-password] Usuário criado com sucesso:",
- userData.id
- );
- console.log(
- "[create-user-with-password] Resposta completa:",
- JSON.stringify(responseData, null, 2)
- );
-
- // A Edge Function já cria o perfil e atribui a role automaticamente
- // Retornar sucesso
- return {
- statusCode: 201,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- success: true,
- user: userData,
- message: responseData.message || "Usuário criado com sucesso",
- }),
- };
- } catch (error) {
- console.error("[create-user-with-password] Erro:", error);
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/create-user.ts b/MEDICONNECT 2/netlify/functions/create-user.ts
deleted file mode 100644
index eec83df16..000000000
--- a/MEDICONNECT 2/netlify/functions/create-user.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-/**
- * Netlify Function: Create User
- * POST /create-user - Cria novo usuário no sistema
- * Requer permissão de admin, gestor ou secretaria
- * Envia magic link automaticamente para o email
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- // create-user pode ser chamado SEM autenticação (para auto-registro)
- // Se houver token, será usado; se não houver, usa apenas anon key
-
- if (event.httpMethod === "POST") {
- const body = JSON.parse(event.body || "{}");
-
- console.log(
- "[create-user] Recebido body:",
- JSON.stringify(body, null, 2)
- );
- console.log("[create-user] Auth header presente?", !!authHeader);
-
- // Validação dos campos obrigatórios
- if (!body.email || !body.full_name) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campos obrigatórios: email, full_name",
- }),
- };
- }
-
- if (!body.role && (!body.roles || body.roles.length === 0)) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "É necessário fornecer role ou roles",
- }),
- };
- }
-
- // Chama a Edge Function do Supabase para criar usuário
- const fetchHeaders: Record = {
- apikey: SUPABASE_ANON_KEY,
- "Content-Type": "application/json",
- // Se houver token de usuário autenticado, usa ele; senão usa anon key
- Authorization: authHeader || `Bearer ${SUPABASE_ANON_KEY}`,
- };
-
- console.log("[create-user] Chamando Supabase com headers:", {
- hasAuthHeader: !!authHeader,
- hasApikey: !!fetchHeaders.apikey,
- authType: authHeader ? "User Token" : "Anon Key",
- });
-
- const response = await fetch(`${SUPABASE_URL}/functions/v1/create-user`, {
- method: "POST",
- headers: fetchHeaders,
- body: JSON.stringify(body),
- });
-
- const data = await response.json();
-
- console.log("[create-user] Resposta do Supabase:", {
- status: response.status,
- data: JSON.stringify(data, null, 2),
- });
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro na API de create user:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/delete-user.ts b/MEDICONNECT 2/netlify/functions/delete-user.ts
deleted file mode 100644
index 972efa383..000000000
--- a/MEDICONNECT 2/netlify/functions/delete-user.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-/**
- * Netlify Function: Delete User (Hard Delete)
- * POST /delete-user - Deleta usuário permanentemente
- * ⚠️ OPERAÇÃO IRREVERSÍVEL - Use com extremo cuidado!
- * Requer permissão de admin ou gestor
- * Usa Admin API do Supabase
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_SERVICE_ROLE_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc1NDk1NDM2OSwiZXhwIjoyMDcwNTMwMzY5fQ.Dez8PQkV8vWv7VkL_fZe-lY-Xs9P5VptNvRRnhkxoXw";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization, apikey",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- if (event.httpMethod !== "POST") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({
- error: "Token de autenticação é obrigatório",
- }),
- };
- }
-
- const body = JSON.parse(event.body || "{}");
-
- console.log("[delete-user] ATENÇÃO: Tentativa de hard delete:", {
- userId: body.userId,
- requestedBy: "via Netlify Function",
- });
-
- // Validação
- if (!body.userId) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "userId é obrigatório",
- }),
- };
- }
-
- // TODO: Aqui deveria verificar se o usuário tem permissão de admin/gestor
- // Verificando o token JWT que foi passado
-
- // Deletar usuário via Admin API do Supabase
- const response = await fetch(
- `${SUPABASE_URL}/auth/v1/admin/users/${body.userId}`,
- {
- method: "DELETE",
- headers: {
- apikey: SUPABASE_SERVICE_ROLE_KEY,
- Authorization: `Bearer ${SUPABASE_SERVICE_ROLE_KEY}`,
- "Content-Type": "application/json",
- },
- }
- );
-
- if (response.ok) {
- console.log("[delete-user] Usuário deletado com sucesso:", body.userId);
- return {
- statusCode: 200,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- success: true,
- message: "Usuário deletado permanentemente",
- userId: body.userId,
- }),
- };
- }
-
- const errorData = await response.json();
- console.error("[delete-user] Erro ao deletar:", errorData);
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- error: errorData.msg || errorData.message || "Erro ao deletar usuário",
- details: errorData,
- }),
- };
- } catch (error) {
- console.error("[delete-user] Erro:", error);
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/doctor-availability.ts b/MEDICONNECT 2/netlify/functions/doctor-availability.ts
deleted file mode 100644
index fa48e3dd4..000000000
--- a/MEDICONNECT 2/netlify/functions/doctor-availability.ts
+++ /dev/null
@@ -1,217 +0,0 @@
-/**
- * Netlify Function: doctor-availability
- *
- * Proxy para operações de disponibilidade dos médicos
- * GET: Lista disponibilidades
- * POST: Criar disponibilidade
- * PATCH: Atualizar disponibilidade
- * DELETE: Deletar disponibilidade
- */
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_API_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-const SUPABASE_SERVICE_ROLE_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc1NDk1NDM2OSwiZXhwIjoyMDcwNTMwMzY5fQ.Dez8PQkV8vWv7VkL_fZe-lY-Xs9P5VptNvRRnhkxoXw";
-
-export default async (req: Request) => {
- // Permitir CORS
- if (req.method === "OPTIONS") {
- return new Response(null, {
- status: 204,
- headers: {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- },
- });
- }
-
- try {
- const url = new URL(req.url);
- const authHeader = req.headers.get("Authorization");
-
- // Extrair ID do path se existir
- const pathParts = url.pathname.split("/");
- const availabilityId = pathParts[pathParts.length - 1];
-
- // GET: Listar disponibilidades
- if (req.method === "GET") {
- const select = url.searchParams.get("select") || "*";
- const doctor_id = url.searchParams.get("doctor_id");
- const active = url.searchParams.get("active");
-
- const queryParams = new URLSearchParams();
- queryParams.append("select", select);
- if (doctor_id) queryParams.append("doctor_id", `eq.${doctor_id}`);
- if (active !== null) queryParams.append("active", `eq.${active}`);
-
- const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_availability?${queryParams}`;
-
- const headers: HeadersInit = {
- apikey: SUPABASE_API_KEY,
- "Content-Type": "application/json",
- };
-
- if (authHeader) {
- headers["Authorization"] = authHeader;
- }
-
- const response = await fetch(supabaseUrl, {
- method: "GET",
- headers,
- });
-
- const data = await response.json();
-
- return new Response(JSON.stringify(data), {
- status: response.status,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- });
- }
-
- // POST: Criar disponibilidade
- if (req.method === "POST") {
- const body = await req.json();
-
- const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_availability`;
-
- // Usa SERVICE ROLE KEY para ignorar políticas RLS
- const headers: HeadersInit = {
- apikey: SUPABASE_SERVICE_ROLE_KEY,
- Authorization: `Bearer ${SUPABASE_SERVICE_ROLE_KEY}`,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- };
-
- const response = await fetch(supabaseUrl, {
- method: "POST",
- headers,
- body: JSON.stringify(body),
- });
-
- const data = await response.json();
-
- return new Response(JSON.stringify(data), {
- status: response.status,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- });
- }
-
- // PATCH: Atualizar disponibilidade
- if (req.method === "PATCH") {
- if (!availabilityId || availabilityId === "doctor-availability") {
- return new Response(
- JSON.stringify({ error: "Availability ID is required" }),
- {
- status: 400,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- }
- );
- }
-
- const body = await req.json();
-
- const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_availability?id=eq.${availabilityId}`;
-
- const headers: HeadersInit = {
- apikey: SUPABASE_API_KEY,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- };
-
- if (authHeader) {
- headers["Authorization"] = authHeader;
- }
-
- const response = await fetch(supabaseUrl, {
- method: "PATCH",
- headers,
- body: JSON.stringify(body),
- });
-
- const data = await response.json();
- const result = Array.isArray(data) && data.length > 0 ? data[0] : data;
-
- return new Response(JSON.stringify(result), {
- status: response.status,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- });
- }
-
- // DELETE: Deletar disponibilidade
- if (req.method === "DELETE") {
- if (!availabilityId || availabilityId === "doctor-availability") {
- return new Response(
- JSON.stringify({ error: "Availability ID is required" }),
- {
- status: 400,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- }
- );
- }
-
- const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_availability?id=eq.${availabilityId}`;
-
- const headers: HeadersInit = {
- apikey: SUPABASE_API_KEY,
- "Content-Type": "application/json",
- };
-
- if (authHeader) {
- headers["Authorization"] = authHeader;
- }
-
- const response = await fetch(supabaseUrl, {
- method: "DELETE",
- headers,
- });
-
- return new Response(null, {
- status: response.status,
- headers: {
- "Access-Control-Allow-Origin": "*",
- },
- });
- }
-
- // Método não suportado
- return new Response(JSON.stringify({ error: "Method not allowed" }), {
- status: 405,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- });
- } catch (error) {
- console.error("Error in doctor-availability function:", error);
- return new Response(
- JSON.stringify({
- error: "Internal server error",
- details: error instanceof Error ? error.message : "Unknown error",
- }),
- {
- status: 500,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- }
- );
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/doctor-exceptions.ts b/MEDICONNECT 2/netlify/functions/doctor-exceptions.ts
deleted file mode 100644
index b5dc31541..000000000
--- a/MEDICONNECT 2/netlify/functions/doctor-exceptions.ts
+++ /dev/null
@@ -1,169 +0,0 @@
-/**
- * Netlify Function: doctor-exceptions
- *
- * Proxy para operações de exceções na agenda dos médicos
- * GET: Lista exceções
- * POST: Criar exceção
- * DELETE: Deletar exceção
- */
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_API_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export default async (req: Request) => {
- // Permitir CORS
- if (req.method === "OPTIONS") {
- return new Response(null, {
- status: 204,
- headers: {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- },
- });
- }
-
- try {
- const url = new URL(req.url);
- const authHeader = req.headers.get("Authorization");
-
- // Extrair ID do path se existir
- const pathParts = url.pathname.split("/");
- const exceptionId = pathParts[pathParts.length - 1];
-
- // GET: Listar exceções
- if (req.method === "GET") {
- const select = url.searchParams.get("select") || "*";
- const doctor_id = url.searchParams.get("doctor_id");
- const date = url.searchParams.get("date");
-
- const queryParams = new URLSearchParams();
- queryParams.append("select", select);
- if (doctor_id) queryParams.append("doctor_id", `eq.${doctor_id}`);
- if (date) queryParams.append("date", `eq.${date}`);
-
- const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_exceptions?${queryParams}`;
-
- const headers: HeadersInit = {
- apikey: SUPABASE_API_KEY,
- "Content-Type": "application/json",
- };
-
- if (authHeader) {
- headers["Authorization"] = authHeader;
- }
-
- const response = await fetch(supabaseUrl, {
- method: "GET",
- headers,
- });
-
- const data = await response.json();
-
- return new Response(JSON.stringify(data), {
- status: response.status,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- });
- }
-
- // POST: Criar exceção
- if (req.method === "POST") {
- const body = await req.json();
-
- const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_exceptions`;
-
- const headers: HeadersInit = {
- apikey: SUPABASE_API_KEY,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- };
-
- if (authHeader) {
- headers["Authorization"] = authHeader;
- }
-
- const response = await fetch(supabaseUrl, {
- method: "POST",
- headers,
- body: JSON.stringify(body),
- });
-
- const data = await response.json();
-
- return new Response(JSON.stringify(data), {
- status: response.status,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- });
- }
-
- // DELETE: Deletar exceção
- if (req.method === "DELETE") {
- if (!exceptionId || exceptionId === "doctor-exceptions") {
- return new Response(
- JSON.stringify({ error: "Exception ID is required" }),
- {
- status: 400,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- }
- );
- }
-
- const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_exceptions?id=eq.${exceptionId}`;
-
- const headers: HeadersInit = {
- apikey: SUPABASE_API_KEY,
- "Content-Type": "application/json",
- };
-
- if (authHeader) {
- headers["Authorization"] = authHeader;
- }
-
- const response = await fetch(supabaseUrl, {
- method: "DELETE",
- headers,
- });
-
- return new Response(null, {
- status: response.status,
- headers: {
- "Access-Control-Allow-Origin": "*",
- },
- });
- }
-
- // Método não suportado
- return new Response(JSON.stringify({ error: "Method not allowed" }), {
- status: 405,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- });
- } catch (error) {
- console.error("Error in doctor-exceptions function:", error);
- return new Response(
- JSON.stringify({
- error: "Internal server error",
- details: error instanceof Error ? error.message : "Unknown error",
- }),
- {
- status: 500,
- headers: {
- "Content-Type": "application/json",
- "Access-Control-Allow-Origin": "*",
- },
- }
- );
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/doctors.ts b/MEDICONNECT 2/netlify/functions/doctors.ts
deleted file mode 100644
index 3527f18c3..000000000
--- a/MEDICONNECT 2/netlify/functions/doctors.ts
+++ /dev/null
@@ -1,237 +0,0 @@
-/**
- * Netlify Function: Doctors CRUD
- * GET /rest/v1/doctors - Lista médicos
- * GET /rest/v1/doctors/{id} - Busca por ID
- * POST /rest/v1/doctors - Cria médico
- * PATCH /rest/v1/doctors/{id} - Atualiza médico
- * DELETE /rest/v1/doctors/{id} - Deleta médico
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- // Extrai ID da URL se houver (doctors/123 ou doctors?id=123)
- const pathParts = event.path.split("/");
- const doctorId =
- pathParts[pathParts.length - 1] !== "doctors"
- ? pathParts[pathParts.length - 1]
- : null;
-
- // GET - Listar ou buscar por ID
- if (event.httpMethod === "GET") {
- let url = `${SUPABASE_URL}/rest/v1/doctors`;
-
- if (doctorId && doctorId !== "doctors") {
- // Buscar por ID específico
- url += `?id=eq.${doctorId}&select=*`;
- } else if (event.queryStringParameters) {
- // Adiciona filtros da query string
- const params = new URLSearchParams(
- event.queryStringParameters as Record
- );
- url += `?${params.toString()}`;
-
- // Adiciona select=* se não tiver
- if (!params.has("select")) {
- url += url.includes("?") ? "&select=*" : "?select=*";
- }
- } else {
- url += "?select=*";
- }
-
- const response = await fetch(url, {
- method: "GET",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- });
-
- let data = await response.json();
-
- // Se buscar por ID, retorna o objeto diretamente (não array)
- if (
- doctorId &&
- doctorId !== "doctors" &&
- Array.isArray(data) &&
- data.length > 0
- ) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- // POST - Criar médico
- if (event.httpMethod === "POST") {
- const body = JSON.parse(event.body || "{}");
-
- if (
- !body.crm ||
- !body.crm_uf ||
- !body.full_name ||
- !body.cpf ||
- !body.email
- ) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campos obrigatórios: crm, crm_uf, full_name, cpf, email",
- }),
- };
- }
-
- const response = await fetch(`${SUPABASE_URL}/rest/v1/doctors`, {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- });
-
- let data = await response.json();
-
- // Supabase retorna array, pega o primeiro
- if (Array.isArray(data) && data.length > 0) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- // PATCH - Atualizar médico
- if (event.httpMethod === "PATCH") {
- if (!doctorId || doctorId === "doctors") {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "ID do médico é obrigatório" }),
- };
- }
-
- const body = JSON.parse(event.body || "{}");
-
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/doctors?id=eq.${doctorId}`,
- {
- method: "PATCH",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- }
- );
-
- let data = await response.json();
-
- if (Array.isArray(data) && data.length > 0) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- // DELETE - Deletar médico
- if (event.httpMethod === "DELETE") {
- if (!doctorId || doctorId === "doctors") {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "ID do médico é obrigatório" }),
- };
- }
-
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/doctors?id=eq.${doctorId}`,
- {
- method: "DELETE",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- }
- );
-
- return {
- statusCode: response.status,
- headers,
- body: "",
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro na API de médicos:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/get-available-slots.ts b/MEDICONNECT 2/netlify/functions/get-available-slots.ts
deleted file mode 100644
index 4cbba71eb..000000000
--- a/MEDICONNECT 2/netlify/functions/get-available-slots.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * Netlify Function: Get Available Slots
- * POST /functions/v1/get-available-slots - Busca horários disponíveis
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- if (event.httpMethod === "POST") {
- const body = JSON.parse(event.body || "{}");
-
- // Validação dos campos obrigatórios
- if (!body.doctor_id || !body.start_date || !body.end_date) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campos obrigatórios: doctor_id, start_date, end_date",
- }),
- };
- }
-
- const response = await fetch(
- `${SUPABASE_URL}/functions/v1/get-available-slots`,
- {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(body),
- }
- );
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro na API de available slots:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/patients.ts b/MEDICONNECT 2/netlify/functions/patients.ts
deleted file mode 100644
index 73bb18a08..000000000
--- a/MEDICONNECT 2/netlify/functions/patients.ts
+++ /dev/null
@@ -1,226 +0,0 @@
-/**
- * Netlify Function: Patients CRUD
- * GET /rest/v1/patients - Lista pacientes
- * GET /rest/v1/patients/{id} - Busca por ID
- * POST /rest/v1/patients - Cria paciente
- * PATCH /rest/v1/patients/{id} - Atualiza paciente
- * DELETE /rest/v1/patients/{id} - Deleta paciente
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- // Extrai ID da URL se houver
- const pathParts = event.path.split("/");
- const patientId =
- pathParts[pathParts.length - 1] !== "patients"
- ? pathParts[pathParts.length - 1]
- : null;
-
- // GET - Listar ou buscar por ID
- if (event.httpMethod === "GET") {
- let url = `${SUPABASE_URL}/rest/v1/patients`;
-
- if (patientId && patientId !== "patients") {
- url += `?id=eq.${patientId}&select=*`;
- } else if (event.queryStringParameters) {
- const params = new URLSearchParams(
- event.queryStringParameters as Record
- );
- url += `?${params.toString()}`;
-
- if (!params.has("select")) {
- url += url.includes("?") ? "&select=*" : "?select=*";
- }
- } else {
- url += "?select=*";
- }
-
- const response = await fetch(url, {
- method: "GET",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- });
-
- let data = await response.json();
-
- if (
- patientId &&
- patientId !== "patients" &&
- Array.isArray(data) &&
- data.length > 0
- ) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- // POST - Criar paciente
- if (event.httpMethod === "POST") {
- const body = JSON.parse(event.body || "{}");
-
- if (!body.full_name || !body.cpf || !body.email || !body.phone_mobile) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campos obrigatórios: full_name, cpf, email, phone_mobile",
- }),
- };
- }
-
- const response = await fetch(`${SUPABASE_URL}/rest/v1/patients`, {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- });
-
- let data = await response.json();
-
- if (Array.isArray(data) && data.length > 0) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- // PATCH - Atualizar paciente
- if (event.httpMethod === "PATCH") {
- if (!patientId || patientId === "patients") {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "ID do paciente é obrigatório" }),
- };
- }
-
- const body = JSON.parse(event.body || "{}");
-
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/patients?id=eq.${patientId}`,
- {
- method: "PATCH",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- }
- );
-
- let data = await response.json();
-
- if (Array.isArray(data) && data.length > 0) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- // DELETE - Deletar paciente
- if (event.httpMethod === "DELETE") {
- if (!patientId || patientId === "patients") {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "ID do paciente é obrigatório" }),
- };
- }
-
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/patients?id=eq.${patientId}`,
- {
- method: "DELETE",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- }
- );
-
- return {
- statusCode: response.status,
- headers,
- body: "",
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro na API de pacientes:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/profiles.ts b/MEDICONNECT 2/netlify/functions/profiles.ts
deleted file mode 100644
index 4c8c02d06..000000000
--- a/MEDICONNECT 2/netlify/functions/profiles.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-/**
- * Netlify Function: Profiles
- * GET /rest/v1/profiles - Lista perfis
- * GET /rest/v1/profiles/{id} - Busca por ID
- * PATCH /rest/v1/profiles/{id} - Atualiza avatar_url
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "GET, PATCH, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- // Extrai ID da URL se houver
- const pathParts = event.path.split("/");
- const profileId =
- pathParts[pathParts.length - 1] !== "profiles"
- ? pathParts[pathParts.length - 1]
- : null;
-
- // GET - Listar ou buscar por ID
- if (event.httpMethod === "GET") {
- let url = `${SUPABASE_URL}/rest/v1/profiles`;
-
- if (profileId && profileId !== "profiles") {
- url += `?id=eq.${profileId}&select=*`;
- } else if (event.queryStringParameters) {
- const params = new URLSearchParams(
- event.queryStringParameters as Record
- );
- url += `?${params.toString()}`;
-
- if (!params.has("select")) {
- url += url.includes("?") ? "&select=*" : "?select=*";
- }
- } else {
- url += "?select=*";
- }
-
- const response = await fetch(url, {
- method: "GET",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- });
-
- let data = await response.json();
-
- if (
- profileId &&
- profileId !== "profiles" &&
- Array.isArray(data) &&
- data.length > 0
- ) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- // PATCH - Atualizar avatar_url
- if (event.httpMethod === "PATCH") {
- if (!profileId || profileId === "profiles") {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "ID do perfil é obrigatório" }),
- };
- }
-
- const body = JSON.parse(event.body || "{}");
-
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/profiles?id=eq.${profileId}`,
- {
- method: "PATCH",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- }
- );
-
- let data = await response.json();
-
- if (Array.isArray(data) && data.length > 0) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro na API de perfis:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/register-patient.ts b/MEDICONNECT 2/netlify/functions/register-patient.ts
deleted file mode 100644
index 304c8f196..000000000
--- a/MEDICONNECT 2/netlify/functions/register-patient.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * Netlify Function: Register Patient (Public)
- * POST /register-patient - Registro público de paciente
- * Não requer autenticação - função pública
- * Validações rigorosas (CPF, rate limiting, rollback)
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- if (event.httpMethod === "POST") {
- const body = JSON.parse(event.body || "{}");
-
- console.log(
- "[register-patient] Recebido body:",
- JSON.stringify(body, null, 2)
- );
-
- // Validação dos campos obrigatórios
- if (!body.email || !body.full_name || !body.cpf || !body.phone_mobile) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campos obrigatórios: email, full_name, cpf, phone_mobile",
- }),
- };
- }
-
- // Chama a Edge Function pública do Supabase
- const response = await fetch(
- `${SUPABASE_URL}/functions/v1/register-patient`,
- {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(body),
- }
- );
-
- const data = await response.json();
-
- console.log("[register-patient] Resposta do Supabase:", {
- status: response.status,
- data: JSON.stringify(data, null, 2),
- });
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("[register-patient] Erro na API:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno do servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/reports.ts b/MEDICONNECT 2/netlify/functions/reports.ts
deleted file mode 100644
index 23323f5d8..000000000
--- a/MEDICONNECT 2/netlify/functions/reports.ts
+++ /dev/null
@@ -1,197 +0,0 @@
-/**
- * Netlify Function: Reports
- * GET /rest/v1/reports - Lista relatórios
- * GET /rest/v1/reports/{id} - Busca por ID
- * POST /rest/v1/reports - Cria relatório
- * PATCH /rest/v1/reports/{id} - Atualiza relatório
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "GET, POST, PATCH, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- // Extrai ID da URL se houver
- const pathParts = event.path.split("/");
- const reportId =
- pathParts[pathParts.length - 1] !== "reports"
- ? pathParts[pathParts.length - 1]
- : null;
-
- // GET - Listar ou buscar por ID
- if (event.httpMethod === "GET") {
- let url = `${SUPABASE_URL}/rest/v1/reports`;
-
- if (reportId && reportId !== "reports") {
- url += `?id=eq.${reportId}&select=*`;
- } else if (event.queryStringParameters) {
- const params = new URLSearchParams(
- event.queryStringParameters as Record
- );
- url += `?${params.toString()}`;
-
- if (!params.has("select")) {
- url += url.includes("?") ? "&select=*" : "?select=*";
- }
- } else {
- url += "?select=*";
- }
-
- const response = await fetch(url, {
- method: "GET",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- });
-
- let data = await response.json();
-
- if (
- reportId &&
- reportId !== "reports" &&
- Array.isArray(data) &&
- data.length > 0
- ) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- // POST - Criar relatório
- if (event.httpMethod === "POST") {
- const body = JSON.parse(event.body || "{}");
-
- if (!body.patient_id) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campo obrigatório: patient_id",
- }),
- };
- }
-
- const response = await fetch(`${SUPABASE_URL}/rest/v1/reports`, {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- });
-
- let data = await response.json();
-
- if (Array.isArray(data) && data.length > 0) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- // PATCH - Atualizar relatório
- if (event.httpMethod === "PATCH") {
- if (!reportId || reportId === "reports") {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "ID do relatório é obrigatório" }),
- };
- }
-
- const body = JSON.parse(event.body || "{}");
-
- const response = await fetch(
- `${SUPABASE_URL}/rest/v1/reports?id=eq.${reportId}`,
- {
- method: "PATCH",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify(body),
- }
- );
-
- let data = await response.json();
-
- if (Array.isArray(data) && data.length > 0) {
- data = data[0];
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro na API de relatórios:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/request-password-reset.ts b/MEDICONNECT 2/netlify/functions/request-password-reset.ts
deleted file mode 100644
index b100d2725..000000000
--- a/MEDICONNECT 2/netlify/functions/request-password-reset.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * Netlify Function: Request Password Reset
- * POST /request-password-reset - Solicita reset de senha via email (público)
- * Não requer autenticação - endpoint público
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-interface PasswordResetRequest {
- email: string;
- redirect_url?: string;
-}
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- if (event.httpMethod !== "POST") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- const body: PasswordResetRequest = JSON.parse(event.body || "{}");
-
- console.log("[request-password-reset] Recebido:", {
- email: body.email,
- hasRedirectUrl: !!body.redirect_url,
- });
-
- if (!body.email) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "Email é obrigatório" }),
- };
- }
-
- // Chama a API do Supabase para enviar email de reset
- const response = await fetch(`${SUPABASE_URL}/auth/v1/recover`, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- apikey: SUPABASE_ANON_KEY,
- },
- body: JSON.stringify({
- email: body.email,
- options: {
- redirectTo:
- body.redirect_url ||
- "https://mediconnectbrasil.netlify.app/reset-password",
- },
- }),
- });
-
- const data = await response.json();
-
- console.log("[request-password-reset] Resposta Supabase:", {
- status: response.status,
- data,
- });
-
- // Supabase sempre retorna 200 mesmo se o email não existir (por segurança)
- if (response.ok) {
- return {
- statusCode: 200,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- success: true,
- message:
- "Email de reset de senha enviado com sucesso. Verifique sua caixa de entrada.",
- }),
- };
- }
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- } catch (error) {
- console.error("[request-password-reset] Erro:", error);
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro ao solicitar reset de senha",
- details: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/send-sms.ts b/MEDICONNECT 2/netlify/functions/send-sms.ts
deleted file mode 100644
index 57e76a4ab..000000000
--- a/MEDICONNECT 2/netlify/functions/send-sms.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * Netlify Function: Send SMS
- * POST /functions/v1/send-sms - Envia SMS via Twilio
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- if (event.httpMethod === "POST") {
- const body = JSON.parse(event.body || "{}");
-
- // Validação dos campos obrigatórios
- if (!body.phone_number || !body.message) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({
- error: "Campos obrigatórios: phone_number, message",
- }),
- };
- }
-
- // Chama a função Supabase de enviar SMS
- const response = await fetch(`${SUPABASE_URL}/functions/v1/send-sms`, {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(body),
- });
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro na API de SMS:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/user-info-by-id.ts b/MEDICONNECT 2/netlify/functions/user-info-by-id.ts
deleted file mode 100644
index 4f3a4e2b5..000000000
--- a/MEDICONNECT 2/netlify/functions/user-info-by-id.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * Netlify Function: User Info By ID
- * POST /user-info-by-id - Retorna dados de usuário específico (apenas admin/gestor)
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- if (event.httpMethod !== "POST") {
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- const body = JSON.parse(event.body || "{}");
-
- if (!body.user_id) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "Campo obrigatório: user_id" }),
- };
- }
-
- // Chama a Edge Function do Supabase
- const response = await fetch(
- `${SUPABASE_URL}/functions/v1/user-info-by-id`,
- {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(body),
- }
- );
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- } catch (error) {
- console.error("Erro na API de user-info-by-id:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/user-info.ts b/MEDICONNECT 2/netlify/functions/user-info.ts
deleted file mode 100644
index e197758fe..000000000
--- a/MEDICONNECT 2/netlify/functions/user-info.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * Netlify Function: User Info
- * GET /functions/v1/user-info - Retorna informações completas do usuário autenticado
- * Inclui: user, profile, roles e permissions calculadas
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- // Aceita tanto POST quanto GET para compatibilidade
- if (event.httpMethod === "POST" || event.httpMethod === "GET") {
- // Chama a Edge Function do Supabase (POST conforme doc 21/10/2025)
- const response = await fetch(`${SUPABASE_URL}/functions/v1/user-info`, {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- },
- });
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro na API de user-info:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/netlify/functions/user-roles.ts b/MEDICONNECT 2/netlify/functions/user-roles.ts
deleted file mode 100644
index b05b6f5a7..000000000
--- a/MEDICONNECT 2/netlify/functions/user-roles.ts
+++ /dev/null
@@ -1,161 +0,0 @@
-/**
- * Netlify Function: User Roles
- * GET /rest/v1/user_roles - Lista roles de usuários
- * POST /rest/v1/user_roles - Adiciona role a um usuário
- * DELETE /rest/v1/user_roles - Remove role de um usuário
- */
-
-import type { Handler, HandlerEvent } from "@netlify/functions";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-const SUPABASE_ANON_KEY =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
-
-export const handler: Handler = async (event: HandlerEvent) => {
- const headers = {
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
- "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
- };
-
- if (event.httpMethod === "OPTIONS") {
- return {
- statusCode: 200,
- headers,
- body: "",
- };
- }
-
- try {
- const authHeader =
- event.headers.authorization || event.headers.Authorization;
-
- if (!authHeader) {
- return {
- statusCode: 401,
- headers,
- body: JSON.stringify({ error: "Token não fornecido" }),
- };
- }
-
- if (event.httpMethod === "GET") {
- let url = `${SUPABASE_URL}/rest/v1/user_roles?select=*`;
-
- if (event.queryStringParameters) {
- const params = new URLSearchParams(
- event.queryStringParameters as Record
- );
- const paramsStr = params.toString();
- if (paramsStr) {
- url += `&${paramsStr}`;
- }
- }
-
- const response = await fetch(url, {
- method: "GET",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- });
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- if (event.httpMethod === "POST") {
- // Adicionar nova role para um usuário
- const body = JSON.parse(event.body || "{}");
-
- if (!body.user_id || !body.role) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "user_id e role são obrigatórios" }),
- };
- }
-
- const response = await fetch(`${SUPABASE_URL}/rest/v1/user_roles`, {
- method: "POST",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- "Content-Type": "application/json",
- Prefer: "return=representation",
- },
- body: JSON.stringify({
- user_id: body.user_id,
- role: body.role,
- }),
- });
-
- const data = await response.json();
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data),
- };
- }
-
- if (event.httpMethod === "DELETE") {
- // Remover role de um usuário
- const params = event.queryStringParameters;
-
- if (!params?.user_id || !params?.role) {
- return {
- statusCode: 400,
- headers,
- body: JSON.stringify({ error: "user_id e role são obrigatórios" }),
- };
- }
-
- const url = `${SUPABASE_URL}/rest/v1/user_roles?user_id=eq.${params.user_id}&role=eq.${params.role}`;
-
- const response = await fetch(url, {
- method: "DELETE",
- headers: {
- apikey: SUPABASE_ANON_KEY,
- Authorization: authHeader,
- },
- });
-
- return {
- statusCode: response.status,
- headers: {
- ...headers,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({ success: true }),
- };
- }
-
- return {
- statusCode: 405,
- headers,
- body: JSON.stringify({ error: "Method Not Allowed" }),
- };
- } catch (error) {
- console.error("Erro na API de user roles:", error);
-
- return {
- statusCode: 500,
- headers,
- body: JSON.stringify({
- error: "Erro interno no servidor",
- message: error instanceof Error ? error.message : "Erro desconhecido",
- }),
- };
- }
-};
diff --git a/MEDICONNECT 2/public/_redirects b/MEDICONNECT 2/public/_redirects
deleted file mode 100644
index 430850547..000000000
--- a/MEDICONNECT 2/public/_redirects
+++ /dev/null
@@ -1 +0,0 @@
-/* /index.html 200
diff --git a/MEDICONNECT 2/public/logo.PNG b/MEDICONNECT 2/public/logo.PNG
new file mode 100644
index 000000000..4e4a03fb6
Binary files /dev/null and b/MEDICONNECT 2/public/logo.PNG differ
diff --git a/MEDICONNECT 2/scripts/cleanup-users.js b/MEDICONNECT 2/scripts/cleanup-users.js
new file mode 100644
index 000000000..0c9290714
--- /dev/null
+++ b/MEDICONNECT 2/scripts/cleanup-users.js
@@ -0,0 +1,134 @@
+import axios from "axios";
+
+const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
+const ANON_KEY =
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
+
+let ACCESS_TOKEN = "";
+
+// 1. Login como admin
+async function login() {
+ console.log("\n🔐 Fazendo login como admin...");
+ try {
+ const response = await axios.post(
+ `${SUPABASE_URL}/auth/v1/token?grant_type=password`,
+ {
+ email: "riseup@popcode.com.br",
+ password: "riseup",
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ },
+ }
+ );
+
+ ACCESS_TOKEN = response.data.access_token;
+ console.log("✅ Login realizado com sucesso!");
+ console.log(`📧 Email: ${response.data.user.email}`);
+ console.log(`🆔 User ID: ${response.data.user.id}`);
+ return response.data;
+ } catch (error) {
+ console.error("❌ Erro no login:", error.response?.data || error.message);
+ process.exit(1);
+ }
+}
+
+// 2. Listar usuários
+async function listUsers() {
+ console.log("\n📋 Listando usuários...\n");
+ try {
+ const response = await axios.get(
+ `${SUPABASE_URL}/rest/v1/profiles?select=id,full_name,email`,
+ {
+ headers: {
+ apikey: ANON_KEY,
+ Authorization: `Bearer ${ACCESS_TOKEN}`,
+ },
+ }
+ );
+
+ console.log(`✅ ${response.data.length} usuários encontrados:\n`);
+
+ response.data.forEach((user, index) => {
+ console.log(`${index + 1}. ${user.full_name || "Sem nome"}`);
+ console.log(` 📧 Email: ${user.email}`);
+ console.log(` 🆔 ID: ${user.id}\n`);
+ });
+
+ return response.data;
+ } catch (error) {
+ console.error(
+ "❌ Erro ao listar usuários:",
+ error.response?.data || error.message
+ );
+ return [];
+ }
+}
+
+// 3. Deletar usuário
+async function deleteUser(userId, userName) {
+ console.log(`\n🗑️ Deletando usuário: ${userName} (${userId})...`);
+ try {
+ const response = await axios.post(
+ `${SUPABASE_URL}/functions/v1/delete-user`,
+ { userId },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ Authorization: `Bearer ${ACCESS_TOKEN}`,
+ },
+ }
+ );
+
+ console.log(`✅ ${userName} deletado com sucesso!`);
+ return response.data;
+ } catch (error) {
+ console.error(
+ `❌ Erro ao deletar ${userName}:`,
+ error.response?.data || error.message
+ );
+ }
+}
+
+// Script principal
+async function main() {
+ console.log("🧹 Iniciando limpeza de usuários de teste...");
+
+ // 1. Login
+ await login();
+
+ // 2. Listar usuários atuais
+ const users = await listUsers();
+
+ // 3. Lista de emails para deletar (apenas os que o assistente criou)
+ const testEmails = [
+ "admin@mediconnect.com",
+ "secretaria@mediconnect.com",
+ "dr.medico@mediconnect.com",
+ "fernando.pirichowski@souunit.com.br",
+ ];
+
+ // 4. Deletar usuários de teste
+ let deletedCount = 0;
+ for (const user of users) {
+ if (testEmails.includes(user.email)) {
+ await deleteUser(user.id, user.full_name || user.email);
+ deletedCount++;
+ // Aguardar 1 segundo entre deleções
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ }
+ }
+
+ console.log(
+ `\n✅ Limpeza concluída! ${deletedCount} usuários de teste deletados.`
+ );
+
+ // 5. Listar usuários finais
+ console.log("\n📊 Usuários restantes:");
+ await listUsers();
+}
+
+main().catch(console.error);
diff --git a/MEDICONNECT 2/scripts/manage-users.js b/MEDICONNECT 2/scripts/manage-users.js
new file mode 100644
index 000000000..2c934b5e1
--- /dev/null
+++ b/MEDICONNECT 2/scripts/manage-users.js
@@ -0,0 +1,275 @@
+import axios from "axios";
+
+const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
+const ANON_KEY =
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
+
+// Credenciais do admin
+const ADMIN_EMAIL = "riseup@popcode.com.br";
+const ADMIN_PASSWORD = "riseup";
+
+let ACCESS_TOKEN = "";
+
+// 1. Fazer login como admin
+async function login() {
+ console.log("\n🔐 Fazendo login como admin...");
+ try {
+ const response = await axios.post(
+ `${SUPABASE_URL}/auth/v1/token?grant_type=password`,
+ {
+ email: ADMIN_EMAIL,
+ password: ADMIN_PASSWORD,
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ },
+ }
+ );
+
+ ACCESS_TOKEN = response.data.access_token;
+ console.log("✅ Login realizado com sucesso!");
+ console.log("📧 Email:", response.data.user.email);
+ console.log("🆔 User ID:", response.data.user.id);
+ return response.data;
+ } catch (error) {
+ console.error("❌ Erro no login:", error.response?.data || error.message);
+ process.exit(1);
+ }
+}
+
+// 2. Listar todos os usuários (via profiles - simplificado)
+async function listUsers() {
+ console.log("\n📋 Listando usuários...");
+ try {
+ const response = await axios.get(
+ `${SUPABASE_URL}/rest/v1/profiles?select=id,full_name,email`,
+ {
+ headers: {
+ apikey: ANON_KEY,
+ Authorization: `Bearer ${ACCESS_TOKEN}`,
+ },
+ }
+ );
+
+ console.log(`\n✅ ${response.data.length} usuários encontrados:\n`);
+ response.data.forEach((user, index) => {
+ console.log(`${index + 1}. ${user.full_name || "Sem nome"}`);
+ console.log(` 📧 Email: ${user.email || "Sem email"}`);
+ console.log(` 🆔 ID: ${user.id}`);
+ console.log("");
+ });
+
+ return response.data;
+ } catch (error) {
+ console.error(
+ "❌ Erro ao listar usuários:",
+ error.response?.data || error.message
+ );
+ return [];
+ }
+}
+
+// 3. Deletar usuário (Edge Function)
+async function deleteUser(userId, userName) {
+ console.log(`\n🗑️ Deletando usuário: ${userName} (${userId})...`);
+ try {
+ const response = await axios.post(
+ `${SUPABASE_URL}/functions/v1/delete-user`,
+ { userId },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ Authorization: `Bearer ${ACCESS_TOKEN}`,
+ },
+ }
+ );
+
+ console.log(`✅ ${userName} deletado com sucesso!`);
+ return response.data;
+ } catch (error) {
+ console.error(
+ `❌ Erro ao deletar ${userName}:`,
+ error.response?.data || error.message
+ );
+ }
+}
+
+// 4. Criar novo usuário com Edge Function
+async function createUserWithPassword(email, password, fullName, role) {
+ console.log(`\n➕ Criando usuário: ${fullName} (${role})...`);
+ try {
+ const response = await axios.post(
+ `${SUPABASE_URL}/functions/v1/create-user`,
+ {
+ email,
+ password,
+ full_name: fullName,
+ role,
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ Authorization: `Bearer ${ACCESS_TOKEN}`,
+ },
+ }
+ );
+
+ console.log(`✅ ${fullName} criado com sucesso!`);
+ console.log(` 📧 Email: ${email}`);
+ console.log(` 🔑 Senha: ${password}`);
+ console.log(` 👤 Role: ${role}`);
+ return response.data;
+ } catch (error) {
+ console.error(
+ `❌ Erro ao criar ${fullName}:`,
+ error.response?.data || error.message
+ );
+ }
+}
+
+// 5. Criar médico com Edge Function
+async function createDoctor(
+ email,
+ password,
+ fullName,
+ especialidade,
+ crm,
+ crmUf,
+ cpf
+) {
+ console.log(`\n➕ Criando médico: ${fullName}...`);
+ try {
+ const response = await axios.post(
+ `${SUPABASE_URL}/functions/v1/create-doctor`,
+ {
+ email,
+ password,
+ full_name: fullName,
+ cpf,
+ especialidade,
+ crm,
+ crm_uf: crmUf,
+ },
+ {
+ headers: {
+ "Content-Type": "application/json",
+ apikey: ANON_KEY,
+ Authorization: `Bearer ${ACCESS_TOKEN}`,
+ },
+ }
+ );
+
+ console.log(`✅ ${fullName} criado com sucesso!`);
+ console.log(` 📧 Email: ${email}`);
+ console.log(` 🔑 Senha: ${password}`);
+ console.log(` 🆔 CPF: ${cpf}`);
+ console.log(` 🩺 Especialidade: ${especialidade}`);
+ console.log(` 📋 CRM: ${crm}-${crmUf}`);
+ return response.data;
+ } catch (error) {
+ console.error(
+ `❌ Erro ao criar ${fullName}:`,
+ error.response?.data || error.message
+ );
+ }
+}
+
+// Script principal
+async function main() {
+ console.log("🚀 Iniciando gerenciamento de usuários...");
+
+ // 1. Login
+ await login();
+
+ // 2. Listar usuários atuais
+ const users = await listUsers();
+
+ // 3. Encontrar e deletar admin e médico específicos (por email)
+ const adminToDelete = users.find((u) => u.email === "admin@mediconnect.com");
+
+ const secretariaToDelete = users.find(
+ (u) => u.email === "secretaria@mediconnect.com"
+ );
+
+ const medicoToDelete = users.find(
+ (u) =>
+ u.email === "medico@mediconnect.com" ||
+ u.email === "dr.medico@mediconnect.com"
+ );
+
+ if (adminToDelete) {
+ await deleteUser(
+ adminToDelete.id,
+ adminToDelete.full_name || adminToDelete.email
+ );
+ } else {
+ console.log("\n⚠️ Nenhum admin adicional encontrado para deletar");
+ }
+
+ if (secretariaToDelete) {
+ await deleteUser(
+ secretariaToDelete.id,
+ secretariaToDelete.full_name || secretariaToDelete.email
+ );
+ } else {
+ console.log("\n⚠️ Nenhuma secretária encontrada para deletar");
+ }
+
+ if (medicoToDelete) {
+ await deleteUser(
+ medicoToDelete.id,
+ medicoToDelete.full_name || medicoToDelete.email
+ );
+ } else {
+ console.log("\n⚠️ Nenhum médico encontrado para deletar");
+ }
+
+ // 4. Aguardar um pouco
+ console.log("\n⏳ Aguardando 2 segundos...");
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+
+ // 5. Criar novos usuários
+ await createUserWithPassword(
+ "admin@mediconnect.com",
+ "admin123",
+ "Administrador Sistema",
+ "admin"
+ );
+
+ await createUserWithPassword(
+ "secretaria@mediconnect.com",
+ "secretaria123",
+ "Secretária Sistema",
+ "secretaria"
+ );
+
+ await createDoctor(
+ "dr.medico@mediconnect.com",
+ "medico123",
+ "Dr. João Silva",
+ "Cardiologia",
+ "12345",
+ "SP",
+ "12345678900"
+ );
+
+ // 6. Listar usuários finais
+ console.log("\n📊 Estado final dos usuários:");
+ await listUsers();
+
+ console.log("\n✅ Processo concluído!");
+ console.log("\n📝 Credenciais dos novos usuários:");
+ console.log(" 👨💼 Admin: admin@mediconnect.com / admin123");
+ console.log(" �💼 Secretária: secretaria@mediconnect.com / secretaria123");
+ console.log(" �👨⚕️ Médico: dr.medico@mediconnect.com / medico123");
+ console.log(" 🆔 CPF: 12345678900");
+ console.log(" 🩺 Especialidade: Cardiologia");
+ console.log(" 📋 CRM: 12345-SP");
+}
+
+// Executar
+main().catch(console.error);
diff --git a/MEDICONNECT 2/search-fernando.cjs b/MEDICONNECT 2/search-fernando.cjs
new file mode 100644
index 000000000..11067f49b
--- /dev/null
+++ b/MEDICONNECT 2/search-fernando.cjs
@@ -0,0 +1,86 @@
+const axios = require('axios');
+
+const ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ';
+const BASE_URL = 'https://yuanqfswhberkoevtmfr.supabase.co';
+
+(async () => {
+ try {
+ console.log('🔐 Fazendo login como admin...');
+ const loginRes = await axios.post(`${BASE_URL}/auth/v1/token?grant_type=password`, {
+ email: 'riseup@popcode.com.br',
+ password: 'riseup'
+ }, {
+ headers: {
+ 'Content-Type': 'application/json',
+ 'apikey': ANON_KEY
+ }
+ });
+
+ console.log('✅ Login admin bem-sucedido!\n');
+ const token = loginRes.data.access_token;
+
+ console.log('🔍 Buscando usuário fernando na tabela profiles...');
+ const profilesRes = await axios.get(`${BASE_URL}/rest/v1/profiles?select=*`, {
+ headers: {
+ 'apikey': ANON_KEY,
+ 'Authorization': `Bearer ${token}`
+ }
+ });
+
+ console.log(`📊 Total de profiles: ${profilesRes.data.length}\n`);
+
+ const fernandoProfile = profilesRes.data.find(u =>
+ u.email && (
+ u.email.toLowerCase().includes('fernando') ||
+ u.full_name?.toLowerCase().includes('fernando')
+ )
+ );
+
+ if (fernandoProfile) {
+ console.log('✅ Fernando encontrado na tabela profiles:');
+ console.log(JSON.stringify(fernandoProfile, null, 2));
+ } else {
+ console.log('❌ Fernando NÃO encontrado na tabela profiles\n');
+ }
+
+ // Buscar nos pacientes também
+ console.log('\n🔍 Buscando fernando na tabela patients...');
+ const patientsRes = await axios.get(`${BASE_URL}/rest/v1/patients?select=*`, {
+ headers: {
+ 'apikey': ANON_KEY,
+ 'Authorization': `Bearer ${token}`
+ }
+ });
+
+ console.log(`📊 Total de patients: ${patientsRes.data.length}\n`);
+
+ const fernandoPatient = patientsRes.data.find(p =>
+ p.email && (
+ p.email.toLowerCase().includes('fernando') ||
+ p.full_name?.toLowerCase().includes('fernando')
+ )
+ );
+
+ if (fernandoPatient) {
+ console.log('✅ Fernando encontrado na tabela patients:');
+ console.log(JSON.stringify(fernandoPatient, null, 2));
+ } else {
+ console.log('❌ Fernando NÃO encontrado na tabela patients\n');
+ }
+
+ // Listar alguns emails para referência
+ if (!fernandoProfile && !fernandoPatient) {
+ console.log('\n📧 Alguns emails cadastrados nos profiles:');
+ profilesRes.data.slice(0, 10).forEach((u, i) => {
+ if (u.email) console.log(` ${i+1}. ${u.email} - ${u.full_name || 'sem nome'}`);
+ });
+ }
+
+ } catch (err) {
+ console.error('❌ Erro:', err.response?.data || err.message);
+ if (err.response) {
+ console.error('Status:', err.response.status);
+ console.error('Headers:', err.response.headers);
+ }
+ }
+})();
diff --git a/MEDICONNECT 2/src/App.tsx b/MEDICONNECT 2/src/App.tsx
index d9e2415bc..2be57c449 100644
--- a/MEDICONNECT 2/src/App.tsx
+++ b/MEDICONNECT 2/src/App.tsx
@@ -26,6 +26,7 @@ import PerfilMedico from "./pages/PerfilMedico";
import PerfilPaciente from "./pages/PerfilPaciente";
import ClearCache from "./pages/ClearCache";
import AuthCallback from "./pages/AuthCallback";
+import ResetPassword from "./pages/ResetPassword";
function App() {
return (
@@ -48,6 +49,7 @@ function App() {
} />
} />
} />
+ } />
} />
} />
} />
diff --git a/MEDICONNECT 2/src/components/Chatbot.tsx b/MEDICONNECT 2/src/components/Chatbot.tsx
new file mode 100644
index 000000000..3c4c108ea
--- /dev/null
+++ b/MEDICONNECT 2/src/components/Chatbot.tsx
@@ -0,0 +1,277 @@
+import React, { useState, useRef, useEffect } from "react";
+import { MessageCircle, X, Send } from "lucide-react";
+
+interface Message {
+ id: string;
+ text: string;
+ sender: "user" | "bot";
+ timestamp: Date;
+}
+
+interface ChatbotProps {
+ className?: string;
+}
+
+const Chatbot: React.FC = ({ className = "" }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [messages, setMessages] = useState([
+ {
+ id: "welcome",
+ text: "Olá! Sou o assistente virtual do MediConnect. Como posso ajudá-lo hoje?",
+ sender: "bot",
+ timestamp: new Date(),
+ },
+ ]);
+ const [inputValue, setInputValue] = useState("");
+ const [isTyping, setIsTyping] = useState(false);
+ const messagesEndRef = useRef(null);
+
+ const scrollToBottom = () => {
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
+ };
+
+ useEffect(() => {
+ scrollToBottom();
+ }, [messages]);
+
+ const quickReplies = [
+ "Como agendar uma consulta?",
+ "Como cancelar agendamento?",
+ "Esqueci minha senha",
+ "Suporte técnico",
+ ];
+
+ const getBotResponse = (userMessage: string): string => {
+ const message = userMessage.toLowerCase();
+
+ // Respostas baseadas em palavras-chave
+ if (message.includes("agendar") || message.includes("marcar")) {
+ return "Para agendar uma consulta:\n\n1. Acesse 'Agendar Consulta' no menu\n2. Selecione o médico desejado\n3. Escolha data e horário disponível\n4. Confirme o agendamento\n\nVocê receberá uma confirmação por e-mail!";
+ }
+
+ if (message.includes("cancelar") || message.includes("remarcar")) {
+ return "Para cancelar ou remarcar uma consulta:\n\n1. Vá em 'Minhas Consultas'\n2. Localize a consulta\n3. Clique em 'Cancelar' ou 'Remarcar'\n\nRecomendamos fazer isso com 24h de antecedência para evitar taxas.";
+ }
+
+ if (message.includes("senha") || message.includes("login")) {
+ return "Para recuperar sua senha:\n\n1. Clique em 'Esqueceu a senha?' na tela de login\n2. Insira seu e-mail cadastrado\n3. Você receberá um link para redefinir a senha\n\nSe não receber o e-mail, verifique sua caixa de spam.";
+ }
+
+ if (message.includes("pagamento") || message.includes("pagar")) {
+ return "Aceitamos as seguintes formas de pagamento:\n\n• Cartão de crédito (parcelamento em até 3x)\n• Cartão de débito\n• PIX\n• Boleto bancário\n\nTodos os pagamentos são processados com segurança.";
+ }
+
+ if (message.includes("teleconsulta") || message.includes("online")) {
+ return "Para realizar uma teleconsulta:\n\n1. Acesse 'Minhas Consultas' no horário agendado\n2. Clique em 'Iniciar Consulta Online'\n3. Permita acesso à câmera e microfone\n\nCertifique-se de ter uma boa conexão de internet!";
+ }
+
+ if (message.includes("histórico") || message.includes("prontuário")) {
+ return "Seu histórico médico pode ser acessado em:\n\n• 'Meu Perfil' > 'Histórico Médico'\n• 'Minhas Consultas' (consultas anteriores)\n\nVocê pode fazer download de relatórios e receitas quando necessário.";
+ }
+
+ if (
+ message.includes("suporte") ||
+ message.includes("ajuda") ||
+ message.includes("atendimento")
+ ) {
+ return "Nossa equipe de suporte está disponível:\n\n📞 Telefone: 0800-123-4567\n📧 E-mail: suporte@mediconnect.com.br\n⏰ Horário: Segunda a Sexta, 8h às 18h\n\nVocê também pode acessar nossa Central de Ajuda completa no menu.";
+ }
+
+ if (message.includes("obrigad") || message.includes("valeu")) {
+ return "Por nada! Estou sempre aqui para ajudar. Se tiver mais dúvidas, é só chamar! 😊";
+ }
+
+ if (
+ message.includes("oi") ||
+ message.includes("olá") ||
+ message.includes("hello")
+ ) {
+ return "Olá! Como posso ajudá-lo hoje? Você pode perguntar sobre agendamentos, consultas, pagamentos ou qualquer dúvida sobre o MediConnect.";
+ }
+
+ // Resposta padrão
+ return "Desculpe, não entendi sua pergunta. Você pode:\n\n• Perguntar sobre agendamentos\n• Consultar formas de pagamento\n• Saber sobre teleconsultas\n• Acessar histórico médico\n• Falar com suporte\n\nOu visite nossa Central de Ajuda para mais informações!";
+ };
+
+ const handleSend = () => {
+ if (!inputValue.trim()) return;
+
+ // Adiciona mensagem do usuário
+ const userMessage: Message = {
+ id: Date.now().toString(),
+ text: inputValue,
+ sender: "user",
+ timestamp: new Date(),
+ };
+ setMessages((prev) => [...prev, userMessage]);
+ setInputValue("");
+
+ // Simula digitação do bot
+ setIsTyping(true);
+ setTimeout(() => {
+ const botResponse: Message = {
+ id: (Date.now() + 1).toString(),
+ text: getBotResponse(inputValue),
+ sender: "bot",
+ timestamp: new Date(),
+ };
+ setMessages((prev) => [...prev, botResponse]);
+ setIsTyping(false);
+ }, 1000);
+ };
+
+ const handleQuickReply = (reply: string) => {
+ setInputValue(reply);
+ };
+
+ const handleKeyPress = (e: React.KeyboardEvent) => {
+ if (e.key === "Enter" && !e.shiftKey) {
+ e.preventDefault();
+ handleSend();
+ }
+ };
+
+ return (
+
+ {/* Floating Button */}
+ {!isOpen && (
+
+ )}
+
+ {/* Chat Window */}
+ {isOpen && (
+
+ {/* Header */}
+
+
+
+
+
+
+
Assistente MediConnect
+
+ Online • Responde em segundos
+
+
+
+
+
+
+ {/* Messages */}
+
+ {messages.map((message) => (
+
+
+
{message.text}
+
+ {message.timestamp.toLocaleTimeString("pt-BR", {
+ hour: "2-digit",
+ minute: "2-digit",
+ })}
+
+
+
+ ))}
+
+ {isTyping && (
+
+ )}
+
+
+
+
+ {/* Quick Replies */}
+ {messages.length <= 1 && (
+
+
+ Perguntas frequentes:
+
+
+ {quickReplies.map((reply, index) => (
+
+ ))}
+
+
+ )}
+
+ {/* Input */}
+
+
+ setInputValue(e.target.value)}
+ onKeyPress={handleKeyPress}
+ placeholder="Digite sua mensagem..."
+ className="flex-1 border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+ />
+
+
+
+
+ )}
+
+ );
+};
+
+export default Chatbot;
diff --git a/MEDICONNECT 2/src/components/HeroBanner.tsx b/MEDICONNECT 2/src/components/HeroBanner.tsx
new file mode 100644
index 000000000..7b54679a5
--- /dev/null
+++ b/MEDICONNECT 2/src/components/HeroBanner.tsx
@@ -0,0 +1,151 @@
+import React, { useState, useEffect } from "react";
+import { Calendar, Clock, ArrowRight } from "lucide-react";
+import { useNavigate } from "react-router-dom";
+import { i18n } from "../i18n";
+
+// Importar as imagens
+import medico1 from "./images/medico1.jpg";
+import medico2 from "./images/medico2.jpg";
+import medico3 from "./images/medico3.jpg";
+
+const images = [medico1, medico2, medico3];
+
+export const HeroBanner: React.FC = () => {
+ const [currentImageIndex, setCurrentImageIndex] = useState(0);
+ const [nextImageIndex, setNextImageIndex] = useState(1);
+ const [isTransitioning, setIsTransitioning] = useState(false);
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ // Rotacionar imagens a cada 5 segundos
+ const interval = setInterval(() => {
+ setIsTransitioning(true);
+
+ // Após 2 segundos (duração da transição), atualizar os índices
+ setTimeout(() => {
+ setCurrentImageIndex((prev) => (prev + 1) % images.length);
+ setNextImageIndex((prev) => (prev + 1) % images.length);
+ setIsTransitioning(false);
+ }, 2000);
+ }, 5000);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ const handleCTA = (action: string, destination: string) => {
+ console.log(`CTA clicked: ${action} -> ${destination}`);
+ navigate(destination);
+ };
+
+ return (
+
+ {/* Background Images com Fade Transition */}
+
+ {/* Imagem Atual */}
+
+
+ {/* Próxima Imagem (para transição suave) */}
+
+
+ {/* Overlay Azul Translúcido */}
+
+
+ {/* Decorative Pattern */}
+
+
+
+ {/* Conteúdo */}
+
+
+ {i18n.t("home.hero.title")}
+
+
+ {i18n.t("home.hero.subtitle")}
+
+
+ {/* CTAs */}
+
+
+
+
+
+
+
+ {/* Indicadores de Imagem (opcionais - pequenos pontos na parte inferior) */}
+
+ {images.map((_, index) => (
+
+
+ );
+};
diff --git a/MEDICONNECT 2/src/components/images/medico1.jpg b/MEDICONNECT 2/src/components/images/medico1.jpg
new file mode 100644
index 000000000..6caeaf733
Binary files /dev/null and b/MEDICONNECT 2/src/components/images/medico1.jpg differ
diff --git a/MEDICONNECT 2/src/components/images/medico2.jpg b/MEDICONNECT 2/src/components/images/medico2.jpg
new file mode 100644
index 000000000..8c8584d3a
Binary files /dev/null and b/MEDICONNECT 2/src/components/images/medico2.jpg differ
diff --git a/MEDICONNECT 2/src/components/images/medico3.jpg b/MEDICONNECT 2/src/components/images/medico3.jpg
new file mode 100644
index 000000000..40e243876
Binary files /dev/null and b/MEDICONNECT 2/src/components/images/medico3.jpg differ
diff --git a/MEDICONNECT 2/src/components/secretaria/SecretaryAppointmentList.tsx b/MEDICONNECT 2/src/components/secretaria/SecretaryAppointmentList.tsx
index e044277e8..e8c9c8037 100644
--- a/MEDICONNECT 2/src/components/secretaria/SecretaryAppointmentList.tsx
+++ b/MEDICONNECT 2/src/components/secretaria/SecretaryAppointmentList.tsx
@@ -24,6 +24,16 @@ export function SecretaryAppointmentList() {
const [searchTerm, setSearchTerm] = useState("");
const [statusFilter, setStatusFilter] = useState("Todos");
const [typeFilter, setTypeFilter] = useState("Todos");
+ const [showCreateModal, setShowCreateModal] = useState(false);
+ const [patients, setPatients] = useState([]);
+ const [doctors, setDoctors] = useState([]);
+ const [formData, setFormData] = useState({
+ patient_id: "",
+ doctor_id: "",
+ scheduled_at: "",
+ appointment_type: "presencial",
+ notes: "",
+ });
const loadAppointments = async () => {
setLoading(true);
@@ -68,8 +78,60 @@ export function SecretaryAppointmentList() {
useEffect(() => {
loadAppointments();
+ loadDoctorsAndPatients();
}, []);
+ const loadDoctorsAndPatients = async () => {
+ try {
+ const [patientsData, doctorsData] = await Promise.all([
+ patientService.list(),
+ doctorService.list(),
+ ]);
+ setPatients(Array.isArray(patientsData) ? patientsData : []);
+ setDoctors(Array.isArray(doctorsData) ? doctorsData : []);
+ } catch (error) {
+ console.error("Erro ao carregar pacientes e médicos:", error);
+ }
+ };
+
+ const handleOpenCreateModal = () => {
+ setFormData({
+ patient_id: "",
+ doctor_id: "",
+ scheduled_at: "",
+ appointment_type: "presencial",
+ notes: "",
+ });
+ setShowCreateModal(true);
+ };
+
+ const handleCreateAppointment = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!formData.patient_id || !formData.doctor_id || !formData.scheduled_at) {
+ toast.error("Preencha todos os campos obrigatórios");
+ return;
+ }
+
+ try {
+ await appointmentService.create({
+ patient_id: formData.patient_id,
+ doctor_id: formData.doctor_id,
+ scheduled_at: new Date(formData.scheduled_at).toISOString(),
+ appointment_type: formData.appointment_type as
+ | "presencial"
+ | "telemedicina",
+ });
+
+ toast.success("Consulta agendada com sucesso!");
+ setShowCreateModal(false);
+ loadAppointments();
+ } catch (error) {
+ console.error("Erro ao criar consulta:", error);
+ toast.error("Erro ao agendar consulta");
+ }
+ };
+
const handleSearch = () => {
loadAppointments();
};
@@ -137,7 +199,10 @@ export function SecretaryAppointmentList() {
Consultas
Gerencie as consultas agendadas
-