high priority medium complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

ExportTriggered event initiates a new export job and sets export state to ExportStatus.pending in the BLoC state
ExportProgressUpdated event updates the numeric progress value (0–100) and optional status message in state without resetting other BLoC state fields
ExportCompleted event stores the download URL string in state and sets status to ExportStatus.completed
ExportFailed event stores a localised error message in state and sets status to ExportStatus.failed, enabling UI retry action
Concurrent export requests are rejected when an export is already in ExportStatus.pending or ExportStatus.inProgress; the UI receives an appropriate status indication
Export state (status, progress, download URL, error) is preserved when the org scope changes; it resets only when a new export is explicitly triggered
Polling interval for job status is configurable and stops automatically on completion or failure to prevent resource leaks
Export state is fully serialisable so it can be rehydrated if the widget tree is rebuilt
BLoC emits at most one state update per polling tick regardless of how many listeners are active
All exported state fields have default/initial values that produce no visible UI effect before an export is triggered

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
Dart async streams
apis
AdminExportService (internal)
Supabase Realtime or REST polling endpoint for export job status
data models
ExportJob
ExportStatus (enum: idle, pending, inProgress, completed, failed)
AdminPortalState
performance requirements
Polling interval must not exceed 3 seconds to keep progress feedback responsive
Polling timer must be cancelled in BLoC close() to prevent memory leaks
State emissions must be debounced or deduplicated if service returns identical status repeatedly
security requirements
Download URL stored in state must not be logged or persisted to disk
Export jobs must be scoped to the authenticated admin's org scope; BLoC must reject events that reference out-of-scope job IDs
Supabase RLS policies must restrict export job reads to the owning admin user

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Model the export lifecycle as a sealed class or enum-driven state machine inside AdminPortalState to make illegal transitions impossible at compile time. Use a Dart Timer.periodic inside the BLoC's event handler and cancel it via a stored reference on completion, failure, or BLoC disposal. Prefer Supabase Realtime channel subscriptions over polling if the export service supports it — this removes the polling interval entirely. Guard against re-entrancy by checking current export status at the top of the ExportTriggered handler.

Store the export sub-state as a nested ExportState value object inside AdminPortalState so other BLoC fields are untouched by export events. Follow the existing pattern in this BLoC for adding new state fields (immutable copyWith).

Testing Requirements

Write unit tests using flutter_test and bloc_test covering: (1) ExportTriggered transitions from idle to pending; (2) ExportProgressUpdated increments progress without affecting other state fields; (3) ExportCompleted stores URL and sets completed status; (4) ExportFailed stores error message; (5) duplicate ExportTriggered while in-progress is ignored and emits a noOp or identical state; (6) BLoC close() cancels the polling timer. Use a mock AdminExportService to simulate each scenario. Aim for 100% branch coverage on the export state machine transitions.

Component
Admin Export Panel
ui medium
Epic Risks (3)
high impact medium prob technical

If org node selection in AdminStateBLoC does not correctly propagate to all dependent data streams (statistics, activity log, user list, certification panel), some panels may show data from the previously selected org scope, creating a confusing and potentially dangerous mixed-scope view.

Mitigation & Contingency

Mitigation: Model org node selection as a single source of truth in AdminStateBLoC. All downstream providers derive their query parameters from this single stream via Riverpod's watch pattern. Write integration tests that verify every data stream emits a reload event when the selected node changes.

Contingency: If scope propagation bugs are detected in QA, add an explicit full-state reset on org node change (clear all cached data and refetch from scratch) as a safe but less efficient fallback until the targeted propagation is fixed.

medium impact medium prob technical

The Admin Dashboard Screen must adapt its layout for Flutter Web (wider viewports, mouse interaction, larger grid) and mobile embedding. Flutter Web responsive layout support has historically required non-trivial workarounds, and the adaptive grid may introduce significant additional development time.

Mitigation & Contingency

Mitigation: Define breakpoints and grid behaviour in the design system before implementation. Use LayoutBuilder with explicit breakpoint constants rather than MediaQuery scattered across widgets. Prototype the web layout with a skeleton screen before implementing live data binding.

Contingency: If web layout proves intractable within sprint, deliver a mobile-first layout for all platforms initially and track a dedicated web-optimisation task for the next sprint.

high impact low prob security

A bug in the Role Assignment Panel's permission scope validation could allow an org_admin to assign roles beyond their authority (e.g., assigning super_admin to a user), representing a serious privilege escalation vulnerability.

Mitigation & Contingency

Mitigation: Enforce role assignment scope on both the client (disable unavailable roles in the panel UI) and the server (UserManagementService validates the target role is within the admin's permitted scope before persisting). Write security-focused tests that attempt out-of-scope role assignments and assert rejection.

Contingency: If an escalation vulnerability is discovered, immediately disable the role assignment panel via feature flag, revoke any incorrectly assigned roles, and deploy a server-side fix before re-enabling.