Add deduplication warning banner and retry UI
epic-bufdir-data-aggregation-ui-task-003 — Extend AggregationProgressIndicator with a dismissible warning banner that surfaces when the BLoC emits AggregationWarning state, showing the deduplication anomaly count and a description of what the anomaly means. Add a retry button that dispatches RetryAggregation event on AggregationFailed state. Ensure the banner uses semantic live-region announcements for screen reader compatibility.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Use BlocConsumer or BlocBuilder with a buildWhen condition to handle the warning banner visibility state locally within the widget — track a local `_warningDismissed` bool in a StatefulWidget wrapper. Reset `_warningDismissed` to false whenever a new AggregationWarning is received so the banner reappears after retries. For the live region announcement, wrap the warning banner content in `Semantics(liveRegion: true, child: ...)` — Flutter's VoiceOver and TalkBack implementations will announce liveRegion widgets when they appear or change. Keep the close button as an IconButton with Icons.close and an explicit Semantics label ('Dismiss warning').
The retry button should call `context.read
Testing Requirements
Widget tests using flutter_test. Test cases: (1) emit AggregationWarning — banner appears with correct anomaly count and description; (2) tap dismiss button — banner hides; (3) emit another AggregationWarning after dismiss — banner reappears; (4) emit AggregationFailed — error message and retry button appear; (5) tap retry button — RetryAggregation event is dispatched to the BLoC (verify via MockBloc); (6) emit AggregationInProgress after AggregationFailed — retry button and error message hide; (7) Semantics test confirming liveRegion is true on the warning banner container. Use MockBloc from bloc_test package to capture dispatched events.
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.
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.