Skip to main content

TDD — Tab Perfil na EBD

Status

Implementado

Contexto

Aba "Perfil na EBD" do ProfileDialog. Cargo desejado, dropdown de papéis eligíveis, solicitações de cargo, aprovações pendentes.

Contratos

  • No BD e na API, active_role e primary_role guardam apenas roleId; a descrição do cargo é usada só para apresentação em tela.
  • USER_ROLES_AVAILABLE_LIST (gateway) — catálogo de papéis por unitId + requesterRole
  • USER_ROLE_REQUEST — solicitar novo cargo
  • USER_ROLE_REQUEST_APPROVE / USER_ROLE_REQUEST_REJECT — aprovar/rejeitar
  • USER_ROLE_REQUEST_DELETE — cancelar solicitação
  • USER_ROLE_REQUESTS_PENDING — pendentes para aprovação

Definições de aprovação

Regras que determinam quem pode aprovar ou rejeitar uma solicitação de cargo e como o backend valida.

  • Quem pode aprovar: apenas usuários cujo papel atual pertence ao conjunto de papéis aprovadores do cargo solicitado (conforme PRD: nível hierárquico superior ao cargo solicitado). Esse conjunto é obtido pelo catálogo do ministério: papéis em nível hierárquico superior ao cargo solicitado (hierarchyLevel menor) e com can_approve_roles: true no catálogo (BD).
  • Escopo: o aprovador e o solicitante devem ser do mesmo ministério (requester.ministryId === actor.ministryId). Caso contrário o backend retorna erro de escopo.
  • Validação no backend (USER_ROLE_REQUEST_APPROVE / REJECT):
    1. Payload exige requestId e requesterId.
    2. Carrega o perfil do solicitante (requesterId); se não existir ou ministério diferente → PROFILE_NOT_FOUND ou FORBIDDEN_SCOPE_MISMATCH.
    3. Localiza a solicitação em requester.roleRequests com o requestId e status PENDING; se não existir ou não estiver pendente → REQUEST_NOT_FOUND.
    4. Obtém os papéis aprovadores para o cargo solicitado: getApproverRoleIdsForRequestedRole(ministryId, requestedRoleId).
    5. Se actor.role não estiver em approverRoleIdsFORBIDDEN_NOT_APPROVER.
    6. Atualiza a solicitação para APPROVED ou REJECTED; em caso de aprovação, atualiza requester.activeRole e requester.roleAssignments.
  • Lista de pendentes (USER_ROLE_REQUESTS_PENDING): o backend retorna apenas solicitações PENDING para as quais o papel do ator está em approverRoleIds do cargo solicitado (e mesmo ministério). O backend usa active_role/primary_role do perfil (BD) do ator como autoridade para decidir quais pendentes mostrar; o papel no token (actor.role) é usado só como fallback. Assim a sub-aba "Aprovações pendentes" reflete o cargo ativo gravado no perfil, e não apenas o token.
  • Aprovar/Rejeitar: na validação de quem pode aprovar, o backend também usa active_role/primary_role do perfil do ator (BD), com fallback para o token.
  • Frontend: a sub-aba "Aprovações pendentes" e os botões Aprovar/Rejeitar são exibidos quando o cargo ativo (ou principal) do perfil tem canApproveRoles === true no catálogo. O backend usa o perfil (BD) para listar pendentes e para permitir aprovação.

Botões na aba Aprovações pendentes

BotãoRegra (quem pode)O que acontece com o pedido
AprovarAprovador: mesmo ministério e papel em approverRoleIds do cargo solicitado (nível hierárquico superior + can_approve_roles). Payload: requestId, requesterId.Status → APPROVED; no perfil do solicitante: active_role = cargo solicitado; novo item em role_assignments com status APPROVED. O pedido deixa de ser PENDING e sai da lista de pendentes.
RejeitarMesma regra que Aprovar.Status → REJECTED; reviewedAt e reviewedBy gravados. O pedido deixa de ser PENDING e sai da lista de pendentes.
Excluir (ícone remover)Na UI atual, Excluir na lista de aprovações pendentes chama a mesma ação que Rejeitar (USER_ROLE_REQUEST_REJECT). Ou seja, o aprovador “exclui” da sua lista rejeitando o pedido.Igual a Rejeitar.

Observação: Só o solicitante pode cancelar a própria solicitação (ação USER_ROLE_REQUEST_DELETE), na aba Minhas solicitações (botão Excluir lá chama deleteRoleRequest). O aprovador não pode “apagar” o pedido sem rejeitar; ele só pode Aprovar ou Rejeitar.

Interface de Usuário (UI)

Aba Perfil na EBD do ProfileDialog.

Componentes e organização:

  • Cargo ativo — ChoiceChips com papéis elegíveis; seleção única; ao mudar, pode exigir confirmação
  • Cargo desejado — Dropdown com papéis disponíveis para solicitação; loading "Carregando papéis habilitados..."
  • Solicitar aprovação — abre sheet para informar cargo e justificativa; envia USER_ROLE_REQUEST

Regra de preenchimento do combo Cargo desejado:

  • O backend retorna papéis elegíveis via getAvailableRoleCatalog(unitId, requesterRole).

  • unitId: igreja selecionada no dropdown (aba Dados) ou organizationNode?.id / unitId do perfil.

  • requesterRole: cargo ativo ou papel principal do usuário.

  • Papéis exibidos são filtrados por:

    1. Visibilidade por igrejaChurchRoleVisibility da igreja; se não configurado, usa o catálogo do ministério.
    2. Hierarquia e teto — todos os papéis abaixo do cargo atual (hierarchyLevel maior) e, entre os acima (hierarchyLevel menor), apenas os elegíveis: se o cargo atual tiver request_ceiling_level no catálogo (BD), são incluídos todos os papéis com hierarchyLevel >= request_ceiling_level (teto = menor nível / maior cargo que o usuário pode solicitar); caso contrário, apenas o nível imediatamente superior.
    3. Filtro por estado do usuário — no combo são exibidos apenas os cargos que o usuário ainda não possui e ainda não solicitou. Ficam fora do combo: (a) cargos já aprovados (presentes em roleAssignments com status APPROVED, ou primaryRole / activeRole); (b) cargos com solicitação pendente (PENDING). Assim o combo só oferece opções que fazem sentido solicitar.
  • Sem requesterRole, mostra todos os papéis visíveis na igreja (respeitando o filtro acima).

  • Solicitações — exibidas em duas sub-abas (uma por vez):

    • Minhas solicitações — lista das próprias solicitações (com opção excluir); mensagem "Nenhuma solicitação sua no momento." quando vazio.
    • Aprovações pendentes — lista de solicitações que o usuário pode aprovar/rejeitar (quando tem cargo com can_approve_roles); botões Aprovar, Rejeitar, Excluir com confirmação quando aplicável; mensagem "Nenhuma aprovação pendente." quando vazio.
  • Exibição de "Cargo atual" e status das solicitações

Observação: Usuário Visitante/Membro vê subset restrito (apenas níveis superiores imediatos). O combo fica vazio quando não há papéis elegíveis (ex.: requester já tem o nível mais alto).

Fluxo

  1. Carregar catálogo: getAvailableRoleCatalog(unitId, requesterRole)
  2. Solicitar cargo: USER_ROLE_REQUEST
  3. Aprovar/rejeitar: USER_ROLE_REQUEST_APPROVE / REJECT

Reflexo em tela (requisito)

Conforme ADR-0021 — Reflexo em tela após ações do usuário: ao executar qualquer comando local que altere o perfil ou realize chamadas ao backend, as mudanças devem ser refletidas imediatamente na aba Perfil na EBD, sem precisar fechar e reabrir o diálogo.

Comandos abrangidos:

AçãoReflexos esperados na tela
Confirmar troca de cargo (ChoiceChip ou botão "Confirma o novo cargo")"Cargo atual de operação" atualizado; ChoiceChips com o novo cargo selecionado; combo "Cargo desejado" recalculado (papéis elegíveis dependem do requesterRole); lista de solicitações próprias e pendentes atualizada se aplicável.
Solicitar aprovação de cargoSub-aba "Minhas solicitações" atualizada (nova solicitação); combo recalculado (cargo solicitado sai da lista, pois fica pendente).
Aprovar / Rejeitar solicitação (pendente)Sub-aba "Aprovações pendentes" atualizada (item removido); demais dados do perfil do aprovador refletidos conforme backend.
Excluir solicitação própriaSub-aba "Minhas solicitações" atualizada (solicitação removida).

Regra geral: Após cada ação bem-sucedida, o estado local do diálogo (_profileState, _availableRoleCatalog, _pendingApprovalRequests) deve ser atualizado a partir do backend (ou do perfil em memória do serviço) e um setState deve ser disparado para redesenhar a aba com cargo atual, combo, chips e listas consistentes.

Código de referência

  • frontend/lib/presentation/modules/profile_dialog/home_profile_dialog_tabs.dart
  • frontend/lib/application/services/role_catalog_service.dart
  • backend/functions/src/application/services/role-catalog-service.ts