critical priority low complexity backend pending backend specialist Tier 1

Acceptance Criteria

convert(TimeWindow.week) returns a DateTimeRange where start is the most recent Monday at 00:00:00 UTC and end is Sunday at 23:59:59.999 UTC relative to the provided reference DateTime
convert(TimeWindow.month) returns a DateTimeRange spanning the full calendar month of the reference date (first day 00:00 UTC to last day 23:59:59.999 UTC)
convert(TimeWindow.quarter) returns a DateTimeRange for the Norwegian fiscal quarter (Q1: Jan–Mar, Q2: Apr–Jun, Q3: Jul–Sep, Q4: Oct–Dec) of the reference date
convert(TimeWindow.year) returns a DateTimeRange from 1 January 00:00:00 UTC to 31 December 23:59:59.999 UTC of the reference year
convert(TimeWindow.custom, customStart: s, customEnd: e) returns a DateTimeRange [s, e]; throws ArgumentError if customStart or customEnd is null, or if customStart is after customEnd
All returned DateTimes are in UTC to avoid DST ambiguity when sent to Supabase
Week calculation correctly handles the Monday anchor across year boundaries (e.g., week containing 1 January 2024 starts 1 Jan or in December 2023)
Unit tests pass for a leap-year date (29 Feb 2024) for month and year windows
Unit tests pass for a DST-transition date in Norway (last Sunday of March / last Sunday of October) — UTC output must not shift by 1 hour

Technical Requirements

frameworks
Dart
data models
TimeWindow
DateTimeRange
performance requirements
convert() must be a pure synchronous function with O(1) complexity — no async I/O
security requirements
Service must not accept or expose user-supplied arbitrary DateTime strings to prevent injection; all input must be typed DateTime objects

Execution Context

Execution Tier
Tier 1

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.

Component
Time Window Service
service low
Epic Risks (3)
medium impact medium prob technical

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.

high impact medium prob integration

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.

high impact low prob security

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.