high priority medium complexity frontend pending frontend specialist Tier 12

Acceptance Criteria

ProxyActivityForm widget is created as a stateless or stateful widget accepting a GlobalKey<FormState> and an onChanged callback typed to ActivityFormData
Form contains exactly four fields: activity type (dropdown), date (date picker), duration (duration picker or numeric input), and notes (multiline text field)
ActivityFormData is an immutable value object with copyWith support covering all four fields with correct Dart types
All fields use design token values for spacing (padding, margin), typography (font size, weight, line height), and border radii — no hardcoded values
Tab/next keyboard action moves focus sequentially: type → date → duration → notes, with the notes field using TextInputAction.done
onChanged fires with the current ActivityFormData every time any field value changes
Widget renders correctly on screen widths from 320px to 428px (standard Flutter mobile range)
All form fields have semantic labels and hints for screen reader compatibility (WCAG 2.2 AA)
Default values are pre-populated: date defaults to today, duration defaults to 30 minutes
Form validates on submission: activity type and date are required; duration must be > 0

Technical Requirements

frameworks
Flutter
flutter_form_builder or native Flutter Form API
data models
ActivityFormData (value object)
ActivityType (enum)
performance requirements
Widget rebuilds must be scoped — avoid full-tree rebuilds on each keystroke by using ValueNotifier or BLoC stream scoping
Date picker and duration picker must open within 100ms of tap
security requirements
Notes field must not log or persist content to analytics or crash reporting services
No PII written to debug console
ui components
AppTextField (existing reusable widget) for notes
AppButton (existing reusable widget) — not rendered here but form must be composable with it
Design token–based DropdownButtonFormField for activity type
Design token–based date picker invoking showDatePicker
Duration picker (custom or package-based, e.g. duration_picker)

Execution Context

Execution Tier
Tier 12

Tier 12 - 5 tasks

Can start after Tier 11 completes

Dependencies (21)
epic-proxy-activity-registration-orchestration-task-001 component Cross-Epic Component proxy-activity-form depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-002 component Cross-Epic Component proxy-activity-form depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-003 component Cross-Epic Component proxy-activity-form depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-004 component Cross-Epic Component proxy-activity-form depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-005 component Cross-Epic Component proxy-activity-form depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-010 component Cross-Epic Component proxy-activity-form depends on proxy-registration-service
epic-proxy-activity-registration-orchestration-task-006 component Cross-Epic Component proxy-activity-form depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-007 component Cross-Epic Component proxy-activity-form depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-008 component Cross-Epic Component proxy-activity-form depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-009 component Cross-Epic Component proxy-activity-form depends on bulk-registration-service
epic-proxy-activity-registration-orchestration-task-011 component Cross-Epic Component proxy-activity-form depends on bulk-registration-service
epic-proxy-activity-registration-core-services-task-001 component Cross-Epic Component proxy-activity-form depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-002 component Cross-Epic Component proxy-activity-form depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-003 component Cross-Epic Component proxy-activity-form depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-008 component Cross-Epic Component proxy-activity-form depends on proxy-duplicate-detection-service
epic-proxy-activity-registration-core-services-task-004 component Cross-Epic Component proxy-activity-form depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-005 component Cross-Epic Component proxy-activity-form depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-006 component Cross-Epic Component proxy-activity-form depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-007 component Cross-Epic Component proxy-activity-form depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-009 component Cross-Epic Component proxy-activity-form depends on activity-attribution-service
epic-proxy-activity-registration-core-services-task-010 component Cross-Epic Component proxy-activity-form depends on activity-attribution-service

Implementation Notes

Define ActivityFormData as a Dart class with final fields and implement == / hashCode via equatable or manually — this prevents unnecessary BLoC events firing on identical values. Use TextEditingController for notes and dispose correctly in StatefulWidget. For the activity type dropdown, source enum values from a central ActivityType enum shared with the backend layer to prevent string drift. Duration picker: Flutter has no built-in duration picker; use the 'duration_picker' pub package or build a simple hours/minutes row with two NumberTextField widgets.

Apply all spacing via AppSpacing.x tokens and typography via AppTextStyle.x tokens from the design token system — never use raw doubles. The form must NOT include a submit button — that is the responsibility of the parent screen.

Testing Requirements

Unit tests: ActivityFormData copyWith, equality, and serialization. Widget tests (flutter_test): render all four fields, verify onChanged fires with correct typed data on each field interaction, verify keyboard focus traversal order, verify default values pre-populated, verify required-field validation errors appear on submit with empty fields. Accessibility test: verify all fields have semanticsLabel set. Golden test (optional): snapshot baseline for form at 390px width.

Component
Proxy Activity Form
ui medium
Epic Risks (4)
medium impact high prob scope

The bulk registration screen combines pre-filled defaults, a dynamic multi-select participant list, per-participant conflict badges, and a batch submission confirmation — making it one of the most complex screens in the application. Scope creep or underestimated interaction complexity could cause the epic to exceed its estimate significantly.

Mitigation & Contingency

Mitigation: Implement the bulk screen in two vertical slices: (1) participant selection and form submission without conflict handling, (2) conflict badge rendering and override flow. Validate slice 1 with coordinators before building slice 2.

Contingency: If the full conflict review UI cannot be completed within the epic, ship the bulk screen with a simplified 'skip all duplicates' fallback mode and defer per-participant override toggles to a follow-up sprint.

medium impact medium prob technical

The proxy-registration-bloc must manage state across two distinct flows (single proxy and bulk) with branching conflict paths, intermediate buffering of bulk participant selections, and reliable state reset. Incorrect state transitions could leave the UI in a loading or stale-conflict state after submission.

Mitigation & Contingency

Mitigation: Model the BLoC state as a sealed class hierarchy with exhaustive pattern matching in the UI. Write state-machine unit tests that exercise every valid transition and assert that invalid transitions are no-ops. Use flutter_bloc's BlocObserver in debug builds to log all transitions.

Contingency: If BLoC state bugs surface in QA, introduce an explicit ResetToIdle event triggered on screen disposal to guarantee clean state, and add a 'Start over' affordance visible to the coordinator at any conflict step.

high impact medium prob technical

The typeahead peer mentor selector with multi-select mode may be difficult to operate with VoiceOver/TalkBack, particularly the dynamic search results list and the selected-chip removal controls, risking WCAG 2.2 AA non-compliance for screen reader users.

Mitigation & Contingency

Mitigation: Wrap all search result items and selected chips with explicit Semantics widgets providing role, label, and selected-state announcements. Test the selector with VoiceOver on iOS and TalkBack on Android during development, not only at QA time.

Contingency: If the multi-select typeahead cannot be made fully accessible within the sprint, provide an alternative flat scrollable list with checkboxes as a fallback mode, toggled by an accessibility-settings flag.

high impact medium prob security

The peer mentor selector must apply RLS chapter-scope filtering to show only mentors the coordinator is responsible for. If the Supabase query for the selector does not correctly join against the coordinator's chapter assignments, coordinators may see mentors from other chapters, violating data isolation.

Mitigation & Contingency

Mitigation: Implement the selector's data query using the same RLS-aware Supabase client used by the contact list feature, which already handles chapter-scope filtering. Write an integration test with a multi-chapter coordinator fixture asserting cross-chapter mentors are not returned.

Contingency: If a data isolation breach is discovered, immediately add a client-side chapter_id filter as a defence-in-depth measure and audit selector query logs for any unauthorised cross-chapter results.