high priority medium complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

Declaration text is loaded from org configuration (confidentiality_declaration table) and rendered in a scrollable read-only panel within the form
Declaration text panel has a visible scroll indicator and minimum height of 120dp to prevent it being skipped
Acknowledgement checkbox is rendered below the declaration panel and is unchecked by default on every form open
Checkbox has an accessible Semantics label: 'I confirm I have read and accept the confidentiality declaration'
Submit button on the parent ExpenseFormBLoC is disabled (greyed out, aria-disabled) until the checkbox is checked
On checkbox activation, DriverHonorariumService records an acknowledgement event with: declaration_id, driver_id (from session), acknowledged_at (UTC timestamp), fully_scrolled (boolean)
fully_scrolled is set to true only when the user has scrolled to within 10dp of the bottom of the declaration panel
Unchecking the checkbox clears the recorded acknowledgement from local BLoC state (does not delete the persisted record — a new record is created on re-check)
Declaration text rendering supports basic markdown (bold, italic, line breaks) for formatting
Loading and error states are handled for declaration text fetch, consistent with task-003 patterns
WCAG 2.2 AA: checkbox minimum touch target 44x44dp, error state has sufficient contrast, VoiceOver/TalkBack announces checkbox state change

Technical Requirements

frameworks
Flutter
BLoC
apis
DriverHonorariumService (internal)
Supabase (via service layer)
data models
confidentiality_declaration
declaration_acknowledgement
performance requirements
Declaration text must render within one frame after fetch completes — no layout jank from long text
Scroll position tracking must not degrade scroll frame rate below 60fps
security requirements
Declaration text fetched over TLS via Supabase SDK — never cached unencrypted on device
Acknowledgement timestamp recorded server-side (via Edge Function or Supabase insert) to prevent client-side timestamp spoofing
Declaration ID validated against the organisation's active declaration before saving acknowledgement
ui components
DeclarationScrollPanel (SingleChildScrollView with ScrollController)
ScrollProgressIndicator (optional visual cue)
AppCheckbox with Semantics wrapper
AcknowledgementConfirmationText (subtitle below checkbox)
Conditional submit button state (via BlocBuilder)

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Use a ScrollController attached to the declaration SingleChildScrollView to detect scroll-to-bottom: listen to controller.position.pixels >= controller.position.maxScrollExtent - 10. Store fullyScrolled as a BLoC state field, not widget local state, so it survives rebuilds. Declaration text should be fetched once when the form is first shown and cached in the BLoC state for the session — avoid re-fetching on every rebuild. Use the flutter_markdown package for rendering declaration text if markdown formatting is required.

The acknowledgement record in declaration_acknowledgement must be inserted via DriverHonorariumService (which calls a Supabase Edge Function) to use server-side timestamps. Do not allow re-use of a previously saved acknowledgement_id across sessions — always create a new record.

Testing Requirements

Widget tests: (1) declaration text renders from mocked service, (2) checkbox unchecked by default, (3) submit button disabled when unchecked, (4) submit button enabled after checkbox checked, (5) acknowledgement event fired with correct data on check, (6) fully_scrolled=false when user has not scrolled to bottom, (7) fully_scrolled=true after full scroll. BLoC unit tests: ExpenseFormBloc blocks submission when declarationAcknowledged=false. Integration test: full flow from form open → scroll declaration → check box → submit enabled. Use flutter_test and mocktail.

Test on both iOS (VoiceOver) and Android (TalkBack) semantic trees.

Component
Driver Honorarium Form
ui medium
Epic Risks (3)
high impact high prob integration

The Dynamics portal (HLF) and Xledger (Blindeforbundet) APIs have organisation-managed API contracts that may change without notice. Field mapping requirements, authentication flows, and export formats are not fully documented and may only be clarified during integration testing.

Mitigation & Contingency

Mitigation: Engage HLF and Blindeforbundet technical contacts early to obtain API documentation, sandbox credentials, and example payloads before implementation starts. Design the accounting integration client as a thin adapter layer with organisation-specific mappers so that field mapping changes require only mapper updates, not core client changes.

Contingency: If API documentation is unavailable or the API is unstable during Phase 3, implement a CSV/JSON file export as an interim deliverable. Coordinators can manually upload the file to their respective accounting systems until the live API integration is completed.

high impact medium prob scope

The confidentiality declaration for Blindeforbundet drivers may have specific legal requirements around content, format, wording, and record-keeping that are not yet specified. Implementing the wrong declaration flow could expose Blindeforbundet to compliance risk.

Mitigation & Contingency

Mitigation: Treat the declaration content and acknowledgement flow as a Blindeforbundet-controlled configuration, not hardcoded text. Implement the declaration as a templated document fetched from Supabase and reviewed by Blindeforbundet before any production deployment. Obtain written sign-off on the declaration text and acknowledgement mechanism before the epic is considered complete.

Contingency: If legal requirements cannot be confirmed in time for the sprint, deliver the driver honorarium form without the confidentiality declaration and gate the entire driver feature behind its feature flag. The declaration can be added in a follow-up sprint once requirements are confirmed, without blocking other feature delivery.

high impact medium prob integration

If the accounting export can be triggered multiple times for the same approved claims batch, duplicate records may be created in Dynamics or Xledger, causing accounting reconciliation problems that are difficult to reverse.

Mitigation & Contingency

Mitigation: Implement idempotent export runs: each export batch is assigned a unique run ID stored in the database. The accounting integration client checks for an existing successful export run for the same claim IDs before submitting. Approved claims that have been exported are marked with exported_at timestamp to prevent re-export.

Contingency: If duplicate exports occur despite idempotency checks (e.g. network failure after API success but before local confirmation), provide coordinators with an export history panel showing run IDs and timestamps. Implement a reconciliation endpoint that can query the accounting system for existing records before re-submitting flagged claims.