Implement Badge Repository
epic-achievement-badges-ui-task-001 — Create the badge-repository data layer component that provides CRUD operations for earned badges stored in Supabase. Implement methods for fetching earned badges by peer mentor ID, inserting new badge awards, and querying badge history. Apply RLS policies to ensure data isolation per organization.
Acceptance Criteria
Technical Requirements
Implementation Notes
Follow the repository pattern already established in the codebase — use an abstract BadgeRepositoryInterface and a concrete SupabaseBadgeRepository implementation to support future swapping or testing. Define EarnedBadge as an immutable Dart class with a fromJson factory and toJson method. Table name: earned_badges. Key columns expected: id (uuid), peer_mentor_id (uuid, FK), badge_definition_id (text), awarded_at (timestamptz), organization_id (uuid).
Register as a Riverpod Provider
Testing Requirements
Unit tests using flutter_test with a mocked Supabase client (use mockito or manual fakes). Test cases must cover: (1) successful fetch returns correctly mapped EarnedBadge list, (2) empty result returns empty list without throwing, (3) Supabase error is wrapped in BadgeRepositoryException, (4) upsert on duplicate award does not throw and returns existing record, (5) fetchBadgeHistory with a DateRange filters results correctly. Integration test against Supabase test instance verifying RLS blocks cross-organization reads.
The badge-earned-celebration overlay must appear within 2 seconds of the triggering activity being saved, but badge evaluation runs server-side in an edge function triggered by a database webhook. Network latency, edge function cold start, and Supabase Realtime delivery delays could cause the overlay to appear late or not at all, breaking the motivational loop.
Mitigation & Contingency
Mitigation: Implement an optimistic UI path: after activity save, badge-bloc immediately checks whether any badge thresholds are crossed client-side using cached stats and badge definitions, showing the overlay speculatively before server confirmation. The server result then reconciles. Subscribe to Supabase Realtime on the earned_badges table for authoritative confirmation.
Contingency: If Realtime delivery is unreliable in production, add a polling fallback: badge-bloc polls for new earned badges 3 seconds after an activity save and shows the overlay if a new record is detected, accepting up to 5-second latency as a fallback SLA.
The celebration overlay uses animation for positive reinforcement, but motion sensitivity (prefers-reduced-motion) and screen reader users require a non-animated or text-only alternative. Failing to handle this risks excluding Blindeforbundet users or triggering vestibular discomfort for motion-sensitive volunteers.
Mitigation & Contingency
Mitigation: Check MediaQuery.disableAnimations in badge-earned-celebration-overlay and skip animation entirely when true, showing a static card instead. Add an ExcludeSemantics wrapper around the decorative animation widget and a separate Semantics node with a live region announcement of the badge name and congratulatory message.
Contingency: If accessibility issues are identified in TestFlight testing with Blindeforbundet's test group, fast-track a patch that defaults to the static card path and gates the animation behind a user preference setting in notification preferences.