critical priority high complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

OrgHierarchyNavigator renders a tree of OrgNode objects sourced from OrgHierarchyService, displaying each node's name and type badge (national / regional / chapter) without layout overflow on any screen width ≥ 320 dp
Tapping an expand/collapse chevron toggles the subtree of that node; expansion state is stored in local StatefulWidget state (Map<String, bool>) and survives widget rebuilds triggered by parent BLoC state changes
Collapsed nodes hide all descendant nodes from the widget tree (not just visually); expanded nodes show direct children only, requiring additional taps to traverse deeper levels
Tapping a leaf or header node emits a selectedNode event (via a callback or BLoC event) carrying the node's ID and type; the parent BLoC reacts by scoping all dashboard data to that node's subtree
The widget handles async data loading gracefully: shows a shimmer/skeleton loader while OrgHierarchyService fetches data, and an error state with a retry action if the fetch fails
Tree correctly reflects hierarchies with at least 3 levels of nesting (national → regional → chapter) and up to 1 400 leaf nodes without perceptible jank (frame render time < 16 ms on a mid-range device)
Horizontal overflow is prevented for long node names: text truncates with ellipsis and a Tooltip exposes the full name on long-press
Each node row has a minimum tap target of 48 × 48 dp in accordance with Material Design accessibility guidelines
Widget is exported as a reusable component with a documented public API (required: onNodeSelected callback; optional: initiallyExpandedIds, maxDepth)

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
apis
OrgHierarchyService.getTree()
OrgHierarchyService.getSubtreeIds(nodeId)
data models
OrgNode (id, name, type, parentId, children)
OrgHierarchyTree
AdminScopeEvent (BLoC)
performance requirements
Use ListView.builder with sliver-based rendering for trees > 50 visible nodes to avoid rendering off-screen nodes
Expansion state mutations must not trigger a full tree rebuild; use ValueNotifier or targeted setState on the affected node subtree
Initial data load must complete within 2 s on a 4G connection; cache response in OrgHierarchyService for the session
security requirements
OrgHierarchyService must only return nodes within the authenticated admin's permitted scope; widget must not add client-side scope filtering as a substitute for server-side enforcement
Node IDs passed in selectedNode events must be validated against the known tree before being forwarded to the BLoC to prevent injection of arbitrary scope IDs
ui components
OrgHierarchyNavigator (StatefulWidget, root export)
OrgTreeNode (recursive StatelessWidget per node row)
NodeTypeBadge (renders 'national' / 'regional' / 'chapter' with design-token color)
TreeShimmer (skeleton loader matching tree row height)
TreeErrorState (error message + retry button)

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Use a recursive widget pattern: OrgHierarchyNavigator holds a Map expansionState, passes it down to OrgTreeNode which calls setState on the parent via a callback. Avoid AnimatedList for initial implementation — a simple AnimatedSize or Visibility with an AnimatedOpacity provides sufficient UX without the complexity of index-based insertion/removal. For large trees (> 200 nodes), wrap the list in a CustomScrollView with SliverList to enable lazy rendering. Use design tokens for indentation per level (e.g., 16 dp per level) so the visual nesting scales correctly.

The BLoC integration point is a single onNodeSelected(OrgNode node) callback injected by the parent screen; the widget itself must remain BLoC-agnostic to maximise reusability. Ensure the OrgHierarchyService call is initiated once in initState and result cached in widget state; do not re-fetch on every expansion event.

Testing Requirements

Write widget tests (flutter_test) covering: (1) tree renders correct node count for a 3-level mock hierarchy; (2) tapping chevron toggles child visibility and persists state across rebuilds; (3) tapping a node fires onNodeSelected with the correct node ID; (4) loading state shows shimmer; (5) error state shows retry button and re-fetches on tap. Performance smoke test: render a 200-node flat list and assert no overflow errors in debugCheckHasNoOverflow. All tests must pass on both iOS (Simulator) and Android (Emulator) CI runners.

Epic Risks (4)
medium impact high prob technical

OrgHierarchyNavigator rendering NHF's full 1,400-chapter tree in a single widget may cause Flutter frame-rate drops below 60 fps on mid-range devices, making the navigator unusable for NHF national admins.

Mitigation & Contingency

Mitigation: Implement lazy expansion: only load immediate children on node expand rather than the full tree upfront. Use virtual scrolling for long sibling lists. Test with a synthetic 1,400-node dataset on a low-end Android device during development.

Contingency: If lazy expansion is insufficient, replace the tree widget with a paginated drill-down navigator (select level → select child) that avoids rendering more than 50 nodes at a time.

medium impact medium prob dependency

Bufdir may update their required export column structure or file format during or after development. If the AdminExportService hardcodes the current Bufdir schema, any format change requires a code release rather than a config update.

Mitigation & Contingency

Mitigation: Drive the Bufdir column mapping from a configuration repository rather than hardcoded constants. Abstract column definitions into a named schema config so that format changes require only a config update and re-deployment without service logic changes.

Contingency: If Bufdir format changes post-launch, release a config update within one sprint. If the change is structural (new required sections), scope a targeted service update and communicate timeline to partner organisations.

high impact medium prob integration

Role transition side-effects in UserManagementService (e.g., certification expiry removing mentor from chapter listing, pause triggering coordinator notification) may interact with external services like HLF's website sync. Incomplete side-effect handling could leave the system in an inconsistent state.

Mitigation & Contingency

Mitigation: Model side-effects as explicit domain events published after the primary state change is persisted. Implement event handlers as idempotent operations so re-processing is safe. Write integration tests that assert all side-effects fire correctly for each role transition type.

Contingency: If a side-effect fails after the primary change is persisted, log the failure with full context and trigger a manual reconciliation alert to the on-call team. Provide an admin-accessible re-trigger action for failed side-effects.

medium impact medium prob scope

If AdminStatisticsService cache TTL is set too long, org_admin may see significantly stale KPI values (e.g., a mentor newly paused an hour ago still appears as active), undermining trust in the dashboard.

Mitigation & Contingency

Mitigation: Default cache TTL to 5 minutes with a manual refresh action on the dashboard. Implement cache invalidation triggered by UserManagementService write operations that affect counted entities.

Contingency: If staleness causes org admin complaints post-launch, reduce TTL to 60 seconds and introduce a real-time Supabase subscription for high-impact counters (paused mentors, expiring certifications).