high priority low complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

NoAccessScreen renders a friendly, non-technical heading such as 'Admin Portal Required' or equivalent — no error codes or technical jargon visible to the user
Body copy clearly explains that global admin functions are available in the web admin portal, not the mobile app
A clearly labeled CTA button opens the admin portal URL in the device's default browser via url_launcher (or equivalent deep link mechanism)
A secondary 'Sign Out' button is present and functional — tapping it triggers logout and navigates to the auth screen
The screen renders with no bottom navigation bar and no hamburger menu — global admins must not see the main app navigation
All text elements meet WCAG 2.2 AA contrast ratio (minimum 4.5:1 for body text) against design token background colors
All interactive elements have a minimum touch target of 48×48dp
Semantic labels are present on the CTA button and Sign Out button for screen reader users
The screen is accessible via keyboard/switch access with logical focus order: heading → body → portal button → sign out button
NoAccessScreen does not make any Supabase data queries — it is a purely presentational redirect screen

Technical Requirements

frameworks
Flutter
Riverpod
apis
url_launcher (for opening admin portal URL in browser)
Supabase Auth (for sign out action only)
data models
UserRole (read-only, to confirm globalAdmin state)
performance requirements
Screen renders in under 100ms — it is purely static content with no data fetching
security requirements
Admin portal URL must be stored as a configuration constant, not hardcoded inline — allows environment-specific overrides (staging vs. production portal URL)
Sign out must call Supabase Auth signOut() and clear all local session state before navigating away
This screen must only be reachable when RoleState is globalAdmin/blocked — navigation guards elsewhere enforce this, but NoAccessScreen itself should assert the role in debug builds
ui components
NoAccessScreen (this widget)
AppButton (reusable — primary variant for portal CTA, secondary/outlined variant for Sign Out)
Design token typography and color classes for heading and body text

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

NoAccessScreen is intentionally simple — resist adding complexity. It is a dead-end screen for a narrow user segment (global admins who accidentally open the mobile app). The tone should be helpful and clear, not alarming — something like: 'You're set up as a global admin. To manage your organisation, please use the admin portal.' followed by a link button.

Store the admin portal URL in a constants file or app configuration (AppConfig.adminPortalUrl) so it can be environment-overridden in staging. Use url_launcher's launchUrl() with LaunchMode.externalApplication to open in the browser rather than an in-app WebView — global admins should be fully in their browser context for admin tasks. Ensure the Sign Out button uses the same logout flow as the settings screen to avoid divergence. Apply design tokens for all colors and typography — do not hardcode any hex values or font sizes.

Testing Requirements

Widget tests using flutter_test. Test: (a) screen renders with correct heading, body text, and both buttons visible, (b) portal CTA button tap calls url_launcher with the correct admin portal URL — mock url_launcher and assert the launched URL, (c) Sign Out button tap triggers auth sign out and navigates to auth screen — use NavigatorObserver and mock the auth provider, (d) bottom nav and hamburger menu are NOT present in the widget tree. Accessibility test: assert Semantics nodes exist with non-empty labels on both buttons. Contrast check: use flutter_test's finder to verify text widgets use design token color values (not hardcoded), as a proxy for contrast compliance.

Component
No-Access Screen
ui low
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.