high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

AggregationSummaryWidget accepts a required AggregationSummaryData constructor parameter and renders without errors when all fields are populated
Participant count card displays the total participant count with Norwegian locale formatting (e.g., 1 234 for 1234)
Activity hours table renders one row per Bufdir category with category name, total hours, and percentage of total columns
Geographic distribution section shows a breakdown list with region name, chapter count, and participant count per entry
All numeric values use Norwegian locale thousand separator (non-breaking space or period per nb_NO locale) via intl package formatters
Widget is entirely stateless — no setState, no BLoC subscriptions, no direct Supabase calls inside the widget
Widget renders correctly with an empty AggregationSummaryData (zero counts, empty lists) without throwing or displaying broken layout
Widget renders correctly when geographic list exceeds 20 entries without horizontal overflow or layout constraint errors
All design token classes (card, table, section header) are used — zero hardcoded color or spacing values
Activity hours table columns are right-aligned for numeric data and left-aligned for text labels
Participant count card uses the stat-card design token with correct data-accent attribute matching Bufdir entity color

Technical Requirements

frameworks
Flutter
Dart intl package (NumberFormat for nb_NO locale)
data models
activity (activity_type_id, duration for hours aggregation)
bufdir_column_schema (category mappings for Bufdir categories)
annual_summary (aggregated participant and hours data)
performance requirements
Widget build method must complete in under 16ms on mid-range Android device (no expensive synchronous computation inside build)
Geographic list must use ListView.builder for lists exceeding 10 entries to avoid building all items at once
NumberFormat instances should be cached as final fields, not recreated on every build
security requirements
Widget must not display raw user IDs or internal UUIDs — only aggregated counts
No PII (names, personal identifiers) rendered in this summary widget — aggregate data only
ui components
StatCard widget (design token stat-card with data-accent)
DataTable or custom TableWidget using design token table classes
SectionHeader widget for section titles
ListView.builder for geographic distribution list
ListTile or CompactListItem for geographic rows

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use Dart's intl package with NumberFormat.decimalPattern('nb_NO') for all numeric formatting — instantiate once as a static/final field. The AggregationSummaryData model should be a pure Dart class (no fromJson complexity needed here — the BLoC state provides it). Structure the widget as a Column of three named sections: _buildParticipantCard(), _buildActivityHoursTable(), _buildGeographicDistribution() — each returning a Widget. This keeps the build method readable and each section independently testable.

For the activity hours table, avoid Flutter's built-in DataTable if the design token table classes are custom-rendered HTML-style; instead use a Column of Row widgets styled with design tokens. Ensure the widget is exported from the feature barrel file so BLoC-connected parent screens can import it cleanly.

Testing Requirements

Widget tests using flutter_test: (1) render with fully populated AggregationSummaryData — assert all three sections are present via find.byType and find.text; (2) render with zero-value data — assert no RenderFlex overflow or null errors; (3) locale formatting — assert '1 234' appears for value 1234 using nb_NO locale; (4) large geographic list (25 entries) — assert ListView.builder is used and no overflow errors; (5) golden tests for visual regression of the complete widget in light and dark themes. Target 90%+ line coverage for the widget file.

Component
Aggregation Summary Widget
ui medium
Epic Risks (2)
medium impact high prob scope

For NHF with 1,400 local chapters, rendering a geographic breakdown as a flat list in the AggregationSummaryWidget would produce an unscrollable wall of data that is unusable on a mobile screen. Coordinators need to verify geographic completeness before export, but the raw chapter list is too long to review.

Mitigation & Contingency

Mitigation: Design the geographic distribution section as a collapsible two-level hierarchy: regions are shown expanded by default with their aggregate count, individual chapters are collapsed under each region and expandable on tap. Show only non-zero regions by default with a 'show empty regions' toggle.

Contingency: If the hierarchical display is too complex to implement before the submission deadline, fall back to a region-only summary view with a total count of active chapters per region and a note that chapter-level detail is available in the full export file.

medium impact medium prob technical

The aggregation BLoC must handle a multi-stage async pipeline with partial success states, warning accumulation across stages, and retry logic for individual failed stages. If the state model is too simplistic (e.g., loading/success/error), warning states and partial results will be lost on retry, confusing coordinators who see warnings disappear and reappear.

Mitigation & Contingency

Mitigation: Model the BLoC state as a rich sealed class hierarchy: AggregationIdle, AggregationRunning(stage, completedStages, accumulatedWarnings), AggregationComplete(result, warnings), AggregationFailed(failedStage, accumulatedWarnings, error). Accumulated warnings persist across retry attempts so coordinators see the full warning history.

Contingency: If state management complexity causes repeated bugs near the deadline, simplify to a single AggregationResult value object that captures success, warnings, and error as nullable fields, and drive both UI components from that single object rather than a live BLoC stream.