high priority low complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

A centered CircularProgressIndicator is displayed when the BLoC/Riverpod state is ContactListLoading
An empty state widget (illustration + message) is shown when the contact list is empty and no search query is active
A distinct empty state widget (illustration + message) is shown when a search query returns zero results, clearly indicating no matches were found
Empty state messages reference org-label terminology (e.g., 'peer mentor' vs 'likeperson') resolved from the OrgLabels system
Empty state messages are role-aware: coordinator sees different message than peer mentor
A network error state is displayed when data fetch fails, including a visible retry button
Tapping the retry button dispatches a reload event to the BLoC/Riverpod provider
All states (loading, empty-no-contacts, empty-search, error) transition without layout jank or widget tree rebuilds outside their subtree
CircularProgressIndicator uses the design token accent color, not the default Material blue
Loading and empty state widgets meet WCAG 2.2 AA contrast requirements
Empty state illustration is decorative and marked with excludeSemantics: true; the message text is exposed to screen readers

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
data models
Contact
PeerMentor
OrgLabels
performance requirements
State transitions must complete within one frame (16ms budget)
Empty state widget must not trigger unnecessary Supabase calls
security requirements
Error messages must not expose raw Supabase error strings or stack traces to the UI
ui components
CircularProgressIndicator (themed)
EmptyStateWidget (reusable, accepts illustration asset + message string)
NetworkErrorWidget (reusable, accepts retry callback)
AppButton (existing reusable widget for retry CTA)

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Use a BLoC/Riverpod state sealed class or enum with distinct variants for Loading, Loaded, EmptyNoContacts, EmptySearchResults, and Error. Avoid overloading a single 'empty' state — the empty-due-to-search vs empty-due-to-no-data distinction requires separate UI copy. Build EmptyStateWidget and NetworkErrorWidget as generic, reusable widgets in the shared widgets library so they can be used across other screens. Resolve org-label strings via the existing OrgLabels system rather than hardcoding — pass the resolved string into EmptyStateWidget.

Keep illustration assets lightweight SVGs or simple Lottie animations; avoid large PNGs. The retry callback should re-dispatch the same event that triggered the initial load, keeping the BLoC as the single source of truth.

Testing Requirements

Unit-test each BLoC/Riverpod state mapping in isolation. Widget-test ContactListScreen with mock states: (1) ContactListLoading → verify CircularProgressIndicator present, (2) ContactListEmpty (no-contacts) → verify correct illustration and role-aware org-label message, (3) ContactListEmpty (search-no-results) → verify search-specific message, (4) ContactListError → verify error widget and retry button present, (5) tap retry → verify reload event dispatched. Test org-label substitution by providing alternate label fixtures. Run all tests with flutter_test; no golden tests required for this task.

Component
Contact List Screen
ui medium
Epic Risks (2)
medium impact medium prob technical

Design token color values used in role badges, certification status indicators, and availability chips may not meet the WCAG 2.2 AA contrast ratio of 4.5:1 when rendered against card backgrounds, requiring rework after accessibility review and potentially blocking acceptance sign-off.

Mitigation & Contingency

Mitigation: Run the contrast-ratio-validator on every new token combination during widget development. Enforce the CI accessibility lint runner on all PRs touching visualization components, and validate against the contrast-safe-color-palette before finalizing card designs.

Contingency: If contrast failures are found late, adjust token values in the design token theme centrally — since all widgets consume design tokens rather than hardcoded colors, all affected widgets will be corrected by a single token update without per-widget changes.

low impact medium prob dependency

The ContactViewSwitcher is required for Barnekreftforeningen but must not appear for other organizations. If the organization labels provider does not yet expose a reliable feature flag for this widget, it may render universally or be conditionally hidden in an inconsistent way, breaking the role-specific layout contract.

Mitigation & Contingency

Mitigation: Implement view switcher visibility as a constructor parameter on ContactListScreen injected from a provider, defaulting to hidden. Document the integration point for the org labels provider so the flag can be wired without changing the widget's API.

Contingency: If org labels integration is delayed beyond this epic, use a feature flag constant keyed to the Barnekreftforeningen organization ID as a temporary gate, with a tracked issue to replace it with the runtime labels provider before general release.