371 lines
9.2 KiB
Markdown
371 lines
9.2 KiB
Markdown
# ✅ Implementação: Opção 2 - Vínculo por Email
|
||
|
||
## 🎯 Solução Implementada
|
||
|
||
**Vínculo entre Supabase Auth e API Mock através do EMAIL**
|
||
|
||
```
|
||
┌──────────────────────────┐
|
||
│ Supabase Auth │
|
||
│ (Login/Autenticação) │
|
||
│ │
|
||
│ email: "user@email.com"│ ◄─┐
|
||
│ password: "senha123!" │ │
|
||
│ userType: "paciente" │ │
|
||
└──────────────────────────┘ │
|
||
│ VÍNCULO
|
||
┌──────────────────────────┐ │ POR EMAIL
|
||
│ API Mock (Apidog) │ │
|
||
│ (Dados do Sistema) │ │
|
||
│ │ │
|
||
│ email: "user@email.com"│ ◄─┘
|
||
│ full_name: "João Silva"│
|
||
│ cpf: "123.456.789-00" │
|
||
└──────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 Código Implementado
|
||
|
||
### `lib/api.ts` - Funções de Criação de Usuários
|
||
|
||
```typescript
|
||
import { ENV_CONFIG } from '@/lib/env-config';
|
||
import { API_KEY } from '@/lib/config';
|
||
|
||
// Gera senha aleatória (formato: senhaXXX!)
|
||
export function gerarSenhaAleatoria(): string {
|
||
const num1 = Math.floor(Math.random() * 10);
|
||
const num2 = Math.floor(Math.random() * 10);
|
||
const num3 = Math.floor(Math.random() * 10);
|
||
return `senha${num1}${num2}${num3}!`;
|
||
}
|
||
|
||
// Cria usuário MÉDICO no Supabase Auth
|
||
export async function criarUsuarioMedico(medico: {
|
||
email: string;
|
||
full_name: string;
|
||
phone_mobile: string;
|
||
}): Promise<CreateUserWithPasswordResponse> {
|
||
const senha = gerarSenhaAleatoria();
|
||
|
||
// Endpoint do Supabase Auth (mesmo que auth.ts usa)
|
||
const signupUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signup`;
|
||
|
||
const payload = {
|
||
email: medico.email, // ◄── VÍNCULO!
|
||
password: senha,
|
||
data: {
|
||
userType: 'profissional',
|
||
full_name: medico.full_name,
|
||
phone: medico.phone_mobile,
|
||
},
|
||
};
|
||
|
||
const response = await fetch(signupUrl, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
apikey: API_KEY,
|
||
},
|
||
body: JSON.stringify(payload),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorText = await response.text();
|
||
throw new Error(`Erro ao criar usuário: ${response.status}`);
|
||
}
|
||
|
||
const responseData = await response.json();
|
||
|
||
return {
|
||
success: true,
|
||
user: responseData.user,
|
||
email: medico.email,
|
||
password: senha,
|
||
};
|
||
}
|
||
|
||
// Cria usuário PACIENTE no Supabase Auth
|
||
export async function criarUsuarioPaciente(paciente: {
|
||
email: string;
|
||
full_name: string;
|
||
phone_mobile: string;
|
||
}): Promise<CreateUserWithPasswordResponse> {
|
||
const senha = gerarSenhaAleatoria();
|
||
|
||
const signupUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signup`;
|
||
|
||
const payload = {
|
||
email: paciente.email, // ◄── VÍNCULO!
|
||
password: senha,
|
||
data: {
|
||
userType: 'paciente',
|
||
full_name: paciente.full_name,
|
||
phone: paciente.phone_mobile,
|
||
},
|
||
};
|
||
|
||
const response = await fetch(signupUrl, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
apikey: API_KEY,
|
||
},
|
||
body: JSON.stringify(payload),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorText = await response.text();
|
||
throw new Error(`Erro ao criar usuário: ${response.status}`);
|
||
}
|
||
|
||
const responseData = await response.json();
|
||
|
||
return {
|
||
success: true,
|
||
user: responseData.user,
|
||
email: paciente.email,
|
||
password: senha,
|
||
};
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔄 Fluxo Completo
|
||
|
||
### 1️⃣ **Admin Cadastra Paciente**
|
||
|
||
```typescript
|
||
// components/forms/patient-registration-form.tsx
|
||
|
||
async function handleSubmit() {
|
||
// 1. Salva paciente na API Mock
|
||
const saved = await salvarPaciente({
|
||
full_name: form.nome,
|
||
email: form.email, // ◄── EMAIL usado como vínculo
|
||
cpf: form.cpf,
|
||
telefone: form.telefone,
|
||
// ...outros dados
|
||
});
|
||
|
||
// 2. Cria usuário no Supabase Auth com MESMO EMAIL
|
||
if (mode === 'create' && form.email) {
|
||
try {
|
||
const credentials = await criarUsuarioPaciente({
|
||
email: form.email, // ◄── MESMO EMAIL!
|
||
full_name: form.nome,
|
||
phone_mobile: form.telefone,
|
||
});
|
||
|
||
// 3. Mostra popup com credenciais
|
||
setCredentials(credentials);
|
||
setShowCredentials(true);
|
||
} catch (error) {
|
||
alert('Paciente cadastrado, mas houve erro ao criar usuário de acesso');
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2️⃣ **Paciente Faz Login**
|
||
|
||
```typescript
|
||
// Paciente vai em /login-paciente
|
||
// Digita: email = "jonas@email.com", password = "senha481!"
|
||
|
||
// hooks/useAuth.tsx
|
||
const login = async (email, password, userType) => {
|
||
// Autentica no Supabase Auth
|
||
const response = await loginUser(email, password, userType);
|
||
|
||
// Token JWT contém o email
|
||
const token = response.access_token;
|
||
const decoded = decodeJWT(token);
|
||
console.log(decoded.email); // "jonas@email.com"
|
||
|
||
// Salva sessão
|
||
localStorage.setItem('token', token);
|
||
|
||
// Redireciona para /paciente
|
||
router.push('/paciente');
|
||
};
|
||
```
|
||
|
||
### 3️⃣ **Buscar Dados do Paciente na Área Logada**
|
||
|
||
```typescript
|
||
// app/paciente/page.tsx
|
||
|
||
export default function PacientePage() {
|
||
const { user } = useAuth(); // user.email = "jonas@email.com"
|
||
|
||
useEffect(() => {
|
||
async function carregarDados() {
|
||
// Busca paciente pelo EMAIL (vínculo)
|
||
const response = await fetch(
|
||
`https://mock.apidog.com/pacientes?email=${user.email}`
|
||
);
|
||
const paciente = await response.json();
|
||
|
||
// Agora tem os dados completos do paciente
|
||
console.log(paciente);
|
||
// {
|
||
// id: "123",
|
||
// full_name: "Jonas Francisco",
|
||
// email: "jonas@email.com", ◄── VÍNCULO!
|
||
// cpf: "123.456.789-00",
|
||
// ...
|
||
// }
|
||
}
|
||
|
||
carregarDados();
|
||
}, [user.email]);
|
||
|
||
return <div>Bem-vindo, {user.email}!</div>;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔐 Estrutura dos Dados
|
||
|
||
### **Supabase Auth (`auth.users`)**
|
||
|
||
```json
|
||
{
|
||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||
"email": "jonas@email.com",
|
||
"encrypted_password": "$2a$10$...",
|
||
"created_at": "2025-10-03T00:00:00",
|
||
"user_metadata": {
|
||
"userType": "paciente",
|
||
"full_name": "Jonas Francisco",
|
||
"phone": "(79) 99649-8907"
|
||
}
|
||
}
|
||
```
|
||
|
||
### **API Mock - Tabela `pacientes`**
|
||
|
||
```json
|
||
{
|
||
"id": "123",
|
||
"full_name": "Jonas Francisco Nascimento Bonfim",
|
||
"email": "jonas@email.com",
|
||
"cpf": "123.456.789-00",
|
||
"telefone": "(79) 99649-8907",
|
||
"data_nascimento": "1990-01-15",
|
||
"endereco": {
|
||
"cep": "49000-000",
|
||
"logradouro": "Rua Principal",
|
||
"cidade": "Aracaju"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Vínculo:** Campo `email` presente em ambos os sistemas!
|
||
|
||
---
|
||
|
||
## 📊 Vantagens da Opção 2
|
||
|
||
✅ **Simples:** Não precisa modificar estrutura da API Mock
|
||
✅ **Natural:** Email já é único e obrigatório
|
||
✅ **Sem duplicação:** Usa campo existente
|
||
✅ **Funcional:** Supabase Auth retorna email no token JWT
|
||
✅ **Escalável:** Fácil de manter e debugar
|
||
|
||
---
|
||
|
||
## ⚠️ Considerações Importantes
|
||
|
||
### **Email DEVE ser único**
|
||
|
||
- Cada email só pode ter um usuário no Supabase Auth
|
||
- Cada email só pode ter um paciente/médico na API Mock
|
||
- Se tentar cadastrar email duplicado, Supabase retorna erro
|
||
|
||
### **Email DEVE ser válido**
|
||
|
||
- Supabase valida formato (nome@dominio.com)
|
||
- Use validação no formulário antes de enviar
|
||
|
||
### **Formato da senha**
|
||
|
||
- Supabase exige mínimo 6 caracteres
|
||
- Geramos: `senhaXXX!` (10 caracteres) ✅
|
||
|
||
---
|
||
|
||
## 🧪 Logs Esperados (Sucesso)
|
||
|
||
```
|
||
🏥 [CRIAR PACIENTE] Iniciando criação no Supabase Auth...
|
||
📧 Email: jonas@email.com
|
||
👤 Nome: Jonas Francisco Nascimento Bonfim
|
||
📱 Telefone: (79) 99649-8907
|
||
🔑 Senha gerada: senha481!
|
||
📤 [CRIAR PACIENTE] Enviando para: https://yuanqfswhberkoevtmfr.supabase.co/auth/v1/signup
|
||
📋 [CRIAR PACIENTE] Status da resposta: 200 OK
|
||
✅ [CRIAR PACIENTE] Usuário criado com sucesso no Supabase Auth!
|
||
🆔 User ID: 550e8400-e29b-41d4-a716-446655440000
|
||
```
|
||
|
||
---
|
||
|
||
## ❌ Possíveis Erros
|
||
|
||
### **Erro: "Este email já está cadastrado no sistema"**
|
||
|
||
```
|
||
❌ [CRIAR PACIENTE] Erro: User already registered
|
||
```
|
||
|
||
**Solução:** Email já existe no Supabase. Use outro email ou delete o usuário existente.
|
||
|
||
### **Erro: "Formato de email inválido"**
|
||
|
||
```
|
||
❌ [CRIAR PACIENTE] Erro: Invalid email format
|
||
```
|
||
|
||
**Solução:** Valide o formato do email antes de enviar.
|
||
|
||
### **Erro: 429 - Too Many Requests**
|
||
|
||
```
|
||
❌ [CRIAR PACIENTE] Erro: 429
|
||
```
|
||
|
||
**Solução:** Aguarde 1 minuto. Supabase limita taxa de requisições.
|
||
|
||
---
|
||
|
||
## ✅ Resultado Final
|
||
|
||
**Agora o sistema funciona assim:**
|
||
|
||
1. ✅ Admin cadastra paciente → Salva na API Mock
|
||
2. ✅ Sistema cria usuário no Supabase Auth (mesmo email)
|
||
3. ✅ Popup mostra credenciais (email + senha)
|
||
4. ✅ Paciente faz login em `/login-paciente`
|
||
5. ✅ Login funciona! Token JWT é gerado
|
||
6. ✅ Sistema busca dados do paciente pelo email
|
||
7. ✅ Paciente acessa área `/paciente` com todos os dados
|
||
|
||
---
|
||
|
||
## 📅 Data da Implementação
|
||
|
||
3 de outubro de 2025
|
||
|
||
## 🔗 Arquivos Modificados
|
||
|
||
- ✅ `susconecta/lib/api.ts` - Funções de criação (Supabase Auth)
|
||
- ✅ `susconecta/components/forms/patient-registration-form.tsx` - Já integrado
|
||
- ✅ `susconecta/components/forms/doctor-registration-form.tsx` - Já integrado
|
||
- ✅ `susconecta/components/credentials-dialog.tsx` - Já implementado
|