high priority medium complexity integration pending integration specialist Tier 2

Acceptance Criteria

On initial load, the dashboard displays a skeleton loading state for each integration card while health data is being fetched
Within 3 seconds of screen mount, all integration status badges and last-sync timestamps are populated with live data
Status badges update in real-time without user interaction when an integration's health state changes (connected, error, syncing, disconnected)
Last-sync timestamp displays as a relative time string (e.g., '3 minutes ago') and updates every 60 seconds
Real-time updates are delivered via Supabase Realtime channel subscription scoped to the current organisation's integrations
If the Realtime connection drops, the dashboard falls back to polling every 30 seconds and shows a subtle 'Live updates paused' indicator
Skeleton loading matches the final card layout (same height/width as populated cards) to prevent layout shift on data arrival
An integration in 'error' state shows the last error message as a tooltip or expandable detail on the status badge
The dashboard correctly handles 0 integrations configured (empty state with CTA to add first integration)
All status updates respect Supabase RLS — users only receive events for integrations belonging to their organisation
Realtime subscription is cancelled and resources released when the dashboard screen is disposed

Technical Requirements

frameworks
Flutter
Riverpod
BLoC
apis
Supabase Realtime: channel subscription on 'integrations' table filtered by organization_id
Integration Health Monitor service: GET /integrations/health (initial fetch)
Supabase PostgreSQL: SELECT id, status, last_sync_at, last_error FROM integrations WHERE organization_id = :orgId
performance requirements
Initial data fetch must complete within 3 seconds on standard mobile network
Realtime event processing must not block the UI thread — process on isolate or async microtask
Skeleton layout must render in first frame with no async dependency
security requirements
Supabase Realtime channel subscription must include JWT — enforced by Supabase SDK automatically
RLS on integrations table ensures organisation isolation — verify policy covers status/last_sync_at columns
Only status, last_sync_at, and last_error fields are transmitted via Realtime — no credential data in payloads
Subscription scoped explicitly to organization_id from JWT claims — never fetch all-org data client-side
ui components
IntegrationStatusBadge (connected/error/syncing/disconnected variants)
LastSyncTimestamp (relative time with auto-refresh timer)
IntegrationCardSkeleton (loading placeholder matching card dimensions)
LiveUpdatesPausedBanner (fallback polling indicator)
EmptyIntegrationsState (zero-config CTA)

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Integration Task

Handles integration between different epics or system components. Requires coordination across multiple development streams.

Implementation Notes

Use Supabase's .channel('integrations-org-{orgId}').onPostgresChanges() to subscribe to UPDATE events on the integrations table filtered by organization_id. Store integration health records in a Riverpod AsyncNotifier> provider. On initial mount: call the health endpoint to populate all records. Then subscribe to Realtime events and apply incremental updates (update the matching record in the list by id).

Implement fallback polling: listen to Supabase RealtimeChannel.onClose() event and start a Timer.periodic(Duration(seconds: 30), fetchHealthData) as fallback; cancel the timer and re-subscribe when Realtime reconnects. For relative timestamps, implement a local Timer.periodic(Duration(seconds: 60)) that triggers a provider update to reformat all timestamps — do not call setState in a loop. Skeleton: use a shimmer-effect placeholder widget (or a simple animated opacity) with hard-coded dimensions matching the IntegrationCard widget to prevent CLS. Dispose all subscriptions and timers in the Riverpod provider's dispose() method to prevent memory leaks.

Testing Requirements

Unit tests: Riverpod provider — test initial loading state, success state with mocked health data, error state with network failure; test fallback-to-polling logic when Realtime disconnects. Widget tests: assert skeleton visible before data arrives; mock data arrival, assert skeletons replaced by populated cards; mock a status change event, assert badge updates without full rebuild. Integration test: subscribe to Supabase Realtime in test environment, insert a status change, assert dashboard reflects change within 2 seconds. Test cleanup: assert subscription cancelled on widget dispose.

Coverage target: 85% on state management, 80% on widget transitions.

Epic Risks (4)
medium impact high prob technical

The multi-step Integration Setup Wizard must render different credential fields, field mapping targets, and validation rules depending on the selected integration type. If the type-specific branching logic is implemented as conditional widget trees rather than driven by the Integration Type Registry, the wizard becomes unmaintainable and adding new integration types requires UI code changes.

Mitigation & Contingency

Mitigation: Design the wizard to be metadata-driven from the Integration Type Registry from day one. Credential form fields, required field validation, and mapping target lists are all fetched from the registry, not hardcoded in widgets. Implement one integration type end-to-end first (Xledger) to validate the pattern before building the others.

Contingency: If the metadata-driven approach proves too complex for the initial delivery, implement Xledger and Dynamics as hardcoded wizard variants and create a registry-driven refactor as a follow-up technical debt ticket with a fixed deadline.

medium impact medium prob dependency

The Excluded Features Configuration Panel must wire directly into the feature flag system to suppress HLF app features. If the feature flag system does not yet expose a writable admin interface, this panel cannot save its configuration, blocking the HLF-specific acceptance criteria.

Mitigation & Contingency

Mitigation: Verify that the Organization-scoped Feature Flags feature (a declared dependency) exposes a Dart API for programmatic flag writes before starting this panel. Coordinate with the feature flags team to ensure the write API is available. If needed, schedule this panel as the last item in the epic.

Contingency: If the feature flag write API is unavailable at implementation time, store excluded features in the integration's JSONB settings column and wire them into a local feature flag provider that merges database state with the standard flag system at app startup.

high impact medium prob scope

The Field Mapping Editor's usability for non-technical org admins is high-risk. If the visual mapping interface is confusing, admins will configure incorrect mappings that cause silent data corruption in accounting exports — a serious financial risk discovered only at month-end reconciliation.

Mitigation & Contingency

Mitigation: Conduct usability testing with at least one admin user from Blindeforbundet on the field mapping editor prototype before full implementation. Provide descriptive labels and sample data values for all fields. Add a 'test mapping' preview that shows a transformed sample record before saving.

Contingency: If usability testing reveals the visual editor is too complex, implement a simplified list-based mapping editor (select app field → select external field, one row at a time) as a fallback, deferring the drag-and-drop visual editor to a future iteration.

medium impact medium prob technical

The Credential Management Form's masked fields and connection-test flow may conflict with screen reader requirements — VoiceOver and JAWS must be able to navigate the form, understand which fields are already configured, and receive feedback on connection test results without exposing credential values in accessible text.

Mitigation & Contingency

Mitigation: Design accessible semantics labels for masked fields (e.g., 'API key: configured, last 4 characters: abcd') from the start. Use Flutter's Semantics widget to provide screen-reader-specific text that differs from visual display. Test with VoiceOver on iOS and TalkBack on Android during development, not only at QA.

Contingency: If accessibility conflicts with security requirements for the credential form, implement a separate 'accessibility mode' flow where credential configuration is done through a separate confirmation step that provides more explicit semantic feedback without risk of value exposure.