import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'dart:async'; import 'package:superport/models/company_model.dart'; import 'package:superport/screens/common/theme_shadcn.dart'; import 'package:superport/screens/common/components/shadcn_components.dart'; import 'package:superport/screens/common/widgets/pagination.dart'; import 'package:superport/screens/common/widgets/unified_search_bar.dart'; import 'package:superport/screens/common/widgets/standard_data_table.dart' as std_table; import 'package:superport/screens/common/widgets/standard_action_bar.dart'; import 'package:superport/screens/common/widgets/standard_states.dart'; import 'package:superport/screens/common/layouts/base_list_screen.dart'; import 'package:superport/services/mock_data_service.dart'; import 'package:superport/screens/company/widgets/company_branch_dialog.dart'; import 'package:superport/screens/company/controllers/company_list_controller.dart'; /// shadcn/ui 스타일로 재설계된 회사 관리 화면 (통일된 UI 컴포넌트 사용) class CompanyListRedesign extends StatefulWidget { const CompanyListRedesign({super.key}); @override State createState() => _CompanyListRedesignState(); } class _CompanyListRedesignState extends State { late CompanyListController _controller; final TextEditingController _searchController = TextEditingController(); Timer? _debounceTimer; int _currentPage = 1; final int _pageSize = 10; @override void initState() { super.initState(); _controller = CompanyListController(dataService: MockDataService()); _controller.initializeWithPageSize(_pageSize); } @override void dispose() { _controller.dispose(); _searchController.dispose(); _debounceTimer?.cancel(); super.dispose(); } /// 검색어 입력 처리 (디바운싱) void _onSearchChanged(String value) { _debounceTimer?.cancel(); _debounceTimer = Timer(const Duration(milliseconds: 500), () { setState(() { _currentPage = 1; }); _controller.updateSearchKeyword(value); }); } /// 회사 추가 화면으로 이동 void _navigateToAddScreen() async { final result = await Navigator.pushNamed(context, '/company/add'); if (result == true) { _controller.refresh(); } } /// 회사 삭제 처리 void _deleteCompany(int id) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('삭제 확인'), content: const Text('이 회사 정보를 삭제하시겠습니까?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('취소'), ), TextButton( onPressed: () async { Navigator.pop(context); final success = await _controller.deleteCompany(id); if (!success) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(_controller.error ?? '삭제에 실패했습니다'), backgroundColor: Colors.red, ), ); } } }, child: const Text('삭제'), ), ], ), ); } /// 지점 다이얼로그 표시 void _showBranchDialog(Company mainCompany) { showDialog( context: context, builder: (context) => CompanyBranchDialog(mainCompany: mainCompany), ); } /// Branch 객체를 Company 객체로 변환 Company _convertBranchToCompany(Branch branch) { return Company( id: branch.id, name: branch.name, address: branch.address, contactName: branch.contactName, contactPosition: branch.contactPosition, contactPhone: branch.contactPhone, contactEmail: branch.contactEmail, companyTypes: [], remark: branch.remark, ); } /// 회사 유형 배지 생성 Widget _buildCompanyTypeChips(List types) { return Wrap( spacing: ShadcnTheme.spacing1, children: types.map((type) { Color bgColor; Color borderColor; Color textColor; switch(type) { case CompanyType.customer: bgColor = ShadcnTheme.green.withValues(alpha: 0.9); textColor = Colors.white; break; case CompanyType.partner: bgColor = ShadcnTheme.purple.withValues(alpha: 0.9); textColor = Colors.white; break; default: bgColor = ShadcnTheme.muted.withValues(alpha: 0.9); textColor = ShadcnTheme.foreground; } return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(4), ), child: Text( companyTypeToString(type), style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: textColor, ), ), ); }).toList(), ); } /// 본사/지점 구분 배지 생성 Widget _buildCompanyTypeLabel(bool isBranch) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: isBranch ? ShadcnTheme.blue.withValues(alpha: 0.9) : ShadcnTheme.primary.withValues(alpha: 0.9), borderRadius: BorderRadius.circular(4), ), child: Text( isBranch ? '지점' : '본사', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: Colors.white, ), ), ); } /// 회사 이름 표시 (지점인 경우 본사명 포함) Widget _buildCompanyNameText( Company company, bool isBranch, { String? mainCompanyName, }) { if (isBranch && mainCompanyName != null) { return Text.rich( TextSpan( children: [ TextSpan(text: '$mainCompanyName > ', style: ShadcnTheme.bodyMuted), TextSpan(text: company.name, style: ShadcnTheme.bodyMedium), ], ), ); } else { return Text(company.name, style: ShadcnTheme.bodyMedium); } } @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: _controller, child: Consumer( builder: (context, controller, child) { // 본사와 지점 구분하기 위한 데이터 준비 final List> displayCompanies = []; for (final company in controller.filteredCompanies) { displayCompanies.add({ 'company': company, 'isBranch': false, 'mainCompanyName': null, }); if (company.branches != null) { for (final branch in company.branches!) { displayCompanies.add({ 'branch': branch, 'companyId': company.id, 'isBranch': true, 'mainCompanyName': company.name, }); } } } final int totalCount = displayCompanies.length; // 페이지네이션을 위한 데이터 처리 final int startIndex = (_currentPage - 1) * _pageSize; final int endIndex = startIndex + _pageSize; final List> pagedCompanies = displayCompanies.sublist( startIndex, endIndex > displayCompanies.length ? displayCompanies.length : endIndex, ); // 로딩 상태 if (controller.isLoading && controller.companies.isEmpty) { return const StandardLoadingState( message: '회사 데이터를 불러오는 중...', ); } return BaseListScreen( isLoading: false, error: controller.error, onRefresh: controller.refresh, emptyMessage: controller.searchKeyword.isNotEmpty ? '검색 결과가 없습니다' : '등록된 회사가 없습니다', emptyIcon: Icons.business_outlined, // 검색바 searchBar: UnifiedSearchBar( controller: _searchController, placeholder: '회사명, 담당자명, 연락처로 검색', onChanged: _onSearchChanged, // 실시간 검색 (디바운싱) onSearch: () => _controller.updateSearchKeyword(_searchController.text), // 즉시 검색 onClear: () { _searchController.clear(); _onSearchChanged(''); }, suffixButton: StandardActionButtons.addButton( text: '회사 추가', onPressed: _navigateToAddScreen, ), ), // 액션바 actionBar: StandardActionBar( leftActions: [], totalCount: totalCount, onRefresh: controller.refresh, statusMessage: controller.searchKeyword.isNotEmpty ? '"${controller.searchKeyword}" 검색 결과' : null, ), // 에러 메시지 filterSection: controller.error != null ? StandardInfoMessage( message: controller.error!, icon: Icons.error_outline, color: ShadcnTheme.destructive, onClose: controller.clearError, ) : null, // 데이터 테이블 dataTable: displayCompanies.isEmpty ? StandardEmptyState( title: controller.searchKeyword.isNotEmpty ? '검색 결과가 없습니다' : '등록된 회사가 없습니다', icon: Icons.business_outlined, action: controller.searchKeyword.isEmpty ? StandardActionButtons.addButton( text: '첫 회사 추가하기', onPressed: _navigateToAddScreen, ) : null, ) : std_table.StandardDataTable( columns: [ std_table.DataColumn(label: '번호', flex: 1), std_table.DataColumn(label: '회사명', flex: 3), std_table.DataColumn(label: '구분', flex: 2), std_table.DataColumn(label: '유형', flex: 2), std_table.DataColumn(label: '연락처', flex: 2), std_table.DataColumn(label: '관리', flex: 2), ], rows: [ ...pagedCompanies.asMap().entries.map((entry) { final int index = startIndex + entry.key; final companyData = entry.value; final bool isBranch = companyData['isBranch'] as bool; final Company company = isBranch ? _convertBranchToCompany(companyData['branch'] as Branch) : companyData['company'] as Company; final String? mainCompanyName = companyData['mainCompanyName'] as String?; return std_table.StandardDataRow( index: index, columns: [ std_table.DataColumn(label: '번호', flex: 1), std_table.DataColumn(label: '회사명', flex: 3), std_table.DataColumn(label: '구분', flex: 2), std_table.DataColumn(label: '유형', flex: 2), std_table.DataColumn(label: '연락처', flex: 2), std_table.DataColumn(label: '관리', flex: 2), ], cells: [ // 번호 Text( '${index + 1}', style: ShadcnTheme.bodySmall, ), // 회사명 _buildCompanyNameText( company, isBranch, mainCompanyName: mainCompanyName, ), // 구분 _buildCompanyTypeLabel(isBranch), // 유형 _buildCompanyTypeChips( company.companyTypes, ), // 연락처 Text( company.contactPhone ?? '-', style: ShadcnTheme.bodySmall, ), // 관리 Row( mainAxisSize: MainAxisSize.min, children: [ if (!isBranch && company.branches != null && company.branches!.isNotEmpty) ...[ ShadcnButton( text: '지점', onPressed: () => _showBranchDialog(company), variant: ShadcnButtonVariant.ghost, size: ShadcnButtonSize.small, ), const SizedBox( width: ShadcnTheme.spacing1, ), ], std_table.StandardActionButtons( onEdit: company.id != null ? () { if (isBranch) { Navigator.pushNamed( context, '/company/edit', arguments: { 'companyId': companyData['companyId'], 'isBranch': true, 'mainCompanyName': mainCompanyName, 'branchId': company.id, }, ).then((result) { if (result == true) controller.refresh(); }); } else { Navigator.pushNamed( context, '/company/edit', arguments: { 'companyId': company.id, 'isBranch': false, }, ).then((result) { if (result == true) controller.refresh(); }); } } : null, onDelete: (!isBranch && company.id != null) ? () => _deleteCompany(company.id!) : null, ), ], ), ], ); }), ], ), // 페이지네이션 (항상 표시) pagination: Pagination( totalCount: totalCount, currentPage: _currentPage, pageSize: _pageSize, onPageChanged: (page) { setState(() { _currentPage = page; }); }, ), ); }, ), ); } }