Implement org admin and peer mentor role filtering
epic-contact-list-management-business-logic-task-003 — Implement the remaining two filtering branches in ContactListService: org admin branch delegates to ContactRepository to fetch all contacts within the organisation scope (respecting RLS), and peer mentor branch fetches only the contacts explicitly assigned to that peer mentor. Validate role resolution against the authenticated user context passed into the service.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Use a sealed class or exhaustive switch on UserRole enum for the dispatch — this ensures the compiler enforces handling of all future roles. Keep the service a plain Dart class (no Riverpod coupling) so it is trivially unit-testable. Inject ContactRepository via constructor. The org admin branch should pass orgId (from UserContext) to the repository, not re-derive it inside the branch.
For peer mentor, the peerMentorId is always UserContext.userId — do not accept it as a parameter to avoid IDOR. Supabase RLS should already restrict the query, but the service should still validate that the returned contacts belong to the expected scope as a defence-in-depth check (assert count > 0 only if organisation is known to have contacts). Avoid async* generators here — return Future> for predictable error propagation.
Testing Requirements
Unit tests (flutter_test) for each role branch using mocked ContactRepository. Test org admin: returns full org contact list. Test peer mentor: returns only assigned contacts. Test unrecognised role: throws correct exception type.
Test null user context: throws before any repo call. Test repository throws: exception propagates unmodified. No integration tests at this layer — repository is mocked. Aim for 100% branch coverage of the role-dispatch logic.
For organizations with large contact lists (NHF has 1,400 local chapters and potentially thousands of contacts), local in-memory filtering may be too slow and Supabase ILIKE queries without supporting indexes may exceed acceptable response times or accumulate excessive read costs, degrading search usability for power users.
Mitigation & Contingency
Mitigation: Define and document the list-size threshold in ContactSearchService before implementation. Confirm that indexes on name and notes columns exist in the Supabase schema before enabling server-side search. Profile ContactSearchService against realistic data volumes in the staging environment using the largest expected org.
Contingency: If response times are unacceptable in staging, introduce result-count pagination in ContactListService and add a user-visible 'showing top N results — refine your search' indicator, deferring full pagination to a follow-up task.
In NHF's multi-chapter context, when a user switches organization, Riverpod providers may emit a brief window of stale contact data scoped to the previous organization before the invalidation cycle completes, transiently exposing contacts from the wrong chapter.
Mitigation & Contingency
Mitigation: Model organization context as a Riverpod provider dependency so that any context change immediately marks contact providers as stale. Render a loading skeleton instead of the stale list during the re-fetch transition. Cover this scenario in integration tests with explicit org-switch sequences.
Contingency: If race conditions are observed during QA, add an explicit organization_id equality check in ContactListService that compares each fetched record's scope to the active session org, discarding any mismatched batch before returning results to the provider.