Create TimeWindow enum and DateTimeRange converter
epic-activity-statistics-dashboard-data-foundation-task-002 — Implement the TimeWindow Service that converts each TimeWindow enum value (week, month, quarter, year, custom) into a concrete DateTimeRange with correct UTC boundaries. The service must handle Norwegian fiscal-year semantics, anchor week boundaries to Monday, and expose a single convert(TimeWindow, {DateTime? customStart, DateTime? customEnd}) method. Unit tests must cover all five window types including leap-year and DST edge cases.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Place service at lib/features/statistics/domain/services/time_window_service.dart. Implement as a plain Dart class (no Flutter dependency) to keep it testable in pure Dart unit tests. Norwegian fiscal year follows calendar year (Jan–Dec), so quarter mapping is straightforward. Use DateTime.utc() constructors exclusively — never DateTime.now() directly; always accept a reference DateTime parameter so tests can inject deterministic values.
For week anchoring, compute weekday offset as: startOfWeek = referenceDate.subtract(Duration(days: referenceDate.weekday - 1)) where DateTime.monday == 1 in Dart. Document the Norwegian DST caveat: Supabase stores timestamps in UTC, so the service's UTC output is correct even if the UI displays local time.
Testing Requirements
Unit tests using flutter_test. Test matrix: (1) each of the 5 TimeWindow values with a standard mid-month reference date; (2) week boundary — reference date is a Monday vs a Sunday; (3) year boundary — reference is 31 December and 1 January; (4) leap year — 29 February for month and year windows; (5) Norwegian DST transition dates (last Sunday of March and October); (6) custom window — valid range, null start, null end, inverted range (start > end). Target 100% branch coverage on the convert() method.
Materialized views over large activity tables may have refresh latency exceeding the 2-second SLA under high insert load, causing stale data to appear on the dashboard immediately after a peer mentor registers an activity.
Mitigation & Contingency
Mitigation: Design the materialized view refresh trigger to run asynchronously via a Supabase Edge Function rather than a synchronous trigger, and set a maximum staleness tolerance of 5 seconds documented in the feature spec. Add a CONCURRENTLY refresh strategy so reads are never blocked.
Contingency: If refresh latency cannot meet SLA, fall back to a regular (non-materialized) view for the dashboard and accept slightly higher query cost per request. Revisit materialized approach once Supabase pg_cron or background workers are available.
The aggregation counting rules for the dashboard may diverge from those used in the Bufdir export pipeline (e.g., which activity types count, how duplicate registrations are handled), creating a reconciliation burden for coordinators at reporting time.
Mitigation & Contingency
Mitigation: Run the BufDir Alignment Validator against a shared reference dataset before any view is merged to main. Encode the counting rules as a shared Supabase function called by both the stats views and the export query builder so there is a single source of truth.
Contingency: If divergence is discovered post-launch, ship a visible banner on the dashboard stating that numbers are indicative and may differ from the export until the reconciliation fix is deployed. Prioritize the fix as a P0 defect.
Multi-chapter coordinators (up to 5 chapters per NHF requirement) require RLS policies that filter on an array of chapter IDs, which is more complex than single-value RLS and could be misconfigured, leaking data across chapters or blocking legitimate access.
Mitigation & Contingency
Mitigation: Write integration tests that verify cross-chapter isolation for a coordinator assigned to chapters A and B cannot see data from chapter C. Use parameterized RLS policies with auth.uid()-based chapter lookup to avoid hardcoded values.
Contingency: If RLS misconfiguration is detected in testing, temporarily restrict coordinator queries to single-chapter scope (coordinator's primary chapter) and ship multi-chapter support as a fast-follow patch once RLS logic is verified.