TDD — Tab Perfil na EBD
Status
Section titled “Status”Implementado
Contexto
Section titled “Contexto”Aba “Perfil na EBD” do ProfileDialog. Cargo desejado, dropdown de papéis eligíveis, solicitações de cargo, aprovações pendentes.
Contratos
Section titled “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 + requesterRoleUSER_ROLE_REQUEST— solicitar novo cargoUSER_ROLE_REQUEST_APPROVE/USER_ROLE_REQUEST_REJECT— aprovar/rejeitarUSER_ROLE_REQUEST_DELETE— cancelar solicitaçãoUSER_ROLE_REQUESTS_PENDING— pendentes para aprovação
Definições de aprovação
Section titled “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: trueno 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):
- Payload exige
requestIderequesterId. - Carrega o perfil do solicitante (
requesterId); se não existir ou ministério diferente →PROFILE_NOT_FOUNDouFORBIDDEN_SCOPE_MISMATCH. - Localiza a solicitação em
requester.roleRequestscom orequestIde statusPENDING; se não existir ou não estiver pendente →REQUEST_NOT_FOUND. - Obtém os papéis aprovadores para o cargo solicitado:
getApproverRoleIdsForRequestedRole(ministryId, requestedRoleId). - Se
actor.rolenão estiver emapproverRoleIds→FORBIDDEN_NOT_APPROVER. - Atualiza a solicitação para APPROVED ou REJECTED; em caso de aprovação, atualiza
requester.activeRoleerequester.roleAssignments.
- Payload exige
- Lista de pendentes (USER_ROLE_REQUESTS_PENDING): o backend retorna apenas solicitações PENDING para as quais o papel do ator está em
approverRoleIdsdo 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 === trueno catálogo. O backend usa o perfil (BD) para listar pendentes e para permitir aprovação.
Botões na aba Aprovações pendentes
Section titled “Botões na aba Aprovações pendentes”| Botão | Regra (quem pode) | O que acontece com o pedido |
|---|---|---|
| Aprovar | Aprovador: 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. |
| Rejeitar | Mesma 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)
Section titled “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/unitIddo perfil. -
requesterRole: cargo ativo ou papel principal do usuário.
-
Papéis exibidos são filtrados por:
- Visibilidade por igreja —
ChurchRoleVisibilityda igreja; se não configurado, usa o catálogo do ministério. - Hierarquia e teto — todos os papéis abaixo do cargo atual (
hierarchyLevelmaior) e, entre os acima (hierarchyLevelmenor), apenas os elegíveis: se o cargo atual tiverrequest_ceiling_levelno catálogo (BD), são incluídos todos os papéis comhierarchyLevel >= request_ceiling_level(teto = menor nível / maior cargo que o usuário pode solicitar); caso contrário, apenas o nível imediatamente superior. - 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
roleAssignmentscom status APPROVED, ouprimaryRole/activeRole); (b) cargos com solicitação pendente (PENDING). Assim o combo só oferece opções que fazem sentido solicitar.
- Visibilidade por igreja —
-
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).
- Carregar catálogo:
getAvailableRoleCatalog(unitId, requesterRole) - Solicitar cargo: USER_ROLE_REQUEST
- Aprovar/rejeitar: USER_ROLE_REQUEST_APPROVE / REJECT
Reflexo em tela (requisito)
Section titled “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ção | Reflexos 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 cargo | Sub-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ópria | Sub-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
Section titled “Código de referência”frontend/lib/presentation/modules/profile_dialog/home_profile_dialog_tabs.dartfrontend/lib/application/services/role_catalog_service.dartbackend/functions/src/application/services/role-catalog-service.ts