Skip to content

User Lifecycle – Frontend

Technical documentation for the User lifecycle frontend in Alan Home (eu-home-app). Intended for developers debugging or developing on the feature. This page focuses on the frontend implementation and, in particular, role request and approval.

Location

  • App: frontend/apps/eu-home-app
  • Main component: app/components/UserRoleManagement.tsx — exports RolesManagementGeneric (generic roles UI for all user types)
  • Role UI: app/components/roles/ (modals and helpers)
  • Utils: utils/roles.ts (queries, mutations, types)

Where the UI appears

User lifecycle and role management are exposed in:

  • Alaner profile: app/pages/AlanerProfile/RolesManagement.tsx wraps RolesManagementGeneric for alaners.
  • External user profile: app/pages/ExternalUserProfile/RolesManagement.tsx wraps RolesManagementGeneric for external users.
  • Service account: app/pages/ServiceAccountManager/RolesManagement.tsx wraps RolesManagementGeneric for service accounts.
  • Team area: app/pages/Team/MembersTab.tsx uses TeamMembershipRequestModal for role grants/requests in the team context.
  • Oncall roster: app/pages/Oncall/OncallRosterTab.tsx uses the same role-request flow when adding/removing members (e.g. link to role request by role_request_id).
  • Role definitions review: app/pages/RoleWithGrantRulesReview.tsx — read-only table of all role definitions grouped by scope (prefix before first :), showing grant rules (with GitHub links), grant_management_permitted_for, and self-service eligibility.

Role request flow (frontend)

  1. Request a role
  2. User clicks “Request a role” (or equivalent), opening RoleRequestModal (app/components/roles/RoleRequestModal.tsx).
  3. Modal is prefillable via URL search params: role, sub-scope, scope, request-reason, start-at.
  4. User selects scope (e.g. area, unit, crew, oncall group), optional sub-scope, role, start/end dates, duration (if applicable), and request reason.
  5. Submit uses useAddUserRoleRequestMutation (POST to /{alaners|external-users|service-accounts}/{id}/role-requests). On success, the UI shows either “auto-approved” or a link to the Slack thread for approval.
  6. For team membership, TeamMembershipRequestModal reuses the same mutation to request the appropriate org role (e.g. area/crew/oncall member).

  7. Pending requests list

  8. UserRoleManagement loads pending requests with useUserRoleRequests(userId, isServiceAccount) (GET /{type}/{id}/role-requests).
  9. Pending requests are those with approved_at === null and rescinded_at === null. They are listed and can be opened for approval or rescission.

  10. Approval / denial

  11. Clicking a pending request opens RoleApprovalModal (app/components/roles/RoleApprovalModal.tsx).
  12. Approvers see requester, grantee, role id, dates, and request reason. They can optionally edit reason and end date via useUpdateRoleRequestMutation (PATCH on the role request).
  13. Approve/deny is done with useAnswerRoleRequestMutation (PUT /{type}/{id}/role-requests/{roleRequestId} with body { approved: true|false, ... }). Success invalidates role-requests and role-grants queries.
  14. Visibility of the “Approve”/“Deny” actions is gated by roleDefinitions[roleRequest.role_id].grant_management_permitted_for (and, where used, useUserPermission / policy checks).

  15. Rescission

  16. Requester or grantee can rescind a request (or an active grant that came from a request) via useRescindUserRoleRequestMutation (DELETE /{type}/{id}/role-requests/{roleRequestId} with rescission reason).
  17. Rescission is offered from the same table row or grant row that shows the request/grant (e.g. in UserRoleManagement).

Data layer (roles)

All hooks and types live in utils/roles.ts. API paths follow /{alaners|external-users|service-accounts}/{id}/role-requests and .../role-grants as per backend.

  • Queries: useRoleDefinitions(), useRoleGrantRules(), useRoleDefinitionsWithGrantRules(), useUserRoleGrants(userId, isServiceAccount), useUserRoleRequests(userId, isServiceAccount), useUserRoleRequest(userId, roleRequestId, isServiceAccount), useLifecycleTaskLogs(userId, isServiceAccount).
  • Mutations: useAddUserRoleRequestMutation, useAnswerRoleRequestMutation, useUpdateRoleRequestMutation, useRescindUserRoleRequestMutation, useRunLifecycleTasksMutation (to trigger lifecycle sync).
  • Types: RoleRequest, RoleDefinition, RoleDefinitionWithGrantRules, RoleGrant, RoleGrantWithFutureFlag, UserLifecycleTaskLog, PrefilledModalParams (URL search param names for modal prefilling).

Role grants display

  • Active and future grants are loaded with useUserRoleGrants. Each grant can carry role_request_id; when present, the UI fetches useUserRoleRequest(granteeId, grant.role_request_id) to show who approved and when (see generateRoleGrantTooltipLabel in app/components/roles/utils.ts).
  • Tables list grants with optional “Requested”, “Approved by”, “Rescind” and “Reactivate” actions, depending on permissions and whether the grant stems from a request.

Lifecycle tasks

  • UserLifecycleTaskLogsModal shows task run history for the user (e.g. sync status). Triggering a run is done via useRunLifecycleTasksMutation from the same UI.