riseup-squad20/IMPLEMENTACAO_OPCAO2_VINCULO_EMAIL.md

9.2 KiB
Raw Blame History

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

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

// 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

// 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

// 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)

{
  "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

{
  "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