high priority low complexity frontend pending frontend specialist Tier 5

Acceptance Criteria

A 'Behaviour flags' section header is rendered above the three toggles using the design token section header typography
Each toggle is rendered as a SwitchListTile (or equivalent project widget) with a title, subtitle (helper text), and the switch control
Toggle 1 — isTravelEligible: title 'Travel reimbursement applies', subtitle 'Peer mentors can claim travel expenses for this activity type', default false
Toggle 2 — isReportRequired: title 'Report required', subtitle 'Peer mentors must submit a session report after completing this activity', default false
Toggle 3 — isReimbursementTrigger: title 'Expense reimbursement eligible', subtitle 'This activity type triggers the expense reimbursement workflow', default false
Each toggle touch target is at minimum 48x48dp as required by WCAG 2.2 AA — verified by a golden test or accessibility audit
Each toggle emits the correct BLoC event on change (e.g., ActivityTypeTravelEligibleChanged(value))
Toggle states are preserved in BLoC state and restored correctly on screen rebuild and orientation change
In edit mode, all three toggles are pre-populated from the fetched ActivityType values
All three toggles default to false in creation mode
The entire section is contained in a design-token card widget consistent with the rest of the form
Semantic label is set on each toggle for screen reader compatibility (VoiceOver/TalkBack announces the toggle name and current state)
The section renders correctly on both narrow (360dp) and wide (600dp+) screens without overflow

Technical Requirements

frameworks
Flutter
BLoC
data models
ActivityType (isTravelEligible, isReportRequired, isReimbursementTrigger fields)
performance requirements
Toggle state changes must reflect in the UI within one frame (< 16ms) — no async operations in the toggle handler
security requirements
isReimbursementTrigger toggle must not be shown to peer mentors — it is a coordinator/admin-only configuration field; validate that the role guard from task-004 covers the entire screen
ui components
SwitchListTile (Flutter Material) or custom equivalent matching design token system
Card or Container with design token border radius and background color for the section
Text widget with section header style from design token typography
BlocBuilder<ActivityTypeFormBloc, ActivityTypeFormState> wrapping the section
Semantics widget if SwitchListTile semanticLabel is insufficient

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Use SwitchListTile.adaptive() to respect platform conventions (Cupertino switch on iOS, Material on Android) — check whether the project already uses .adaptive() elsewhere and match. Wrap the three SwitchListTiles in a Column inside a Card or design-token container; do not use a ListView.builder for only three items. Set the SwitchListTile's contentPadding using design token spacing constants to ensure consistent alignment with text fields above. The semantic label should be the full descriptive title string — e.g., 'Travel reimbursement applies, switch, on'.

Test with TalkBack/VoiceOver during development to confirm announcement quality. If isTravelEligible and isReimbursementTrigger are conceptually linked (travel implies reimbursement), consider adding a one-line informational note — but do NOT automatically change one toggle when the other is toggled without explicit product sign-off.

Testing Requirements

Write widget tests: (1) all three toggles render with correct title and subtitle text; (2) tapping isTravelEligible toggle dispatches ActivityTypeTravelEligibleChanged(true) then ActivityTypeTravelEligibleChanged(false) on second tap; (3) identical tests for isReportRequired and isReimbursementTrigger; (4) in edit mode with all flags true, all three toggles render in the ON state; (5) in creation mode, all three toggles render in the OFF state; (6) orientation change preserves all toggle states via BLoC; (7) accessibility test using flutter_test's SemanticsController confirms each toggle has a meaningful semantic label and correct checked/unchecked state announced. Target 100% branch coverage for toggle interaction handlers.

Component
Activity Type Form Screen
ui medium
Epic Risks (3)
high impact medium prob dependency

The Bufdir reporting category list is defined externally by Bufdir and may change between reporting years. If the dropdown in ActivityTypeFormScreen is hardcoded, existing activity type mappings could become invalid after a Bufdir schema update, breaking export validation for all organisations.

Mitigation & Contingency

Mitigation: Store the valid Bufdir category list in a Supabase configuration table (bufdir_categories) rather than as a Dart constant, so it can be updated by an admin without a mobile app release. Load the list in the form screen via a lightweight repository call cached locally.

Contingency: If the Bufdir category list cannot be externalised before the admin screen ships, expose a manual override field that allows coordinators to enter a raw Bufdir category code as a fallback, and schedule the configuration table migration as a follow-up task.

medium impact medium prob technical

Reusing ActivityTypeFormScreen for both creation and editing requires careful Riverpod provider scoping. If the form provider is not properly reset between navigation events, stale values from a previously edited type may pre-populate a new creation form, leading to incorrect data being saved.

Mitigation & Contingency

Mitigation: Scope the form state provider to the route using Riverpod's autoDispose modifier, ensuring the state is torn down when the screen is popped. Write a widget test that navigates to edit type A, pops, navigates to create new, and asserts all fields are empty.

Contingency: If provider scoping proves complex with the current router setup, fall back to separate widget implementations for create and edit that share a common form widget but maintain independent provider instances.

high impact low prob integration

Archiving an activity type must not break historical Bufdir export queries that filter activities by type. If the export pipeline performs an INNER JOIN against only active activity types, archived types will cause historical activities to be silently excluded from exports, producing incorrect reporting data.

Mitigation & Contingency

Mitigation: Audit all downstream query builders (Bufdir export, stats aggregation) before shipping the archive feature to confirm they join against all activity types regardless of is_active status. Add an integration test that archives a type, then asserts historical activity records for that type still appear in export queries.

Contingency: If a downstream query is discovered to filter on is_active post-launch, apply a targeted Supabase view fix that unions active and archived types for export contexts without requiring a mobile app update.