import 'package:flutter/material.dart'; import 'package:superport/domain/entities/company_hierarchy.dart'; import 'package:superport/screens/common/theme_shadcn.dart'; /// 회사 계층 구조 Tree View 컴포넌트 class CompanyTreeView extends StatelessWidget { final CompanyHierarchy hierarchy; final Map expandedNodes; final Function(String) onToggleExpand; final Function(String)? onNodeTap; final Function(String)? onEdit; final Function(String)? onDelete; final String? selectedNodeId; const CompanyTreeView({ super.key, required this.hierarchy, required this.expandedNodes, required this.onToggleExpand, this.onNodeTap, this.onEdit, this.onDelete, this.selectedNodeId, }); @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: ShadcnTheme.card, borderRadius: BorderRadius.circular(8), border: Border.all(color: ShadcnTheme.border), ), child: SingleChildScrollView( child: Column( children: hierarchy.children .map((node) => _buildTreeNode(context, node, 0)) .toList(), ), ), ); } Widget _buildTreeNode(BuildContext context, CompanyHierarchy node, int level) { final isExpanded = expandedNodes[node.id] ?? false; final hasChildren = node.children.isNotEmpty; final isSelected = selectedNodeId == node.id; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 노드 헤더 InkWell( onTap: () => onNodeTap?.call(node.id), child: Container( decoration: BoxDecoration( color: isSelected ? ShadcnTheme.accent.withValues(alpha: 0.1) : null, border: Border( bottom: BorderSide( color: ShadcnTheme.border.withValues(alpha: 0.5), width: 0.5, ), ), ), child: Padding( padding: EdgeInsets.only( left: 16.0 + (level * 24.0), right: 8.0, top: 8.0, bottom: 8.0, ), child: Row( children: [ // 확장/축소 버튼 if (hasChildren) GestureDetector( onTap: () => onToggleExpand(node.id), child: Padding( padding: const EdgeInsets.only(right: 8.0), child: Icon( isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, size: 20, color: ShadcnTheme.muted, ), ), ) else const SizedBox(width: 28), // 아이콘 Icon( level == 0 ? Icons.business : Icons.domain, size: 18, color: level == 0 ? ShadcnTheme.primary : ShadcnTheme.muted, ), const SizedBox(width: 8), // 회사명 Expanded( child: Text( node.name, style: ShadcnTheme.bodyMedium.copyWith( fontWeight: level == 0 ? FontWeight.w600 : FontWeight.normal, color: isSelected ? ShadcnTheme.primary : null, ), ), ), // 자손 수 표시 if (node.totalDescendants > 0) Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2, ), decoration: BoxDecoration( color: ShadcnTheme.muted.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(12), ), child: Text( '${node.totalDescendants}', style: ShadcnTheme.bodySmall.copyWith( fontSize: 11, color: ShadcnTheme.mutedForeground, ), ), ), // 액션 버튼들 if (onEdit != null || onDelete != null) ...[ const SizedBox(width: 8), _buildActionButtons(node.id), ], ], ), ), ), ), // 자식 노드들 if (hasChildren && isExpanded) ...node.children .map((childNode) => _buildTreeNode(context, childNode, level + 1)) , ], ); } Widget _buildActionButtons(String nodeId) { return Row( mainAxisSize: MainAxisSize.min, children: [ if (onEdit != null) IconButton( icon: const Icon(Icons.edit_outlined), iconSize: 16, padding: const EdgeInsets.all(4), constraints: const BoxConstraints( minWidth: 24, minHeight: 24, ), onPressed: () => onEdit!(nodeId), color: ShadcnTheme.muted, tooltip: '수정', ), if (onDelete != null) IconButton( icon: const Icon(Icons.delete_outline), iconSize: 16, padding: const EdgeInsets.all(4), constraints: const BoxConstraints( minWidth: 24, minHeight: 24, ), onPressed: () => onDelete!(nodeId), color: ShadcnTheme.destructive, tooltip: '삭제', ), ], ); } } /// 계층 구조 경로 표시 위젯 (Breadcrumb) class CompanyHierarchyPath extends StatelessWidget { final String fullPath; final TextStyle? style; const CompanyHierarchyPath({ super.key, required this.fullPath, this.style, }); @override Widget build(BuildContext context) { final pathParts = fullPath.split('/').where((p) => p.isNotEmpty).toList(); return Row( children: [ Icon( Icons.account_tree, size: 14, color: ShadcnTheme.muted, ), const SizedBox(width: 4), Expanded( child: Text( pathParts.join(' > '), style: style ?? ShadcnTheme.bodySmall.copyWith( color: ShadcnTheme.mutedForeground, ), overflow: TextOverflow.ellipsis, ), ), ], ); } }