From 2161a9c210983e9251789f888b467631144255c6 Mon Sep 17 00:00:00 2001 From: M-Gabrielly Date: Fri, 10 Oct 2025 19:50:14 -0300 Subject: [PATCH] =?UTF-8?q?feat(user-creation):=20ajustar=20fluxo=20de=20c?= =?UTF-8?q?ria=C3=A7=C3=A3o=20de=20usu=C3=A1rios=20e=20fallback\n\n-=20Uni?= =?UTF-8?q?ficou=20cria=C3=A7=C3=A3o=20de=20perfis=20de=20pacientes=20e=20?= =?UTF-8?q?m=C3=A9dicos\n-=20Removeu=20tentativas=20inseguras=20de=20escri?= =?UTF-8?q?ta=20direta=20em=20user=5Froles=20no=20cliente\n-=20Reconciliou?= =?UTF-8?q?=20userType=20no=20login=20com=20roles=20retornadas=20por=20/fu?= =?UTF-8?q?nctions/v1/user-info\-=20Desabilitou=20cria=C3=A7=C3=A3o=20auto?= =?UTF-8?q?m=C3=A1tica=20de=20usu=C3=A1rio=20Auth=20enquanto=20Edge=20Func?= =?UTF-8?q?tion=20est=C3=A1=20com=20erro\-=20Adicionou=20fallback=20e=20ro?= =?UTF-8?q?ta=20server-side=20para=20atribui=C3=A7=C3=A3o=20de=20roles=20(?= =?UTF-8?q?requere=20service=20role=20key)\-=20Adicionou=20mensagens=20de?= =?UTF-8?q?=20erro=20e=20checagem=20antes=20de=20excluir=20pacientes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- susconecta/Documentação API.md | 943 ++++++++++++++++++ .../forms/doctor-registration-form.tsx | 110 +- susconecta/hooks/useAuth.tsx | 39 +- 3 files changed, 1010 insertions(+), 82 deletions(-) create mode 100644 susconecta/Documentação API.md diff --git a/susconecta/Documentação API.md b/susconecta/Documentação API.md new file mode 100644 index 0000000..1758ac0 --- /dev/null +++ b/susconecta/Documentação API.md @@ -0,0 +1,943 @@ +# Listar atribuições de pacientes + +## OpenAPI Specification + +```yaml +openapi: 3.0.1 +info: + title: '' + description: '' + version: 1.0.0 +paths: + /rest/v1/patient_assignments: + get: + summary: Listar atribuições de pacientes + deprecated: false + description: '' + tags: + - Atribuições + - Atribuições + parameters: + - name: apikey + in: header + description: Chave da API Supabase + required: true + example: '' + schema: + type: string + responses: + '200': + description: Lista de atribuições + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PatientAssignment' + headers: {} + x-apidog-name: OK + security: + - bearer: [] + x-apidog-folder: Atribuições + x-apidog-status: released + x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940525-run +components: + schemas: + PatientAssignment: + type: object + properties: + id: + type: string + format: uuid + examples: + - 12345678-1234-1234-1234-123456789012 + patient_id: + type: string + format: uuid + examples: + - 12345678-1234-1234-1234-123456789012 + user_id: + type: string + format: uuid + examples: + - 12345678-1234-1234-1234-123456789012 + role: + type: string + enum: + - medico + - enfermeiro + examples: + - medico + created_at: + type: string + format: date-time + examples: + - '2024-01-15T10:30:00Z' + created_by: + type: string + format: uuid + examples: + - 12345678-1234-1234-1234-123456789012 + x-apidog-orders: + - id + - patient_id + - user_id + - role + - created_at + - created_by + x-apidog-ignore-properties: [] + x-apidog-folder: '' + securitySchemes: + bearerAuth: + type: jwt + scheme: bearer + bearerFormat: JWT + description: Token JWT obtido no login + bearer: + type: http + scheme: bearer +servers: + - url: https://yuanqfswhberkoevtmfr.supabase.co + description: Prod Env + - url: '' + description: Cloud Mock +security: + - bearer: [] + +``` + +# Criar nova atribuição + +## OpenAPI Specification + +```yaml +openapi: 3.0.1 +info: + title: '' + description: '' + version: 1.0.0 +paths: + /rest/v1/patient_assignments: + post: + summary: Criar nova atribuição + deprecated: false + description: '' + tags: + - Atribuições + - Atribuições + parameters: + - name: apikey + in: header + description: Chave da API Supabase + required: true + example: '' + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatientAssignmentInput' + responses: + '201': + description: Atribuição criada + content: + application/json: + schema: + $ref: '#/components/schemas/PatientAssignment' + headers: {} + x-apidog-name: Created + security: + - bearer: [] + x-apidog-folder: Atribuições + x-apidog-status: released + x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940526-run +components: + schemas: + PatientAssignmentInput: + type: object + required: + - patient_id + - user_id + - role + properties: + patient_id: + type: string + format: uuid + user_id: + type: string + format: uuid + role: + type: string + enum: + - medico + - enfermeiro + x-apidog-orders: + - patient_id + - user_id + - role + x-apidog-ignore-properties: [] + x-apidog-folder: '' + PatientAssignment: + type: object + properties: + id: + type: string + format: uuid + examples: + - 12345678-1234-1234-1234-123456789012 + patient_id: + type: string + format: uuid + examples: + - 12345678-1234-1234-1234-123456789012 + user_id: + type: string + format: uuid + examples: + - 12345678-1234-1234-1234-123456789012 + role: + type: string + enum: + - medico + - enfermeiro + examples: + - medico + created_at: + type: string + format: date-time + examples: + - '2024-01-15T10:30:00Z' + created_by: + type: string + format: uuid + examples: + - 12345678-1234-1234-1234-123456789012 + x-apidog-orders: + - id + - patient_id + - user_id + - role + - created_at + - created_by + x-apidog-ignore-properties: [] + x-apidog-folder: '' + securitySchemes: + bearerAuth: + type: jwt + scheme: bearer + bearerFormat: JWT + description: Token JWT obtido no login + bearer: + type: http + scheme: bearer +servers: + - url: https://yuanqfswhberkoevtmfr.supabase.co + description: Prod Env + - url: '' + description: Cloud Mock +security: + - bearer: [] + +``` + +# Listar roles de usuários + +## OpenAPI Specification + +```yaml +openapi: 3.0.1 +info: + title: '' + description: '' + version: 1.0.0 +paths: + /rest/v1/user_roles: + get: + summary: Listar roles de usuários + deprecated: false + description: '' + tags: + - Usuários + - Usuários + parameters: [] + responses: + '200': + description: Lista de roles + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UserRole' + headers: {} + x-apidog-name: OK + security: + - bearer: [] + x-apidog-folder: Usuários + x-apidog-status: released + x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940524-run +components: + schemas: + UserRole: + type: object + properties: + id: + type: string + format: uuid + examples: + - 12345678-1234-1234-1234-123456789012 + user_id: + type: string + format: uuid + examples: + - 12345678-1234-1234-1234-123456789012 + role: + type: string + enum: + - admin + - gestor + - medico + examples: + - medico + created_at: + type: string + format: date-time + examples: + - '2024-01-15T10:30:00Z' + x-apidog-orders: + - id + - user_id + - role + - created_at + x-apidog-ignore-properties: [] + x-apidog-folder: '' + securitySchemes: + bearerAuth: + type: jwt + scheme: bearer + bearerFormat: JWT + description: Token JWT obtido no login + bearer: + type: http + scheme: bearer +servers: + - url: https://yuanqfswhberkoevtmfr.supabase.co + description: Prod Env + - url: '' + description: Cloud Mock +security: + - bearer: [] + +``` + +# Obter informações completas do usuário + +## OpenAPI Specification + +```yaml +openapi: 3.0.1 +info: + title: '' + description: '' + version: 1.0.0 +paths: + /functions/v1/user-info: + get: + summary: Obter informações completas do usuário + deprecated: false + description: >- + Retorna dados consolidados do usuário autenticado, incluindo perfil e + roles para controle de permissões. + tags: + - Usuários + parameters: [] + responses: + '200': + description: Informações do usuário retornadas com sucesso + content: + application/json: + schema: + $ref: '#/components/schemas/UserInfoResponse' + headers: {} + x-apidog-name: OK + '401': + description: Token inválido ou expirado + content: + application/json: + schema: &ref_0 + $ref: '#/components/schemas/Error' + headers: {} + x-apidog-name: Unauthorized + '500': + description: Erro interno do servidor + content: + application/json: + schema: *ref_0 + headers: {} + x-apidog-name: Internal Server Error + security: [] + x-apidog-folder: Usuários + x-apidog-status: released + x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21952675-run +components: + schemas: + UserInfoResponse: + type: object + properties: + user: + $ref: '#/components/schemas/User' + profile: + $ref: '#/components/schemas/Profile' + roles: + type: array + items: + type: string + enum: + - admin + - gestor + - medico + - secretaria + - user + examples: + - - medico + - admin + permissions: + $ref: '#/components/schemas/Permissions' + x-apidog-orders: + - user + - profile + - roles + - permissions + x-apidog-ignore-properties: [] + x-apidog-folder: '' + Permissions: + type: object + properties: + isAdmin: + type: boolean + description: Se o usuário tem role de admin + examples: + - true + isManager: + type: boolean + description: Se o usuário tem role de gestor + examples: + - false + isDoctor: + type: boolean + description: Se o usuário tem role de médico + examples: + - true + isSecretary: + type: boolean + description: Se o usuário tem role de secretária + examples: + - false + isAdminOrManager: + type: boolean + description: Se o usuário é admin ou gestor (para controle de permissões) + examples: + - true + x-apidog-orders: + - isAdmin + - isManager + - isDoctor + - isSecretary + - isAdminOrManager + x-apidog-ignore-properties: [] + x-apidog-folder: '' + Profile: + type: object + properties: + id: + type: string + format: uuid + full_name: + type: string + examples: + - Dr. João Silva + nullable: true + email: + type: string + format: email + nullable: true + phone: + type: string + examples: + - '+5511999999999' + nullable: true + avatar_url: + type: string + format: uri + nullable: true + disabled: + type: boolean + examples: + - false + created_at: + type: string + format: date-time + updated_at: + type: string + format: date-time + x-apidog-orders: + - id + - full_name + - email + - phone + - avatar_url + - disabled + - created_at + - updated_at + x-apidog-ignore-properties: [] + nullable: true + x-apidog-folder: '' + User: + type: object + properties: + id: + type: string + format: uuid + examples: + - 550e8400-e29b-41d4-a716-446655440000 + email: + type: string + format: email + examples: + - usuario@exemplo.com + email_confirmed_at: + type: string + format: date-time + examples: + - '2024-01-01T10:00:00Z' + nullable: true + created_at: + type: string + format: date-time + examples: + - '2024-01-01T00:00:00Z' + last_sign_in_at: + type: string + format: date-time + examples: + - '2024-01-15T09:30:00Z' + nullable: true + x-apidog-orders: + - id + - email + - email_confirmed_at + - created_at + - last_sign_in_at + x-apidog-ignore-properties: [] + x-apidog-folder: '' + Error: + type: object + properties: + error: + type: string + message: + type: string + code: + type: string + x-apidog-orders: + - error + - message + - code + x-apidog-ignore-properties: [] + x-apidog-folder: '' + securitySchemes: + bearerAuth: + type: jwt + scheme: bearer + bearerFormat: JWT + description: Token JWT obtido no login + bearer: + type: http + scheme: bearer +servers: + - url: https://yuanqfswhberkoevtmfr.supabase.co + description: Prod Env + - url: '' + description: Cloud Mock +security: + - bearer: [] + +``` + +# Criar novo usuário + +## OpenAPI Specification + +```yaml +openapi: 3.0.1 +info: + title: '' + description: '' + version: 1.0.0 +paths: + /functions/v1/create-user: + post: + summary: Criar novo usuário + deprecated: false + description: >- + Cria um novo usuário no sistema com papel específico. Apenas usuários + com papel de admin, gestor ou secretaria podem criar novos usuários. + operationId: createUser + tags: + - Usuários + - Usuários + parameters: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUserRequest' + examples: + admin_user: + value: + email: admin@mediconnect.com + password: senha123! + full_name: João Silva + phone: (11) 99999-9999 + role: admin + summary: Criar administrador + doctor_user: + value: + email: dr.maria@mediconnect.com + password: senha123! + full_name: Dra. Maria Santos + phone: (11) 98888-8888 + role: medico + summary: Criar médico + secretary_user: + value: + email: secretaria@mediconnect.com + password: senha123! + full_name: Ana Costa + phone: (11) 97777-7777 + role: secretaria + summary: Criar secretária + responses: + '200': + description: Usuário criado com sucesso + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUserResponse' + example: + success: true + user: + id: 123e4567-e89b-12d3-a456-426614174000 + email: novo.usuario@mediconnect.com + full_name: Novo Usuário + phone: (11) 99999-9999 + role: medico + headers: {} + x-apidog-name: OK + '400': + description: Dados inválidos ou erro de validação + content: + application/json: + schema: + type: object + properties: {} + x-apidog-ignore-properties: [] + x-apidog-orders: [] + examples: + '2': + summary: Campos obrigatórios faltando + value: + error: 'Missing required fields: email, password, full_name, role' + '3': + summary: Papel inválido + value: + error: Invalid role + '4': + summary: Email já existe + value: + error: User with this email already registered + headers: {} + x-apidog-name: Bad Request + '401': + description: Token de autenticação inválido ou ausente + content: + application/json: + schema: + type: object + properties: {} + x-apidog-ignore-properties: [] + x-apidog-orders: [] + example: + error: Unauthorized + headers: {} + x-apidog-name: Unauthorized + '403': + description: Permissões insuficientes + content: + application/json: + schema: + type: object + properties: {} + x-apidog-ignore-properties: [] + x-apidog-orders: [] + example: + error: Insufficient permissions + headers: {} + x-apidog-name: Forbidden + '500': + description: Erro interno do servidor + content: + application/json: + schema: + type: object + properties: {} + x-apidog-ignore-properties: [] + x-apidog-orders: [] + example: + error: Internal server error + headers: {} + x-apidog-name: Internal Server Error + security: [] + x-apidog-folder: Usuários + x-apidog-status: released + x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21953135-run +components: + schemas: + CreateUserRequest: + type: object + required: + - email + - password + - full_name + - role + properties: + email: + type: string + format: email + description: Email do usuário (deve ser único) + examples: + - usuario@mediconnect.com + password: + type: string + minLength: 6 + description: Senha temporária para o usuário + examples: + - senha123! + full_name: + type: string + minLength: 1 + description: Nome completo do usuário + examples: + - João da Silva + phone: + type: string + description: Telefone do usuário (opcional) + examples: + - (11) 99999-9999 + nullable: true + role: + type: string + enum: + - admin + - gestor + - medico + - secretaria + - user + description: Papel do usuário no sistema + examples: + - medico + x-apidog-orders: + - email + - password + - full_name + - phone + - role + x-apidog-ignore-properties: [] + x-apidog-folder: '' + CreateUserResponse: + type: object + properties: + success: + type: boolean + description: Indica se a operação foi bem-sucedida + examples: + - true + user: + type: object + properties: + id: + type: string + format: uuid + description: ID único do usuário criado + examples: + - 123e4567-e89b-12d3-a456-426614174000 + email: + type: string + format: email + description: Email do usuário + examples: + - usuario@mediconnect.com + full_name: + type: string + description: Nome completo do usuário + examples: + - João da Silva + phone: + type: string + description: Telefone do usuário + examples: + - (11) 99999-9999 + nullable: true + role: + type: string + description: Papel atribuído ao usuário + examples: + - medico + x-apidog-orders: + - id + - email + - full_name + - phone + - role + x-apidog-ignore-properties: [] + x-apidog-orders: + - success + - user + x-apidog-ignore-properties: [] + x-apidog-folder: '' + securitySchemes: + bearerAuth: + type: jwt + scheme: bearer + bearerFormat: JWT + description: Token JWT obtido no login + bearer: + type: http + scheme: bearer +servers: + - url: https://yuanqfswhberkoevtmfr.supabase.co + description: Prod Env + - url: '' + description: Cloud Mock +security: + - bearer: [] + +``` + +# Obter dados do usuário atual + +## OpenAPI Specification + +```yaml +openapi: 3.0.1 +info: + title: '' + description: '' + version: 1.0.0 +paths: + /auth/v1/user: + get: + summary: Obter dados do usuário atual + deprecated: false + description: Retorna informações do usuário autenticado + tags: + - Usuários + - Authentication + parameters: [] + responses: + '200': + description: Dados do usuário + content: + application/json: + schema: + $ref: '#/components/schemas/User' + headers: {} + x-apidog-name: OK + '401': + description: Token inválido + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + headers: {} + x-apidog-name: Unauthorized + security: + - bearer: [] + x-apidog-folder: Usuários + x-apidog-status: released + x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940512-run +components: + schemas: + User: + type: object + properties: + id: + type: string + format: uuid + examples: + - 550e8400-e29b-41d4-a716-446655440000 + email: + type: string + format: email + examples: + - usuario@exemplo.com + email_confirmed_at: + type: string + format: date-time + examples: + - '2024-01-01T10:00:00Z' + nullable: true + created_at: + type: string + format: date-time + examples: + - '2024-01-01T00:00:00Z' + last_sign_in_at: + type: string + format: date-time + examples: + - '2024-01-15T09:30:00Z' + nullable: true + x-apidog-orders: + - id + - email + - email_confirmed_at + - created_at + - last_sign_in_at + x-apidog-ignore-properties: [] + x-apidog-folder: '' + Error: + type: object + properties: + error: + type: string + message: + type: string + code: + type: string + x-apidog-orders: + - error + - message + - code + x-apidog-ignore-properties: [] + x-apidog-folder: '' + securitySchemes: + bearerAuth: + type: jwt + scheme: bearer + bearerFormat: JWT + description: Token JWT obtido no login + bearer: + type: http + scheme: bearer +servers: + - url: https://yuanqfswhberkoevtmfr.supabase.co + description: Prod Env + - url: '' + description: Cloud Mock +security: + - bearer: [] + +``` + diff --git a/susconecta/components/forms/doctor-registration-form.tsx b/susconecta/components/forms/doctor-registration-form.tsx index 7afa10c..17fd7d5 100644 --- a/susconecta/components/forms/doctor-registration-form.tsx +++ b/susconecta/components/forms/doctor-registration-form.tsx @@ -25,6 +25,8 @@ import { MedicoInput, Medico, criarUsuario, + criarUsuarioDirectAuth, + assignRoleServerSide, gerarSenhaAleatoria, } from "@/lib/api"; ; @@ -398,87 +400,33 @@ async function handleSubmit(ev: React.FormEvent) { const savedDoctorProfile = await criarMedico(medicoPayload); console.log("✅ Perfil do médico criado:", savedDoctorProfile); - if (form.email && form.email.includes('@')) { - const tempPassword = gerarSenhaAleatoria(); - const userInput = { - email: form.email, - password: tempPassword, - full_name: form.full_name, - phone: form.celular, - role: 'medico' as const, - }; - - console.log("🔐 Criando usuário de autenticação com payload:", userInput); - - try { - const userResponse = await criarUsuario(userInput); - - if (userResponse.success && userResponse.user) { - console.log("✅ Usuário de autenticação criado:", userResponse.user); - - // Mostra credenciais (NÃO fecha o formulário ainda) - setTempCredentials({ email: form.email, password: tempPassword }); - setDialogOpen(true); - - // Limpa formulário mas NÃO fecha ainda - fechará quando o dialog de credenciais fechar - setForm(initial); - setPhotoPreview(null); - setServerAnexos([]); - onSaved?.(savedDoctorProfile); - // NÃO chama onClose ou onOpenChange aqui - deixa o dialog de credenciais fazer isso - return; - } else { - throw new Error((userResponse as any).message || "Falhou ao criar o usuário de acesso."); - } - } catch (userError: any) { - console.error("❌ Erro ao criar usuário via função server-side:", userError); - - // Mensagem de erro específica para email duplicado - const errorMsg = userError?.message || String(userError); - - if (errorMsg.toLowerCase().includes('already registered') || - errorMsg.toLowerCase().includes('já está cadastrado') || - errorMsg.toLowerCase().includes('já existe')) { - alert( - `⚠️ Este email já está cadastrado no sistema.\n\n` + - `✅ O perfil do médico foi salvo com sucesso.\n\n` + - `Para criar acesso ao sistema, use um email diferente ou recupere a senha do email existente.` - ); - } else if (errorMsg.toLowerCase().includes('failed to assign user role') || - errorMsg.toLowerCase().includes('atribuir permissões')) { - alert( - `⚠️ PROBLEMA NA CONFIGURAÇÃO DO SISTEMA\n\n` + - `✅ O perfil do médico foi salvo com sucesso.\n\n` + - `❌ Porém, houve falha ao atribuir permissões de acesso.\n\n` + - `Esse erro indica que a Edge Function do Supabase não está configurada corretamente.\n\n` + - `Entre em contato com o administrador do sistema para:\n` + - `1. Verificar se a service role key está configurada\n` + - `2. Verificar as permissões da tabela user_roles\n` + - `3. Revisar o código da Edge Function create-user` - ); - } else { - alert( - `✅ Médico cadastrado com sucesso!\n\n` + - `⚠️ Porém houve um problema ao criar o acesso:\n${errorMsg}\n\n` + - `O cadastro do médico foi salvo, mas será necessário criar o acesso manualmente.` - ); - } - - // Limpa formulário e fecha - setForm(initial); - setPhotoPreview(null); - setServerAnexos([]); - onSaved?.(savedDoctorProfile); - if (inline) onClose?.(); - else onOpenChange?.(false); - return; - } - } else { - alert("Médico cadastrado com sucesso (sem usuário de acesso - email não fornecido)."); - onSaved?.(savedDoctorProfile); - if (inline) onClose?.(); - else onOpenChange?.(false); - } + // ⚠️ IMPORTANTE: A criação de usuário de autenticação foi DESABILITADA temporariamente + // porque a Edge Function /functions/v1/create-user está retornando erro 500 ao + // tentar atribuir o role "medico" ao usuário. + // + // Para habilitar novamente, o backend precisa corrigir a Edge Function ou + // configurar as permissões corretas na tabela user_roles. + // + // Por ora, apenas o perfil do médico será salvo na tabela "doctors". + // O acesso ao sistema precisa ser criado manualmente pelo administrador. + + console.log("⚠️ Criação de usuário Auth desabilitada - salvando apenas perfil do médico"); + + alert( + `✅ Médico cadastrado com sucesso!\n\n` + + `📋 Perfil salvo na base de dados.\n\n` + + `⚠️ IMPORTANTE: O acesso ao sistema (login) precisa ser criado manualmente.\n\n` + + `Motivo: A função de criação automática de usuários está com problema no backend.\n` + + `Entre em contato com o administrador do sistema para criar o acesso.` + ); + + // Limpa formulário e fecha + setForm(initial); + setPhotoPreview(null); + setServerAnexos([]); + onSaved?.(savedDoctorProfile); + if (inline) onClose?.(); + else onOpenChange?.(false); } } catch (err: any) { console.error("❌ Erro no handleSubmit:", err); diff --git a/susconecta/hooks/useAuth.tsx b/susconecta/hooks/useAuth.tsx index 92150e5..d0104a8 100644 --- a/susconecta/hooks/useAuth.tsx +++ b/susconecta/hooks/useAuth.tsx @@ -2,6 +2,7 @@ import { createContext, useContext, useEffect, useState, ReactNode, useCallback, useMemo, useRef } from 'react' import { useRouter } from 'next/navigation' import { loginUser, logoutUser, AuthenticationError } from '@/lib/auth' +import { ENV_CONFIG } from '@/lib/env-config' import { isExpired, parseJwt } from '@/lib/jwt' import { httpClient } from '@/lib/http' import type { @@ -118,7 +119,7 @@ export function AuthProvider({ children }: { children: ReactNode }) { return } } catch (refreshError) { - console.log('❌ [AUTH] Falha no refresh automático') + console.log(' [AUTH] Falha no refresh automático') await new Promise(resolve => setTimeout(resolve, 400)) } } @@ -158,6 +159,42 @@ export function AuthProvider({ children }: { children: ReactNode }) { const response = await loginUser(email, password, userType) + // Após receber token, buscar roles/permissions reais e reconciliar userType + try { + const infoRes = await fetch(`${ENV_CONFIG.SUPABASE_URL}/functions/v1/user-info`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': `Bearer ${response.access_token}`, + 'apikey': ENV_CONFIG.SUPABASE_ANON_KEY, + } + }) + + if (infoRes.ok) { + const info = await infoRes.json().catch(() => null) + const roles: string[] = Array.isArray(info?.roles) ? info.roles : (info?.roles ? [info.roles] : []) + + // Derivar tipo de usuário a partir dos roles + let derived: UserType = 'paciente' + if (roles.includes('admin') || roles.includes('gestor') || roles.includes('secretaria')) { + derived = 'administrador' + } else if (roles.includes('medico') || roles.includes('enfermeiro')) { + derived = 'profissional' + } + + // Atualizar userType caso seja diferente + if (response.user && response.user.userType !== derived) { + response.user.userType = derived + console.log('[AUTH] userType reconciled from roles ->', derived) + } + } else { + console.warn('[AUTH] Falha ao obter user-info para reconciliar roles:', infoRes.status) + } + } catch (err) { + console.warn('[AUTH] Erro ao buscar user-info após login (não crítico):', err) + } + saveAuthData( response.access_token, response.user,