high priority low complexity backend pending backend specialist Tier 2

Acceptance Criteria

fetchMentorsByOrganization(String organizationId, {MentorStatus? statusFilter}) is an async method returning Future<List<PeerMentorProfile>>
Method always applies .eq('organization_id', organizationId) as the primary filter
When statusFilter is non-null, method additionally applies .eq('status', statusFilter.name) to the query
When statusFilter is null, no status filter is applied and all statuses are returned
An empty result set returns an empty list [] — no exception is thrown for zero results
Result list is mapped from raw JSON array using PeerMentorProfile.fromJson() for each element
Method does NOT sort results client-side — ordering is delegated to the Supabase query (apply .order('created_at', ascending: false) as default)
organizationId is validated as non-empty before the Supabase call
Throws RepositoryException for PostgrestException
Unit tests pass for: returns populated list, returns empty list, applies status filter correctly, no status filter returns all, empty organizationId throws

Technical Requirements

frameworks
Flutter
Riverpod
Dart
apis
Supabase PostgREST — peer_mentors table SELECT filtered by organization_id and optionally status
data models
PeerMentorProfile
MentorStatus (enum)
performance requirements
Select only the columns needed for list display (id, full_name, status, profile_photo_url, certification_expiry) rather than SELECT * to reduce payload size
For large organizations with many mentors, consider adding a limit parameter in a future iteration — document this in a TODO comment
security requirements
Do not pass the organizationId from untrusted client input without validation — the caller must ensure it matches the authenticated user's organization
RLS on the peer_mentors table must enforce that users cannot query mentors outside their authorized organization; the client filter is a UX convenience, not a security boundary

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Build the Supabase query chain conditionally: start with the base query, then apply the status filter only if non-null. In Dart this looks like: var query = _supabase.from('peer_mentors').select('id, full_name, status, ...').eq('organization_id', organizationId); if (statusFilter != null) { query = query.eq('status', statusFilter.name); } final response = await query.order('created_at', ascending: false);. Parse the response as (response as List).map((e) => PeerMentorProfile.fromJson(e)).toList(). This method will be consumed by coordinator and org admin screens showing mentor lists — keeping the column selection minimal improves scroll performance on large lists.

Do not add pagination in this task; document it as a known future requirement.

Testing Requirements

Unit tests using flutter_test with mocked SupabaseClient. Append to existing repository test file or create test/features/peer_mentor/data/repositories/peer_mentor_list_test.dart. Required cases: (1) returns list of two PeerMentorProfile objects from mock JSON array, (2) returns empty list when Supabase returns empty array, (3) when statusFilter=active, query chain includes .eq('status', 'active'), (4) when statusFilter=null, no status .eq() is applied to the query, (5) throws ArgumentError when organizationId is empty. Use argument captors to verify the Supabase query builder receives correct filter chain.

Component
Peer Mentor 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.