critical priority low complexity backend pending backend specialist Tier 0

Acceptance Criteria

StatsSnapshot model contains fields: totalActivities (int), totalHours (double), totalReimbursementAmount (double), windowStart (DateTime), windowEnd (DateTime), chapterId (String?), and is immutable (all fields final)
PeerMentorStatRow contains: peerMentorId (String), peerMentorName (String), activityCount (int), totalHours (double), reimbursementAmount (double), chapterId (String) — field names exactly match Supabase view column names
ChartDataPoint contains: x (DateTime), y (double), label (String?) and supports both numeric and date x-axis rendering
TimeWindow enum defines exactly 5 values: week, month, quarter, year, custom
All models implement == and hashCode via either manual override or a code-gen approach (e.g., equatable package)
fromJson factory constructors parse all fields without throwing on null optional fields
toJson() returns a Map<String, dynamic> that round-trips through fromJson without data loss
Dart analysis (dart analyze) reports zero errors or warnings on all model files
Unit tests verify fromJson/toJson round-trip for all four models with realistic data fixtures
Unit tests verify equality: two instances with identical data are == and produce the same hashCode

Technical Requirements

frameworks
Flutter
Dart
data models
StatsSnapshot
PeerMentorStatRow
ChartDataPoint
TimeWindow
performance requirements
Models must be immutable to support safe use in BLoC/Riverpod state without defensive copies
JSON deserialisation of a list of 500 PeerMentorStatRow items must complete in under 50 ms on a mid-range device
security requirements
No PII (names, personal identifiers) logged in debug output from model constructors
Nullable fields for any data that may be absent per role to prevent accidental data exposure

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Place all models under lib/features/statistics/domain/models/. Use Dart's built-in const constructors wherever possible to enable widget-tree const propagation. Avoid code-generation tools (build_runner/freezed) unless already present in the project to minimise build complexity — manual == and hashCode via IDE generation is acceptable for these simple value objects. Field names in fromJson must use the exact snake_case column names from the Supabase SQL views (e.g., 'peer_mentor_id', 'total_hours') so the PostgreSQL response maps directly without a translation layer.

Document each field with a one-line DartDoc comment referencing the originating Supabase column. Expose a static empty() factory on StatsSnapshot for use as BLoC initial state.

Testing Requirements

Unit tests using flutter_test covering: (1) fromJson/toJson round-trip for all models with realistic fixture maps, (2) equality checks — equal data produces true ==, differing data produces false, (3) null-safety edge cases — optional nullable fields do not throw when absent from the JSON map, (4) TimeWindow enum serialisation — all 5 values map to stable string keys and back. Target 100% line coverage for all model files. No integration or widget tests required at this stage.

Component
Stats Data Models
data 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.