fix: corrigir redirecionamento do magic link para painel correto baseado no contexto
This commit is contained in:
parent
31aacb7a3d
commit
f22bca225c
348
MEDICONNECT 2/API-CONFIG.md
Normal file
348
MEDICONNECT 2/API-CONFIG.md
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
# Configuração das APIs - MediConnect
|
||||||
|
|
||||||
|
## ✅ APIs Testadas e Funcionando
|
||||||
|
|
||||||
|
### 1. Autenticação (Auth API)
|
||||||
|
|
||||||
|
**Base URL:** `https://yuanqfswhberkoevtmfr.supabase.co/auth/v1`
|
||||||
|
|
||||||
|
#### Endpoints Funcionais:
|
||||||
|
|
||||||
|
- **Login** ✅
|
||||||
|
|
||||||
|
- `POST /token?grant_type=password`
|
||||||
|
- Body: `{ email, password }`
|
||||||
|
- Retorna: `{ access_token, refresh_token, user }`
|
||||||
|
|
||||||
|
- **Recuperação de Senha** ✅
|
||||||
|
|
||||||
|
- `POST /recover`
|
||||||
|
- Body: `{ email, options: { redirectTo: url } }`
|
||||||
|
- Envia email com link de recuperação
|
||||||
|
|
||||||
|
- **Atualizar Senha** ✅
|
||||||
|
- `PUT /user`
|
||||||
|
- Headers: `Authorization: Bearer <access_token>`
|
||||||
|
- Body: `{ password: "nova_senha" }`
|
||||||
|
- **IMPORTANTE:** Nova senha deve ser diferente da anterior (erro 422 se for igual)
|
||||||
|
|
||||||
|
### 2. REST API
|
||||||
|
|
||||||
|
**Base URL:** `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1`
|
||||||
|
|
||||||
|
#### Tabelas e Campos Corretos:
|
||||||
|
|
||||||
|
##### **appointments** ✅
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
id: string (UUID)
|
||||||
|
order_number: string (auto-gerado: APT-YYYY-NNNN)
|
||||||
|
patient_id: string (UUID)
|
||||||
|
doctor_id: string (UUID)
|
||||||
|
scheduled_at: string (ISO 8601 DateTime)
|
||||||
|
duration_minutes: number
|
||||||
|
appointment_type: "presencial" | "telemedicina"
|
||||||
|
status: "requested" | "confirmed" | "checked_in" | "in_progress" | "completed" | "cancelled" | "no_show"
|
||||||
|
chief_complaint: string | null
|
||||||
|
patient_notes: string | null
|
||||||
|
notes: string | null
|
||||||
|
insurance_provider: string | null
|
||||||
|
checked_in_at: string | null
|
||||||
|
completed_at: string | null
|
||||||
|
cancelled_at: string | null
|
||||||
|
cancellation_reason: string | null
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
created_by: string (UUID)
|
||||||
|
updated_by: string | null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Criar Consulta:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
POST /rest/v1/appointments
|
||||||
|
Headers:
|
||||||
|
- apikey: <SUPABASE_ANON_KEY>
|
||||||
|
- Authorization: Bearer <user_access_token>
|
||||||
|
- Content-Type: application/json
|
||||||
|
- Prefer: return=representation
|
||||||
|
|
||||||
|
Body:
|
||||||
|
{
|
||||||
|
"patient_id": "uuid",
|
||||||
|
"doctor_id": "uuid",
|
||||||
|
"scheduled_at": "2025-11-03T10:00:00.000Z",
|
||||||
|
"duration_minutes": 30,
|
||||||
|
"appointment_type": "presencial",
|
||||||
|
"chief_complaint": "Motivo da consulta"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### **doctor_availability** ✅
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
id: string (UUID)
|
||||||
|
doctor_id: string (UUID)
|
||||||
|
weekday: "sunday" | "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday"
|
||||||
|
start_time: string (HH:MM:SS, ex: "07:00:00")
|
||||||
|
end_time: string (HH:MM:SS, ex: "19:00:00")
|
||||||
|
slot_duration_minutes: number (ex: 30)
|
||||||
|
appointment_type: "presencial" | "telemedicina"
|
||||||
|
is_active: boolean
|
||||||
|
created_at: string
|
||||||
|
updated_at: string
|
||||||
|
created_by: string (UUID)
|
||||||
|
updated_by: string | null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Criar Disponibilidade:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
POST /rest/v1/doctor_availability
|
||||||
|
Headers:
|
||||||
|
- apikey: <SUPABASE_ANON_KEY>
|
||||||
|
- Authorization: Bearer <admin_access_token>
|
||||||
|
- Content-Type: application/json
|
||||||
|
- Prefer: return=representation
|
||||||
|
|
||||||
|
Body:
|
||||||
|
{
|
||||||
|
"doctor_id": "uuid",
|
||||||
|
"weekday": "monday", // ⚠️ Texto, não número!
|
||||||
|
"start_time": "07:00:00",
|
||||||
|
"end_time": "19:00:00",
|
||||||
|
"slot_duration_minutes": 30,
|
||||||
|
"appointment_type": "presencial",
|
||||||
|
"is_active": true,
|
||||||
|
"created_by": "admin_user_id"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### **patients** ✅
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
id: string(UUID);
|
||||||
|
user_id: string(UUID); // ⚠️ Deve estar vinculado ao auth.users
|
||||||
|
full_name: string;
|
||||||
|
email: string;
|
||||||
|
cpf: string;
|
||||||
|
phone_mobile: string;
|
||||||
|
// ... outros campos
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Atualizar Patient:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PATCH /rest/v1/patients?id=eq.<patient_id>
|
||||||
|
Headers:
|
||||||
|
- apikey: <SUPABASE_ANON_KEY>
|
||||||
|
- Authorization: Bearer <admin_access_token>
|
||||||
|
- Content-Type: application/json
|
||||||
|
|
||||||
|
Body:
|
||||||
|
{
|
||||||
|
"user_id": "auth_user_id"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### **doctors** ✅
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
id: string(UUID);
|
||||||
|
user_id: string(UUID);
|
||||||
|
full_name: string;
|
||||||
|
email: string;
|
||||||
|
crm: string;
|
||||||
|
crm_uf: string;
|
||||||
|
specialty: string;
|
||||||
|
// ... outros campos
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Edge Functions
|
||||||
|
|
||||||
|
**Base URL:** `https://yuanqfswhberkoevtmfr.supabase.co/functions/v1`
|
||||||
|
|
||||||
|
#### Funcionais:
|
||||||
|
|
||||||
|
- **create-user-with-password** ✅
|
||||||
|
- `POST /functions/v1/create-user-with-password`
|
||||||
|
- Cria usuário com senha e perfil completo
|
||||||
|
- Body:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"email": "email@example.com",
|
||||||
|
"password": "senha123",
|
||||||
|
"full_name": "Nome Completo",
|
||||||
|
"phone_mobile": "(11) 99999-9999",
|
||||||
|
"cpf": "12345678900",
|
||||||
|
"create_patient_record": true,
|
||||||
|
"role": "paciente"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Com Problemas:
|
||||||
|
|
||||||
|
- **request-password-reset** ❌
|
||||||
|
- CORS blocking - não usar
|
||||||
|
- Usar diretamente `/auth/v1/recover` em vez disso
|
||||||
|
|
||||||
|
## 🔑 Chaves de API
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
SUPABASE_ANON_KEY =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
|
||||||
|
```
|
||||||
|
|
||||||
|
## 👥 Usuários de Teste
|
||||||
|
|
||||||
|
### Admin
|
||||||
|
|
||||||
|
- Email: `riseup@popcode.com.br`
|
||||||
|
- Senha: `riseup`
|
||||||
|
|
||||||
|
### Dr. Fernando Pirichowski
|
||||||
|
|
||||||
|
- Email: `fernando.pirichowski@souunit.com.br`
|
||||||
|
- Senha: `fernando123`
|
||||||
|
- User ID: `38aca60d-7418-4c35-95b6-cb206bb18a0a`
|
||||||
|
- Doctor ID: `6dad001d-229b-40b5-80f3-310243c4599c`
|
||||||
|
- CRM: `24245`
|
||||||
|
- Disponibilidade: Segunda a Domingo, 07:00-19:00
|
||||||
|
|
||||||
|
### Aurora Sabrina Clara Nascimento (Paciente)
|
||||||
|
|
||||||
|
- Email: `aurora-nascimento94@gmx.com`
|
||||||
|
- Senha: `auroranasc94`
|
||||||
|
- User ID: `6dc15cc5-7dae-4b30-924a-a4b4fa142f24`
|
||||||
|
- Patient ID: `b85486f7-9135-4b67-9aa7-b884d9603d12`
|
||||||
|
- CPF: `66864784231`
|
||||||
|
- Telefone: `(21) 99856-3014`
|
||||||
|
|
||||||
|
## ⚠️ Pontos de Atenção
|
||||||
|
|
||||||
|
### 1. Weekday no doctor_availability
|
||||||
|
|
||||||
|
- ❌ **NÃO** usar números (0-6)
|
||||||
|
- ✅ **USAR** strings em inglês: `"sunday"`, `"monday"`, `"tuesday"`, `"wednesday"`, `"thursday"`, `"friday"`, `"saturday"`
|
||||||
|
|
||||||
|
### 2. scheduled_at em appointments
|
||||||
|
|
||||||
|
- ❌ **NÃO** usar campos separados `appointment_date` e `appointment_time`
|
||||||
|
- ✅ **USAR** campo único `scheduled_at` com ISO 8601 DateTime
|
||||||
|
- Exemplo: `"2025-11-03T10:00:00.000Z"`
|
||||||
|
|
||||||
|
### 3. user_id nas tabelas patients e doctors
|
||||||
|
|
||||||
|
- ⚠️ Sempre vincular ao `auth.users.id`
|
||||||
|
- Sem esse vínculo, queries por `user_id` não funcionam
|
||||||
|
|
||||||
|
### 4. Senha na recuperação
|
||||||
|
|
||||||
|
- ⚠️ Nova senha DEVE ser diferente da anterior
|
||||||
|
- Erro 422 com `error_code: "same_password"` se tentar usar a mesma
|
||||||
|
|
||||||
|
### 5. redirectTo no password recovery
|
||||||
|
|
||||||
|
- ⚠️ Supabase pode ignorar o parâmetro `redirectTo`
|
||||||
|
- ✅ Implementar detecção de token no lado do cliente
|
||||||
|
- Verificar tanto query string `?token=` quanto hash `#access_token=`
|
||||||
|
|
||||||
|
## 📦 Estrutura de Serviços no Frontend
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Tudo configurado em:
|
||||||
|
src / services / api / config.ts; // URLs e chaves
|
||||||
|
src / services / api / client.ts; // Cliente axios
|
||||||
|
src /
|
||||||
|
services /
|
||||||
|
appointments / // Serviço de consultas
|
||||||
|
src /
|
||||||
|
services /
|
||||||
|
availability / // Disponibilidade médicos
|
||||||
|
src /
|
||||||
|
services /
|
||||||
|
auth / // Autenticação
|
||||||
|
src /
|
||||||
|
services /
|
||||||
|
doctors / // Médicos
|
||||||
|
src /
|
||||||
|
services /
|
||||||
|
patients / // Pacientes
|
||||||
|
src /
|
||||||
|
services /
|
||||||
|
index.ts; // Exportações centralizadas
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✅ Status Atual
|
||||||
|
|
||||||
|
- [x] Autenticação funcionando
|
||||||
|
- [x] Recuperação de senha funcionando
|
||||||
|
- [x] Criação de usuários funcionando
|
||||||
|
- [x] Criação de pacientes funcionando
|
||||||
|
- [x] Criação de disponibilidade médica funcionando
|
||||||
|
- [x] Criação de consultas funcionando
|
||||||
|
- [x] Vinculação user_id ↔ patient_id corrigida
|
||||||
|
- [x] Todos os serviços usando campos corretos
|
||||||
|
|
||||||
|
## 🚀 Próximos Passos
|
||||||
|
|
||||||
|
1. Testar agendamento completo no frontend
|
||||||
|
2. Verificar listagem de consultas
|
||||||
|
3. Testar cancelamento e atualização de consultas
|
||||||
|
4. Verificar notificações SMS
|
||||||
|
5. Testar fluxo completo de check-in e prontuário
|
||||||
|
|
||||||
|
## 📝 Exemplos de Uso
|
||||||
|
|
||||||
|
### Criar Consulta
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { appointmentService } from "@/services";
|
||||||
|
|
||||||
|
const appointment = await appointmentService.create({
|
||||||
|
patient_id: "patient-uuid",
|
||||||
|
doctor_id: "doctor-uuid",
|
||||||
|
scheduled_at: "2025-11-03T10:00:00.000Z",
|
||||||
|
duration_minutes: 30,
|
||||||
|
appointment_type: "presencial",
|
||||||
|
chief_complaint: "Consulta de rotina",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Criar Disponibilidade
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { availabilityService } from "@/services";
|
||||||
|
|
||||||
|
const availability = await availabilityService.create({
|
||||||
|
doctor_id: "doctor-uuid",
|
||||||
|
weekday: "monday",
|
||||||
|
start_time: "07:00:00",
|
||||||
|
end_time: "19:00:00",
|
||||||
|
slot_duration_minutes: 30,
|
||||||
|
appointment_type: "presencial",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Login
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { authService } from "@/services";
|
||||||
|
|
||||||
|
const response = await authService.login({
|
||||||
|
email: "user@example.com",
|
||||||
|
password: "senha123",
|
||||||
|
});
|
||||||
|
|
||||||
|
// response.access_token - JWT token
|
||||||
|
// response.user - dados do usuário
|
||||||
|
```
|
||||||
292
MEDICONNECT 2/CHECKLIST-TESTES.md
Normal file
292
MEDICONNECT 2/CHECKLIST-TESTES.md
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
# ✅ Checklist de Testes - MediConnect
|
||||||
|
|
||||||
|
## 🎯 Testes Funcionais
|
||||||
|
|
||||||
|
### 1. Autenticação ✅
|
||||||
|
|
||||||
|
#### Login
|
||||||
|
|
||||||
|
- [x] Login de admin funcionando
|
||||||
|
- [x] Login de médico (Dr. Fernando) funcionando
|
||||||
|
- [x] Login de paciente (Aurora) funcionando
|
||||||
|
- [x] Token JWT sendo retornado corretamente
|
||||||
|
- [x] Refresh token funcionando
|
||||||
|
|
||||||
|
#### Recuperação de Senha
|
||||||
|
|
||||||
|
- [x] Email de recuperação sendo enviado
|
||||||
|
- [x] Token de recuperação detectado na URL
|
||||||
|
- [x] Reset de senha funcionando (senha diferente da anterior)
|
||||||
|
- [x] Erro 422 tratado quando senha é igual à anterior
|
||||||
|
- [x] Redirecionamento para página de reset funcionando
|
||||||
|
|
||||||
|
### 2. Gestão de Usuários ✅
|
||||||
|
|
||||||
|
#### Criação de Paciente
|
||||||
|
|
||||||
|
- [x] Edge Function `create-user-with-password` funcionando
|
||||||
|
- [x] Paciente criado com auth user
|
||||||
|
- [x] Registro na tabela `patients` criado
|
||||||
|
- [x] `user_id` vinculado corretamente ao `auth.users.id`
|
||||||
|
- [x] Credenciais de login funcionando após criação
|
||||||
|
|
||||||
|
**Usuário Teste:**
|
||||||
|
|
||||||
|
- Email: aurora-nascimento94@gmx.com
|
||||||
|
- Senha: auroranasc94
|
||||||
|
- Patient ID: b85486f7-9135-4b67-9aa7-b884d9603d12
|
||||||
|
|
||||||
|
#### Médicos
|
||||||
|
|
||||||
|
- [x] Dr. Fernando no sistema
|
||||||
|
- [x] User ID vinculado corretamente
|
||||||
|
- [x] Doctor ID identificado
|
||||||
|
- [x] CRM registrado
|
||||||
|
|
||||||
|
**Médico Teste:**
|
||||||
|
|
||||||
|
- Email: fernando.pirichowski@souunit.com.br
|
||||||
|
- Senha: fernando123
|
||||||
|
- Doctor ID: 6dad001d-229b-40b5-80f3-310243c4599c
|
||||||
|
|
||||||
|
### 3. Disponibilidade Médica ✅
|
||||||
|
|
||||||
|
#### Criação de Disponibilidade
|
||||||
|
|
||||||
|
- [x] Script de criação funcionando
|
||||||
|
- [x] Campo `weekday` usando strings em inglês
|
||||||
|
- [x] Formato de horário correto (HH:MM:SS)
|
||||||
|
- [x] Disponibilidade criada para todos os dias da semana
|
||||||
|
- [x] Horário: 07:00 - 19:00
|
||||||
|
- [x] Duração de slot: 30 minutos
|
||||||
|
- [x] Tipo: presencial
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
|
||||||
|
- ✅ 7 dias configurados (Domingo a Sábado)
|
||||||
|
- ✅ Dr. Fernando disponível das 07:00 às 19:00
|
||||||
|
|
||||||
|
### 4. Agendamento de Consultas ✅
|
||||||
|
|
||||||
|
#### Criação de Consulta
|
||||||
|
|
||||||
|
- [x] API de appointments funcionando
|
||||||
|
- [x] Campo `scheduled_at` usando ISO 8601 DateTime
|
||||||
|
- [x] Consulta criada com status "requested"
|
||||||
|
- [x] Order number gerado automaticamente (APT-YYYY-NNNN)
|
||||||
|
- [x] Duração de 30 minutos configurada
|
||||||
|
- [x] Tipo presencial configurado
|
||||||
|
|
||||||
|
**Consulta Teste:**
|
||||||
|
|
||||||
|
- Paciente: Aurora
|
||||||
|
- Médico: Dr. Fernando
|
||||||
|
- Data: 03/11/2025 às 10:00
|
||||||
|
- Order Number: APT-2025-00027
|
||||||
|
- ID: cb4f608f-e580-437f-8653-75ec74621065
|
||||||
|
|
||||||
|
### 5. Frontend - Componentes
|
||||||
|
|
||||||
|
#### AgendamentoConsulta.tsx ✅
|
||||||
|
|
||||||
|
- [x] Usando `appointmentService` correto
|
||||||
|
- [x] Campo `scheduled_at` implementado
|
||||||
|
- [x] Formato ISO 8601 DateTime
|
||||||
|
- [x] Integração com availability service
|
||||||
|
- [x] Integração com exceptions service
|
||||||
|
- [x] SMS notification configurado
|
||||||
|
|
||||||
|
#### Outros Componentes
|
||||||
|
|
||||||
|
- [ ] BookAppointment.tsx - não usado (pode ser removido)
|
||||||
|
- [ ] AgendamentoConsultaSimples.tsx - não usado (pode ser removido)
|
||||||
|
|
||||||
|
## 🔧 Configurações Verificadas
|
||||||
|
|
||||||
|
### API Config ✅
|
||||||
|
|
||||||
|
- [x] `src/services/api/config.ts` - URLs corretas
|
||||||
|
- [x] SUPABASE_ANON_KEY atualizada
|
||||||
|
- [x] Endpoints configurados corretamente
|
||||||
|
|
||||||
|
### Services ✅
|
||||||
|
|
||||||
|
- [x] `appointmentService` - usando campos corretos
|
||||||
|
- [x] `availabilityService` - usando weekday strings
|
||||||
|
- [x] `authService` - recuperação de senha funcionando
|
||||||
|
- [x] `patientService` - CRUD funcionando
|
||||||
|
- [x] `doctorService` - CRUD funcionando
|
||||||
|
- [x] Todos exportados em `src/services/index.ts`
|
||||||
|
|
||||||
|
## 🧪 Testes Pendentes
|
||||||
|
|
||||||
|
### Fluxo Completo de Agendamento
|
||||||
|
|
||||||
|
- [ ] Paciente faz login
|
||||||
|
- [ ] Paciente busca médicos disponíveis
|
||||||
|
- [ ] Paciente visualiza horários disponíveis
|
||||||
|
- [ ] Paciente agenda consulta
|
||||||
|
- [ ] Consulta aparece na lista do paciente
|
||||||
|
- [ ] Médico visualiza consulta na agenda
|
||||||
|
- [ ] Notificação SMS enviada
|
||||||
|
|
||||||
|
### Check-in e Atendimento
|
||||||
|
|
||||||
|
- [ ] Check-in de paciente
|
||||||
|
- [ ] Status da consulta muda para "checked_in"
|
||||||
|
- [ ] Médico inicia atendimento
|
||||||
|
- [ ] Status muda para "in_progress"
|
||||||
|
- [ ] Preenchimento de prontuário
|
||||||
|
- [ ] Finalização da consulta
|
||||||
|
- [ ] Status muda para "completed"
|
||||||
|
|
||||||
|
### Cancelamento
|
||||||
|
|
||||||
|
- [ ] Paciente cancela consulta
|
||||||
|
- [ ] Médico cancela consulta
|
||||||
|
- [ ] Status muda para "cancelled"
|
||||||
|
- [ ] Motivo do cancelamento registrado
|
||||||
|
- [ ] Horário fica disponível novamente
|
||||||
|
|
||||||
|
### Exceções de Disponibilidade
|
||||||
|
|
||||||
|
- [ ] Criar exceção (feriado, folga)
|
||||||
|
- [ ] Exceção bloqueia horários
|
||||||
|
- [ ] Listar exceções
|
||||||
|
- [ ] Remover exceção
|
||||||
|
|
||||||
|
## 📊 Métricas e Relatórios
|
||||||
|
|
||||||
|
- [ ] Dashboard de consultas
|
||||||
|
- [ ] Estatísticas de atendimento
|
||||||
|
- [ ] Relatório de faturamento
|
||||||
|
- [ ] Exportação de dados
|
||||||
|
|
||||||
|
## 🔐 Segurança
|
||||||
|
|
||||||
|
### Autenticação
|
||||||
|
|
||||||
|
- [x] JWT tokens funcionando
|
||||||
|
- [x] Refresh tokens implementados
|
||||||
|
- [x] Session storage configurado
|
||||||
|
- [ ] Expiração de tokens tratada
|
||||||
|
- [ ] Logout funcionando corretamente
|
||||||
|
|
||||||
|
### Autorização
|
||||||
|
|
||||||
|
- [ ] RLS (Row Level Security) configurado no Supabase
|
||||||
|
- [ ] Paciente só vê suas próprias consultas
|
||||||
|
- [ ] Médico só vê consultas atribuídas
|
||||||
|
- [ ] Admin tem acesso total
|
||||||
|
- [ ] Secretária tem permissões específicas
|
||||||
|
|
||||||
|
## 🌐 Deploy e Performance
|
||||||
|
|
||||||
|
- [ ] Build de produção funcionando
|
||||||
|
- [ ] Deploy no Cloudflare Pages
|
||||||
|
- [ ] URLs de produção configuradas
|
||||||
|
- [ ] Performance otimizada
|
||||||
|
- [ ] Lazy loading de componentes
|
||||||
|
- [ ] Cache configurado
|
||||||
|
|
||||||
|
## 📱 Responsividade
|
||||||
|
|
||||||
|
- [ ] Desktop (1920x1080)
|
||||||
|
- [ ] Laptop (1366x768)
|
||||||
|
- [ ] Tablet (768x1024)
|
||||||
|
- [ ] Mobile (375x667)
|
||||||
|
|
||||||
|
## ♿ Acessibilidade
|
||||||
|
|
||||||
|
- [ ] Menu de acessibilidade funcionando
|
||||||
|
- [ ] Contraste de cores ajustável
|
||||||
|
- [ ] Tamanho de fonte ajustável
|
||||||
|
- [ ] Leitura de tela compatível
|
||||||
|
- [ ] Navegação por teclado
|
||||||
|
|
||||||
|
## 🐛 Bugs Conhecidos
|
||||||
|
|
||||||
|
Nenhum bug crítico identificado até o momento.
|
||||||
|
|
||||||
|
## 📝 Notas Importantes
|
||||||
|
|
||||||
|
### Campos Corretos nas APIs
|
||||||
|
|
||||||
|
1. **appointments.scheduled_at**
|
||||||
|
|
||||||
|
- ❌ NÃO: `appointment_date` e `appointment_time` separados
|
||||||
|
- ✅ SIM: `scheduled_at` com ISO 8601 DateTime
|
||||||
|
|
||||||
|
2. **doctor_availability.weekday**
|
||||||
|
|
||||||
|
- ❌ NÃO: Números 0-6
|
||||||
|
- ✅ SIM: Strings "sunday", "monday", etc.
|
||||||
|
|
||||||
|
3. **patients.user_id**
|
||||||
|
|
||||||
|
- ⚠️ DEVE estar vinculado ao `auth.users.id`
|
||||||
|
- Sem isso, queries por user_id falham
|
||||||
|
|
||||||
|
4. **Password Recovery**
|
||||||
|
- ⚠️ Nova senha DEVE ser diferente da anterior
|
||||||
|
- Erro 422 com `error_code: "same_password"` se igual
|
||||||
|
|
||||||
|
### Scripts Úteis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Login como usuário
|
||||||
|
node get-fernando-user-id.cjs
|
||||||
|
|
||||||
|
# Buscar dados de paciente
|
||||||
|
node get-aurora-info.cjs
|
||||||
|
|
||||||
|
# Criar disponibilidade
|
||||||
|
node create-fernando-availability.cjs
|
||||||
|
|
||||||
|
# Criar consulta
|
||||||
|
node create-aurora-appointment.cjs
|
||||||
|
|
||||||
|
# Corrigir user_id
|
||||||
|
node fix-aurora-user-id.cjs
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Próximas Funcionalidades
|
||||||
|
|
||||||
|
1. **Telemedicina**
|
||||||
|
|
||||||
|
- [ ] Integração com serviço de videochamada
|
||||||
|
- [ ] Sala de espera virtual
|
||||||
|
- [ ] Gravação de consultas (opcional)
|
||||||
|
|
||||||
|
2. **Prontuário Eletrônico**
|
||||||
|
|
||||||
|
- [ ] CRUD completo de prontuários
|
||||||
|
- [ ] Histórico de consultas
|
||||||
|
- [ ] Anexos e exames
|
||||||
|
- [ ] Prescrições médicas
|
||||||
|
|
||||||
|
3. **Notificações**
|
||||||
|
|
||||||
|
- [x] SMS via Twilio configurado
|
||||||
|
- [ ] Email notifications
|
||||||
|
- [ ] Push notifications (PWA)
|
||||||
|
- [ ] Lembretes de consulta
|
||||||
|
|
||||||
|
4. **Pagamentos**
|
||||||
|
|
||||||
|
- [ ] Integração com gateway de pagamento
|
||||||
|
- [ ] Registro de pagamentos
|
||||||
|
- [ ] Emissão de recibos
|
||||||
|
- [ ] Relatório financeiro
|
||||||
|
|
||||||
|
5. **Telemática**
|
||||||
|
- [ ] Assinatura digital de documentos
|
||||||
|
- [ ] Certificação digital A1/A3
|
||||||
|
- [ ] Integração com e-SUS
|
||||||
|
- [ ] Compliance LGPD
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Última atualização:** 27/10/2025
|
||||||
|
**Status:** ✅ APIs configuradas e funcionando
|
||||||
|
**Próximo passo:** Testar fluxo completo no frontend
|
||||||
128
MEDICONNECT 2/create-aurora-appointment.cjs
Normal file
128
MEDICONNECT 2/create-aurora-appointment.cjs
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
const SUPABASE_ANON_KEY =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
|
||||||
|
|
||||||
|
async function createAppointment() {
|
||||||
|
try {
|
||||||
|
console.log("🔐 Fazendo login como Aurora...");
|
||||||
|
|
||||||
|
// Login como Aurora
|
||||||
|
const loginResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
|
||||||
|
{
|
||||||
|
email: "aurora-nascimento94@gmx.com",
|
||||||
|
password: "auroranasc94",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const auroraToken = loginResponse.data.access_token;
|
||||||
|
console.log("✅ Login realizado como Aurora");
|
||||||
|
|
||||||
|
// Buscar o patient_id da Aurora
|
||||||
|
console.log("\n👤 Buscando dados da paciente...");
|
||||||
|
const patientResponse = await axios.get(
|
||||||
|
`${SUPABASE_URL}/rest/v1/patients?user_id=eq.${loginResponse.data.user.id}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${auroraToken}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!patientResponse.data || patientResponse.data.length === 0) {
|
||||||
|
throw new Error("Paciente não encontrada");
|
||||||
|
}
|
||||||
|
|
||||||
|
const patientId = patientResponse.data[0].id;
|
||||||
|
console.log(`✅ Patient ID: ${patientId}`);
|
||||||
|
|
||||||
|
// Buscar disponibilidade do Dr. Fernando para segunda-feira
|
||||||
|
console.log("\n📅 Buscando disponibilidade do Dr. Fernando...");
|
||||||
|
const availabilityResponse = await axios.get(
|
||||||
|
`${SUPABASE_URL}/rest/v1/doctor_availability?doctor_id=eq.6dad001d-229b-40b5-80f3-310243c4599c&weekday=eq.monday`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${auroraToken}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!availabilityResponse.data || availabilityResponse.data.length === 0) {
|
||||||
|
throw new Error("Disponibilidade não encontrada");
|
||||||
|
}
|
||||||
|
|
||||||
|
const availability = availabilityResponse.data[0];
|
||||||
|
console.log(
|
||||||
|
`✅ Disponibilidade encontrada: ${availability.start_time} - ${availability.end_time}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Criar consulta para próxima segunda-feira às 10:00
|
||||||
|
const today = new Date();
|
||||||
|
const daysUntilMonday = (1 - today.getDay() + 7) % 7 || 7; // Próxima segunda
|
||||||
|
const appointmentDate = new Date(today);
|
||||||
|
appointmentDate.setDate(today.getDate() + daysUntilMonday);
|
||||||
|
appointmentDate.setHours(10, 0, 0, 0);
|
||||||
|
|
||||||
|
const scheduledAt = appointmentDate.toISOString();
|
||||||
|
|
||||||
|
console.log(`\n📝 Criando consulta para ${scheduledAt}...`);
|
||||||
|
|
||||||
|
const appointmentData = {
|
||||||
|
patient_id: patientId,
|
||||||
|
doctor_id: "6dad001d-229b-40b5-80f3-310243c4599c",
|
||||||
|
scheduled_at: scheduledAt,
|
||||||
|
duration_minutes: 30,
|
||||||
|
appointment_type: "presencial",
|
||||||
|
chief_complaint: "Consulta de rotina",
|
||||||
|
};
|
||||||
|
|
||||||
|
const appointmentResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/rest/v1/appointments`,
|
||||||
|
appointmentData,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${auroraToken}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Prefer: "return=representation",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("\n✅ Consulta criada com sucesso!");
|
||||||
|
console.log("\n📋 Detalhes da consulta:");
|
||||||
|
console.log(` - Paciente: Aurora Sabrina Clara Nascimento`);
|
||||||
|
console.log(` - Médico: Dr. Fernando Pirichowski`);
|
||||||
|
console.log(` - Data/Hora: ${scheduledAt}`);
|
||||||
|
console.log(` - Duração: 30 minutos`);
|
||||||
|
console.log(` - Tipo: presencial`);
|
||||||
|
|
||||||
|
if (appointmentResponse.data && appointmentResponse.data.length > 0) {
|
||||||
|
console.log(` - ID da consulta: ${appointmentResponse.data[0].id}`);
|
||||||
|
console.log(
|
||||||
|
` - Order Number: ${appointmentResponse.data[0].order_number}`
|
||||||
|
);
|
||||||
|
console.log(` - Status: ${appointmentResponse.data[0].status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
"❌ Erro ao criar consulta:",
|
||||||
|
error.response?.data || error.message
|
||||||
|
);
|
||||||
|
if (error.response?.data) {
|
||||||
|
console.error("Detalhes:", JSON.stringify(error.response.data, null, 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createAppointment();
|
||||||
93
MEDICONNECT 2/create-fernando-availability.cjs
Normal file
93
MEDICONNECT 2/create-fernando-availability.cjs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
const SUPABASE_ANON_KEY =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
|
||||||
|
|
||||||
|
// IDs
|
||||||
|
const DOCTOR_ID = "6dad001d-229b-40b5-80f3-310243c4599c"; // Fernando (CRM 24245)
|
||||||
|
const ADMIN_ID = "c7fcd702-9a6e-4b7c-abd3-956b25af407d"; // Admin (riseup)
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
console.log("🔐 Fazendo login como admin...");
|
||||||
|
|
||||||
|
const loginResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
|
||||||
|
{
|
||||||
|
email: "riseup@popcode.com.br",
|
||||||
|
password: "riseup",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const adminToken = loginResponse.data.access_token;
|
||||||
|
console.log("✅ Login realizado\n");
|
||||||
|
|
||||||
|
console.log("📅 Criando disponibilidade para Dr. Fernando...");
|
||||||
|
console.log("⏰ Horário: 07:00 às 19:00");
|
||||||
|
console.log("📆 Dias: Segunda a Domingo\n");
|
||||||
|
|
||||||
|
const weekdays = [
|
||||||
|
{ num: "sunday", name: "Domingo" },
|
||||||
|
{ num: "monday", name: "Segunda-feira" },
|
||||||
|
{ num: "tuesday", name: "Terça-feira" },
|
||||||
|
{ num: "wednesday", name: "Quarta-feira" },
|
||||||
|
{ num: "thursday", name: "Quinta-feira" },
|
||||||
|
{ num: "friday", name: "Sexta-feira" },
|
||||||
|
{ num: "saturday", name: "Sábado" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const day of weekdays) {
|
||||||
|
try {
|
||||||
|
const availabilityData = {
|
||||||
|
doctor_id: DOCTOR_ID,
|
||||||
|
weekday: day.num,
|
||||||
|
start_time: "07:00:00",
|
||||||
|
end_time: "19:00:00",
|
||||||
|
slot_minutes: 30,
|
||||||
|
appointment_type: "presencial",
|
||||||
|
active: true,
|
||||||
|
created_by: ADMIN_ID,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await axios.post(
|
||||||
|
`${SUPABASE_URL}/rest/v1/doctor_availability`,
|
||||||
|
availabilityData,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${adminToken}`,
|
||||||
|
Prefer: "return=representation",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`✅ ${day.name}: Disponibilidade criada`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`❌ ${day.name}: Erro -`,
|
||||||
|
error.response?.data?.message || error.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("\n🎉 Disponibilidade criada com sucesso!");
|
||||||
|
console.log("\n📋 Resumo:");
|
||||||
|
console.log("- Médico: Dr. Fernando Pirichowski");
|
||||||
|
console.log("- Dias: Todos os dias da semana (Domingo a Sábado)");
|
||||||
|
console.log("- Horário: 07:00 às 19:00");
|
||||||
|
console.log("- Duração consulta: 30 minutos");
|
||||||
|
console.log("- Tipo: Presencial");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Erro geral:", error.response?.data || error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
80
MEDICONNECT 2/create-patient-with-password.cjs
Normal file
80
MEDICONNECT 2/create-patient-with-password.cjs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
// Configuração
|
||||||
|
const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
const SUPABASE_ANON_KEY =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
|
||||||
|
|
||||||
|
// Credenciais do admin
|
||||||
|
const ADMIN_EMAIL = "riseup@popcode.com.br";
|
||||||
|
const ADMIN_PASSWORD = "riseup";
|
||||||
|
|
||||||
|
// Dados do paciente (Aurora Sabrina Clara Nascimento)
|
||||||
|
const PATIENT_DATA = {
|
||||||
|
email: "aurora-nascimento94@gmx.com",
|
||||||
|
password: "auroranasc94",
|
||||||
|
full_name: "Aurora Sabrina Clara Nascimento",
|
||||||
|
phone_mobile: "(21) 99856-3014",
|
||||||
|
cpf: "66864784231", // CPF sem pontuação
|
||||||
|
create_patient_record: true,
|
||||||
|
role: "paciente",
|
||||||
|
};
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
console.log("🔐 1. Fazendo login como admin...");
|
||||||
|
|
||||||
|
// 1. Login do admin
|
||||||
|
const loginResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
|
||||||
|
{
|
||||||
|
email: ADMIN_EMAIL,
|
||||||
|
password: ADMIN_PASSWORD,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const adminToken = loginResponse.data.access_token;
|
||||||
|
console.log("✅ Login realizado com sucesso!");
|
||||||
|
console.log("🔑 Token:", adminToken.substring(0, 30) + "...");
|
||||||
|
|
||||||
|
console.log("\n👤 2. Criando paciente...");
|
||||||
|
console.log("Dados:", JSON.stringify(PATIENT_DATA, null, 2));
|
||||||
|
|
||||||
|
// 2. Criar paciente
|
||||||
|
const createResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/functions/v1/create-user-with-password`,
|
||||||
|
PATIENT_DATA,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${adminToken}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("\n✅ Paciente criado com sucesso!");
|
||||||
|
console.log("Resposta:", JSON.stringify(createResponse.data, null, 2));
|
||||||
|
|
||||||
|
if (createResponse.data.patient_id) {
|
||||||
|
console.log("\n📋 ID do paciente:", createResponse.data.patient_id);
|
||||||
|
console.log("✉️ Email de confirmação enviado para:", PATIENT_DATA.email);
|
||||||
|
console.log("🔐 Senha temporária:", PATIENT_DATA.password);
|
||||||
|
console.log(
|
||||||
|
"\n⚠️ O usuário precisa confirmar o email antes de fazer login!"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Erro:", error.response?.data || error.message);
|
||||||
|
console.error("Status:", error.response?.status);
|
||||||
|
console.error("URL:", error.config?.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
72
MEDICONNECT 2/fix-aurora-user-id.cjs
Normal file
72
MEDICONNECT 2/fix-aurora-user-id.cjs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
const SUPABASE_ANON_KEY =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
|
||||||
|
|
||||||
|
async function fixAuroraUserId() {
|
||||||
|
try {
|
||||||
|
console.log("🔐 Fazendo login como admin...");
|
||||||
|
|
||||||
|
const loginResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
|
||||||
|
{
|
||||||
|
email: "riseup@popcode.com.br",
|
||||||
|
password: "riseup",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const adminToken = loginResponse.data.access_token;
|
||||||
|
console.log("✅ Login realizado");
|
||||||
|
|
||||||
|
// Fazer login como Aurora para pegar o user_id
|
||||||
|
console.log("\n👤 Fazendo login como Aurora...");
|
||||||
|
const auroraLoginResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
|
||||||
|
{
|
||||||
|
email: "aurora-nascimento94@gmx.com",
|
||||||
|
password: "auroranasc94",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const auroraUserId = auroraLoginResponse.data.user.id;
|
||||||
|
console.log(`✅ User ID da Aurora: ${auroraUserId}`);
|
||||||
|
|
||||||
|
// Atualizar patient com user_id
|
||||||
|
console.log("\n📝 Atualizando registro da paciente...");
|
||||||
|
const updateResponse = await axios.patch(
|
||||||
|
`${SUPABASE_URL}/rest/v1/patients?id=eq.b85486f7-9135-4b67-9aa7-b884d9603d12`,
|
||||||
|
{
|
||||||
|
user_id: auroraUserId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${adminToken}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Prefer: "return=representation",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("✅ Registro atualizado com sucesso!");
|
||||||
|
console.log(` - Patient ID: b85486f7-9135-4b67-9aa7-b884d9603d12`);
|
||||||
|
console.log(` - User ID: ${auroraUserId}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Erro:", error.response?.data || error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fixAuroraUserId();
|
||||||
59
MEDICONNECT 2/get-aurora-info.cjs
Normal file
59
MEDICONNECT 2/get-aurora-info.cjs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
const SUPABASE_ANON_KEY =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
console.log("🔐 Fazendo login como admin...");
|
||||||
|
|
||||||
|
const loginResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
|
||||||
|
{
|
||||||
|
email: "riseup@popcode.com.br",
|
||||||
|
password: "riseup",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const adminToken = loginResponse.data.access_token;
|
||||||
|
console.log("✅ Login realizado\n");
|
||||||
|
|
||||||
|
console.log("👤 Buscando dados de Aurora na tabela patients...");
|
||||||
|
|
||||||
|
const patientsResponse = await axios.get(
|
||||||
|
`${SUPABASE_URL}/rest/v1/patients?email=eq.aurora-nascimento94@gmx.com`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${adminToken}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (patientsResponse.data.length > 0) {
|
||||||
|
const patient = patientsResponse.data[0];
|
||||||
|
console.log("✅ Paciente encontrada!\n");
|
||||||
|
console.log("📋 DADOS DA AURORA:\n");
|
||||||
|
console.log("User ID (auth):", patient.id);
|
||||||
|
console.log("Patient ID:", patient.id); // Em patients, o id é o mesmo do auth
|
||||||
|
console.log("Nome:", patient.full_name);
|
||||||
|
console.log("Email:", patient.email);
|
||||||
|
console.log("CPF:", patient.cpf);
|
||||||
|
console.log("Telefone:", patient.phone_mobile);
|
||||||
|
console.log("\n📄 Dados completos:", JSON.stringify(patient, null, 2));
|
||||||
|
} else {
|
||||||
|
console.log("❌ Paciente não encontrada na tabela patients");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Erro:", error.response?.data || error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
80
MEDICONNECT 2/get-fernando-complete.cjs
Normal file
80
MEDICONNECT 2/get-fernando-complete.cjs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
const SUPABASE_ANON_KEY =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
console.log("🔐 Fazendo login como admin...");
|
||||||
|
|
||||||
|
const loginResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
|
||||||
|
{
|
||||||
|
email: "riseup@popcode.com.br",
|
||||||
|
password: "riseup",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const adminToken = loginResponse.data.access_token;
|
||||||
|
console.log("✅ Login realizado\n");
|
||||||
|
|
||||||
|
console.log("🔍 Buscando Fernando em profiles...");
|
||||||
|
|
||||||
|
const profilesResponse = await axios.get(
|
||||||
|
`${SUPABASE_URL}/rest/v1/profiles?email=eq.fernando.pirichowski@souunit.com.br`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${adminToken}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (profilesResponse.data.length > 0) {
|
||||||
|
console.log(
|
||||||
|
`✅ ${profilesResponse.data.length} perfil(is) encontrado(s)!\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
profilesResponse.data.forEach((profile, index) => {
|
||||||
|
console.log(`📋 PERFIL ${index + 1}:\n`);
|
||||||
|
console.log("User ID:", profile.id);
|
||||||
|
console.log("Email:", profile.email);
|
||||||
|
console.log("Nome:", profile.full_name);
|
||||||
|
console.log("Telefone:", profile.phone || "Não informado");
|
||||||
|
console.log("\n" + "=".repeat(60) + "\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pegar roles do primeiro perfil
|
||||||
|
const userId = profilesResponse.data[0].id;
|
||||||
|
console.log("🔍 Buscando roles...");
|
||||||
|
|
||||||
|
const rolesResponse = await axios.get(
|
||||||
|
`${SUPABASE_URL}/rest/v1/user_roles?user_id=eq.${userId}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${adminToken}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"📌 Roles:",
|
||||||
|
rolesResponse.data.map((r) => r.role).join(", ")
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log("❌ Nenhum perfil encontrado");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Erro:", error.response?.data || error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
82
MEDICONNECT 2/get-fernando-doctor-id.cjs
Normal file
82
MEDICONNECT 2/get-fernando-doctor-id.cjs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
const SUPABASE_ANON_KEY =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
console.log("🔐 Fazendo login como admin...");
|
||||||
|
|
||||||
|
const loginResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
|
||||||
|
{
|
||||||
|
email: "riseup@popcode.com.br",
|
||||||
|
password: "riseup",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const adminToken = loginResponse.data.access_token;
|
||||||
|
console.log("✅ Login realizado\n");
|
||||||
|
|
||||||
|
console.log("👨⚕️ Buscando dados de Fernando na tabela doctors...");
|
||||||
|
|
||||||
|
const doctorsResponse = await axios.get(
|
||||||
|
`${SUPABASE_URL}/rest/v1/doctors?email=eq.fernando.pirichowski@souunit.com.br`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${adminToken}`,
|
||||||
|
Prefer: "return=representation",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (doctorsResponse.data.length > 0) {
|
||||||
|
console.log(
|
||||||
|
`✅ ${doctorsResponse.data.length} médico(s) encontrado(s)!\n`
|
||||||
|
);
|
||||||
|
|
||||||
|
doctorsResponse.data.forEach((doctor, index) => {
|
||||||
|
console.log(`📋 MÉDICO ${index + 1}:\n`);
|
||||||
|
console.log("Doctor ID:", doctor.id);
|
||||||
|
console.log("Nome:", doctor.full_name);
|
||||||
|
console.log("Email:", doctor.email);
|
||||||
|
console.log("CRM:", doctor.crm);
|
||||||
|
console.log("Especialidade:", doctor.specialty || "Não informada");
|
||||||
|
console.log("Telefone:", doctor.phone || "Não informado");
|
||||||
|
console.log("\n" + "=".repeat(60) + "\n");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("❌ Nenhum médico chamado Fernando encontrado");
|
||||||
|
console.log("\n🔍 Buscando todos os médicos...");
|
||||||
|
|
||||||
|
const allDoctorsResponse = await axios.get(
|
||||||
|
`${SUPABASE_URL}/rest/v1/doctors`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
Authorization: `Bearer ${adminToken}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`\n📊 Total de médicos cadastrados: ${allDoctorsResponse.data.length}`
|
||||||
|
);
|
||||||
|
allDoctorsResponse.data.forEach((doctor, index) => {
|
||||||
|
console.log(`${index + 1}. ${doctor.full_name} - ${doctor.email}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Erro:", error.response?.data || error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
38
MEDICONNECT 2/get-fernando-user-id.cjs
Normal file
38
MEDICONNECT 2/get-fernando-user-id.cjs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
const SUPABASE_ANON_KEY =
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ";
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
console.log("🔐 Fazendo login como Fernando...");
|
||||||
|
|
||||||
|
const loginResponse = await axios.post(
|
||||||
|
`${SUPABASE_URL}/auth/v1/token?grant_type=password`,
|
||||||
|
{
|
||||||
|
email: "fernando.pirichowski@souunit.com.br",
|
||||||
|
password: "fernando123",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
apikey: SUPABASE_ANON_KEY,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const userData = loginResponse.data;
|
||||||
|
console.log("✅ Login realizado\n");
|
||||||
|
|
||||||
|
console.log("📋 DADOS DO FERNANDO (AUTH):\n");
|
||||||
|
console.log("User ID:", userData.user.id);
|
||||||
|
console.log("Email:", userData.user.email);
|
||||||
|
console.log("Role:", userData.user.role);
|
||||||
|
console.log("\n" + "=".repeat(60));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Erro:", error.response?.data || error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
@ -23,7 +23,9 @@ const RecoveryRedirect: React.FC = () => {
|
|||||||
const queryType = searchParams.get("type");
|
const queryType = searchParams.get("type");
|
||||||
|
|
||||||
if (queryToken && queryType === "recovery") {
|
if (queryToken && queryType === "recovery") {
|
||||||
console.log("[RecoveryRedirect] ✅ Token de recovery no query string detectado");
|
console.log(
|
||||||
|
"[RecoveryRedirect] ✅ Token de recovery no query string detectado"
|
||||||
|
);
|
||||||
shouldRedirect = true;
|
shouldRedirect = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +36,9 @@ const RecoveryRedirect: React.FC = () => {
|
|||||||
const hashType = hashParams.get("type");
|
const hashType = hashParams.get("type");
|
||||||
|
|
||||||
if (hashToken && hashType === "recovery") {
|
if (hashToken && hashType === "recovery") {
|
||||||
console.log("[RecoveryRedirect] ✅ Token de recovery no hash detectado");
|
console.log(
|
||||||
|
"[RecoveryRedirect] ✅ Token de recovery no hash detectado"
|
||||||
|
);
|
||||||
shouldRedirect = true;
|
shouldRedirect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,7 +46,9 @@ const RecoveryRedirect: React.FC = () => {
|
|||||||
if (shouldRedirect) {
|
if (shouldRedirect) {
|
||||||
console.log("[RecoveryRedirect] 🔄 Redirecionando para /reset-password");
|
console.log("[RecoveryRedirect] 🔄 Redirecionando para /reset-password");
|
||||||
// Preservar os parâmetros e redirecionar
|
// Preservar os parâmetros e redirecionar
|
||||||
navigate(`/reset-password${location.search}${location.hash}`, { replace: true });
|
navigate(`/reset-password${location.search}${location.hash}`, {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log("[RecoveryRedirect] ℹ️ Nenhum token de recovery detectado");
|
console.log("[RecoveryRedirect] ℹ️ Nenhum token de recovery detectado");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,7 +89,7 @@ const AcompanhamentoPaciente: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
const url = avatarService.getPublicUrl({
|
const url = avatarService.getPublicUrl({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
ext: ext as "jpg" | "png" | "webp"
|
ext: ext as "jpg" | "png" | "webp",
|
||||||
});
|
});
|
||||||
const response = await fetch(url, { method: "HEAD" });
|
const response = await fetch(url, { method: "HEAD" });
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
|||||||
@ -35,7 +35,9 @@ export default function AuthCallback() {
|
|||||||
|
|
||||||
// Se for recovery, redirecionar para página de reset
|
// Se for recovery, redirecionar para página de reset
|
||||||
if (type === "recovery" && accessToken) {
|
if (type === "recovery" && accessToken) {
|
||||||
console.log("[AuthCallback] ✅ Token de recovery detectado, redirecionando para /reset-password");
|
console.log(
|
||||||
|
"[AuthCallback] ✅ Token de recovery detectado, redirecionando para /reset-password"
|
||||||
|
);
|
||||||
setStatus("success");
|
setStatus("success");
|
||||||
setMessage("Redirecionando para página de redefinição de senha...");
|
setMessage("Redirecionando para página de redefinição de senha...");
|
||||||
|
|
||||||
@ -81,20 +83,32 @@ export default function AuthCallback() {
|
|||||||
setMessage("Autenticado com sucesso! Redirecionando...");
|
setMessage("Autenticado com sucesso! Redirecionando...");
|
||||||
toast.success("Login realizado com sucesso!");
|
toast.success("Login realizado com sucesso!");
|
||||||
|
|
||||||
// Redirecionar baseado no role
|
// Redirecionar baseado no contexto salvo ou role do usuário
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
// Verificar se há redirecionamento salvo do magic link
|
||||||
|
const savedRedirect = localStorage.getItem("magic_link_redirect");
|
||||||
|
|
||||||
|
if (savedRedirect) {
|
||||||
|
console.log("[AuthCallback] Redirecionando para:", savedRedirect);
|
||||||
|
localStorage.removeItem("magic_link_redirect"); // Limpar após uso
|
||||||
|
navigate(savedRedirect, { replace: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: redirecionar baseado no role
|
||||||
const userRole = session.user.user_metadata?.role || "paciente";
|
const userRole = session.user.user_metadata?.role || "paciente";
|
||||||
|
console.log("[AuthCallback] Redirecionando baseado no role:", userRole);
|
||||||
|
|
||||||
switch (userRole) {
|
switch (userRole) {
|
||||||
case "medico":
|
case "medico":
|
||||||
navigate("/painel-medico");
|
navigate("/painel-medico", { replace: true });
|
||||||
break;
|
break;
|
||||||
case "secretaria":
|
case "secretaria":
|
||||||
navigate("/painel-secretaria");
|
navigate("/painel-secretaria", { replace: true });
|
||||||
break;
|
break;
|
||||||
case "paciente":
|
case "paciente":
|
||||||
default:
|
default:
|
||||||
navigate("/acompanhamento");
|
navigate("/acompanhamento", { replace: true });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}, 1500);
|
}, 1500);
|
||||||
|
|||||||
@ -202,9 +202,12 @@ const LoginMedico: React.FC = () => {
|
|||||||
}
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
// Salvar contexto para redirecionamento correto após magic link
|
||||||
|
localStorage.setItem("magic_link_redirect", "/painel-medico");
|
||||||
|
|
||||||
await authService.sendMagicLink(
|
await authService.sendMagicLink(
|
||||||
formData.email,
|
formData.email,
|
||||||
"https://mediconnectbrasil.netlify.app/medico/painel"
|
`${window.location.origin}/auth/callback`
|
||||||
);
|
);
|
||||||
toast.success(
|
toast.success(
|
||||||
"Link de acesso enviado para seu email! Verifique sua caixa de entrada.",
|
"Link de acesso enviado para seu email! Verifique sua caixa de entrada.",
|
||||||
|
|||||||
@ -312,9 +312,12 @@ const LoginPaciente: React.FC = () => {
|
|||||||
}
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
// Salvar contexto para redirecionamento correto após magic link
|
||||||
|
localStorage.setItem("magic_link_redirect", "/acompanhamento");
|
||||||
|
|
||||||
await authService.sendMagicLink(
|
await authService.sendMagicLink(
|
||||||
formData.email,
|
formData.email,
|
||||||
"https://mediconnectbrasil.netlify.app/paciente/agendamento"
|
`${window.location.origin}/auth/callback`
|
||||||
);
|
);
|
||||||
toast.success(
|
toast.success(
|
||||||
"Link de acesso enviado para seu email! Verifique sua caixa de entrada.",
|
"Link de acesso enviado para seu email! Verifique sua caixa de entrada.",
|
||||||
|
|||||||
@ -212,9 +212,12 @@ const LoginSecretaria: React.FC = () => {
|
|||||||
}
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
// Salvar contexto para redirecionamento correto após magic link
|
||||||
|
localStorage.setItem("magic_link_redirect", "/painel-secretaria");
|
||||||
|
|
||||||
await authService.sendMagicLink(
|
await authService.sendMagicLink(
|
||||||
formData.email,
|
formData.email,
|
||||||
"https://mediconnectbrasil.netlify.app/secretaria/painel"
|
`${window.location.origin}/auth/callback`
|
||||||
);
|
);
|
||||||
toast.success(
|
toast.success(
|
||||||
"Link de acesso enviado para seu email! Verifique sua caixa de entrada.",
|
"Link de acesso enviado para seu email! Verifique sua caixa de entrada.",
|
||||||
|
|||||||
@ -111,7 +111,7 @@ const PainelMedico: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
const url = avatarService.getPublicUrl({
|
const url = avatarService.getPublicUrl({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
ext: ext as "jpg" | "png" | "webp"
|
ext: ext as "jpg" | "png" | "webp",
|
||||||
});
|
});
|
||||||
const response = await fetch(url, { method: "HEAD" });
|
const response = await fetch(url, { method: "HEAD" });
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
|||||||
@ -192,9 +192,7 @@ export default function PerfilPaciente() {
|
|||||||
<div className="flex items-center justify-center min-h-screen bg-gray-50">
|
<div className="flex items-center justify-center min-h-screen bg-gray-50">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="w-16 h-16 border-4 border-blue-600 border-t-transparent rounded-full animate-spin mx-auto mb-4" />
|
<div className="w-16 h-16 border-4 border-blue-600 border-t-transparent rounded-full animate-spin mx-auto mb-4" />
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">Carregando perfil...</p>
|
||||||
Carregando perfil...
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -204,9 +202,7 @@ export default function PerfilPaciente() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center min-h-screen bg-gray-50">
|
<div className="flex items-center justify-center min-h-screen bg-gray-50">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<p className="text-red-600 mb-4">
|
<p className="text-red-600 mb-4">Usuário não identificado</p>
|
||||||
Usuário não identificado
|
|
||||||
</p>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate("/paciente")}
|
onClick={() => navigate("/paciente")}
|
||||||
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
|
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
|
||||||
@ -233,9 +229,7 @@ export default function PerfilPaciente() {
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold text-gray-900">
|
<h1 className="text-3xl font-bold text-gray-900">Meu Perfil</h1>
|
||||||
Meu Perfil
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">
|
||||||
Gerencie suas informações pessoais e médicas
|
Gerencie suas informações pessoais e médicas
|
||||||
</p>
|
</p>
|
||||||
@ -286,9 +280,7 @@ export default function PerfilPaciente() {
|
|||||||
<p className="font-medium text-gray-900">
|
<p className="font-medium text-gray-900">
|
||||||
{formData.full_name || "Carregando..."}
|
{formData.full_name || "Carregando..."}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-gray-500">
|
<p className="text-gray-500">{formData.email || "Sem email"}</p>
|
||||||
{formData.email || "Sem email"}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -688,4 +680,3 @@ export default function PerfilPaciente() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,10 @@ const ResetPassword: React.FC = () => {
|
|||||||
token = hashParams.get("access_token");
|
token = hashParams.get("access_token");
|
||||||
type = hashParams.get("type");
|
type = hashParams.get("type");
|
||||||
|
|
||||||
console.log("[ResetPassword] Token do hash:", token ? token.substring(0, 20) + "..." : "null");
|
console.log(
|
||||||
|
"[ResetPassword] Token do hash:",
|
||||||
|
token ? token.substring(0, 20) + "..." : "null"
|
||||||
|
);
|
||||||
console.log("[ResetPassword] Type do hash:", type);
|
console.log("[ResetPassword] Type do hash:", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,17 +46,24 @@ const ResetPassword: React.FC = () => {
|
|||||||
token = queryParams.get("token");
|
token = queryParams.get("token");
|
||||||
type = queryParams.get("type");
|
type = queryParams.get("type");
|
||||||
|
|
||||||
console.log("[ResetPassword] Token do query:", token ? token.substring(0, 20) + "..." : "null");
|
console.log(
|
||||||
|
"[ResetPassword] Token do query:",
|
||||||
|
token ? token.substring(0, 20) + "..." : "null"
|
||||||
|
);
|
||||||
console.log("[ResetPassword] Type do query:", type);
|
console.log("[ResetPassword] Type do query:", type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
setAccessToken(token);
|
setAccessToken(token);
|
||||||
console.log("[ResetPassword] ✅ Token de recuperação detectado e armazenado");
|
console.log(
|
||||||
|
"[ResetPassword] ✅ Token de recuperação detectado e armazenado"
|
||||||
|
);
|
||||||
console.log("[ResetPassword] Type:", type);
|
console.log("[ResetPassword] Type:", type);
|
||||||
} else {
|
} else {
|
||||||
console.error("[ResetPassword] ❌ Token não encontrado no hash nem no query string");
|
console.error(
|
||||||
|
"[ResetPassword] ❌ Token não encontrado no hash nem no query string"
|
||||||
|
);
|
||||||
console.log("[ResetPassword] URL completa:", window.location.href);
|
console.log("[ResetPassword] URL completa:", window.location.href);
|
||||||
toast.error("Link de recuperação inválido ou expirado");
|
toast.error("Link de recuperação inválido ou expirado");
|
||||||
setTimeout(() => navigate("/"), 3000);
|
setTimeout(() => navigate("/"), 3000);
|
||||||
@ -112,7 +122,7 @@ const ResetPassword: React.FC = () => {
|
|||||||
message?: string;
|
message?: string;
|
||||||
msg?: string;
|
msg?: string;
|
||||||
error_code?: string;
|
error_code?: string;
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
message?: string;
|
message?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -164,8 +164,13 @@ class AuthService {
|
|||||||
const redirectUrl = `${API_CONFIG.APP_URL}/reset-password`;
|
const redirectUrl = `${API_CONFIG.APP_URL}/reset-password`;
|
||||||
|
|
||||||
console.log("[authService.requestPasswordReset] Email:", email);
|
console.log("[authService.requestPasswordReset] Email:", email);
|
||||||
console.log("[authService.requestPasswordReset] Redirect URL:", redirectUrl);
|
console.log(
|
||||||
console.log("[authService.requestPasswordReset] Usando endpoint /auth/v1/recover");
|
"[authService.requestPasswordReset] Redirect URL:",
|
||||||
|
redirectUrl
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
"[authService.requestPasswordReset] Usando endpoint /auth/v1/recover"
|
||||||
|
);
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
email,
|
email,
|
||||||
@ -174,28 +179,33 @@ class AuthService {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("[authService.requestPasswordReset] Payload:", JSON.stringify(payload, null, 2));
|
console.log(
|
||||||
|
"[authService.requestPasswordReset] Payload:",
|
||||||
await axios.post(
|
JSON.stringify(payload, null, 2)
|
||||||
`${API_CONFIG.AUTH_URL}/recover`,
|
|
||||||
payload,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("[authService.requestPasswordReset] ✅ Email de recuperação enviado");
|
await axios.post(`${API_CONFIG.AUTH_URL}/recover`, payload, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"[authService.requestPasswordReset] ✅ Email de recuperação enviado"
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: "Email de recuperação de senha enviado com sucesso. Verifique sua caixa de entrada.",
|
message:
|
||||||
|
"Email de recuperação de senha enviado com sucesso. Verifique sua caixa de entrada.",
|
||||||
};
|
};
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("[authService.requestPasswordReset] ❌ Erro:", error);
|
console.error("[authService.requestPasswordReset] ❌ Erro:", error);
|
||||||
console.error("[authService.requestPasswordReset] Response:", error.response?.data);
|
console.error(
|
||||||
|
"[authService.requestPasswordReset] Response:",
|
||||||
|
error.response?.data
|
||||||
|
);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,10 +219,21 @@ class AuthService {
|
|||||||
newPassword: string
|
newPassword: string
|
||||||
): Promise<{ success: boolean; message: string }> {
|
): Promise<{ success: boolean; message: string }> {
|
||||||
try {
|
try {
|
||||||
console.log("[authService.updatePassword] Iniciando atualização de senha");
|
console.log(
|
||||||
console.log("[authService.updatePassword] Token (primeiros 30 chars):", accessToken.substring(0, 30));
|
"[authService.updatePassword] Iniciando atualização de senha"
|
||||||
console.log("[authService.updatePassword] Nova senha length:", newPassword.length);
|
);
|
||||||
console.log("[authService.updatePassword] URL:", `${API_CONFIG.AUTH_URL}/user`);
|
console.log(
|
||||||
|
"[authService.updatePassword] Token (primeiros 30 chars):",
|
||||||
|
accessToken.substring(0, 30)
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
"[authService.updatePassword] Nova senha length:",
|
||||||
|
newPassword.length
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
"[authService.updatePassword] URL:",
|
||||||
|
`${API_CONFIG.AUTH_URL}/user`
|
||||||
|
);
|
||||||
|
|
||||||
const response = await axios.put(
|
const response = await axios.put(
|
||||||
`${API_CONFIG.AUTH_URL}/user`,
|
`${API_CONFIG.AUTH_URL}/user`,
|
||||||
@ -228,33 +249,74 @@ class AuthService {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("[authService.updatePassword] ✅ Senha atualizada com sucesso");
|
console.log(
|
||||||
console.log("[authService.updatePassword] Response status:", response.status);
|
"[authService.updatePassword] ✅ Senha atualizada com sucesso"
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
"[authService.updatePassword] Response status:",
|
||||||
|
response.status
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: "Senha atualizada com sucesso",
|
message: "Senha atualizada com sucesso",
|
||||||
};
|
};
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("[authService.updatePassword] ❌ Erro ao atualizar senha:", error);
|
console.error(
|
||||||
console.error("[authService.updatePassword] Error response:", error.response?.data);
|
"[authService.updatePassword] ❌ Erro ao atualizar senha:",
|
||||||
console.error("[authService.updatePassword] Error response (stringified):", JSON.stringify(error.response?.data, null, 2));
|
error
|
||||||
console.error("[authService.updatePassword] Error status:", error.response?.status);
|
);
|
||||||
console.error("[authService.updatePassword] Error statusText:", error.response?.statusText);
|
console.error(
|
||||||
console.error("[authService.updatePassword] Error headers:", error.response?.headers);
|
"[authService.updatePassword] Error response:",
|
||||||
console.error("[authService.updatePassword] Request URL:", error.config?.url);
|
error.response?.data
|
||||||
console.error("[authService.updatePassword] Request headers:", error.config?.headers);
|
);
|
||||||
console.error("[authService.updatePassword] Request data:", error.config?.data);
|
console.error(
|
||||||
|
"[authService.updatePassword] Error response (stringified):",
|
||||||
|
JSON.stringify(error.response?.data, null, 2)
|
||||||
|
);
|
||||||
|
console.error(
|
||||||
|
"[authService.updatePassword] Error status:",
|
||||||
|
error.response?.status
|
||||||
|
);
|
||||||
|
console.error(
|
||||||
|
"[authService.updatePassword] Error statusText:",
|
||||||
|
error.response?.statusText
|
||||||
|
);
|
||||||
|
console.error(
|
||||||
|
"[authService.updatePassword] Error headers:",
|
||||||
|
error.response?.headers
|
||||||
|
);
|
||||||
|
console.error(
|
||||||
|
"[authService.updatePassword] Request URL:",
|
||||||
|
error.config?.url
|
||||||
|
);
|
||||||
|
console.error(
|
||||||
|
"[authService.updatePassword] Request headers:",
|
||||||
|
error.config?.headers
|
||||||
|
);
|
||||||
|
console.error(
|
||||||
|
"[authService.updatePassword] Request data:",
|
||||||
|
error.config?.data
|
||||||
|
);
|
||||||
|
|
||||||
// Se for 422, mostrar mensagem específica
|
// Se for 422, mostrar mensagem específica
|
||||||
if (error.response?.status === 422) {
|
if (error.response?.status === 422) {
|
||||||
const errorMsg = error.response?.data?.msg || error.response?.data?.message || error.response?.data?.error_description || "Token inválido ou expirado";
|
const errorMsg =
|
||||||
console.error("[authService.updatePassword] ⚠️ Erro 422 - Possíveis causas:");
|
error.response?.data?.msg ||
|
||||||
|
error.response?.data?.message ||
|
||||||
|
error.response?.data?.error_description ||
|
||||||
|
"Token inválido ou expirado";
|
||||||
|
console.error(
|
||||||
|
"[authService.updatePassword] ⚠️ Erro 422 - Possíveis causas:"
|
||||||
|
);
|
||||||
console.error(" 1. Token de recuperação expirado (válido por 1 hora)");
|
console.error(" 1. Token de recuperação expirado (válido por 1 hora)");
|
||||||
console.error(" 2. Token já foi usado");
|
console.error(" 2. Token já foi usado");
|
||||||
console.error(" 3. Formato do token incorreto");
|
console.error(" 3. Formato do token incorreto");
|
||||||
console.error(" 4. Usuário não existe");
|
console.error(" 4. Usuário não existe");
|
||||||
console.error("[authService.updatePassword] Mensagem do servidor:", errorMsg);
|
console.error(
|
||||||
|
"[authService.updatePassword] Mensagem do servidor:",
|
||||||
|
errorMsg
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@ -53,7 +53,7 @@ class AvatarService {
|
|||||||
formData,
|
formData,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
"x-upsert": "true",
|
"x-upsert": "true",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -91,7 +91,9 @@ class AvatarService {
|
|||||||
try {
|
try {
|
||||||
// Não há endpoint de delete, então apenas removemos a referência do perfil
|
// Não há endpoint de delete, então apenas removemos a referência do perfil
|
||||||
// O upload futuro irá sobrescrever a imagem antiga
|
// O upload futuro irá sobrescrever a imagem antiga
|
||||||
console.log("Avatar será removido do perfil. Upload futuro sobrescreverá a imagem.");
|
console.log(
|
||||||
|
"Avatar será removido do perfil. Upload futuro sobrescreverá a imagem."
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erro ao deletar avatar:", error);
|
console.error("Erro ao deletar avatar:", error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user