Wire state layer to ReportHistoryService
epic-bufdir-report-history-ui-task-005 — Create the BLoC or Riverpod provider/notifier for the report history feature that manages paginated loading, filter state, and re-export triggering. The state layer must delegate data fetching to ReportHistoryService and coordinate re-export actions through ReportReexportCoordinator. Expose distinct states for loading, loaded (with paginated list), empty, and error so the UI can render appropriate feedback.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
If using BLoC: define events as a sealed class (FetchReportHistory, FilterChanged, LoadNextPage, ReexportReport, RedownloadReport) and use `EventTransformer.droppable()` for LoadNextPage and `EventTransformer.restartable()` for filter changes to handle in-flight cancellation. If using Riverpod: use `AsyncNotifier` with an `updateFilter` method and a `loadNextPage` method guarded by an `_isFetching` flag. Model per-record re-export loading state as a `Map
Dispose the state layer at route pop to clear sensitive report data from memory.
Testing Requirements
Unit tests using flutter_test + mocktail: test that initial state is ReportHistoryInitial; test that after construction the first fetch is triggered and state transitions to ReportHistoryLoading then ReportHistoryLoaded; test that a filter change resets to page 1 and re-fetches; test that LoadNextPage appends records and updates hasNextPage correctly; test that LoadNextPage is ignored when already loading; test that service error on initial fetch transitions to ReportHistoryError; test that re-export success emits a SnackbarMessage side-effect without leaving loaded state; test that re-export error emits an error SnackbarMessage. All tests use mocked service/coordinator with pre-defined responses.
The ReportSummaryMetricsWidget must display metrics as they were at time of submission, not recalculated from current data. If the metrics are not stored as a JSON snapshot in the history record at export time, the widget will either show wrong data or require a full re-aggregation on every list load.
Mitigation & Contingency
Mitigation: Ensure the Bufdir export pipeline (bufdir-report-export feature) writes a summary_metrics JSONB column in the history record at export time, containing total_activities, total_hours, and participants_reached. The UI widget reads only from this snapshot field — never from live aggregation queries.
Contingency: If snapshot data is missing for historical records (e.g., older exports before the column existed), display a 'Metrics not available for this report' placeholder in the widget rather than showing zeros or triggering a live aggregation that could return different figures.
Re-export can take several seconds (it runs the full generation pipeline). Without adequate progress feedback, coordinators may tap the button multiple times, triggering duplicate exports and duplicate history records.
Mitigation & Contingency
Mitigation: Disable the re-export button immediately on first tap and show an inline progress indicator on the list item. Guard against duplicate invocations at the service layer using an in-progress flag keyed by report ID. Display a loading state on the list item throughout the operation.
Contingency: If duplicate re-export records are created (e.g., due to a race condition), the history table will show multiple entries for the same original report — which is harmless but confusing. Add a deduplication UI hint ('Re-exported N times') and a backend guard that prevents more than one in-flight re-export for the same source record ID simultaneously.