critical priority high complexity frontend pending frontend specialist Tier 6

Acceptance Criteria

Screen is only accessible to users with the admin role — non-admin users navigating to this route are redirected to a no-access screen
HierarchyTreeView is rendered as the main content area and occupies the full available height
A search bar at the top of the screen filters the tree in real-time as the user types, with matching nodes highlighted and non-matching nodes dimmed
Clearing the search bar restores the full tree to its previous expand/collapse state
'Add root node' button is always visible and opens HierarchyNodeEditorScreen in create mode with no parent pre-selected
When a node is selected in the tree, an 'Add child node' button becomes visible and opens HierarchyNodeEditorScreen with the selected node as the pre-selected parent
Long-pressing or tapping a node shows a contextual action menu with: Edit, Delete, Move options
Edit action opens HierarchyNodeEditorScreen in edit mode for the selected node
Delete action shows a confirmation dialog stating the number of child nodes that will also be deleted; confirmed deletion calls HierarchyService.deleteNode() and refreshes the tree
Move action opens a node picker allowing the admin to select a new parent; on confirmation, calls HierarchyService.moveNode() and refreshes the tree
Member count badge is visible on each node in the tree (rendered by HierarchyTreeView, no additional work needed if task-014 is complete)
After any mutation (add, edit, delete, move), the tree view updates to reflect the change without requiring a full page reload
Screen has a descriptive page title and breadcrumb indicating the admin context
All interactive elements meet WCAG 2.2 AA requirements; admin actions are not accessible to screen-reader users without confirmation prompts

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
apis
HierarchyService.deleteNode(nodeId)
HierarchyService.moveNode(nodeId, newParentId)
HierarchyService.searchNodes(query)
data models
HierarchyNode
HierarchyAdminPortalState
UserRole
performance requirements
Search filtering debounced by 300ms to prevent excessive API calls or re-renders
Tree refresh after mutation completes within 500ms and does not cause full widget tree rebuild
Screen initial load (root nodes + auth check) completes within 800ms
security requirements
Admin role guard enforced at route level using GoRouter redirect or equivalent — never rely solely on hiding UI elements
Delete and move operations must be confirmed both client-side (dialog) and validated server-side (RLS / service layer)
Audit logging: all structural mutations (create, edit, delete, move) must be recorded via HierarchyService to the audit trail
Do not expose child-count or membership data for nodes outside the admin's authority scope
ui components
HierarchyAdminPortalScreen (top-level scaffold)
HierarchySearchBar (debounced text input)
AddRootNodeFAB or AddRootNodeButton
AddChildNodeButton (contextual, appears on node selection)
NodeContextActionMenu (ModalBottomSheet or PopupMenuButton: Edit, Delete, Move)
DeleteNodeConfirmationDialog (shows affected child count)
MoveNodePickerSheet (wraps HierarchyTreeView in selection mode)
AdminRoleGuard widget/route wrapper
HierarchyTreeView (from task-014)
HierarchyNodeEditorScreen (from task-015)

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Compose this screen from the already-built sub-widgets (HierarchyTreeView, HierarchyNodeEditorScreen, UnitAssignmentPanel) — this task is primarily integration and orchestration, not new widget building. The HierarchyAdminPortalCubit manages: selectedNode, searchQuery, and isLoading state. Keep search debounce in the cubit using a Timer that resets on each search input event. For the contextual action menu, use a ModalBottomSheet with three ListTile options rather than a PopupMenuButton, as bottom sheets are more accessible and ergonomic on mobile.

The delete confirmation dialog should query HierarchyService.getDescendantCount(nodeId) and include this in the dialog message (e.g., 'This will also delete 34 child nodes'). For the move operation, reuse HierarchyTreeView in a special 'picker mode' (pass a mode enum or a selectionCallback to differentiate from normal navigation). After any mutation, emit a RefreshTreeEvent to the HierarchyTreeViewCubit — this triggers a root-level re-fetch while preserving the expand/collapse state for unchanged nodes. This screen is the primary tool for org administrators at NHF managing 1,400 local chapters — prioritize reliability and clear feedback over visual complexity.

Testing Requirements

Write widget and integration tests for: (1) non-admin user redirected to no-access screen, (2) search bar filters tree and clears correctly, (3) 'Add root node' button opens editor in create mode with no parent, (4) selecting a node shows 'Add child node' button, (5) 'Add child node' opens editor with correct parent pre-selected, (6) long-press on node opens action menu with Edit/Delete/Move, (7) delete confirmation shows correct child count and calls HierarchyService.deleteNode on confirm, (8) move picker opens and HierarchyService.moveNode called on confirm, (9) tree refreshes after each mutation. E2E test (against Supabase test instance): full admin workflow — create root, add child, edit child name, move child to different root, delete node. Accessibility test: verify admin controls are not reachable by unauthorized roles in widget tests. Aim for 85%+ coverage.

Component
Hierarchy Admin Portal Screen
ui high
Epic Risks (3)
high impact medium prob security

If the AccessScopeService and the Supabase RLS policies use different logic to determine accessible units, a coordinator could see data in the client that RLS blocks server-side, causing confusing empty states, or worse, RLS could block data the scope service declares accessible.

Mitigation & Contingency

Mitigation: Define the canonical scope computation in a single Supabase Postgres function shared by both the RLS policies and the RPC endpoint called by AccessScopeService. The client-side service calls this RPC rather than reimplementing the logic, ensuring a single source of truth.

Contingency: Add integration tests that execute the same access decision through both the RLS policy path and the AccessScopeService path and assert identical results. Use these as regression guards in the CI pipeline.

medium impact medium prob integration

When a user switches active chapter via the ChapterSwitcher, widgets that are already built may not receive the context-change event if they subscribe incorrectly to the ActiveChapterState BLoC, leading to stale data being displayed under the new chapter context.

Mitigation & Contingency

Mitigation: Use Riverpod's ref.watch on the active chapter provider at the root of each scoped data subtree rather than at individual leaf widgets. Trigger a global data refresh by invalidating all scoped providers when the chapter changes.

Contingency: Add an app-level chapter-change listener that forces a full navigation stack reset to the home screen on chapter switch, guaranteeing all widgets rebuild from scratch with the new context. Accept the UX cost of navigation reset for correctness.

medium impact medium prob scope

Non-technical organization administrators may find the hierarchy management interface too complex for the structural changes they need to make frequently (e.g., chapter renaming, coordinator reassignment), leading to low adoption and continued reliance on manual processes.

Mitigation & Contingency

Mitigation: Conduct usability testing with at least one NHF administrator before finalizing the admin portal screen layout. Prioritize the most common operations (rename, reparent, add child) as primary actions in the UI. Include inline help text and confirmation dialogs with plain-language descriptions of consequences.

Contingency: Provide a simplified 'quick edit' mode that exposes only the three most common operations (rename, deactivate, add child) and hides advanced structural operations behind an 'Advanced' toggle.