high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

CoordinatorRecruitmentDashboard widget renders as a named section within the coordinator statistics screen without breaking existing stats sections
Per-mentor referral stats list displays each mentor row with: mentor display name, links issued count, clicks count, confirmed registrations count — all sourced from BLoC state
Each mentor row is tappable and navigates to MentorReferralDetailScreen (task-006) passing mentorId and active date range
Dashboard section uses design system card components (AppCard or equivalent) with no inline styles — all styling via design tokens
Color coding for referral stat columns follows unified entity color system: issued uses primary, clicked uses secondary, registered uses success accent consistently
Loading state renders skeleton placeholder cards (one per expected mentor row) while BLoC is in loading state
Empty state renders a descriptive empty-state widget when no referral data exists for the selected date range
Error state renders a retry button that dispatches a reload event to the BLoC
Widget rebuilds reactively when BLoC emits new state (date range change, data refresh)
Dashboard section header shows the active date range label (e.g. 'Last 30 days') sourced from BLoC state
All mentor name text is accessible with semantic labels for screen readers (Semantics widget wrapping stat values)
Widget is covered by widget tests verifying all three states: loading, loaded, error

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
apis
ReferralAttributionService (internal)
Supabase PostgreSQL 15 via repository layer
data models
assignment
contact
activity
performance requirements
Mentor list must render first frame within 300ms from BLoC loaded state emission
List must support at least 100 mentor rows without jank — use ListView.builder, not Column
No redundant rebuilds: use BlocBuilder with buildWhen to scope rebuilds to referral data changes only
security requirements
Coordinator-scoped data only: BLoC must receive organization_id from auth context, never from UI layer
No PII (email, phone) displayed in the stats list rows — display name only
RLS enforced server-side; UI must not implement its own access filtering as sole guard
ui components
CoordinatorRecruitmentDashboard (new StatelessWidget/BlocBuilder)
MentorReferralStatRow (new reusable row widget)
ReferralStatChip (count + label chip per stat type)
SectionHeader widget (reuse existing pattern)
AppSkeletonCard (loading placeholder)
AppEmptyState (empty state widget)
AppErrorRetry (error state widget)

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Slot CoordinatorRecruitmentDashboard as a child widget inside the existing coordinator statistics screen's scrollable body — do not replace or restructure existing stats sections. Use BlocBuilder and gate the section on coordinator role check already present in the screen. For the mentor stat rows, avoid rebuilding the entire list on every BLoC emit — consider using const constructors for MentorReferralStatRow where data is stable. Design tokens for colors must be sourced from AppColors or equivalent token file, not hardcoded hex values.

Reuse the existing SectionHeader pattern from the coordinator home screen for visual consistency. Keep widget files under 150 lines — extract MentorReferralStatRow to its own file.

Testing Requirements

Widget tests (flutter_test) required for CoordinatorRecruitmentDashboard covering: (1) loading state renders skeleton cards, (2) loaded state with mock data renders correct mentor count and stat values, (3) error state renders retry button and dispatching tap triggers BLoC reload event, (4) empty state renders when BLoC emits empty list, (5) tapping a mentor row invokes correct navigation with correct arguments. Use MockBloc (mocktail) for BLoC injection. No integration tests required for this widget in isolation — integration covered by coordinator statistics screen tests.

Component
Coordinator Recruitment Dashboard
ui medium
Epic Risks (3)
medium impact high prob dependency

BadgeCriteriaIntegration must reference specific badge definition IDs from the badge-definition-repository for recruitment badges. If those badge definitions have not been created in the database when this epic is implemented, the integration will silently fail to award badges.

Mitigation & Contingency

Mitigation: As the first task of this epic, create the four recruitment badge definitions (seed data migration) with known, stable IDs. BadgeCriteriaIntegration hardcodes these IDs as constants. Include an assertion in the integration tests that verifies the badge definition records exist in the test database.

Contingency: If the badge definitions system does not support seeding at migration time, store the badge definition IDs in a feature-flag-style config table and look them up at runtime, falling back to a no-op with a warning log if they are absent.

medium impact medium prob technical

The coordinator dashboard aggregates referral stats across all peer mentors in an organisation. For large organisations (HLF has many peer mentors nationally), the aggregation query may be slow, causing the dashboard to feel unresponsive.

Mitigation & Contingency

Mitigation: Implement the aggregation as a Supabase database view or RPC that runs server-side with appropriate indexes on (mentor_id, org_id, created_at, event_type). Add a composite index on referral_events during the foundation epic's migration. Cache the result in the Riverpod provider with a 5-minute TTL.

Contingency: If query performance remains unacceptable at scale, materialise the aggregation in a nightly pg_cron job into a stats_cache table, and serve the dashboard from the cache with a 'last updated' timestamp shown to the coordinator.

medium impact low prob integration

The existing badge award service is implemented by the achievement-badges feature. If that feature's public API (BadgeAwardService interface) changes while this epic is in progress, the BadgeCriteriaIntegration will break at compile time or behave incorrectly at runtime.

Mitigation & Contingency

Mitigation: Confirm the BadgeAwardService interface is stable and document the exact method signatures this integration depends on. Write a narrow integration test that constructs the real BadgeAwardService against a test database to detect breaking changes immediately.

Contingency: If the badge service interface changes, adapt the BadgeCriteriaIntegration adapter class to match the new contract. The adapter pattern used here isolates the change to a single class.