import 'package:flutter/material.dart'; import 'package:superport/screens/equipment/controllers/equipment_list_controller.dart'; import 'package:superport/screens/equipment/widgets/equipment_table.dart'; import 'package:superport/utils/equipment_display_helper.dart'; import 'package:superport/services/mock_data_service.dart'; import 'package:superport/screens/common/theme_tailwind.dart'; import 'package:superport/screens/common/main_layout.dart'; import 'package:superport/utils/constants.dart'; import 'package:superport/models/equipment_unified_model.dart'; import 'package:superport/screens/common/widgets/pagination.dart'; // 장비 목록 화면 (UI만 담당, 상태/로직/헬퍼/위젯 분리) class EquipmentListScreen extends StatefulWidget { final String currentRoute; const EquipmentListScreen({super.key, this.currentRoute = Routes.equipment}); @override State createState() => _EquipmentListScreenState(); } class _EquipmentListScreenState extends State { late final EquipmentListController _controller; bool _showDetailedColumns = true; final ScrollController _horizontalScrollController = ScrollController(); final ScrollController _verticalScrollController = ScrollController(); int _currentPage = 1; final int _pageSize = 10; String _searchKeyword = ''; String _appliedSearchKeyword = ''; @override void initState() { super.initState(); _controller = EquipmentListController(dataService: MockDataService()); _controller.loadData(); WidgetsBinding.instance.addPostFrameCallback((_) { _adjustColumnsForScreenSize(); }); } @override void didChangeDependencies() { super.didChangeDependencies(); _setDefaultFilterByRoute(); } @override void didUpdateWidget(EquipmentListScreen oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.currentRoute != widget.currentRoute) { _setDefaultFilterByRoute(); } } @override void dispose() { _horizontalScrollController.dispose(); _verticalScrollController.dispose(); super.dispose(); } // 라우트에 따라 기본 필터 설정 void _setDefaultFilterByRoute() { String? newFilter; if (widget.currentRoute == Routes.equipmentInList) { newFilter = EquipmentStatus.in_; } else if (widget.currentRoute == Routes.equipmentOutList) { newFilter = EquipmentStatus.out; } else if (widget.currentRoute == Routes.equipmentRentList) { newFilter = EquipmentStatus.rent; } else if (widget.currentRoute == Routes.equipment) { newFilter = null; } if ((newFilter != _controller.selectedStatusFilter) || widget.currentRoute != Routes.equipment) { setState(() { _controller.selectedStatusFilter = newFilter; _controller.loadData(); }); } } // 화면 크기에 따라 컬럼 표시 조정 void _adjustColumnsForScreenSize() { final width = MediaQuery.of(context).size.width; setState(() { _showDetailedColumns = width > 900; }); } // 상태 필터 변경 void _onStatusFilterChanged(String? status) { setState(() { _controller.changeStatusFilter(status); }); } // 장비 선택/해제 void _onEquipmentSelected(int? id, String status, bool? isSelected) { setState(() { _controller.selectEquipment(id, status, isSelected); }); } // 출고 처리 버튼 핸들러 void _handleOutEquipment() async { if (_controller.getSelectedInStockCount() == 0) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('출고할 장비를 선택해주세요.'))); return; } // 선택된 장비들의 요약 정보를 가져와서 출고 폼으로 전달 final selectedEquipmentsSummary = _controller.getSelectedEquipmentsSummary(); final result = await Navigator.pushNamed( context, Routes.equipmentOutAdd, arguments: {'selectedEquipments': selectedEquipmentsSummary}, ); if (result == true) { setState(() { _controller.loadData(); }); } } // 대여 처리 버튼 핸들러 void _handleRentEquipment() async { if (_controller.getSelectedInStockCount() == 0) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('대여할 장비를 선택해주세요.'))); return; } // 선택된 장비들의 요약 정보를 가져와서 대여 폼으로 전달 final selectedEquipmentsSummary = _controller.getSelectedEquipmentsSummary(); // 현재는 대여 기능이 준비되지 않았으므로 간단히 스낵바 표시 ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( '${selectedEquipmentsSummary.length}개 장비 대여 기능은 준비 중입니다.', ), ), ); } // 폐기 처리 버튼 핸들러 void _handleDisposeEquipment() { if (_controller.getSelectedInStockCount() == 0) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('폐기할 장비를 선택해주세요.'))); return; } // 선택된 장비들의 요약 정보를 가져옴 final selectedEquipmentsSummary = _controller.getSelectedEquipmentsSummary(); showDialog( context: context, builder: (context) => AlertDialog( title: const Text('폐기 확인'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '선택한 ${selectedEquipmentsSummary.length}개 장비를 폐기하시겠습니까?', ), const SizedBox(height: 16), const Text( '폐기할 장비 목록:', style: TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), ...selectedEquipmentsSummary.map((equipmentData) { final equipment = equipmentData['equipment'] as Equipment; return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Text( '${equipment.manufacturer} ${equipment.name} (${equipment.quantity}개)', style: const TextStyle(fontSize: 14), ), ); }).toList(), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('취소'), ), TextButton( onPressed: () { // 여기에 폐기 로직 추가 예정 ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('폐기 기능은 준비 중입니다.')), ); Navigator.pop(context); }, child: const Text('폐기'), ), ], ), ); } // 카테고리 축약 표기 함수 (예: 컴... > 태... > 안드로...) String _shortenCategory(String category) { if (category.length <= 2) return category; return category.substring(0, 2) + '...'; } // 카테고리 툴팁 위젯 (UI만 담당, 축약 표기 적용) Widget _buildCategoryWithTooltip(UnifiedEquipment equipment) { final fullCategory = EquipmentDisplayHelper.formatCategory( equipment.equipment.category, equipment.equipment.subCategory, equipment.equipment.subSubCategory, ); // 축약 표기 적용 final shortCategory = [ _shortenCategory(equipment.equipment.category), _shortenCategory(equipment.equipment.subCategory), _shortenCategory(equipment.equipment.subSubCategory), ].join(' > '); return Tooltip(message: fullCategory, child: Text(shortCategory)); } @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; final maxContentWidth = screenWidth > 1200 ? 1200.0 : screenWidth - 32; String screenTitle = '장비 목록'; if (widget.currentRoute == Routes.equipmentInList) { screenTitle = '입고된 장비'; } else if (widget.currentRoute == Routes.equipmentOutList) { screenTitle = '출고된 장비'; } else if (widget.currentRoute == Routes.equipmentRentList) { screenTitle = '대여된 장비'; } final int totalCount = _controller.equipments.length; final List filteredEquipments = _appliedSearchKeyword.isEmpty ? _controller.equipments : _controller.equipments.where((e) { final keyword = _appliedSearchKeyword.toLowerCase(); // 모든 주요 필드에서 검색 return [ e.equipment.manufacturer, e.equipment.name, e.equipment.category, e.equipment.subCategory, e.equipment.subSubCategory, e.equipment.serialNumber ?? '', e.equipment.barcode ?? '', e.equipment.remark ?? '', e.equipment.warrantyLicense ?? '', e.notes ?? '', ].any((field) => field.toLowerCase().contains(keyword)); }).toList(); final int filteredCount = filteredEquipments.length; final int startIndex = (_currentPage - 1) * _pageSize; final int endIndex = (startIndex + _pageSize) > filteredCount ? filteredCount : (startIndex + _pageSize); final pagedEquipments = filteredEquipments.sublist(startIndex, endIndex); // 선택된 장비 개수 final int selectedCount = _controller.getSelectedEquipmentCount(); final int selectedInCount = _controller.getSelectedInStockCount(); final int selectedOutCount = _controller.getSelectedEquipmentCountByStatus( EquipmentStatus.out, ); final int selectedRentCount = _controller.getSelectedEquipmentCountByStatus( EquipmentStatus.rent, ); return MainLayout( title: screenTitle, currentRoute: widget.currentRoute, actions: [ IconButton( icon: Icon( _showDetailedColumns ? Icons.view_column : Icons.view_compact, color: Colors.grey, ), tooltip: _showDetailedColumns ? '간소화된 보기' : '상세 보기', onPressed: () { setState(() { _showDetailedColumns = !_showDetailedColumns; }); }, ), IconButton( icon: const Icon(Icons.refresh), onPressed: () { setState(() { _controller.loadData(); _currentPage = 1; }); }, color: Colors.grey, ), ], child: Container( width: maxContentWidth, padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( padding: const EdgeInsets.symmetric(vertical: 12), child: Text( screenTitle, style: AppThemeTailwind.headingStyle, ), ), if (selectedCount > 0) Container( padding: const EdgeInsets.symmetric( vertical: 12, horizontal: 16, ), decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(4), ), child: Text( '$selectedCount개 선택됨', style: const TextStyle(fontWeight: FontWeight.bold), ), ), if (widget.currentRoute == Routes.equipmentInList) Row( children: [ ElevatedButton.icon( onPressed: selectedInCount > 0 ? _handleOutEquipment : null, icon: const Icon( Icons.exit_to_app, color: Colors.white, ), label: const Text( '출고', style: TextStyle(color: Colors.white), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, disabledBackgroundColor: Colors.blue.withOpacity(0.5), padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), textStyle: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 8), ElevatedButton.icon( onPressed: () async { final result = await Navigator.pushNamed( context, Routes.equipmentInAdd, ); if (result == true) { setState(() { _controller.loadData(); _currentPage = 1; }); } }, icon: const Icon(Icons.add, color: Colors.white), label: const Text( '입고', style: TextStyle(color: Colors.white), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), textStyle: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 16), SizedBox( width: 220, child: Row( children: [ Expanded( child: TextField( decoration: const InputDecoration( hintText: '장비 검색', prefixIcon: Icon(Icons.search), border: OutlineInputBorder(), isDense: true, contentPadding: EdgeInsets.symmetric( vertical: 8, horizontal: 12, ), ), onChanged: (value) { setState(() { _searchKeyword = value; }); }, onSubmitted: (value) { setState(() { _appliedSearchKeyword = value; _currentPage = 1; }); }, ), ), const SizedBox(width: 4), IconButton( icon: const Icon(Icons.arrow_forward), tooltip: '검색', onPressed: () { setState(() { _appliedSearchKeyword = _searchKeyword; _currentPage = 1; }); }, ), ], ), ), ], ), // 출고 목록 화면일 때 버튼들 if (widget.currentRoute == Routes.equipmentOutList) Row( children: [ ElevatedButton.icon( onPressed: selectedOutCount > 0 ? () { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('재입고 기능은 준비 중입니다.'), ), ); } : null, icon: const Icon( Icons.assignment_return, color: Colors.white, ), label: const Text( '재입고', style: TextStyle(color: Colors.white), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, disabledBackgroundColor: Colors.green.withOpacity( 0.5, ), padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), textStyle: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 8), ElevatedButton.icon( onPressed: selectedOutCount > 0 ? () { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('수리 요청 기능은 준비 중입니다.'), ), ); } : null, icon: const Icon(Icons.build, color: Colors.white), label: const Text( '수리 요청', style: TextStyle(color: Colors.white), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.orange, disabledBackgroundColor: Colors.orange.withOpacity( 0.5, ), padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), textStyle: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, ), ), ), ], ), // 대여 목록 화면일 때 버튼들 if (widget.currentRoute == Routes.equipmentRentList) Row( children: [ ElevatedButton.icon( onPressed: selectedRentCount > 0 ? () { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('대여 반납 기능은 준비 중입니다.'), ), ); } : null, icon: const Icon( Icons.keyboard_return, color: Colors.white, ), label: const Text( '반납', style: TextStyle(color: Colors.white), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, disabledBackgroundColor: Colors.green.withOpacity( 0.5, ), padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), textStyle: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 8), ElevatedButton.icon( onPressed: selectedRentCount > 0 ? () { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('대여 연장 기능은 준비 중입니다.'), ), ); } : null, icon: const Icon(Icons.date_range, color: Colors.white), label: const Text( '연장', style: TextStyle(color: Colors.white), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, disabledBackgroundColor: Colors.blue.withOpacity(0.5), padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 10, ), textStyle: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, ), ), ), ], ), ], ), const SizedBox(height: 8), Expanded( child: pagedEquipments.isEmpty ? const Center(child: Text('장비 정보가 없습니다.')) : SingleChildScrollView( scrollDirection: Axis.horizontal, child: ConstrainedBox( constraints: BoxConstraints( minWidth: maxContentWidth, ), child: EquipmentTable( equipments: pagedEquipments, selectedEquipmentIds: _controller.selectedEquipmentIds, showDetailedColumns: _showDetailedColumns, onEquipmentSelected: _onEquipmentSelected, getOutEquipmentInfo: _controller.getOutEquipmentInfo, buildCategoryWithTooltip: _buildCategoryWithTooltip, // 수정 버튼 동작: 입고 폼(수정 모드)로 이동 onEdit: (id, status) async { if (status == EquipmentStatus.in_) { final result = await Navigator.pushNamed( context, Routes.equipmentInEdit, arguments: id, ); if (result == true) { setState(() { _controller.loadData(); }); } } else { // 출고/대여 등은 별도 폼으로 이동 필요시 구현 } }, // 삭제 버튼 동작: 삭제 다이얼로그 및 삭제 처리 onDelete: (id, status) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('삭제 확인'), content: const Text('이 장비 정보를 삭제하시겠습니까?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('취소'), ), TextButton( onPressed: () { setState(() { // 입고/출고 상태에 따라 삭제 처리 if (status == EquipmentStatus.in_) { MockDataService() .deleteEquipmentIn(id); } else if (status == EquipmentStatus.out) { MockDataService() .deleteEquipmentOut(id); } _controller.loadData(); }); Navigator.pop(context); }, child: const Text('삭제'), ), ], ), ); }, getSelectedInStockCount: _controller.getSelectedInStockCount, ), ), ), ), if (totalCount > _pageSize) Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Pagination( totalCount: filteredCount, currentPage: _currentPage, pageSize: _pageSize, onPageChanged: (page) { setState(() { _currentPage = page; }); }, ), ), ], ), ), ); } }