critical priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

BufdirPreviewRepository is a concrete class with constructor-injected SupabaseClient dependency
fetchAggregatedData(String periodId, String orgId) method is implemented and delegates to the Supabase query layer from task-001
fetchPriorPeriodData(String periodId, String orgId) method is implemented returning Future<BufdirReportData?>
A Riverpod StreamProvider<AsyncValue<BufdirReportData>> is defined and exposed for the current-period data stream
The StreamProvider emits AsyncLoading while fetching, AsyncData on success, and AsyncError with typed BufdirRepositoryException on failure
The provider is registered in the Riverpod ProviderScope at app startup — not created ad-hoc in widgets
Widgets consuming the provider via ref.watch() correctly rebuild on data, loading, and error state changes
The repository class implements an abstract BufdirPreviewRepositoryInterface to allow mocking in tests
All Supabase exceptions are caught inside the repository and rethrown as BufdirRepositoryException — no raw PostgrestException reaches callers
The implementation compiles and passes flutter test with mocked SupabaseClient

Technical Requirements

frameworks
Flutter
Riverpod
supabase_flutter
apis
Supabase Dart SDK
Riverpod StreamProvider
Riverpod AsyncValue
BufdirPreviewRepository.fetchAggregatedData()
BufdirPreviewRepository.fetchPriorPeriodData()
data models
BufdirReportData
BufdirAggregatedField
BufdirRepositoryException
AsyncValue<BufdirReportData>
performance requirements
StreamProvider must emit the first event (AsyncLoading) synchronously on subscription — no artificial delay before loading state
Repository must not hold open Supabase realtime subscriptions unless real-time updates are explicitly required for this feature
security requirements
SupabaseClient must be injected — never call Supabase.instance.client inside the repository class body
periodId and orgId must be validated as non-empty strings before any Supabase call
Catch all PostgrestException and AuthException types explicitly — do not use a bare catch clause

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Define an abstract class BufdirPreviewRepositoryInterface with the two fetch method signatures — the concrete BufdirPreviewRepository implements it and the mock in tests also implements it. This avoids relying on mocktail's generated mocks for a class with Supabase internals. The Riverpod StreamProvider should be a family provider parameterised by (periodId, orgId) — bufdirPreviewDataProvider(({String periodId, String orgId} params)) — so different period/org combinations can be independently watched without provider conflicts. Use ref.onDispose to cancel any in-flight futures.

Avoid ref.read inside the StreamProvider body — pass dependencies via provider overrides in tests.

Testing Requirements

Unit tests using flutter_test and mocktail: (1) fetchAggregatedData returns BufdirReportData on successful Supabase response, (2) fetchAggregatedData throws BufdirRepositoryException on PostgrestException, (3) fetchPriorPeriodData returns null when no prior-period row exists, (4) StreamProvider emits AsyncLoading then AsyncData in sequence, (5) StreamProvider emits AsyncError on repository failure. Mock SupabaseClient using mocktail. Use ProviderContainer from riverpod/test for provider testing without a full widget tree. Assert AsyncValue state transitions.

Component
Bufdir Preview Repository
data medium
Epic Risks (2)
high impact medium prob integration

The preview repository depends on aggregated data produced by the Bufdir Data Aggregation feature. If the aggregation RPC schema or Supabase view columns change during parallel development, the repository's typed Dart models will break, causing compile errors or runtime null-dereference failures.

Mitigation & Contingency

Mitigation: Define a shared Dart interface (abstract class) for the aggregated data contract early and have both features code against it. Use Supabase typed generated clients so schema mismatches surface at code generation time rather than runtime.

Contingency: If the aggregation schema changes after the repository is complete, run `supabase gen types dart` immediately, update the repository model, and run repository unit tests before unblocking UI development. Keep a mock data fixture so UI work can continue during the fix.

high impact medium prob scope

The BufdirReportStructureMapper must map internal activity category IDs to the exact label strings used on the official Bufdir reporting form. If the mapping is incomplete or uses outdated labels, coordinators will see mismatches when cross-referencing the preview with the paper form, potentially leading to incorrect submissions.

Mitigation & Contingency

Mitigation: Obtain the current Bufdir reporting form PDF directly from Bufdir (Norse Digital Products has an existing Bufdir dialogue). Extract all field labels and section names into a static constants file reviewed by at least one coordinator from NHF or HLF before implementation begins.

Contingency: If incorrect labels are discovered during UAT on TestFlight, update the static constants file and redeploy. Because the mapper is a pure Dart class with no database storage, corrections require no migration — only a new build.