high priority medium complexity frontend pending frontend specialist Tier 9

Acceptance Criteria

Long-pressing a list item opens a context menu with at minimum an 'Archive' option
Tapping the three-dot overflow menu on a list item also exposes the 'Archive' option
Tapping 'Archive' opens a bottom sheet confirmation with the exact message: 'Archiving this activity type will hide it from peer mentor selection. All historical activity records are preserved.'
The bottom sheet has two actions: 'Cancel' (dismisses sheet, no action) and 'Archive' (confirms)
On confirmation, the item is optimistically removed from the active list immediately (before API call completes)
A snackbar appears with message 'Activity type archived' and an 'Undo' action button; the snackbar auto-dismisses after 5 seconds
If the API call fails, the item is restored to its original position in the list and an error snackbar is shown
Tapping 'Undo' within 5 seconds cancels the archive operation (calls ActivityTypeService.unarchive() or equivalent) and the item reappears in the list
A 'Show archived' toggle in the list header filters the list to include archived items when enabled
Archived items displayed when the toggle is enabled show the grey 'Archived' status badge and cannot be archived again (action hidden/disabled)
The archive action is not available to the peer mentor role (button/menu entry hidden)

Technical Requirements

frameworks
Flutter
BLoC / flutter_bloc
apis
ActivityTypeService.archive(activityTypeId)
ActivityTypeService.unarchive(activityTypeId)
ActivityTypeService.getAll(orgId, includeArchived: true)
data models
ActivityType
performance requirements
Optimistic removal animates out within one frame using AnimatedList or equivalent
Snackbar appears within 100ms of confirmation tap
security requirements
Archive action only available to coordinator and org-admin roles — enforce check in Cubit before dispatching to service, in addition to RLS on Supabase
Undo operation must re-validate the session before calling unarchive to prevent replay attacks on expired tokens
ui components
ModalBottomSheet confirmation dialog
PopupMenuButton (three-dot overflow menu)
GestureDetector (long-press) with HapticFeedback
ScaffoldMessenger SnackBar with action button
AnimatedList for smooth item removal/reinsertion
Toggle filter widget in list header (Switch or FilterChip)

Execution Context

Execution Tier
Tier 9

Tier 9 - 22 tasks

Can start after Tier 8 completes

Implementation Notes

Manage the optimistic update entirely in the ActivityTypeListCubit: on `archiveRequested(id)`, immediately emit a new state with the item removed, then call the service. If the service throws, emit a `revertArchive(item, originalIndex)` event to restore. Use `AnimatedList` keyed by activityType.id to animate removal and reinsertion smoothly. For the undo snackbar, store a reference to the `ScaffoldFeatureController` returned by `ScaffoldMessenger.showSnackBar()` — call `controller.close()` if the API call fails before the snackbar auto-dismisses (to prevent a confusing 'Undo' button after a failed archive).

The 'Show archived' toggle should be a local widget state (no need for Cubit involvement) that gates whether `getAll(includeArchived: true)` or the cached active list is displayed. Use `HapticFeedback.mediumImpact()` on long-press to provide tactile confirmation before the context menu appears.

Testing Requirements

Write widget tests for: (1) long-press triggers context menu; (2) three-dot menu exposes archive option; (3) Cancel on bottom sheet dismisses without action; (4) Confirm triggers optimistic removal and snackbar; (5) Undo snackbar action restores the item; (6) API failure causes item revert and error snackbar; (7) toggle filter shows/hides archived items; (8) archive action is absent when role is peer_mentor. Write an integration test against the Supabase local emulator: archive an item, verify it moves to archived state in the database; trigger undo, verify it returns to active state. Test the 5-second auto-dismiss by using fake timers in flutter_test.

Component
Activity Type Admin 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.