high priority medium complexity integration pending fullstack developer Tier 5

Acceptance Criteria

A bufdirPreviewProvider (AsyncNotifierProvider) exposes the decorated model from BufdirPreviewService with loading, error, and data states
BufdirReportSectionWidget list is derived via a select() or derived provider from bufdirPreviewProvider — rebuilds only when section list changes
Each BufdirFieldRowWidget receives its BufdirFieldDecoratedData via a family provider keyed by fieldIdentifier — rebuilds only when its own field's state changes
BufdirValidationSummaryBanner subscribes to a validationSummaryProvider derived from bufdirPreviewProvider that recomputes BufdirValidationSummaryData on each update
When BufdirPreviewService pushes a new decorated model (e.g., after user edits a field), all three widgets update reactively without manual setState
Loading state from bufdirPreviewProvider renders a shimmer/skeleton placeholder in place of the section list
Error state from bufdirPreviewProvider renders a localized error message with a retry button that calls ref.refresh(bufdirPreviewProvider)
No widget directly calls BufdirPreviewService methods — all access is via Riverpod providers
Integration test confirms end-to-end flow: seed mock decorated model → verify section list renders → mutate a field's validation state → verify BufdirFieldRowWidget and banner update
Riverpod DevTools (or equivalent logging) confirms no provider is rebuilt more than once per state change

Technical Requirements

frameworks
Flutter
Riverpod
data models
bufdir_column_schema
bufdir_export_audit_log
activity
performance requirements
Use select() on bufdirPreviewProvider for all derived providers to prevent unnecessary widget rebuilds
family providers for BufdirFieldRowWidget prevent O(n) rebuilds when a single field changes
validationSummaryProvider must recompute synchronously — no async gap between model update and banner refresh
security requirements
BufdirPreviewService must be accessed server-side via Supabase Edge Functions only for any data mutation — mobile layer is read-only for preview
Row-Level Security on Supabase ensures only the authenticated coordinator's report data is loaded
JWT from Supabase Auth must be included in all BufdirPreviewService API calls
ui components
BufdirReportSectionWidget
BufdirFieldRowWidget
BufdirValidationSummaryBanner
BufdirPreviewLoadingPlaceholder (new)
BufdirPreviewErrorView (new)

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Define a clear provider hierarchy: bufdirPreviewProvider (AsyncNotifierProvider) → sectionListProvider (derived via select) → fieldProvider.family(fieldId) (derived per field) → validationSummaryProvider (derived). This prevents any widget from depending on the full model. BufdirPreviewNotifier should expose a refresh() method and an applyFieldEdit(fieldId, value) method for future edit support. Keep BufdirPreviewService as a plain Dart class injected into the notifier via Riverpod's ref.read — do not use get_it or other DI mechanisms.

For the shimmer placeholder, use the existing design token spacing and card dimensions to match the real layout dimensions.

Testing Requirements

Write both widget tests and integration tests: (1) widget test — inject mock bufdirPreviewProvider with loading state, assert shimmer visible; (2) widget test — inject error state, assert error view with retry button visible; (3) widget test — inject data state with two sections, assert two BufdirReportSectionWidget instances in tree; (4) widget test — mutate a single field's validation via StateNotifier, assert only that field's BufdirFieldRowWidget rebuilds (use RenderObject.debugNeedsLayout counter or custom test hooks); (5) integration test — full provider graph wired to a mock BufdirPreviewService that returns deterministic data; verify banner counts match injected issue count. Use ProviderContainer for unit-testing provider logic in isolation.

Component
Bufdir Report Section Widget
ui medium
Epic Risks (2)
high impact medium prob technical

Implementing the tap-to-scroll-and-focus behavior from the validation banner to a specific field row in a long scrollable list is complex in Flutter. If focus management is incorrectly implemented, VoiceOver users who navigate to the banner and select an issue will not be moved to the relevant field row, breaking the accessibility workflow and violating WCAG 2.4.3 (Focus Order).

Mitigation & Contingency

Mitigation: Use BufdirAccessibilityUtils focus management utilities (built in the foundation epic) with explicit GlobalKey-based scroll anchors on each field row. Test with a real iOS device running VoiceOver during widget development, not only in the Flutter accessibility inspector.

Contingency: If programmatic scroll-to-focus cannot be reliably achieved before the TestFlight deadline, fall back to a navigation approach where tapping a banner issue opens a modal detail sheet for that field row rather than scrolling in place, and file a follow-up ticket for the inline scroll implementation.

medium impact low prob technical

The validation summary banner must reactively update its issue count as underlying aggregated data changes (e.g., if the coordinator has navigated away and data was refreshed). If the banner's Riverpod provider is not correctly scoped, it may display stale issue counts or fail to disappear when all issues are resolved, eroding coordinator trust in the validation system.

Mitigation & Contingency

Mitigation: Drive the banner exclusively from the same Riverpod provider that powers the full preview model — do not maintain a separate local state for issue counts. Write a widget test that simulates a data refresh mid-review and asserts the banner updates within one frame.

Contingency: If stale state reaches production, add a manual refresh button to the banner as a short-term workaround while the provider scoping is corrected in the next release cycle.