import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'dart:async'; import 'package:superport/core/constants/app_constants.dart'; 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'; // Mock 서비스 제거 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(); _controller.initializeWithPageSize(_pageSize); } @override void dispose() { _controller.dispose(); _searchController.dispose(); _debounceTimer?.cancel(); super.dispose(); } /// 검색어 입력 처리 (디바운싱) void _onSearchChanged(String value) { _debounceTimer?.cancel(); _debounceTimer = Timer(AppConstants.searchDebounce, () { setState(() { _currentPage = 1; }); _controller.search(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); try { await _controller.deleteCompany(id); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(e.toString()), 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) { // 유형이 없으면 기본값 표시 if (types.isEmpty) { return Text( '-', style: ShadcnTheme.bodySmall.copyWith(color: ShadcnTheme.muted), ); } return Row( mainAxisSize: MainAxisSize.min, children: [ Flexible( child: Wrap( spacing: 4, runSpacing: 2, children: types.map((type) { ShadcnBadgeVariant variant; String displayText; switch (type) { case CompanyType.customer: variant = ShadcnBadgeVariant.companyCustomer; // Orange displayText = '고객사'; break; case CompanyType.partner: variant = ShadcnBadgeVariant.companyPartner; // Green displayText = '파트너사'; break; default: variant = ShadcnBadgeVariant.secondary; displayText = companyTypeToString(type); } return ShadcnBadge( text: displayText, variant: variant, size: ShadcnBadgeSize.small, ); }).toList(), ), ), ], ); } /// 본사/지점 구분 배지 생성 Widget _buildCompanyTypeLabel(bool isBranch) { return ShadcnBadge( text: isBranch ? '지점' : '본사', variant: isBranch ? ShadcnBadgeVariant.companyBranch // Purple (#7C3AED) - 차별화 : ShadcnBadgeVariant.companyHeadquarters, // Blue (#2563EB) size: ShadcnBadgeSize.small, ); } /// 회사 이름 표시 (지점인 경우 본사명 포함) 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; // 디버그 로그 추가 print('🔍 [VIEW DEBUG] 화면 페이지네이션 상태'); print(' • filteredCompanies 수: ${controller.filteredCompanies.length}개'); print(' • displayCompanies 수: ${displayCompanies.length}개 (지점 포함)'); print(' • 현재 페이지: $_currentPage'); print(' • 페이지 크기: $_pageSize'); print(' • startIndex: $startIndex, endIndex: $endIndex'); // startIndex가 displayCompanies.length보다 크거나 같으면 첫 페이지로 리셋 if (startIndex >= displayCompanies.length && displayCompanies.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { _currentPage = 1; }); }); } final List> pagedCompanies = displayCompanies.isEmpty ? [] : displayCompanies.sublist( startIndex.clamp(0, displayCompanies.length), endIndex.clamp(0, displayCompanies.length), ); print(' • 화면에 표시될 항목 수: ${pagedCompanies.length}개'); // 로딩 상태 if (controller.isLoading && controller.companies.isEmpty) { return const StandardLoadingState(message: '회사 데이터를 불러오는 중...'); } return BaseListScreen( isLoading: false, error: controller.error, onRefresh: controller.refresh, emptyMessage: controller.searchQuery.isNotEmpty ? '검색 결과가 없습니다' : '등록된 회사가 없습니다', emptyIcon: Icons.business_outlined, // 검색바 searchBar: UnifiedSearchBar( controller: _searchController, placeholder: '회사명, 담당자명, 연락처로 검색', onChanged: _onSearchChanged, // 실시간 검색 (디바운싱) onSearch: () => _controller.search( _searchController.text, ), // 즉시 검색 onClear: () { _searchController.clear(); _onSearchChanged(''); }, suffixButton: StandardActionButtons.addButton( text: '회사 추가', onPressed: _navigateToAddScreen, ), ), // 액션바 actionBar: StandardActionBar( leftActions: [], totalCount: totalCount, onRefresh: controller.refresh, statusMessage: controller.searchQuery.isNotEmpty ? '"${controller.searchQuery}" 검색 결과' : 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.searchQuery.isNotEmpty ? '검색 결과가 없습니다' : '등록된 회사가 없습니다', icon: Icons.business_outlined, action: controller.searchQuery.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: 1), 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: 1), 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, ), // 구분 Align( alignment: Alignment.centerLeft, child: _buildCompanyTypeLabel(isBranch), ), // 유형 Align( alignment: Alignment.centerLeft, child: _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; }); }, ), ); }, ), ); } }