high priority low complexity backend pending backend specialist Tier 2

Acceptance Criteria

fetchActivityLogs(mentorId) returns a non-null List<ActivityLogEntry> ordered by activity_date descending
When a DateRange is provided, only entries within the inclusive start–end date bounds are returned
When limit is provided, at most that many records are returned per page
Cursor-based pagination: the response includes a nextCursor value (last entry's activity_date or ID) that, when passed on the next call, returns the subsequent page without duplication or omission
When no records exist for the mentorId, an empty list is returned (never null, never an exception)
Each returned ActivityLogEntry maps all typed fields: id, mentor_id, activity_date, activity_type, duration_minutes, notes, contact_id (nullable), and created_at
Supabase query uses .eq('mentor_id', mentorId), .order('activity_date', ascending: false), and .limit(limit) correctly composed
DateRange filter is applied via .gte('activity_date', range.start.toIso8601String()).lte('activity_date', range.end.toIso8601String())
Any Supabase PostgrestException is caught and rethrown as a typed domain exception (e.g., ActivityLogFetchException)
Method signature matches the interface defined in the previous task (task-006)

Technical Requirements

frameworks
Flutter
Dart
supabase_flutter
apis
Supabase PostgREST — activity_logs table
Supabase RLS policies for mentor-scoped access
data models
ActivityLogEntry
DateRange
ActivityPeriod enum
performance requirements
Default page size should be 20; maximum allowed limit is 100 to prevent large payload fetches
Query must use an index on (mentor_id, activity_date DESC) — confirm index exists in Supabase before shipping
Response time under 400 ms for the default page size on a standard connection
security requirements
Supabase RLS enforces that a mentor can only read their own activity_logs rows; repository must not add client-side org-scoping that could bypass or conflict with RLS
mentorId must be validated as non-empty string before constructing the query to prevent empty-string RLS bypass edge cases
No raw SQL construction — use the supabase_flutter typed query builder exclusively

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Build on top of the abstract interface defined in task-006 — do not duplicate the interface declaration. Use Dart's named optional parameters with defaults (limit defaults to 20, range defaults to null meaning no date filter). For cursor pagination, use keyset pagination on activity_date combined with the row id as a tiebreaker to avoid skipping rows with identical timestamps. Return a value object (e.g., ActivityLogPage) that bundles List + String?

nextCursor rather than returning a bare list, so the UI layer can detect end-of-data. Keep fromJson factory constructors on ActivityLogEntry; do not put mapping logic inside the repository method itself.

Testing Requirements

Unit tests using flutter_test and mockito (or mocktail). Mock the Supabase client at the PostgREST builder level. Test cases: (1) successful fetch returns correctly typed list in descending date order; (2) DateRange filter passes correct gte/lte parameters; (3) limit parameter is forwarded; (4) empty result set returns empty list, not null; (5) cursor value matches last entry's activity_date; (6) PostgrestException is rethrown as ActivityLogFetchException; (7) null/empty mentorId throws ArgumentError before any network call. Aim for 100% branch coverage on the repository method.

Component
Mentor Activity Log Repository
data low
Epic Risks (3)
high impact medium prob security

Supabase RLS policies for peer mentor data may block coordinator queries if the RLS rules are written for peer-mentor-self access only, requiring policy updates that affect other features sharing the same tables.

Mitigation & Contingency

Mitigation: Review existing RLS policies on peer_mentors, certification_records, and activity_log tables before writing repository queries. Coordinate with the database team to add coordinator-role predicates without weakening existing mentor-self policies.

Contingency: If policy changes are blocked, implement a Supabase Edge Function as a secure query proxy that enforces authorization server-side, avoiding direct RLS policy modification.

medium impact medium prob technical

The activity log table schema may not have a mentor_id foreign key column or may require a JOIN through an intermediate table, making the aggregation query significantly more complex than anticipated.

Mitigation & Contingency

Mitigation: Inspect the actual Supabase activity_log table schema before starting the MentorActivityLogRepository implementation. Document the exact JOIN path needed and validate it returns correct results for a known mentor.

Contingency: If schema requires complex multi-table aggregation, implement a Supabase database function (RPC) and expose it via the repository's fetchSummary method to keep Dart code clean.

high impact low prob dependency

The Blindeforbundet assignment table may not yet exist in the shared Supabase schema or may have a different structure than assumed, blocking the AssignmentHistoryRepository implementation.

Mitigation & Contingency

Mitigation: Verify the assignments table exists and confirm its column structure with the Contact Detail & Edit Screen team which also depends on assignment data (assignment-repository in that feature).

Contingency: If the assignments table is not yet available, implement the AssignmentHistoryRepository with a stub returning empty list and a TODO marker, unblocking the aggregation service while the schema is finalized.