high priority low complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

AssignedContactsList renders a ListView.builder with NeverScrollableScrollPhysics so it scrolls with the parent CustomScrollView without scroll conflicts
Each row displays the contact's full name and an assignment status indicator (e.g., colored dot or chip matching the design token for that status)
Each row is tappable and calls the provided onContactTap callback with the contact's ID — navigation is handled by the caller, not inside this widget
When the contacts list is empty, an empty-state widget is displayed with an icon and descriptive text (no rows rendered)
The list correctly handles 1, 5, 20, and 100 contacts without overflow, jank, or layout errors
The widget is stateless and receives contacts data and the onContactTap callback as constructor parameters — no internal data fetching or Supabase calls
Row height is consistent and meets the 44dp minimum touch target height
A section header (e.g., 'Assigned Contacts') is rendered above the list

Technical Requirements

frameworks
Flutter
flutter_test
data models
AssignedContact (id, displayName, assignmentStatus)
performance requirements
ListView.builder must be used (not ListView with children) to ensure O(1) rendering cost regardless of list length
Row widgets must not trigger rebuilds of sibling rows when one row changes
security requirements
Contact names rendered in rows must be HTML-escaped if displayed in any web-view context — not applicable for pure Flutter text widgets, but note this for future web embedding
ui components
ListView.builder with NeverScrollableScrollPhysics
Contact row widget (name Text + status indicator + InkWell tap handler)
Empty-state widget (icon + message Text)
Section header Text widget

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Define AssignedContactsList as a StatelessWidget with parameters: List contacts, void Function(String contactId) onContactTap, and an optional String sectionTitle. Use ListView.builder(shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), ...) for correct embedding in CustomScrollView. The shrinkWrap: true is necessary when the list is a child of a Column inside a CustomScrollView — document this constraint in code comments. For the status indicator, use a Container with a circular shape and a color derived from a switch on AssignedContact.assignmentStatus — map status enum values to design token colors.

For the empty state, create a simple EmptyContactsState widget (reuse or create new) with a neutral icon (e.g., Icons.people_outline) and the text 'No assigned contacts yet'. Keep the row widget as a private _ContactRow StatelessWidget inside the same file for encapsulation. Define the AssignedContact data class with the fields above, or reuse an existing model if one exists — check the codebase before creating a new one.

Testing Requirements

Write widget tests using flutter_test. Test 1: pass an empty list — assert the empty-state widget is present and no ListTile/Row widgets are rendered. Test 2: pass a list of 3 contacts — assert exactly 3 row widgets are rendered and each displays the correct name. Test 3: tap the first row — assert onContactTap is called with the correct contact ID.

Test 4: verify NeverScrollableScrollPhysics is applied by checking widget type in the tree. Test 5: pass 100 contacts — assert the widget builds without overflow errors (use tester.pump() and check for no exceptions). Use pump() not pumpAndSettle() to avoid timeout on large lists.

Component
Assigned Contacts List
ui low
Epic Risks (2)
medium impact medium prob dependency

The org labels system may not yet have label keys defined for peer mentor detail screen terminology (role labels, section headings), requiring additions to the label key registry that must be coordinated with the admin configuration team.

Mitigation & Contingency

Mitigation: Audit existing label keys in the terminology system before starting OrgLabelsProvider integration. Submit required new label keys for admin configuration in parallel with component implementation.

Contingency: If label keys are not available at integration time, use hardcoded English fallbacks with a clear TODO for admin configuration, ensuring the widget renders correctly while keys are being provisioned.

high impact low prob technical

The design token semantic colors (warning, error surface) may not meet WCAG 2.2 AA 4.5:1 contrast ratio when rendered on the app's background surface tokens, requiring design system changes that affect the entire app.

Mitigation & Contingency

Mitigation: Run contrast ratio validation on the token palette during Epic 1 design token implementation. Flag any failing pairs to the design system owner before building UI components that depend on them.

Contingency: If tokens fail contrast requirements, define supplementary high-contrast override tokens specific to alert and badge contexts that meet AA without modifying the global palette.