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/models/company_item_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 CompanyList extends StatefulWidget { const CompanyList({super.key}); @override State createState() => _CompanyListState(); } class _CompanyListState extends State { late CompanyListController _controller; final TextEditingController _searchController = TextEditingController(); Timer? _debounceTimer; @override void initState() { super.initState(); _controller = CompanyListController(); _controller.initialize(pageSize: 10); // 통일된 초기화 방식 } @override void dispose() { _controller.dispose(); _searchController.dispose(); _debounceTimer?.cancel(); super.dispose(); } /// 검색어 입력 처리 (디바운싱) void _onSearchChanged(String value) { _debounceTimer?.cancel(); _debounceTimer = Timer(AppConstants.searchDebounce, () { _controller.search(value); // Controller가 페이지 리셋 처리 }); } /// 회사 추가 화면으로 이동 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), ); } /// 지점 삭제 처리 void _deleteBranch(int companyId, int branchId) { 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.deleteBranch(companyId, branchId); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(e.toString()), backgroundColor: Colors.red, ), ); } } }, child: const Text('삭제'), ), ], ), ); } /// 회사 유형 배지 생성 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, ); } /// CompanyItem의 계층적 이름 표시 Widget _buildDisplayNameText(CompanyItem item) { if (item.isBranch) { return Text.rich( TextSpan( children: [ TextSpan(text: '${item.parentCompanyName} > ', style: ShadcnTheme.bodyMuted), TextSpan(text: item.name, style: ShadcnTheme.bodyMedium), ], ), ); } else { return Text(item.name, style: ShadcnTheme.bodyMedium); } } /// 활성 상태 배지 생성 Widget _buildStatusBadge(bool isActive) { return ShadcnBadge( text: isActive ? '활성' : '비활성', variant: isActive ? ShadcnBadgeVariant.success : ShadcnBadgeVariant.secondary, size: ShadcnBadgeSize.small, ); } /// 날짜 포맷팅 String _formatDate(DateTime? date) { if (date == null) return '-'; return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}'; } /// 담당자 정보 통합 표시 (이름 + 직책) Widget _buildContactInfo(CompanyItem item) { final name = item.contactName ?? '-'; final position = item.contactPosition; if (position != null && position.isNotEmpty) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( name, style: ShadcnTheme.bodySmall.copyWith(fontWeight: FontWeight.w500), ), Text( position, style: ShadcnTheme.bodySmall.copyWith( color: ShadcnTheme.muted, fontSize: 11, ), ), ], ); } else { return Text(name, style: ShadcnTheme.bodySmall); } } /// 연락처 정보 통합 표시 (전화 + 이메일) Widget _buildContactDetails(CompanyItem item) { final phone = item.contactPhone ?? '-'; final email = item.contactEmail; if (email != null && email.isNotEmpty) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( phone, style: ShadcnTheme.bodySmall, ), Text( email, style: ShadcnTheme.bodySmall.copyWith( color: ShadcnTheme.muted, fontSize: 11, ), ), ], ); } else { return Text(phone, style: ShadcnTheme.bodySmall); } } /// 파트너/고객 플래그 표시 Widget _buildPartnerCustomerFlags(CompanyItem item) { if (item.isBranch) { return Text('-', style: ShadcnTheme.bodySmall); } final flags = []; if (item.isPartner) { flags.add(ShadcnBadge( text: '파트너', variant: ShadcnBadgeVariant.companyPartner, size: ShadcnBadgeSize.small, )); } if (item.isCustomer) { flags.add(ShadcnBadge( text: '고객', variant: ShadcnBadgeVariant.companyCustomer, size: ShadcnBadgeSize.small, )); } if (flags.isEmpty) { return Text('-', style: ShadcnTheme.bodySmall); } return Wrap( spacing: 4, runSpacing: 2, children: flags, ); } /// 등록일/수정일 표시 Widget _buildDateInfo(CompanyItem item) { final createdAt = item.createdAt; final updatedAt = item.updatedAt; if (createdAt == null) { return Text('-', style: ShadcnTheme.bodySmall); } final created = _formatDate(createdAt); if (updatedAt != null && updatedAt != createdAt) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( '등록: $created', style: ShadcnTheme.bodySmall, ), Text( '수정: ${_formatDate(updatedAt)}', style: ShadcnTheme.bodySmall.copyWith( color: ShadcnTheme.muted, fontSize: 11, ), ), ], ); } else { return Text(created, style: ShadcnTheme.bodySmall); } } @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: _controller, child: Consumer( builder: (context, controller, child) { // CompanyItem 데이터 직접 사용 (복잡한 변환 로직 제거) final companyItems = controller.companyItems; final int totalCount = controller.total; print('🔍 [VIEW DEBUG] CompanyItem 페이지네이션 상태'); print(' • CompanyItem items: ${controller.companyItems.length}개'); print(' • 전체 개수: ${controller.total}개'); print(' • 현재 페이지: ${controller.currentPage}'); print(' • 페이지 크기: ${controller.pageSize}'); // 로딩 상태 if (controller.isLoading && controller.companyItems.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(''); }, ), // 액션바 actionBar: StandardActionBar( leftActions: [ // 회사 추가 버튼을 검색창 아래로 이동 StandardActionButtons.addButton( text: '회사 추가', onPressed: _navigateToAddScreen, ), ], rightActions: [ // 관리자용 비활성 포함 체크박스 // TODO: 실제 권한 체크 로직 추가 필요 Row( children: [ Checkbox( value: controller.includeInactive, onChanged: (_) => controller.toggleIncludeInactive(), ), const Text('비활성 포함'), ], ), ], 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: companyItems.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: 3), std_table.DataColumn(label: '담당자', flex: 2), std_table.DataColumn(label: '연락처', flex: 3), std_table.DataColumn(label: '파트너/고객', flex: 2), 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: [ ...companyItems.asMap().entries.map((entry) { final int index = ((controller.currentPage - 1) * controller.pageSize) + entry.key; final CompanyItem item = entry.value; 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: 3), std_table.DataColumn(label: '담당자', flex: 2), std_table.DataColumn(label: '연락처', flex: 3), std_table.DataColumn(label: '파트너/고객', flex: 2), 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, ), // 회사명 (계층적 표시) _buildDisplayNameText(item), // 구분 (본사/지점 배지) Align( alignment: Alignment.centerLeft, child: _buildCompanyTypeLabel(item.isBranch), ), // 주소 Text( item.address.isNotEmpty ? item.address : '-', style: ShadcnTheme.bodySmall, overflow: TextOverflow.ellipsis, ), // 담당자 (이름 + 직책) _buildContactInfo(item), // 연락처 (전화 + 이메일) _buildContactDetails(item), // 파트너/고객 플래그 Align( alignment: Alignment.centerLeft, child: _buildPartnerCustomerFlags(item), ), // 상태 Align( alignment: Alignment.centerLeft, child: _buildStatusBadge(item.isActive), ), // 등록/수정일 _buildDateInfo(item), // 비고 Text( item.remark ?? '-', style: ShadcnTheme.bodySmall, overflow: TextOverflow.ellipsis, maxLines: 2, ), // 관리 (액션 버튼들) Row( mainAxisSize: MainAxisSize.min, children: [ std_table.StandardActionButtons( onEdit: item.id != null ? () { if (item.isBranch) { // 지점 수정 - 별도 화면으로 이동 (Phase 3에서 구현) // TODO: Phase 3에서 별도 지점 수정 화면 구현 Navigator.pushNamed( context, '/company/branch/edit', arguments: { 'companyId': item.parentCompanyId, 'branchId': item.id, 'parentCompanyName': item.parentCompanyName, }, ).then((result) { if (result == true) controller.refresh(); }); } else { // 본사 수정 Navigator.pushNamed( context, '/company/edit', arguments: { 'companyId': item.id, 'isBranch': false, }, ).then((result) { if (result == true) controller.refresh(); }); } } : null, onDelete: item.id != null ? () { if (item.isBranch) { // 지점 삭제 _deleteBranch(item.parentCompanyId!, item.id!); } else { // 본사 삭제 _deleteCompany(item.id!); } } : null, ), ], ), ], ); }), ], ), // 페이지네이션 (BaseListController의 goToPage 사용) pagination: Pagination( totalCount: controller.total, currentPage: controller.currentPage, pageSize: controller.pageSize, onPageChanged: (page) { controller.goToPage(page); }, ), ); }, ), ); } }