high priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

BufdirReportStructureMapper.getOrderedSections() is called once per screen load and returns an ordered list of section descriptors
Each section descriptor is rendered as a BufdirReportSectionWidget child inside a ListView.builder
Sections are rendered in the exact order returned by BufdirReportStructureMapper — no client-side reordering
Section data (field values) is sourced from BufdirPreviewService state and passed as a parameter to each BufdirReportSectionWidget
If BufdirReportStructureMapper returns an empty list, an informational empty-state widget is displayed instead of a blank list
If a section's data is missing from BufdirPreviewService state, the section widget renders a placeholder indicating 'no data' rather than throwing
ListView uses ListView.builder with itemCount equal to the section list length — no pre-built list of widgets
Scrolling through all sections is smooth with no jank on a mid-range device (verified manually and via flutter_test pump)
Section headers are visually distinct and identifiable at a glance
Integration test confirms sections render in the same order as BufdirReportStructureMapper.getOrderedSections() output

Technical Requirements

frameworks
Flutter
Riverpod
data models
BufdirReportSection
BufdirReportSectionData
BufdirPreviewState
performance requirements
ListView.builder used exclusively — never ListView with a pre-built children list
BufdirReportSectionWidget must be const-constructible where possible to minimise rebuilds
Section data lookup must be O(1) — use a Map keyed by section ID, not a linear search
security requirements
Section field values must be rendered as escaped text — no raw HTML or markdown injection possible
ui components
ListView.builder (section container)
BufdirReportSectionWidget (per-section card/panel)
EmptyStateWidget (fallback when no sections)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

BufdirReportStructureMapper should be accessed via a Riverpod Provider (not instantiated inline) so it can be overridden in tests. Convert the ordered section list to a Map in the BufdirPreviewService state for O(1) lookup in the builder callback. In the itemBuilder, use the section ID to look up data: if null, pass an explicit 'empty' data object to the widget rather than passing null. Add a SliverList alternative comment for future scroll perf improvements but use ListView.builder for now to keep the implementation straightforward.

Section ordering is a regulatory requirement for Bufdir submission — document this explicitly in a code comment referencing the Bufdir schema version so future developers understand why reordering is forbidden.

Testing Requirements

Write widget tests for: (1) correct number of BufdirReportSectionWidget instances rendered matches mapper output, (2) sections appear in the order returned by mapper, (3) empty mapper output triggers empty-state widget, (4) missing section data renders placeholder not an exception. Mock BufdirReportStructureMapper to return a deterministic ordered list. Mock BufdirPreviewService with a full state including section data map. Use find.byType(BufdirReportSectionWidget) and verify ordering via tester.getTopLeft() coordinates.

Include an integration test that scrolls to the last section and verifies it is visible.

Component
Bufdir Report Preview Screen
ui high
Epic Risks (3)
medium impact medium prob integration

The preview screen must pass period, scope, and aggregation session state to the Bufdir Report Export screen via navigation arguments. If the export screen's navigation argument schema is not yet finalized when this epic begins, the handoff will require rework — potentially after TestFlight is already in use by coordinators.

Mitigation & Contingency

Mitigation: Define the shared BufdirExportNavigationArgs Dart class jointly with the Bufdir Report Export feature team before this epic starts. Store it in a shared models package both features depend on. Treat the class as an API contract with a minor-version bump policy.

Contingency: If the export screen's argument schema changes after the preview screen is implemented, the BufdirPreviewService session state model can be adapted with a compatibility shim. The preview screen itself requires only a one-line navigation call change.

medium impact low prob technical

The period diff view loads prior-period data on demand and renders it inline with the current report. On a large report (many sections, many fields) combined with slow Supabase connectivity, the diff overlay could block the UI or produce a janky re-render that degrades the coordinator experience during the pre-submission review.

Mitigation & Contingency

Mitigation: Load prior-period data in a background Riverpod FutureProvider that starts prefetching when the preview screen mounts (not when the user taps the diff toggle). Show a shimmer placeholder on each field row's prior-period column while loading. Cache prior-period data in BufdirPreviewRepository using the same local cache as current-period data.

Contingency: If diff view performance is unacceptable on TestFlight devices, disable the toggle for the initial TestFlight release and ship the diff view in the following sprint after profiling the Supabase query plan and adding an appropriate Postgres index on the prior-period data table.

high impact medium prob scope

The full preview screen will be reviewed by coordinators on TestFlight who will cross-reference it against their physical Bufdir reporting forms. Any section ordering difference or label mismatch discovered during UAT will require a fix before the feature can be signed off, potentially delaying the entire Bufdir reporting pipeline.

Mitigation & Contingency

Mitigation: Conduct a pre-TestFlight alignment review with at least one coordinator from NHF and one from HLF using a static screenshot of the preview screen layout. Obtain written sign-off on section order and field labels before distributing to the full test group of 5-8 people.

Contingency: If label mismatches are found during TestFlight UAT, update BufdirReportStructureMapper constants and rebuild. Since the mapper is a pure Dart class with no persisted state, corrections are deployed in the next TestFlight build with no database migration required.