Implement Assignment Repository
epic-contact-detail-and-edit-main-screen-task-001 — Build the assignment repository layer that provides data access for contact assignment records. Implement Supabase queries to fetch assignment data including current assignments, assignment history, and status information. Expose typed Dart interfaces for upstream service consumption.
Acceptance Criteria
Technical Requirements
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.
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.
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.