high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

Selecting a role from RoleSwitchWidget dispatches RoleChangedEvent to the Role State Manager within the same frame
A brief loading indicator (CircularProgressIndicator or shimmer) is shown on the home screen or app bar during role transition
Loading indicator is dismissed once the new role state is confirmed (BLoC emits RoleActiveState)
A SnackBar or toast confirmation is shown with the message 'Switched to [Role Name]' after successful role change
The home screen re-renders immediately to show role-specific content for the newly active role
The bottom navigation re-renders to show only the tabs permitted for the new role without a full app restart or Navigator pop-to-root
The role transition completes within 500ms from user selection (excluding any async Supabase calls)
If role change fails (network error, permission denied), an error SnackBar is shown and the previous role remains active
The confirmation SnackBar is accessible (Semantics label, sufficient duration ≥4s per WCAG 2.2)
No duplicate state emissions occur if the user selects the currently active role

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase — optional role validation call if server-side confirmation is required
data models
ActiveRoleState
RoleChangedEvent
RoleTransitionState
performance requirements
Role state cascade must complete UI re-render within 500ms
No unnecessary widget rebuilds outside consumers of the role state
security requirements
Role change must be validated against the user's permitted roles list before state update — prevent arbitrary role injection
Do not persist active role to device storage without encryption
ui components
CircularProgressIndicator or shimmer overlay during transition
SnackBar with role confirmation message
BlocBuilder/Consumer wrappers on HomeScreen and RoleAwareBottomNav

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use BlocListener (not BlocBuilder) for side effects like SnackBar and loading overlay — BlocBuilder only for widget tree reconstruction. Wrap the loading indicator in an IgnorePointer to prevent accidental taps during transition. The SnackBar should use ScaffoldMessenger.of(context).showSnackBar() to avoid context issues. Ensure the Role State Manager BLoC is provided at a high enough level in the widget tree (e.g.

MaterialApp level or above the Navigator) so both HomeScreen and RoleAwareBottomNav rebuild from the same state. Use equatable on BLoC states to prevent duplicate emissions. For Riverpod users: use a StateNotifier and watch the provider at the relevant widget level.

Testing Requirements

Write BLoC unit tests: (1) assert RoleChangedEvent transitions state to RoleLoadingState then RoleActiveState, (2) assert error event transitions to RoleErrorState with previous role preserved. Widget tests: (1) mock BLoC and verify SnackBar appears after state emission, (2) verify loading indicator shown during RoleLoadingState, (3) verify home screen rebuilds with correct role variant after RoleActiveState. Integration test: select role, assert full UI update cycle completes. Test edge case: selecting the already-active role produces no state change.

Target 85% branch coverage on state transition logic.

Component
Role Switch Widget
ui medium
Epic Risks (3)
high impact medium prob technical

Combining GoRouter's declarative redirect logic in the route guard with StatefulShellRoute's stateful branch management is known to produce subtle bugs where the shell rebuilds unnecessarily on role switches, losing tab state or causing double-navigation events.

Mitigation & Contingency

Mitigation: Implement the route guard as a GoRouter redirect callback that only evaluates role from an already-resolved Riverpod provider (not async). Use a dedicated ShellRoute navigator key per tab branch to anchor state independently of role-driven rebuilds. Write integration tests for the full navigation graph.

Contingency: If StatefulShellRoute state loss is confirmed during QA, fall back to a manual tab state preservation approach using a TabStateManager service that caches the last route per tab and restores it after role switches, decoupling tab state from the shell lifecycle.

medium impact high prob scope

The role-based home screen must render three significantly different layouts (coordinator dashboard, peer mentor activity summary, org admin overview). If these variants are implemented as a single widget with conditionals, the file will become unmaintainable and difficult to test in isolation, especially as each variant grows with downstream feature additions.

Mitigation & Contingency

Mitigation: Design the role-based home screen as a router/dispatcher widget that delegates to three separate variant widgets (CoordinatorHomeView, PeerMentorHomeView, OrgAdminHomeView). Each variant is independently testable and can be developed by separate team members in parallel.

Contingency: If variant coupling has already occurred before this risk is addressed, refactor to the dispatcher pattern in a dedicated cleanup task before feature handoff. The dispatcher pattern is a straightforward extraction that carries low refactoring risk.

medium impact medium prob integration

The no-access screen must link global admin users to the correct admin portal URL, which may differ per organization (NHF, HLF, Blindeforbundet each have their own admin portals). Hardcoding a single URL will result in wrong or broken links for some global admin users.

Mitigation & Contingency

Mitigation: Source the admin portal URL from the organization's configuration record in Supabase rather than hardcoding it. The no-access screen reads the active org context and resolves the portal URL dynamically. Provide a safe fallback to a generic Norse Digital Products support page if the URL is not configured.

Contingency: If dynamic URL resolution is not ready when the no-access screen ships, display a static instruction to contact the organization's administrator along with a support email address as an interim measure, and track the URL configuration task as a follow-up.