high priority low complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

Widget renders a KPI tile with: label text, value text, optional trend indicator (up/down/neutral arrow + percentage), and optional icon
Loading state shows a shimmer or skeleton placeholder occupying the same space as the loaded tile
Error state shows a subtle error icon and a short fallback label, not a raw exception string
Tile is tappable and triggers an onTap callback (or navigation route) passed as a constructor parameter
All colours (background, label, value, trend colours) use design tokens — zero hardcoded hex values in the widget file
Typography (label size, value size, trend size) uses design token text styles — no hardcoded font sizes
Trend indicator uses green (token: colorPositiveTrend) for positive, red (token: colorNegativeTrend) for negative, and grey (token: colorNeutralTrend) for neutral
Widget adapts layout for wide screens (Flutter Web): tile width expands proportionally with LayoutBuilder constraints
Widget is accessible: the tile has a Semantics label combining label, value, and trend direction for screen readers
Widget accepts a testKey parameter for use in widget tests via Key(testKey)
Widget renders correctly at 1x, 2x, and 3x device pixel ratios (no overflow or clipping)

Technical Requirements

frameworks
flutter
flutter_bloc
data models
KpiStatData (label, value, trendPercent, trendDirection, icon)
performance requirements
Widget build method runs in under 1ms — no expensive computations in build()
Shimmer animation uses RepaintBoundary to isolate repaints from parent tree
security requirements
Value strings must be sanitised before display — no raw database content rendered without validation
ui components
AdminKpiStatWidget
TrendIndicator (internal sub-widget: arrow + percentage text)
ShimmerTile (loading placeholder)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

Build AdminKpiStatWidget as a stateless widget accepting a KpiStatData model and an onTap VoidCallback. Define KpiStatData as a simple immutable Dart class (or freezed) with: String label, String displayValue, double? trendPercent, TrendDirection trendDirection (enum: up, down, neutral), IconData? icon, and bool isLoading, bool hasError.

Apply design tokens via a centralized AppTokens class (or equivalent token system already in the project). Use LayoutBuilder for responsive width — on web, tiles in a grid expand to fill their GridView cell. For the shimmer effect, use a pre-existing project shimmer widget if available, otherwise a simple AnimatedContainer with alternating grey shades. The trend colour tokens (colorPositiveTrend, colorNegativeTrend, colorNeutralTrend) must already exist in the design token system — do not add new tokens without checking existing ones first.

Keep this widget purely presentational with no BLoC dependency — data is injected from the parent screen.

Testing Requirements

Widget tests using flutter_test. Cover: (1) renders label and value correctly with valid data, (2) renders trend up indicator with correct color, (3) renders trend down indicator with correct color, (4) renders neutral trend with no arrow, (5) loading state shows shimmer not data, (6) error state shows error icon, (7) onTap callback fires when tapped, (8) semantics label includes label + value + trend, (9) renders without overflow at 320px width (mobile min), (10) renders without overflow at 1440px width (web max). Use golden tests for visual regression of the three tile states (loaded, loading, error) stored in test/goldens/.

Component
Admin Dashboard Screen
ui high
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.