import 'package:flutter/material.dart'; import 'package:uto_manager/domain/entities/unit_node.dart'; import 'package:uto_manager/domain/services/unit_tree_service.dart'; /// Displays a single unit node as a card in the org chart canvas. /// /// Shows the unit's echelon, branch, name, and fill rate indicator. class UnitNodeCard extends StatelessWidget { const UnitNodeCard({ super.key, required this.treeNode, required this.isSelected, required this.onTap, }); final UnitTreeNode treeNode; final bool isSelected; final VoidCallback onTap; @override Widget build(BuildContext context) { final unit = treeNode.unit; final fillRate = treeNode.fillRate; final theme = Theme.of(context); return GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 150), width: 160, decoration: BoxDecoration( color: isSelected ? theme.colorScheme.primaryContainer : theme.colorScheme.surfaceContainerHighest, border: Border.all( color: isSelected ? theme.colorScheme.primary : _echelonColor(unit), width: isSelected ? 2.5 : 1.5, ), borderRadius: BorderRadius.circular(6), boxShadow: isSelected ? [ BoxShadow( color: theme.colorScheme.primary.withValues(alpha: 0.3), blurRadius: 8, offset: const Offset(0, 2), ), ] : null, ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Header: echelon badge Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), decoration: BoxDecoration( color: _echelonColor(unit).withValues(alpha: 0.85), borderRadius: const BorderRadius.vertical(top: Radius.circular(4)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( unit.echelon.displayName.toUpperCase(), style: theme.textTheme.labelSmall?.copyWith( color: Colors.white, fontWeight: FontWeight.bold, letterSpacing: 0.8, ), ), Text( unit.branch.displayName, style: theme.textTheme.labelSmall?.copyWith( color: Colors.white70, fontSize: 9, ), ), ], ), ), // Body: unit name Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), child: Text( unit.name, style: theme.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w600, color: isSelected ? theme.colorScheme.onPrimaryContainer : theme.colorScheme.onSurface, ), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ), ), // Footer: fill rate bar Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 6), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Fill', style: theme.textTheme.labelSmall?.copyWith(fontSize: 9), ), Text( '${fillRate.toStringAsFixed(0)}%', style: theme.textTheme.labelSmall?.copyWith( fontSize: 9, color: _fillRateColor(fillRate), fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 2), ClipRRect( borderRadius: BorderRadius.circular(2), child: LinearProgressIndicator( value: fillRate / 100.0, minHeight: 4, backgroundColor: theme.colorScheme.surfaceContainerLow, valueColor: AlwaysStoppedAnimation(_fillRateColor(fillRate)), ), ), ], ), ), ], ), ), ); } Color _echelonColor(UnitNode unit) { switch (unit.echelon.level) { case 1: return const Color(0xFF1B3A6B); // Brigade — Army blue case 2: return const Color(0xFF2E5FA3); // Battalion case 3: return const Color(0xFF3A7BD5); // Company case 4: return const Color(0xFF4A90D9); // Platoon case 5: return const Color(0xFF5BA3E0); // Section case 6: return const Color(0xFF6CB4E8); // Squad default: return const Color(0xFF7EC8F0); // Team } } Color _fillRateColor(double rate) { if (rate >= 90) return const Color(0xFF4CAF50); // Green if (rate >= 70) return const Color(0xFFFF9800); // Amber return const Color(0xFFF44336); // Red } }