medium priority low complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

Widget renders a scrollable list of activity history entries sourced from `contact-detail-service` with pagination (page size: 20 items)
Each row displays: activity type icon (from design token icon set), formatted date (locale-aware), duration in hours/minutes, and coordinator display name
Infinite scroll triggers the next page load when the user scrolls within 3 rows of the list end; a loading spinner is shown at the list bottom during fetch
Skeleton loading rows (3 placeholder items) are shown during the initial data fetch before first page arrives
Empty state: when no activities exist, a non-intrusive empty message is shown (e.g., 'No activity history yet')
Error state: if the data fetch fails, a retry button is shown with an accessible label
Each list row has a `Semantics` widget with a combined announcement: '[activity type], [date], [duration], coordinated by [name]' for screen readers
Focus returns to the last focused row after a page load (or to the first new row on append), not to the top of the list
Widget is stateless/presentation-only — all data and pagination state are owned by the parent BLoC/Riverpod provider
Widget renders correctly in both light and dark modes using design tokens only (no hardcoded colors)

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
contact-detail-service (activity history stream / paginated fetch)
data models
ActivityHistoryEntry (activityType, date, durationMinutes, coordinatorName)
ActivityType (enum with icon mapping)
performance requirements
Initial render of first 20 rows must complete within 300ms of data availability
List must maintain 60fps scroll performance on mid-range Android devices
Skeleton rows must appear within one frame of widget mount
security requirements
Coordinator name displayed in rows must come from the service layer, not from unvalidated user input
No PII beyond coordinator name and activity metadata is displayed in this widget
ui components
ActivityHistoryRow (row item widget)
ActivitySkeletonRow (loading placeholder)
ActivityEmptyState
ActivityErrorState with retry button
Pagination trigger via ScrollController listener

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use `ListView.builder` with a `ScrollController` attached. Detect near-bottom via `controller.position.pixels >= controller.position.maxScrollExtent - (3 * rowHeight)`. Pass a callback prop (e.g., `onLoadMore`) to keep the widget decoupled from the service. Use `shimmer` package or a simple `AnimatedContainer` pulse for skeleton rows — avoid third-party packages not already in pubspec.

Activity type icons should be mapped from a `const Map` in a shared constants file, not inline in the widget. All text styles must use design token `TextStyle` references. Date formatting must use `intl` package with the device locale.

Testing Requirements

Widget tests (flutter_test): (1) render with mock data list — verify each row shows correct icon, date, duration, coordinator name; (2) render with empty list — verify empty state widget is shown; (3) simulate scroll to bottom — verify pagination callback is invoked; (4) verify skeleton rows appear when state is loading; (5) verify error state renders retry button with accessible label. Accessibility test: use `flutter_test` `SemanticsHandle` to verify each row has a combined Semantics announcement. No golden tests required. Target ≥80% widget test coverage.

Component
Contact Detail Screen
ui medium
Epic Risks (2)
low impact medium prob dependency

The Peer Mentor Profile tab on the contact detail screen depends on the peer-mentor-detail-screen-widget being delivered by the separate Peer Mentor Detail feature. If that feature is delayed, the navigation affordance will be present but lead to a stub screen, which may confuse coordinators in the TestFlight pilot.

Mitigation & Contingency

Mitigation: Implement the peer mentor tab with a feature flag guard. When the Peer Mentor Detail feature is incomplete, the flag disables the tab. Coordinate delivery timelines with the team responsible for Peer Mentor Detail to align TestFlight releases.

Contingency: If the Peer Mentor Detail feature is significantly delayed, ship the contact detail screen without the peer mentor tab in the first TestFlight build and add it as an incremental update once the dependent screen is ready.

medium impact medium prob technical

The contact detail screen must adapt its layout significantly based on organisation context: NHF shows affiliation chips, Blindeforbundet shows encrypted fields and assignment status, standard contacts show neither. Managing this conditional rendering without introducing bugs in each variant is complex and increases the risk of organisation-specific regressions.

Mitigation & Contingency

Mitigation: Define a ContactDetailViewModel that resolves all org-specific flags (showEncryptedFields, showAssignmentStatus, showMultiChapterChips) from the organisation config before the widget tree renders. Widget tests must cover all three organisation variants as separate test cases to catch regressions.

Contingency: If conditional rendering logic grows unwieldy, refactor into separate composable section widgets (ProfileHeaderSection, AffiliationSection, EncryptedFieldsSection) that are conditionally included by the parent screen, isolating org-specific logic to individual components.