diff --git a/susconecta/Documentação API.md b/susconecta/Documentação API.md deleted file mode 100644 index b1b9604..0000000 --- a/susconecta/Documentação API.md +++ /dev/null @@ -1,2565 +0,0 @@ -# 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: [] - -``` -# Fazer login e obter token JWT - -## OpenAPI Specification - -```yaml -openapi: 3.0.1 -info: - title: '' - description: '' - version: 1.0.0 -paths: - /auth/v1/token: - post: - summary: Fazer login e obter token JWT - deprecated: false - description: >- - Autentica o usuário e retorna um token JWT para usar em outras - requisições. - tags: - - Authentication - parameters: - - name: grant_type - in: query - description: '' - required: true - schema: - type: string - enum: - - password - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LoginRequest' - examples: {} - responses: - '200': - description: Login realizado com sucesso - content: - application/json: - schema: - $ref: '#/components/schemas/LoginResponse' - headers: {} - x-apidog-name: OK - '400': - description: Credenciais inválidas - content: - application/json: - schema: &ref_0 - $ref: '#/components/schemas/Error' - headers: {} - x-apidog-name: Bad Request - '401': - description: Email ou senha incorretos - content: - application/json: - schema: *ref_0 - headers: {} - x-apidog-name: Unauthorized - security: - - bearer: [] - x-apidog-folder: Authentication - x-apidog-status: released - x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940510-run -components: - schemas: - LoginRequest: - type: object - required: - - email - - password - properties: - email: - type: string - format: email - examples: - - usuario@exemplo.com - password: - type: string - minLength: 6 - examples: - - senha123 - x-apidog-orders: - - email - - password - x-apidog-ignore-properties: [] - x-apidog-folder: '' - LoginResponse: - type: object - properties: - access_token: - type: string - description: Token JWT para autenticação - examples: - - eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - token_type: - type: string - examples: - - bearer - expires_in: - type: integer - description: Tempo de expiração do token em segundos - examples: - - 3600 - refresh_token: - type: string - description: Token para renovar o access_token - user: - $ref: '#/components/schemas/AuthUser' - x-apidog-orders: - - access_token - - token_type - - expires_in - - refresh_token - - user - x-apidog-ignore-properties: [] - x-apidog-folder: '' - AuthUser: - type: object - properties: - id: - type: string - format: uuid - email: - type: string - format: email - email_confirmed_at: - type: string - format: date-time - nullable: true - created_at: - type: string - format: date-time - x-apidog-orders: - - id - - email - - email_confirmed_at - - created_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: [] - -``` - -# Logout do usuário - -## OpenAPI Specification - -```yaml -openapi: 3.0.1 -info: - title: '' - description: '' - version: 1.0.0 -paths: - /auth/v1/logout: - post: - summary: Logout do usuário - deprecated: false - description: Encerrar sessão do usuário - tags: - - Authentication - - Authentication - parameters: [] - responses: - '204': - description: Logout realizado com sucesso - headers: {} - x-apidog-name: No Content - '401': - description: Token inválido - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - headers: {} - x-apidog-name: Unauthorized - security: - - bearer: [] - x-apidog-folder: Authentication - x-apidog-status: released - x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940511-run -components: - schemas: - 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: [] - -``` - -# Listar perfis de usuários - -## OpenAPI Specification - -```yaml -openapi: 3.0.1 -info: - title: '' - description: '' - version: 1.0.0 -paths: - /rest/v1/profiles: - get: - summary: Listar perfis de usuários - deprecated: false - description: '' - tags: - - Perfis - - Perfis - parameters: - - name: apikey - in: header - description: Chave da API Supabase - required: true - example: '' - schema: - type: string - responses: - '200': - description: Lista de perfis - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Profile' - headers: {} - x-apidog-name: OK - security: - - bearer: [] - x-apidog-folder: Perfis - x-apidog-status: released - x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940522-run -components: - schemas: - 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: '' - 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: [] - -``` - -# Atualizar perfil do usuário - -## OpenAPI Specification - -```yaml -openapi: 3.0.1 -info: - title: '' - description: '' - version: 1.0.0 -paths: - /rest/v1/profiles: - patch: - summary: Atualizar perfil do usuário - deprecated: false - description: '' - tags: - - Perfis - - Perfis - parameters: - - name: apikey - in: header - description: Chave da API Supabase - required: true - example: '' - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ProfileInput' - responses: - '200': - description: Perfil atualizado - content: - application/json: - schema: - $ref: '#/components/schemas/Profile' - headers: {} - x-apidog-name: OK - security: - - bearer: [] - x-apidog-folder: Perfis - x-apidog-status: released - x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940523-run -components: - schemas: - ProfileInput: - type: object - properties: - full_name: - type: string - avatar_url: - type: string - phone: - type: string - x-apidog-orders: - - full_name - - avatar_url - - phone - 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: '' - 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 paciente por ID - -## OpenAPI Specification - -```yaml -openapi: 3.0.1 -info: - title: '' - description: '' - version: 1.0.0 -paths: - /rest/v1/patients/{id}: - get: - summary: Obter paciente por ID - deprecated: false - description: Retorna dados de um paciente específico - tags: - - Pacientes - - Pacientes - parameters: - - name: id - in: path - description: ID do paciente - required: true - example: '' - schema: - type: string - format: uuid - - name: apikey - in: header - description: Chave da API Supabase - required: true - example: '' - schema: - type: string - responses: - '200': - description: Dados do paciente - content: - application/json: - schema: - $ref: '#/components/schemas/Patient' - headers: {} - x-apidog-name: OK - '401': - description: Não autorizado - content: - application/json: - schema: &ref_0 - $ref: '#/components/schemas/Error' - headers: {} - x-apidog-name: Unauthorized - '404': - description: Paciente não encontrado - content: - application/json: - schema: *ref_0 - headers: {} - x-apidog-name: Not Found - security: - - bearer: [] - x-apidog-folder: Pacientes - x-apidog-status: released - x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940515-run -components: - schemas: - Patient: - type: object - properties: - id: - type: string - format: uuid - examples: - - 12345678-1234-1234-1234-123456789012 - full_name: - type: string - examples: - - Maria Santos Silva - cpf: - type: string - examples: - - '12345678901' - email: - type: string - format: email - examples: - - maria@email.com - phone_mobile: - type: string - examples: - - (11) 99999-9999 - birth_date: - type: string - format: date - examples: - - '1980-01-15' - social_name: - type: string - examples: - - Maria Santos - sex: - type: string - examples: - - F - blood_type: - type: string - examples: - - A+ - weight_kg: - type: number - examples: - - 65.5 - height_m: - type: number - examples: - - 1.65 - bmi: - type: number - examples: - - 24.1 - street: - type: string - examples: - - Rua das Flores, 123 - number: - type: string - examples: - - '123' - complement: - type: string - examples: - - Apt 45 - neighborhood: - type: string - examples: - - Centro - city: - type: string - examples: - - São Paulo - state: - type: string - examples: - - SP - cep: - type: string - examples: - - 01234-567 - created_at: - type: string - format: date-time - examples: - - '2024-01-15T10:30:00Z' - updated_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 - - full_name - - cpf - - email - - phone_mobile - - birth_date - - social_name - - sex - - blood_type - - weight_kg - - height_m - - bmi - - street - - number - - complement - - neighborhood - - city - - state - - cep - - created_at - - updated_at - - created_by - 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: [] - -``` - -# Atualizar paciente - -## OpenAPI Specification - -```yaml -openapi: 3.0.1 -info: - title: '' - description: '' - version: 1.0.0 -paths: - /rest/v1/patients/{id}: - patch: - summary: Atualizar paciente - deprecated: false - description: Atualizar dados de um paciente existente - tags: - - Pacientes - - Pacientes - parameters: - - name: id - in: path - description: ID do paciente - required: true - example: '' - schema: - type: string - format: uuid - - name: apikey - in: header - description: Chave da API Supabase - required: true - example: '' - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PatientInput' - responses: - '200': - description: Paciente atualizado com sucesso - content: - application/json: - schema: - $ref: '#/components/schemas/Patient' - headers: {} - x-apidog-name: OK - '401': - description: Não autorizado - content: - application/json: - schema: &ref_0 - $ref: '#/components/schemas/Error' - headers: {} - x-apidog-name: Unauthorized - '404': - description: Paciente não encontrado - content: - application/json: - schema: *ref_0 - headers: {} - x-apidog-name: Not Found - security: - - bearer: [] - x-apidog-folder: Pacientes - x-apidog-status: released - x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940516-run -components: - schemas: - PatientInput: - type: object - required: - - full_name - - cpf - - email - - phone_mobile - properties: - full_name: - type: string - examples: - - Maria Santos Silva - cpf: - type: string - examples: - - '12345678901' - email: - type: string - format: email - examples: - - maria@email.com - phone_mobile: - type: string - examples: - - (11) 99999-9999 - birth_date: - type: string - format: date - examples: - - '1980-01-15' - social_name: - type: string - sex: - type: string - examples: - - F - blood_type: - type: string - weight_kg: - type: number - height_m: - type: number - street: - type: string - number: - type: string - complement: - type: string - neighborhood: - type: string - city: - type: string - state: - type: string - cep: - type: string - x-apidog-orders: - - full_name - - cpf - - email - - phone_mobile - - birth_date - - social_name - - sex - - blood_type - - weight_kg - - height_m - - street - - number - - complement - - neighborhood - - city - - state - - cep - x-apidog-ignore-properties: [] - x-apidog-folder: '' - Patient: - type: object - properties: - id: - type: string - format: uuid - examples: - - 12345678-1234-1234-1234-123456789012 - full_name: - type: string - examples: - - Maria Santos Silva - cpf: - type: string - examples: - - '12345678901' - email: - type: string - format: email - examples: - - maria@email.com - phone_mobile: - type: string - examples: - - (11) 99999-9999 - birth_date: - type: string - format: date - examples: - - '1980-01-15' - social_name: - type: string - examples: - - Maria Santos - sex: - type: string - examples: - - F - blood_type: - type: string - examples: - - A+ - weight_kg: - type: number - examples: - - 65.5 - height_m: - type: number - examples: - - 1.65 - bmi: - type: number - examples: - - 24.1 - street: - type: string - examples: - - Rua das Flores, 123 - number: - type: string - examples: - - '123' - complement: - type: string - examples: - - Apt 45 - neighborhood: - type: string - examples: - - Centro - city: - type: string - examples: - - São Paulo - state: - type: string - examples: - - SP - cep: - type: string - examples: - - 01234-567 - created_at: - type: string - format: date-time - examples: - - '2024-01-15T10:30:00Z' - updated_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 - - full_name - - cpf - - email - - phone_mobile - - birth_date - - social_name - - sex - - blood_type - - weight_kg - - height_m - - bmi - - street - - number - - complement - - neighborhood - - city - - state - - cep - - created_at - - updated_at - - created_by - 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: [] - -``` - -# Deletar paciente - -## OpenAPI Specification - -```yaml -openapi: 3.0.1 -info: - title: '' - description: '' - version: 1.0.0 -paths: - /rest/v1/patients/{id}: - delete: - summary: Deletar paciente - deprecated: false - description: Remover um paciente do sistema (apenas admins/gestores) - tags: - - Pacientes - - Pacientes - parameters: - - name: id - in: path - description: ID do paciente - required: true - example: '' - schema: - type: string - format: uuid - - name: apikey - in: header - description: Chave da API Supabase - required: true - example: '' - schema: - type: string - responses: - '204': - description: Paciente deletado com sucesso - headers: {} - x-apidog-name: No Content - '401': - description: Não autorizado - content: - application/json: - schema: &ref_0 - $ref: '#/components/schemas/Error' - headers: {} - x-apidog-name: Unauthorized - '403': - description: Sem permissão - content: - application/json: - schema: *ref_0 - headers: {} - x-apidog-name: Forbidden - '404': - description: Paciente não encontrado - content: - application/json: - schema: *ref_0 - headers: {} - x-apidog-name: Not Found - security: - - bearer: [] - x-apidog-folder: Pacientes - x-apidog-status: released - x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940517-run -components: - schemas: - 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 paciente - -## OpenAPI Specification - -```yaml -openapi: 3.0.1 -info: - title: '' - description: '' - version: 1.0.0 -paths: - /rest/v1/patients: - post: - summary: Criar novo paciente - deprecated: false - description: Cadastrar um novo paciente no sistema - tags: - - Pacientes - - Pacientes - parameters: - - name: apikey - in: header - description: Chave da API Supabase - required: true - example: '{{API_KEY}}' - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PatientInput' - example: - full_name: Maria Santos - cpf: '12345678901' - email: maria@email.com - phone_mobile: (11) 99999-9999 - birth_date: '1980-01-15' - responses: - '201': - description: Paciente criado com sucesso - content: - application/json: - schema: - $ref: '#/components/schemas/Patient' - headers: {} - x-apidog-name: Created - '400': - description: Dados inválidos - content: - application/json: - schema: &ref_0 - $ref: '#/components/schemas/Error' - headers: {} - x-apidog-name: Bad Request - '401': - description: Não autorizado - content: - application/json: - schema: *ref_0 - headers: {} - x-apidog-name: Unauthorized - security: - - bearer: [] - x-apidog-folder: Pacientes - x-apidog-status: released - x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940514-run -components: - schemas: - PatientInput: - type: object - required: - - full_name - - cpf - - email - - phone_mobile - properties: - full_name: - type: string - examples: - - Maria Santos Silva - cpf: - type: string - examples: - - '12345678901' - email: - type: string - format: email - examples: - - maria@email.com - phone_mobile: - type: string - examples: - - (11) 99999-9999 - birth_date: - type: string - format: date - examples: - - '1980-01-15' - social_name: - type: string - sex: - type: string - examples: - - F - blood_type: - type: string - weight_kg: - type: number - height_m: - type: number - street: - type: string - number: - type: string - complement: - type: string - neighborhood: - type: string - city: - type: string - state: - type: string - cep: - type: string - x-apidog-orders: - - full_name - - cpf - - email - - phone_mobile - - birth_date - - social_name - - sex - - blood_type - - weight_kg - - height_m - - street - - number - - complement - - neighborhood - - city - - state - - cep - x-apidog-ignore-properties: [] - x-apidog-folder: '' - Patient: - type: object - properties: - id: - type: string - format: uuid - examples: - - 12345678-1234-1234-1234-123456789012 - full_name: - type: string - examples: - - Maria Santos Silva - cpf: - type: string - examples: - - '12345678901' - email: - type: string - format: email - examples: - - maria@email.com - phone_mobile: - type: string - examples: - - (11) 99999-9999 - birth_date: - type: string - format: date - examples: - - '1980-01-15' - social_name: - type: string - examples: - - Maria Santos - sex: - type: string - examples: - - F - blood_type: - type: string - examples: - - A+ - weight_kg: - type: number - examples: - - 65.5 - height_m: - type: number - examples: - - 1.65 - bmi: - type: number - examples: - - 24.1 - street: - type: string - examples: - - Rua das Flores, 123 - number: - type: string - examples: - - '123' - complement: - type: string - examples: - - Apt 45 - neighborhood: - type: string - examples: - - Centro - city: - type: string - examples: - - São Paulo - state: - type: string - examples: - - SP - cep: - type: string - examples: - - 01234-567 - created_at: - type: string - format: date-time - examples: - - '2024-01-15T10:30:00Z' - updated_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 - - full_name - - cpf - - email - - phone_mobile - - birth_date - - social_name - - sex - - blood_type - - weight_kg - - height_m - - bmi - - street - - number - - complement - - neighborhood - - city - - state - - cep - - created_at - - updated_at - - created_by - 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: [] - -``` - -# Listar pacientes - -## OpenAPI Specification - -```yaml -openapi: 3.0.1 -info: - title: '' - description: '' - version: 1.0.0 -paths: - /rest/v1/patients: - get: - summary: Listar pacientes - deprecated: false - description: Retorna lista de pacientes com base nas permissões do usuário - tags: - - Pacientes - - Pacientes - parameters: - - name: apikey - in: header - description: '' - required: false - example: '{{apikey}}' - schema: - type: string - responses: - '200': - description: Lista de pacientes - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Patient' - headers: {} - x-apidog-name: OK - '401': - description: Não autorizado - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - headers: {} - x-apidog-name: Unauthorized - security: - - bearer: [] - x-apidog-folder: Pacientes - x-apidog-status: released - x-run-in-apidog: https://app.apidog.com/web/project/1053378/apis/api-21940513-run -components: - schemas: - Patient: - type: object - properties: - id: - type: string - format: uuid - examples: - - 12345678-1234-1234-1234-123456789012 - full_name: - type: string - examples: - - Maria Santos Silva - cpf: - type: string - examples: - - '12345678901' - email: - type: string - format: email - examples: - - maria@email.com - phone_mobile: - type: string - examples: - - (11) 99999-9999 - birth_date: - type: string - format: date - examples: - - '1980-01-15' - social_name: - type: string - examples: - - Maria Santos - sex: - type: string - examples: - - F - blood_type: - type: string - examples: - - A+ - weight_kg: - type: number - examples: - - 65.5 - height_m: - type: number - examples: - - 1.65 - bmi: - type: number - examples: - - 24.1 - street: - type: string - examples: - - Rua das Flores, 123 - number: - type: string - examples: - - '123' - complement: - type: string - examples: - - Apt 45 - neighborhood: - type: string - examples: - - Centro - city: - type: string - examples: - - São Paulo - state: - type: string - examples: - - SP - cep: - type: string - examples: - - 01234-567 - created_at: - type: string - format: date-time - examples: - - '2024-01-15T10:30:00Z' - updated_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 - - full_name - - cpf - - email - - phone_mobile - - birth_date - - social_name - - sex - - blood_type - - weight_kg - - height_m - - bmi - - street - - number - - complement - - neighborhood - - city - - state - - cep - - created_at - - updated_at - - created_by - 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/app/(main-routes)/dashboard/page.tsx b/susconecta/app/(main-routes)/dashboard/page.tsx index ffd17fe..9d9c5c0 100644 --- a/susconecta/app/(main-routes)/dashboard/page.tsx +++ b/susconecta/app/(main-routes)/dashboard/page.tsx @@ -1,41 +1,403 @@ -export default function DashboardPage() { - return ( - <> -
-
-

Dashboard

-

- Bem-vindo ao painel de controle -

-
+'use client'; -
-
-

- Total de Pacientes -

-

1,234

-
-
-

- Consultas Hoje -

-

28

-
-
-

- Próximas Consultas -

-

45

-
-
-

- Receita Mensal -

-

R$ 45.230

+import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { + countTotalPatients, + countTotalDoctors, + countAppointmentsToday, + getUpcomingAppointments, + getAppointmentsByDateRange, + getNewUsersLastDays, + getPendingReports, + getDisabledUsers, + getDoctorsAvailabilityToday, + getPatientById, + getDoctorById, +} from '@/lib/api'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { Alert, AlertDescription } from '@/components/ui/alert'; +import { AlertCircle, Calendar, Users, Stethoscope, Clock, FileText, AlertTriangle, Plus, ArrowLeft } from 'lucide-react'; +import Link from 'next/link'; +import { PatientRegistrationForm } from '@/components/forms/patient-registration-form'; +import { DoctorRegistrationForm } from '@/components/forms/doctor-registration-form'; + +interface DashboardStats { + totalPatients: number; + totalDoctors: number; + appointmentsToday: number; +} + +interface UpcomingAppointment { + id: string; + scheduled_at: string; + status: string; + doctor_id: string; + patient_id: string; + doctor?: { full_name?: string }; + patient?: { full_name?: string }; +} + +export default function DashboardPage() { + const router = useRouter(); + const [stats, setStats] = useState({ + totalPatients: 0, + totalDoctors: 0, + appointmentsToday: 0, + }); + const [appointments, setAppointments] = useState([]); + const [appointmentData, setAppointmentData] = useState([]); + const [newUsers, setNewUsers] = useState([]); + const [pendingReports, setPendingReports] = useState([]); + const [disabledUsers, setDisabledUsers] = useState([]); + const [doctors, setDoctors] = useState>(new Map()); + const [patients, setPatients] = useState>(new Map()); + const [loading, setLoading] = useState(true); + + // Estados para os modais de formulário + const [showPatientForm, setShowPatientForm] = useState(false); + const [showDoctorForm, setShowDoctorForm] = useState(false); + const [editingPatientId, setEditingPatientId] = useState(null); + const [editingDoctorId, setEditingDoctorId] = useState(null); + + useEffect(() => { + loadDashboardData(); + }, []); + + const loadDashboardData = async () => { + try { + setLoading(true); + + // 1. Carrega stats + const [patientCount, doctorCount, todayCount] = await Promise.all([ + countTotalPatients(), + countTotalDoctors(), + countAppointmentsToday(), + ]); + + setStats({ + totalPatients: patientCount, + totalDoctors: doctorCount, + appointmentsToday: todayCount, + }); + + // 2. Carrega dados dos widgets em paralelo + const [upcomingAppts, appointmentDataRange, newUsersList, pendingReportsList, disabledUsersList] = await Promise.all([ + getUpcomingAppointments(5), + getAppointmentsByDateRange(7), + getNewUsersLastDays(7), + getPendingReports(5), + getDisabledUsers(5), + ]); + + setAppointments(upcomingAppts); + setAppointmentData(appointmentDataRange); + setNewUsers(newUsersList); + setPendingReports(pendingReportsList); + setDisabledUsers(disabledUsersList); + + // 3. Busca detalhes de pacientes e médicos para as próximas consultas + const doctorMap = new Map(); + const patientMap = new Map(); + + for (const appt of upcomingAppts) { + if (appt.doctor_id && !doctorMap.has(appt.doctor_id)) { + const doctor = await getDoctorById(appt.doctor_id); + if (doctor) doctorMap.set(appt.doctor_id, doctor); + } + if (appt.patient_id && !patientMap.has(appt.patient_id)) { + const patient = await getPatientById(appt.patient_id); + if (patient) patientMap.set(appt.patient_id, patient); + } + } + + setDoctors(doctorMap); + setPatients(patientMap); + } catch (err) { + console.error('[Dashboard] Erro ao carregar dados:', err); + } finally { + setLoading(false); + } + }; + + const handlePatientFormSaved = () => { + setShowPatientForm(false); + setEditingPatientId(null); + loadDashboardData(); + }; + + const handleDoctorFormSaved = () => { + setShowDoctorForm(false); + setEditingDoctorId(null); + loadDashboardData(); + }; + + const formatDate = (dateStr: string) => { + return new Date(dateStr).toLocaleDateString('pt-BR', { + day: '2-digit', + month: '2-digit', + hour: '2-digit', + minute: '2-digit', + }); + }; + + const getStatusBadge = (status: string) => { + const statusMap: Record = { + confirmed: { variant: 'default', label: 'Confirmado' }, + completed: { variant: 'secondary', label: 'Concluído' }, + cancelled: { variant: 'destructive', label: 'Cancelado' }, + requested: { variant: 'outline', label: 'Solicitado' }, + }; + const s = statusMap[status] || { variant: 'outline', label: status }; + return {s.label}; + }; + + if (loading) { + return ( +
+
+
+
+ {[1, 2, 3, 4].map(i => ( +
+ ))}
- + ); + } + + // Se está exibindo formulário de paciente + if (showPatientForm) { + return ( +
+
+ +

{editingPatientId ? "Editar paciente" : "Novo paciente"}

+
+ + { + setShowPatientForm(false); + setEditingPatientId(null); + }} + /> +
+ ); + } + + // Se está exibindo formulário de médico + if (showDoctorForm) { + return ( +
+
+ +

{editingDoctorId ? "Editar Médico" : "Novo Médico"}

+
+ + { + setShowDoctorForm(false); + setEditingDoctorId(null); + }} + /> +
+ ); + } + + return ( +
+ {/* Header */} +
+

Dashboard

+

Bem-vindo ao painel de controle

+
+ + {/* 1. CARDS RESUMO */} +
+
+
+
+

Total de Pacientes

+

{stats.totalPatients}

+
+ +
+
+ +
+
+
+

Total de Médicos

+

{stats.totalDoctors}

+
+ +
+
+ +
+
+
+

Consultas Hoje

+

{stats.appointmentsToday}

+
+ +
+
+ +
+
+
+

Relatórios Pendentes

+

{pendingReports.length}

+
+ +
+
+
+ + {/* 6. AÇÕES RÁPIDAS */} +
+

Ações Rápidas

+
+ + + + +
+
+ + {/* 2. PRÓXIMAS CONSULTAS */} +
+
+

Próximas Consultas (7 dias)

+ {appointments.length > 0 ? ( +
+ {appointments.map(appt => ( +
+
+

+ {patients.get(appt.patient_id)?.full_name || 'Paciente desconhecido'} +

+

+ Médico: {doctors.get(appt.doctor_id)?.full_name || 'Médico desconhecido'} +

+

{formatDate(appt.scheduled_at)}

+
+
+ {getStatusBadge(appt.status)} +
+
+ ))} +
+ ) : ( +

Nenhuma consulta agendada para os próximos 7 dias

+ )} +
+ + {/* 5. RELATÓRIOS PENDENTES */} +
+

+ + Relatórios Pendentes +

+ {pendingReports.length > 0 ? ( +
+ {pendingReports.map(report => ( +
+

{report.order_number}

+

{report.exam || 'Sem descrição'}

+
+ ))} + +
+ ) : ( +

Sem relatórios pendentes

+ )} +
+
+ + {/* 4. NOVOS USUÁRIOS */} +
+

Novos Usuários (últimos 7 dias)

+ {newUsers.length > 0 ? ( +
+ {newUsers.map(user => ( +
+

{user.full_name || 'Sem nome'}

+

{user.email}

+
+ ))} +
+ ) : ( +

Nenhum novo usuário nos últimos 7 dias

+ )} +
+ + {/* 8. ALERTAS */} + {disabledUsers.length > 0 && ( +
+

+ + Alertas - Usuários Desabilitados +

+
+ {disabledUsers.map(user => ( + + + + {user.full_name} ({user.email}) está desabilitado + + + ))} +
+
+ )} + + {/* 11. LINK PARA RELATÓRIOS */} +
+

Seção de Relatórios

+

+ Acesse a seção de relatórios médicos para gerenciar, visualizar e exportar documentos. +

+ +
+
); } + diff --git a/susconecta/app/(main-routes)/perfil/loading.tsx b/susconecta/app/(main-routes)/perfil/loading.tsx new file mode 100644 index 0000000..9b7a1af --- /dev/null +++ b/susconecta/app/(main-routes)/perfil/loading.tsx @@ -0,0 +1,34 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +export default function PerfillLoading() { + return ( +
+
+ +
+ + +
+
+ +
+
+ +
+ + + +
+
+ +
+ +
+ + +
+
+
+
+ ); +} diff --git a/susconecta/app/(main-routes)/perfil/page.tsx b/susconecta/app/(main-routes)/perfil/page.tsx new file mode 100644 index 0000000..2db4cf9 --- /dev/null +++ b/susconecta/app/(main-routes)/perfil/page.tsx @@ -0,0 +1,653 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter } from "next/navigation"; +import { Button } from "@/components/ui/button"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Badge } from "@/components/ui/badge"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Label } from "@/components/ui/label"; +import { Input } from "@/components/ui/input"; +import { UploadAvatar } from "@/components/ui/upload-avatar"; +import { AlertCircle, ArrowLeft, CheckCircle, XCircle } from "lucide-react"; +import { getUserInfoById } from "@/lib/api"; +import { useAuth } from "@/hooks/useAuth"; +import { formatTelefone, formatCEP, validarCEP, buscarCEP } from "@/lib/utils"; + +interface UserProfile { + user: { + id: string; + email: string; + created_at: string; + last_sign_in_at: string | null; + email_confirmed_at: string | null; + }; + profile: { + id: string; + full_name: string | null; + email: string | null; + phone: string | null; + avatar_url: string | null; + cep?: string | null; + street?: string | null; + number?: string | null; + complement?: string | null; + neighborhood?: string | null; + city?: string | null; + state?: string | null; + disabled: boolean; + created_at: string; + updated_at: string; + } | null; + roles: string[]; + permissions: { + isAdmin: boolean; + isManager: boolean; + isDoctor: boolean; + isSecretary: boolean; + isAdminOrManager: boolean; + }; +} + +export default function PerfilPage() { + const router = useRouter(); + const { user: authUser } = useAuth(); + const [userInfo, setUserInfo] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [isEditing, setIsEditing] = useState(false); + const [editingData, setEditingData] = useState<{ + phone?: string; + full_name?: string; + avatar_url?: string; + cep?: string; + street?: string; + number?: string; + complement?: string; + neighborhood?: string; + city?: string; + state?: string; + }>({}); + const [cepLoading, setCepLoading] = useState(false); + const [cepValid, setCepValid] = useState(null); + + useEffect(() => { + async function loadUserInfo() { + try { + setLoading(true); + + if (!authUser?.id) { + throw new Error("ID do usuário não encontrado"); + } + + console.log('[PERFIL] Chamando getUserInfoById com ID:', authUser.id); + + // Para admin/gestor, usar getUserInfoById com o ID do usuário logado + const info = await getUserInfoById(authUser.id); + console.log('[PERFIL] Sucesso ao carregar info:', info); + setUserInfo(info as UserProfile); + setError(null); + } catch (err: any) { + console.error('[PERFIL] Erro ao carregar:', err); + setError(err?.message || "Erro ao carregar informações do perfil"); + setUserInfo(null); + } finally { + setLoading(false); + } + } + + if (authUser) { + console.log('[PERFIL] useEffect acionado, authUser:', authUser); + loadUserInfo(); + } + }, [authUser]); + + if (authUser?.userType !== 'administrador') { + return ( +
+
+ + + + Você não tem permissão para acessar esta página. + + + +
+
+ ); + } + + if (loading) { + return ( +
+
+
+
+
+
+
+
+ ); + } + + if (error) { + return ( +
+
+ + + {error} + + +
+
+ ); + } + + if (!userInfo) { + return ( +
+
+ + + + Nenhuma informação de perfil disponível. + + +
+
+ ); + } + + const getInitials = (name: string | null | undefined) => { + if (!name) return "AD"; + return name + .split(" ") + .map((n) => n[0]) + .join("") + .toUpperCase() + .slice(0, 2); + }; + + const handleEditClick = () => { + if (!isEditing && userInfo) { + setEditingData({ + full_name: userInfo.profile?.full_name || "", + phone: userInfo.profile?.phone || "", + avatar_url: userInfo.profile?.avatar_url || "", + cep: userInfo.profile?.cep || "", + street: userInfo.profile?.street || "", + number: userInfo.profile?.number || "", + complement: userInfo.profile?.complement || "", + neighborhood: userInfo.profile?.neighborhood || "", + city: userInfo.profile?.city || "", + state: userInfo.profile?.state || "", + }); + // Se já existe CEP, marcar como válido + if (userInfo.profile?.cep) { + setCepValid(true); + } + } + setIsEditing(!isEditing); + }; + + const handleSaveEdit = async () => { + try { + // Aqui você implementaria a chamada para atualizar o perfil + console.log('[PERFIL] Salvando alterações:', editingData); + // await atualizarPerfil(userInfo?.user.id, editingData); + setIsEditing(false); + setUserInfo((prev) => + prev ? { + ...prev, + profile: prev.profile ? { + ...prev.profile, + full_name: editingData.full_name || prev.profile.full_name, + phone: editingData.phone || prev.profile.phone, + avatar_url: editingData.avatar_url || prev.profile.avatar_url, + cep: editingData.cep || prev.profile.cep, + street: editingData.street || prev.profile.street, + number: editingData.number || prev.profile.number, + complement: editingData.complement || prev.profile.complement, + neighborhood: editingData.neighborhood || prev.profile.neighborhood, + city: editingData.city || prev.profile.city, + state: editingData.state || prev.profile.state, + } : null, + } : null + ); + } catch (err: any) { + console.error('[PERFIL] Erro ao salvar:', err); + } + }; + + const handleCancelEdit = () => { + setIsEditing(false); + setEditingData({}); + setCepValid(null); + }; + + const handleCepChange = async (cepValue: string) => { + // Formatar CEP + const formatted = formatCEP(cepValue); + setEditingData({...editingData, cep: formatted}); + + // Validar CEP + const isValid = validarCEP(cepValue); + setCepValid(isValid ? null : false); // null = não validado ainda, false = inválido + + if (isValid) { + setCepLoading(true); + try { + const resultado = await buscarCEP(cepValue); + if (resultado) { + setCepValid(true); + // Preencher campos automaticamente + setEditingData(prev => ({ + ...prev, + street: resultado.street, + neighborhood: resultado.neighborhood, + city: resultado.city, + state: resultado.state, + })); + console.log('[PERFIL] CEP preenchido com sucesso:', resultado); + } else { + setCepValid(false); + } + } catch (err) { + console.error('[PERFIL] Erro ao buscar CEP:', err); + setCepValid(false); + } finally { + setCepLoading(false); + } + } + }; + + const handlePhoneChange = (phoneValue: string) => { + const formatted = formatTelefone(phoneValue); + setEditingData({...editingData, phone: formatted}); + }; + + return ( +
+
+
+ {/* Header com Título e Botão */} +
+
+

Meu Perfil

+

Bem-vindo à sua área exclusiva.

+
+ {!isEditing ? ( + + ) : ( +
+ + +
+ )} +
+ + {/* Grid de 2 colunas */} +
+ {/* Coluna Esquerda - Informações Pessoais */} +
+ {/* Informações Pessoais */} +
+

Informações Pessoais

+ +
+ {/* Nome Completo */} +
+ + {isEditing ? ( + setEditingData({...editingData, full_name: e.target.value})} + className="mt-2" + /> + ) : ( + <> +
+ {userInfo.profile?.full_name || "Não preenchido"} +
+

+ Este campo não pode ser alterado +

+ + )} +
+ + {/* Email */} +
+ +
+ {userInfo.user.email} +
+

+ Este campo não pode ser alterado +

+
+ + {/* UUID */} +
+ +
+ {userInfo.user.id} +
+

+ Este campo não pode ser alterado +

+
+ + {/* Permissões */} +
+ +
+ {userInfo.roles && userInfo.roles.length > 0 ? ( + userInfo.roles.map((role) => ( + + {role} + + )) + ) : ( + + Nenhuma permissão atribuída + + )} +
+
+
+
+ + {/* Endereço e Contato */} +
+

Endereço e Contato

+ +
+ {/* Telefone */} +
+ + {isEditing ? ( + handlePhoneChange(e.target.value)} + className="mt-2" + placeholder="(00) 00000-0000" + maxLength={15} + /> + ) : ( +
+ {userInfo.profile?.phone || "Não preenchido"} +
+ )} +
+ + {/* Endereço */} +
+ + {isEditing ? ( + setEditingData({...editingData, street: e.target.value})} + className="mt-2" + placeholder="Rua, avenida, etc." + /> + ) : ( +
+ {userInfo.profile?.street || "Não preenchido"} +
+ )} +
+ + {/* Número */} +
+ + {isEditing ? ( + setEditingData({...editingData, number: e.target.value})} + className="mt-2" + placeholder="123" + /> + ) : ( +
+ {userInfo.profile?.number || "Não preenchido"} +
+ )} +
+ + {/* Complemento */} +
+ + {isEditing ? ( + setEditingData({...editingData, complement: e.target.value})} + className="mt-2" + placeholder="Apto 42, Bloco B, etc." + /> + ) : ( +
+ {userInfo.profile?.complement || "Não preenchido"} +
+ )} +
+ + {/* Bairro */} +
+ + {isEditing ? ( + setEditingData({...editingData, neighborhood: e.target.value})} + className="mt-2" + placeholder="Vila, bairro, etc." + /> + ) : ( +
+ {userInfo.profile?.neighborhood || "Não preenchido"} +
+ )} +
+ + {/* Cidade */} +
+ + {isEditing ? ( + setEditingData({...editingData, city: e.target.value})} + className="mt-2" + placeholder="São Paulo" + /> + ) : ( +
+ {userInfo.profile?.city || "Não preenchido"} +
+ )} +
+ + {/* Estado */} +
+ + {isEditing ? ( + setEditingData({...editingData, state: e.target.value})} + className="mt-2" + placeholder="SP" + maxLength={2} + /> + ) : ( +
+ {userInfo.profile?.state || "Não preenchido"} +
+ )} +
+ + {/* CEP */} +
+ + {isEditing ? ( +
+
+
+ handleCepChange(e.target.value)} + className="mt-2" + placeholder="00000-000" + maxLength={9} + disabled={cepLoading} + /> +
+ {cepValid === true && ( + + )} + {cepValid === false && ( + + )} +
+ {cepLoading && ( +

Buscando CEP...

+ )} + {cepValid === false && ( +

CEP inválido ou não encontrado

+ )} + {cepValid === true && ( +

✓ CEP preenchido com sucesso

+ )} +
+ ) : ( +
+ {userInfo.profile?.cep || "Não preenchido"} +
+ )} +
+
+
+
+ + {/* Coluna Direita - Foto do Perfil */} +
+
+

Foto do Perfil

+ + {isEditing ? ( +
+ setEditingData({...editingData, avatar_url: newUrl})} + userName={editingData.full_name || userInfo.profile?.full_name || "Usuário"} + /> +
+ ) : ( +
+ + + + {getInitials(userInfo.profile?.full_name)} + + + +
+

+ {getInitials(userInfo.profile?.full_name)} +

+
+
+ )} + + {/* Informações de Status */} +
+
+ +
+ + {userInfo.profile?.disabled ? "Desabilitado" : "Ativo"} + +
+
+
+
+
+
+ + {/* Botão Voltar */} +
+ +
+
+
+
+ ); +} diff --git a/susconecta/components/dashboard/header.tsx b/susconecta/components/dashboard/header.tsx index 0872b66..002d16e 100644 --- a/susconecta/components/dashboard/header.tsx +++ b/susconecta/components/dashboard/header.tsx @@ -6,11 +6,13 @@ import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { useState, useEffect, useRef } from "react" +import { useRouter } from "next/navigation" import { SidebarTrigger } from "../ui/sidebar" import { SimpleThemeToggle } from "@/components/simple-theme-toggle"; export function PagesHeader({ title = "", subtitle = "" }: { title?: string, subtitle?: string }) { const { logout, user } = useAuth(); + const router = useRouter(); const [dropdownOpen, setDropdownOpen] = useState(false); const dropdownRef = useRef(null); @@ -84,7 +86,14 @@ export function PagesHeader({ title = "", subtitle = "" }: { title?: string, sub
-