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();
|
||||
@ -14,27 +14,31 @@ const RecoveryRedirect: React.FC = () => {
|
||||
console.log("[RecoveryRedirect] Pathname:", location.pathname);
|
||||
console.log("[RecoveryRedirect] Search:", location.search);
|
||||
console.log("[RecoveryRedirect] Hash:", location.hash);
|
||||
|
||||
|
||||
let shouldRedirect = false;
|
||||
|
||||
|
||||
// Verificar query string: ?token=xxx&type=recovery
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const queryToken = searchParams.get("token");
|
||||
const queryType = searchParams.get("type");
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// Verificar hash: #access_token=xxx&type=recovery
|
||||
if (location.hash) {
|
||||
const hashParams = new URLSearchParams(location.hash.substring(1));
|
||||
const hashToken = hashParams.get("access_token");
|
||||
const hashType = hashParams.get("type");
|
||||
|
||||
|
||||
if (hashToken && hashType === "recovery") {
|
||||
console.log("[RecoveryRedirect] ✅ Token de recovery no hash detectado");
|
||||
console.log(
|
||||
"[RecoveryRedirect] ✅ Token de recovery no hash detectado"
|
||||
);
|
||||
shouldRedirect = true;
|
||||
}
|
||||
}
|
||||
@ -42,7 +46,9 @@ const RecoveryRedirect: React.FC = () => {
|
||||
if (shouldRedirect) {
|
||||
console.log("[RecoveryRedirect] 🔄 Redirecionando para /reset-password");
|
||||
// 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 {
|
||||
console.log("[RecoveryRedirect] ℹ️ Nenhum token de recovery detectado");
|
||||
}
|
||||
|
||||
@ -87,9 +87,9 @@ const AcompanhamentoPaciente: React.FC = () => {
|
||||
const testAvatar = async () => {
|
||||
for (const ext of extensions) {
|
||||
try {
|
||||
const url = avatarService.getPublicUrl({
|
||||
userId: user.id,
|
||||
ext: ext as "jpg" | "png" | "webp"
|
||||
const url = avatarService.getPublicUrl({
|
||||
userId: user.id,
|
||||
ext: ext as "jpg" | "png" | "webp",
|
||||
});
|
||||
const response = await fetch(url, { method: "HEAD" });
|
||||
if (response.ok) {
|
||||
|
||||
@ -22,23 +22,25 @@ export default function AuthCallback() {
|
||||
const handleCallback = async () => {
|
||||
try {
|
||||
console.log("[AuthCallback] Iniciando processamento");
|
||||
|
||||
|
||||
// Verificar se é um token de recovery
|
||||
const hash = window.location.hash;
|
||||
const hashParams = new URLSearchParams(hash.substring(1));
|
||||
const accessToken = hashParams.get("access_token");
|
||||
const type = hashParams.get("type");
|
||||
|
||||
|
||||
console.log("[AuthCallback] Hash:", hash);
|
||||
console.log("[AuthCallback] Type:", type);
|
||||
console.log("[AuthCallback] Access Token presente:", !!accessToken);
|
||||
|
||||
// Se for recovery, redirecionar para página de reset
|
||||
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");
|
||||
setMessage("Redirecionando para página de redefinição de senha...");
|
||||
|
||||
|
||||
// Redirecionar preservando o hash com o token
|
||||
setTimeout(() => {
|
||||
navigate(`/reset-password${hash}`, { replace: true });
|
||||
@ -81,20 +83,32 @@ export default function AuthCallback() {
|
||||
setMessage("Autenticado com sucesso! Redirecionando...");
|
||||
toast.success("Login realizado com sucesso!");
|
||||
|
||||
// Redirecionar baseado no role
|
||||
// Redirecionar baseado no contexto salvo ou role do usuário
|
||||
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";
|
||||
console.log("[AuthCallback] Redirecionando baseado no role:", userRole);
|
||||
|
||||
switch (userRole) {
|
||||
case "medico":
|
||||
navigate("/painel-medico");
|
||||
navigate("/painel-medico", { replace: true });
|
||||
break;
|
||||
case "secretaria":
|
||||
navigate("/painel-secretaria");
|
||||
navigate("/painel-secretaria", { replace: true });
|
||||
break;
|
||||
case "paciente":
|
||||
default:
|
||||
navigate("/acompanhamento");
|
||||
navigate("/acompanhamento", { replace: true });
|
||||
break;
|
||||
}
|
||||
}, 1500);
|
||||
|
||||
@ -100,7 +100,7 @@ const Home: React.FC = () => {
|
||||
<div className="space-y-8" id="main-content">
|
||||
{/* Componente invisível que detecta tokens de recuperação e redireciona */}
|
||||
<RecoveryRedirect />
|
||||
|
||||
|
||||
{/* Hero Section com Background Rotativo */}
|
||||
<HeroBanner />
|
||||
|
||||
|
||||
@ -202,9 +202,12 @@ const LoginMedico: React.FC = () => {
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
// Salvar contexto para redirecionamento correto após magic link
|
||||
localStorage.setItem("magic_link_redirect", "/painel-medico");
|
||||
|
||||
await authService.sendMagicLink(
|
||||
formData.email,
|
||||
"https://mediconnectbrasil.netlify.app/medico/painel"
|
||||
`${window.location.origin}/auth/callback`
|
||||
);
|
||||
toast.success(
|
||||
"Link de acesso enviado para seu email! Verifique sua caixa de entrada.",
|
||||
|
||||
@ -312,9 +312,12 @@ const LoginPaciente: React.FC = () => {
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
// Salvar contexto para redirecionamento correto após magic link
|
||||
localStorage.setItem("magic_link_redirect", "/acompanhamento");
|
||||
|
||||
await authService.sendMagicLink(
|
||||
formData.email,
|
||||
"https://mediconnectbrasil.netlify.app/paciente/agendamento"
|
||||
`${window.location.origin}/auth/callback`
|
||||
);
|
||||
toast.success(
|
||||
"Link de acesso enviado para seu email! Verifique sua caixa de entrada.",
|
||||
|
||||
@ -212,9 +212,12 @@ const LoginSecretaria: React.FC = () => {
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
// Salvar contexto para redirecionamento correto após magic link
|
||||
localStorage.setItem("magic_link_redirect", "/painel-secretaria");
|
||||
|
||||
await authService.sendMagicLink(
|
||||
formData.email,
|
||||
"https://mediconnectbrasil.netlify.app/secretaria/painel"
|
||||
`${window.location.origin}/auth/callback`
|
||||
);
|
||||
toast.success(
|
||||
"Link de acesso enviado para seu email! Verifique sua caixa de entrada.",
|
||||
|
||||
@ -109,9 +109,9 @@ const PainelMedico: React.FC = () => {
|
||||
const testAvatar = async () => {
|
||||
for (const ext of extensions) {
|
||||
try {
|
||||
const url = avatarService.getPublicUrl({
|
||||
userId: user.id,
|
||||
ext: ext as "jpg" | "png" | "webp"
|
||||
const url = avatarService.getPublicUrl({
|
||||
userId: user.id,
|
||||
ext: ext as "jpg" | "png" | "webp",
|
||||
});
|
||||
const response = await fetch(url, { method: "HEAD" });
|
||||
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="text-center">
|
||||
<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">
|
||||
Carregando perfil...
|
||||
</p>
|
||||
<p className="text-gray-600">Carregando perfil...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -204,9 +202,7 @@ export default function PerfilPaciente() {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen bg-gray-50">
|
||||
<div className="text-center">
|
||||
<p className="text-red-600 mb-4">
|
||||
Usuário não identificado
|
||||
</p>
|
||||
<p className="text-red-600 mb-4">Usuário não identificado</p>
|
||||
<button
|
||||
onClick={() => navigate("/paciente")}
|
||||
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 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">
|
||||
Meu Perfil
|
||||
</h1>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Meu Perfil</h1>
|
||||
<p className="text-gray-600">
|
||||
Gerencie suas informações pessoais e médicas
|
||||
</p>
|
||||
@ -286,9 +280,7 @@ export default function PerfilPaciente() {
|
||||
<p className="font-medium text-gray-900">
|
||||
{formData.full_name || "Carregando..."}
|
||||
</p>
|
||||
<p className="text-gray-500">
|
||||
{formData.email || "Sem email"}
|
||||
</p>
|
||||
<p className="text-gray-500">{formData.email || "Sem email"}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -688,4 +680,3 @@ export default function PerfilPaciente() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ const ResetPassword: React.FC = () => {
|
||||
useEffect(() => {
|
||||
// Extrair access_token do hash da URL (#access_token=...)
|
||||
// OU do query string (?token=...&type=recovery)
|
||||
|
||||
|
||||
let token: string | null = null;
|
||||
let type: string | null = null;
|
||||
|
||||
@ -28,8 +28,11 @@ const ResetPassword: React.FC = () => {
|
||||
const hashParams = new URLSearchParams(hash.substring(1));
|
||||
token = hashParams.get("access_token");
|
||||
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);
|
||||
}
|
||||
|
||||
@ -37,23 +40,30 @@ const ResetPassword: React.FC = () => {
|
||||
if (!token) {
|
||||
const search = window.location.search;
|
||||
console.log("[ResetPassword] Query string completo:", search);
|
||||
|
||||
|
||||
if (search) {
|
||||
const queryParams = new URLSearchParams(search);
|
||||
token = queryParams.get("token");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (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);
|
||||
} 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);
|
||||
toast.error("Link de recuperação inválido ou expirado");
|
||||
setTimeout(() => navigate("/"), 3000);
|
||||
@ -106,24 +116,24 @@ const ResetPassword: React.FC = () => {
|
||||
} catch (error: unknown) {
|
||||
console.error("[ResetPassword] Erro ao atualizar senha:", error);
|
||||
const err = error as {
|
||||
response?: {
|
||||
data?: {
|
||||
error_description?: string;
|
||||
response?: {
|
||||
data?: {
|
||||
error_description?: string;
|
||||
message?: string;
|
||||
msg?: string;
|
||||
error_code?: string;
|
||||
}
|
||||
};
|
||||
};
|
||||
message?: string;
|
||||
};
|
||||
|
||||
|
||||
// Mensagem específica para senha igual
|
||||
if (err?.response?.data?.error_code === "same_password") {
|
||||
toast.error("A nova senha deve ser diferente da senha atual");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const errorMessage =
|
||||
err?.response?.data?.msg ||
|
||||
err?.response?.data?.error_description ||
|
||||
|
||||
@ -154,7 +154,7 @@ class AuthService {
|
||||
/**
|
||||
* Solicita reset de senha via email (público)
|
||||
* POST /auth/v1/recover
|
||||
*
|
||||
*
|
||||
* Envia redirectTo dentro de options para controlar para onde o Supabase redireciona
|
||||
*/
|
||||
async requestPasswordReset(
|
||||
@ -162,40 +162,50 @@ class AuthService {
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
const redirectUrl = `${API_CONFIG.APP_URL}/reset-password`;
|
||||
|
||||
|
||||
console.log("[authService.requestPasswordReset] Email:", email);
|
||||
console.log("[authService.requestPasswordReset] Redirect URL:", redirectUrl);
|
||||
console.log("[authService.requestPasswordReset] Usando endpoint /auth/v1/recover");
|
||||
|
||||
console.log(
|
||||
"[authService.requestPasswordReset] Redirect URL:",
|
||||
redirectUrl
|
||||
);
|
||||
console.log(
|
||||
"[authService.requestPasswordReset] Usando endpoint /auth/v1/recover"
|
||||
);
|
||||
|
||||
const payload = {
|
||||
email,
|
||||
options: {
|
||||
redirectTo: redirectUrl,
|
||||
},
|
||||
};
|
||||
|
||||
console.log("[authService.requestPasswordReset] Payload:", JSON.stringify(payload, null, 2));
|
||||
|
||||
await axios.post(
|
||||
`${API_CONFIG.AUTH_URL}/recover`,
|
||||
payload,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
apikey: API_CONFIG.SUPABASE_ANON_KEY,
|
||||
},
|
||||
}
|
||||
|
||||
console.log(
|
||||
"[authService.requestPasswordReset] Payload:",
|
||||
JSON.stringify(payload, null, 2)
|
||||
);
|
||||
|
||||
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 {
|
||||
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) {
|
||||
console.error("[authService.requestPasswordReset] ❌ Erro:", error);
|
||||
console.error("[authService.requestPasswordReset] Response:", error.response?.data);
|
||||
console.error(
|
||||
"[authService.requestPasswordReset] Response:",
|
||||
error.response?.data
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -209,10 +219,21 @@ class AuthService {
|
||||
newPassword: string
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
console.log("[authService.updatePassword] Iniciando atualização de senha");
|
||||
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`);
|
||||
console.log(
|
||||
"[authService.updatePassword] Iniciando atualização de senha"
|
||||
);
|
||||
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(
|
||||
`${API_CONFIG.AUTH_URL}/user`,
|
||||
@ -228,35 +249,76 @@ class AuthService {
|
||||
}
|
||||
);
|
||||
|
||||
console.log("[authService.updatePassword] ✅ Senha atualizada com sucesso");
|
||||
console.log("[authService.updatePassword] Response status:", response.status);
|
||||
console.log(
|
||||
"[authService.updatePassword] ✅ Senha atualizada com sucesso"
|
||||
);
|
||||
console.log(
|
||||
"[authService.updatePassword] Response status:",
|
||||
response.status
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Senha atualizada com sucesso",
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("[authService.updatePassword] ❌ Erro ao atualizar senha:", error);
|
||||
console.error("[authService.updatePassword] Error response:", error.response?.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);
|
||||
|
||||
console.error(
|
||||
"[authService.updatePassword] ❌ Erro ao atualizar senha:",
|
||||
error
|
||||
);
|
||||
console.error(
|
||||
"[authService.updatePassword] Error response:",
|
||||
error.response?.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
|
||||
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";
|
||||
console.error("[authService.updatePassword] ⚠️ Erro 422 - Possíveis causas:");
|
||||
const errorMsg =
|
||||
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(" 2. Token já foi usado");
|
||||
console.error(" 3. Formato do token incorreto");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,11 +22,11 @@ class AvatarService {
|
||||
async upload(data: UploadAvatarInput): Promise<UploadAvatarResponse> {
|
||||
try {
|
||||
const token = localStorage.getItem(API_CONFIG.STORAGE_KEYS.ACCESS_TOKEN);
|
||||
|
||||
|
||||
if (!token) {
|
||||
throw new Error("Token de autenticação não encontrado");
|
||||
}
|
||||
|
||||
|
||||
// Determina a extensão do arquivo
|
||||
const ext = data.file.name.split(".").pop()?.toLowerCase() || "jpg";
|
||||
const filePath = `${data.userId}/avatar.${ext}`;
|
||||
@ -46,14 +46,14 @@ class AvatarService {
|
||||
|
||||
// Upload usando Supabase Storage API
|
||||
// x-upsert: true permite sobrescrever arquivos existentes
|
||||
// Importante: NÃO definir Content-Type manualmente, deixar o axios/navegador
|
||||
// Importante: NÃO definir Content-Type manualmente, deixar o axios/navegador
|
||||
// definir automaticamente com o boundary correto para multipart/form-data
|
||||
const response = await axios.post(
|
||||
`${this.STORAGE_URL}/${this.BUCKET_NAME}/${filePath}`,
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Bearer ${token}`,
|
||||
Authorization: `Bearer ${token}`,
|
||||
"x-upsert": "true",
|
||||
},
|
||||
}
|
||||
@ -91,7 +91,9 @@ class AvatarService {
|
||||
try {
|
||||
// Não há endpoint de delete, então apenas removemos a referência do perfil
|
||||
// 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) {
|
||||
console.error("Erro ao deletar avatar:", error);
|
||||
throw error;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user