medium priority medium complexity frontend pending frontend specialist Tier 7

Acceptance Criteria

Each HierarchyNodeTile displays the unit type label resolved from OrganizationLabelsProvider (e.g., 'Region' for NHF, 'District' for another org) — never hardcoded English or Norwegian strings
Member count is displayed next to or below the node name, formatted as a localized integer (e.g., '42 members'), pulled from the hierarchy cache field memberCount
Unit type badge uses the unified entity color system: badge background and text colors are set via design token constants (not hardcoded hex values)
When OrganizationLabelsProvider returns null or an empty string for a unit type, the badge is omitted entirely — no empty badge is shown
When memberCount is null or unavailable in the cache, the member count display is omitted — no placeholder '0 members' or error text
Member count updates reactively when the hierarchy cache is refreshed (Riverpod rebuild propagates to node tile)
Unit type badge text does not exceed one line; overflow is handled with TextOverflow.ellipsis
Design token typography is used for all text in the node tile: node name uses AppTypography.bodyMedium, badge uses AppTypography.labelSmall, member count uses AppTypography.caption
Widget tests verify: correct label from provider, correct member count from cache, null label hides badge, null count hides member count display

Technical Requirements

frameworks
Flutter
Riverpod
data models
HierarchyNode
OrganizationLabel
HierarchyCache
performance requirements
OrganizationLabelsProvider result must be cached in Riverpod state — do not perform a lookup per node render
Badge rendering must not trigger additional async operations; all label data must be pre-resolved in the provider layer
security requirements
Member count values must come only from the hierarchy cache (server-validated data) — never from client-side calculation that could be manipulated
ui components
UnitTypeBadge widget (reusable, accepts unitType string and resolves color from design tokens)
MemberCountLabel widget (accepts count int?, renders null-safely)
Design token constants: AppTypography.bodyMedium, AppTypography.labelSmall, AppTypography.caption
Unified entity color map (Map<String, Color> from design token constants)

Execution Context

Execution Tier
Tier 7

Tier 7 - 84 tasks

Can start after Tier 6 completes

Implementation Notes

Extract UnitTypeBadge as a reusable widget (lib/shared/widgets/unit_type_badge.dart) because it will likely be used outside the hierarchy tree (e.g., contact cards, search results). The badge color lookup should be a pure function: Color resolveUnitTypeColor(String unitType, AppColorTokens tokens) — this makes it unit-testable without Flutter context. OrganizationLabelsProvider should expose a synchronous selector so HierarchyNodeTile does not need to handle loading states for labels (labels should be loaded once at app startup). Keep member count formatting consistent with the rest of the app — use NumberFormat.decimalPattern() from the intl package if already in the dependency tree, otherwise use a simple toString() with a suffix string from the localization layer.

Testing Requirements

Widget tests (flutter_test): override OrganizationLabelsProvider with a stub returning 'Region' for unitType 'region'; assert badge displays 'Region'. Override with null return; assert no badge is rendered. Provide a HierarchyNode with memberCount = 42; assert '42' appears in the rendered widget. Provide memberCount = null; assert no member count text is rendered.

Test that label and count update when ProviderScope is updated mid-test to simulate a cache refresh. Verify UnitTypeBadge does not overflow with a 30-character label string.

Component
Hierarchy Tree View
ui high
Epic Risks (4)
high impact medium prob security

Injecting all unit assignment IDs into JWT claims for users assigned to many units (up to 5 for NHF peer mentors, many more for national coordinators) may exceed JWT size limits, causing authentication failures.

Mitigation & Contingency

Mitigation: Store unit IDs in a Supabase session variable or a dedicated Postgres function rather than embedding them directly in the JWT payload. Use set_config('app.unit_ids', ...) within RLS helper functions querying the assignments table at policy evaluation time.

Contingency: Fall back to querying the unit_assignments table directly within RLS policies using the authenticated user ID, accepting a small per-query overhead in exchange for removing the JWT size constraint.

medium impact medium prob technical

Rendering 1,400+ nodes in a recursive Flutter tree widget may cause jank or memory pressure on lower-end devices used by field peer mentors, degrading the admin experience.

Mitigation & Contingency

Mitigation: Implement lazy tree expansion — only the root level is rendered on initial load. Child nodes are rendered on demand when the parent is expanded. Use const constructors and ListView.builder for all node lists to minimize rebuild scope.

Contingency: Add a search/filter bar that scopes the visible tree to matching nodes, reducing the visible node count. Provide a 'flat list' fallback view for administrators who prefer searching over browsing the tree.

medium impact medium prob scope

Requirements for what constitutes a valid hierarchy structure may expand during NHF sign-off (e.g., mandatory coordinator assignments per chapter, minimum member counts per region), requiring repeated validator redesign.

Mitigation & Contingency

Mitigation: Design the validator as a pluggable rule engine where each check is a discrete, independently testable function. New rules can be added without changing the core validation orchestration. Surface all rules in a configuration table per organization.

Contingency: Defer non-blocking validation rules to warning-level feedback rather than hard blocks, allowing structural changes to proceed while flagging potential issues for admin review.

high impact low prob integration

Deploying RLS policy migrations to a shared Supabase project used by multiple organizations simultaneously could lock tables or interrupt active sessions, causing downtime during production migration.

Mitigation & Contingency

Mitigation: Write all RLS policies as CREATE POLICY IF NOT EXISTS statements. Schedule migrations during off-peak hours. Use Supabase's migration preview environment to validate policies against production data shapes before applying.

Contingency: Prepare rollback migration scripts for every RLS policy. If a migration causes issues, execute the rollback immediately and re-test the policy logic in staging before reattempting.