high priority low complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

MentorStatusEvent is declared as a sealed class with two subclasses: PauseMentorEvent (required String reason) and ReactivateMentorEvent (no payload)
MentorStatusState is declared as a sealed class with four subclasses: MentorStatusInitial, MentorStatusLoading, MentorStatusSuccess (required MentorStatus newStatus), MentorStatusError (required String message, required bool isInvalidTransition)
All event and state classes use Equatable (or @immutable + override == / hashCode) following existing BLoC conventions
File structure follows the existing feature-layer pattern (e.g., mentor_status_event.dart, mentor_status_state.dart, mentor_status_bloc.dart or a single barrel)
MentorStatusInitial and MentorStatusLoading carry no payload — they are simple value objects
MentorStatusSuccess.newStatus is typed as MentorStatus enum, not a raw string
MentorStatusError.isInvalidTransition defaults to false so callers that only care about the message need not handle it explicitly
Dart sealed class exhaustiveness is enforced — all switch expressions over MentorStatusState must be exhaustive at compile time

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Equatable
data models
MentorStatus (enum)
MentorStatusEvent
MentorStatusState
security requirements
Event payloads (reason string) must not exceed a documented maximum length — define a MentorPauseReasonMaxLength constant

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Use Dart 3 sealed classes (sealed class MentorStatusState {}) — this enables exhaustive pattern matching in switch expressions and is the modern BLoC pattern. Pair with Equatable from the equatable package for value equality; call super([...props]) with all fields in props getter. Place events and states in separate files: mentor_status_event.dart and mentor_status_state.dart, then export both from mentor_status_bloc.dart barrel. Define MentorStatus enum in a shared domain types file (e.g., mentor_domain.dart) so it can be imported by both the BLoC and the UI widgets without creating a circular dependency.

The isInvalidTransition flag distinguishes 'cannot pause an already-paused mentor' (UI should show a specific message) from generic network failures (generic retry message).

Testing Requirements

Unit tests: instantiate each event and state subclass, assert equality via Equatable. Unit tests: assert MentorStatusError.isInvalidTransition defaults to false when not provided. Static analysis: ensure all switch(state) expressions in the codebase using MentorStatusState are exhaustive (dart analyze must pass with no warnings). Unit test: verify PauseMentorEvent with identical reason strings are equal (Equatable correctness).

Component
Mentor Status BLoC
infrastructure low
Epic Risks (3)
medium impact medium prob technical

PauseConfirmationDialog must meet WCAG 2.2 AA focus trap requirements: when the dialog opens, focus must move to the first interactive element; when it closes, focus must return to the triggering toggle. Flutter's default showDialog does not always guarantee correct focus restoration on Android, which could trap screen-reader users.

Mitigation & Contingency

Mitigation: Use a custom modal implementation wrapping Flutter's Dialog with explicit FocusScope management and test with TalkBack on Android and VoiceOver on iOS during development, not just in final QA. Reference the accessible-modal-sheet pattern already used elsewhere in the codebase.

Contingency: If full WCAG focus management cannot be achieved within the sprint, ship the feature with a documented accessibility defect and schedule a dedicated accessibility remediation task. Communicate the known gap to the accessibility stakeholder at HLF/NHF.

low impact medium prob technical

If the user taps the PauseReactivateToggle rapidly before the BLoC emits a loading state, multiple PauseMentorEvents could be added to the BLoC stream, resulting in duplicate service calls and inconsistent UI state (toggle flickering between states).

Mitigation & Contingency

Mitigation: Disable the toggle widget immediately on first tap by emitting MentorStatusLoading synchronously before the async service call begins. Use BLoC's event transformer with droppable() to discard subsequent events while a transition is in progress.

Contingency: If BLoC event deduplication is not sufficient, add a debounce at the widget level (300 ms) and a server-side idempotency check in MentorStatusService that no-ops if the requested transition is already in progress.

low impact low prob security

PauseStatusBanner must conditionally render the reactivate shortcut only for users with coordinator permission. If the permission check relies on a stale role state in the Riverpod provider, a peer mentor could briefly see a reactivate action they are not authorised to use, which could cause a confusing permission-denied error if tapped.

Mitigation & Contingency

Mitigation: Source the permission check directly from the role-resolution Riverpod provider (which is kept in sync with Supabase auth) rather than from local component state. Add a widget test asserting the shortcut is absent when the user role is peer_mentor.

Contingency: If the shortcut is accidentally shown to an unauthorised user, the underlying MentorStatusService enforces role validation server-side, so the worst outcome is a visible error message rather than an actual unauthorised state change.