high priority low complexity database pending backend specialist Tier 0

Acceptance Criteria

A class `AssignmentRepository` is created in `lib/data/repositories/assignment_repository.dart`
Method `getActiveAssignments(String contactId, String orgId)` returns `Future<List<Assignment>>` with all active assignment records for the contact
Method `getAssignmentHistory(String contactId, String orgId)` returns `Future<List<Assignment>>` with historical/completed assignments ordered by date descending
Method `getAssignmentById(String assignmentId, String orgId)` returns `Future<Assignment?>` (null if not found or not accessible)
All methods include `orgId` for RLS scoping — queries must never return data outside the caller's org
Supabase query errors (network failure, RLS rejection) are caught and converted to typed exceptions (`AssignmentRepositoryException`) rather than propagating raw Supabase errors
The `Assignment` model class covers: id, contactId, orgId, assignedToId, status (enum: active/pending/completed/cancelled), startDate, endDate (nullable), notes (nullable)
Repository does not perform any business logic — no filtering, sorting, or transformation beyond mapping JSON to typed models
All public methods are documented with Dart doc comments (`///`) explaining parameters, return value, and thrown exceptions
Running `flutter analyze` on the file reports zero warnings
Unit tests pass for happy path, empty result, and exception path (see testing requirements)

Technical Requirements

frameworks
Flutter
Supabase
Riverpod
apis
Supabase PostgREST (assignments table)
Supabase RLS policies for org-scoped access
data models
Assignment
AssignmentStatus (enum)
AssignmentRepositoryException
performance requirements
Active assignments query must include a `.limit(100)` guard to prevent unbounded result sets
History query must support pagination via `.range(from, to)` — implement with optional `page` and `pageSize` parameters defaulting to page 0, size 20
Avoid `select('*')` — specify only the columns needed for the Assignment model
security requirements
orgId must be included in every query filter — never rely solely on RLS without an explicit where clause for defense in depth
assignmentId and contactId parameters must be validated as non-empty strings before constructing the query
Raw Supabase exceptions must not be surfaced to callers — wrap in AssignmentRepositoryException to avoid leaking internal schema details

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Follow the same structural pattern as `ContactDetailRepository` (task-010's output) for consistency — same file location convention, same exception handling pattern, same model mapping approach. The `Assignment` model should be an immutable Dart class using `copyWith` — consider using `freezed` only if it is already a project dependency; otherwise a plain class is sufficient. The Supabase table name is likely `assignments` — verify against the database schema before writing queries. Use `supabase.from('assignments').select('id,contact_id,org_id,...').eq('org_id', orgId).eq('contact_id', contactId).eq('status', 'active')` pattern.

Map `snake_case` JSON keys to `camelCase` Dart fields in a `fromJson` factory. The repository should be injected via the `assignmentRepositoryProvider(orgId)` family provider defined in task-017 — ensure the constructor signature accepts a `SupabaseClient` as a dependency (not constructed internally) to support provider injection and test overrides.

Testing Requirements

Write unit tests in `test/data/repositories/assignment_repository_test.dart` using a `FakeSupabaseClient` (implement the Supabase client interface with in-memory data). Test scenarios: (1) getActiveAssignments returns correctly mapped Assignment list for a known contactId; (2) getActiveAssignments returns empty list when no records match; (3) getAssignmentHistory returns records sorted by date descending; (4) getAssignmentById returns null for a non-existent ID; (5) Supabase network error is converted to AssignmentRepositoryException (not rethrown raw); (6) orgId mismatch yields empty result (simulating RLS). Use `flutter_test`. Aim for 100% branch coverage of the repository class.

Component
Contact Detail Screen
ui medium
Epic Risks (2)
low impact medium prob dependency

The Peer Mentor Profile tab on the contact detail screen depends on the peer-mentor-detail-screen-widget being delivered by the separate Peer Mentor Detail feature. If that feature is delayed, the navigation affordance will be present but lead to a stub screen, which may confuse coordinators in the TestFlight pilot.

Mitigation & Contingency

Mitigation: Implement the peer mentor tab with a feature flag guard. When the Peer Mentor Detail feature is incomplete, the flag disables the tab. Coordinate delivery timelines with the team responsible for Peer Mentor Detail to align TestFlight releases.

Contingency: If the Peer Mentor Detail feature is significantly delayed, ship the contact detail screen without the peer mentor tab in the first TestFlight build and add it as an incremental update once the dependent screen is ready.

medium impact medium prob technical

The contact detail screen must adapt its layout significantly based on organisation context: NHF shows affiliation chips, Blindeforbundet shows encrypted fields and assignment status, standard contacts show neither. Managing this conditional rendering without introducing bugs in each variant is complex and increases the risk of organisation-specific regressions.

Mitigation & Contingency

Mitigation: Define a ContactDetailViewModel that resolves all org-specific flags (showEncryptedFields, showAssignmentStatus, showMultiChapterChips) from the organisation config before the widget tree renders. Widget tests must cover all three organisation variants as separate test cases to catch regressions.

Contingency: If conditional rendering logic grows unwieldy, refactor into separate composable section widgets (ProfileHeaderSection, AffiliationSection, EncryptedFieldsSection) that are conditionally included by the parent screen, isolating org-specific logic to individual components.