critical priority high complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

On screen open, the report schema service is called with the current organization_id; a loading skeleton is shown while the schema is fetched.
All dynamic field types defined in the org schema (text, multiline, checkbox, dropdown, date) are rendered in the correct order with correct labels and validation rules.
The way-forward section widget is always rendered below the dynamic fields, regardless of schema contents, as a required part of the form (per Blindeforbundet requirements).
When the schema fetch fails, an error state widget is shown with a retry button; the form is not partially rendered in a broken state.
When the schema is fetched but contains zero fields, an empty-schema state is shown with a message ('No report fields configured for your organisation') alongside the way-forward section which remains visible.
All form field values are routed through the ReportFormOrchestratorBloc — no local StatefulWidget state is used for form data.
Form validation runs on submit: required dynamic fields show inline error text if empty; the submit button is disabled while validation errors exist.
Re-entering the screen with a saved draft pre-populates all dynamic fields and the way-forward section from the draft state in the orchestrator.
The screen passes golden tests comparing rendered output for loading, error, empty-schema, and populated-form states.

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase PostgreSQL 15 — report_field_schemas table
Supabase Edge Functions — schema fetch endpoint if schema is computed server-side
data models
activity
activity_type
performance requirements
Schema fetch must complete within 2 seconds on a 4G connection; show skeleton loader after 300ms if still loading
Dynamic field list must render without jank for schemas up to 30 fields — use ListView.builder, not Column
Form state updates must not trigger full tree rebuilds — use BlocBuilder with buildWhen conditions
security requirements
Schema fetch must be scoped to the authenticated user's organization_id — never load another org's schema
Field values containing personal health information (e.g. helsetilstand) must not be logged to console or crash reporting tools
RLS on report_field_schemas table restricts schema reads to rows matching the user's organization_id claim
ui components
DynamicFieldRenderer — renders schema-driven field list
WayForwardSectionWidget — static structured section for next-steps input
ReportSchemaLoadingSkeleton — skeleton placeholder during fetch
ReportSchemaErrorWidget — error state with retry CTA
EmptySchemaWidget — empty state message with way-forward still visible
ReportFormOrchestratorBloc — single source of truth for all form state

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Model the report screen as a BlocProvider + BlocBuilder tree at the top level; pass the orchestrator BLoC down rather than recreating it per widget. The DynamicFieldRenderer should be a stateless widget receiving a list of FieldSchema objects and a callback for value changes — it must not own any state itself. Use a sealed class or freezed union for form field types (TextField, CheckboxField, DropdownField, DateField) to ensure exhaustive handling in the renderer. The WayForwardSectionWidget should always be appended after the dynamic fields list, never inside the dynamic list, so it cannot be accidentally omitted by a schema with no fields.

For draft detection, check the orchestrator BLoC state on initState — if hasDraft is true, dispatch a RestoreDraftEvent before building the form. Avoid Navigator.pop inside form submit logic; instead emit a navigation event from the BLoC and handle it in a BlocListener to keep navigation out of business logic.

Testing Requirements

Widget tests (flutter_test): render loading skeleton when BLoC is in loading state; render dynamic fields matching a mock schema of 5 fields; render way-forward section in all states; render error widget with retry button on schema fetch failure; render empty-schema state when schema has zero fields; pre-populate fields from draft state. BLoC unit tests: emits loading → loaded(schema) on successful fetch; emits loading → error on fetch failure; field update events mutate correct field in state; submit event validates required fields and emits validationError or submitting states. Golden tests for all four screen states. Integration test: full schema fetch from Supabase test instance returns correct fields for test organization.

Component
Post-Session Report Screen
ui high
Epic Risks (2)
medium impact high prob technical

End-to-end integration tests that span Flutter UI → Supabase → RLS → storage are inherently flaky in CI due to network timing, test database state, and Supabase cold-start latency. Flaky tests erode confidence and slow the release pipeline.

Mitigation & Contingency

Mitigation: Use a dedicated Supabase test project with seeded org and user fixtures. Wrap all E2E tests in retry logic with a fixed seed and tear-down hooks. Keep E2E tests in a separate test suite that runs on-demand rather than on every PR, with unit and widget tests as the primary CI gate.

Contingency: If E2E tests remain unreliable, replace the Supabase calls in integration tests with a verified fake (in-memory repository implementations) and promote the real Supabase tests to a nightly scheduled run rather than blocking PR merges.

high impact medium prob security

Health status, course interest, and assistive device fields contain personal health data. If any logger, analytics event, or crash reporter captures field values — through automated error serialisation or developer-added debug logs — the feature could violate GDPR and Blindeforbundet's data processor agreement.

Mitigation & Contingency

Mitigation: Audit all log statements in the report feature's code paths before the epic is marked done. Apply a PII-safe logging wrapper that strips field values from any serialised form state before it reaches the logger. Add a CI lint rule that flags direct logger calls within report-related files.

Contingency: If PII is found in logs post-launch, immediately disable the affected logging call and rotate any credentials that were exposed. Notify the data protection officer and document the incident per GDPR Article 33 requirements.