import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:superport/models/equipment_unified_model.dart'; import 'package:superport/models/company_model.dart'; import 'package:superport/models/company_branch_info.dart'; import 'package:superport/models/address_model.dart'; import 'package:superport/screens/common/custom_widgets.dart'; import 'package:superport/screens/common/theme_tailwind.dart'; import 'package:superport/services/mock_data_service.dart'; import 'package:superport/screens/equipment/controllers/equipment_out_form_controller.dart'; import 'package:superport/screens/equipment/widgets/equipment_summary_card.dart'; import 'package:superport/screens/equipment/widgets/equipment_summary_row.dart'; import 'package:superport/screens/common/widgets/remark_input.dart'; class EquipmentOutFormScreen extends StatefulWidget { final int? equipmentOutId; final Equipment? selectedEquipment; final int? selectedEquipmentInId; final List>? selectedEquipments; const EquipmentOutFormScreen({ Key? key, this.equipmentOutId, this.selectedEquipment, this.selectedEquipmentInId, this.selectedEquipments, }) : super(key: key); @override State createState() => _EquipmentOutFormScreenState(); } class _EquipmentOutFormScreenState extends State { late final EquipmentOutFormController _controller; @override void initState() { super.initState(); _controller = EquipmentOutFormController(dataService: MockDataService()); _controller.isEditMode = widget.equipmentOutId != null; _controller.equipmentOutId = widget.equipmentOutId; _controller.selectedEquipment = widget.selectedEquipment; _controller.selectedEquipmentInId = widget.selectedEquipmentInId; _controller.selectedEquipments = widget.selectedEquipments; _controller.loadDropdownData(); if (_controller.isEditMode) { // 수정 모드: 기존 출고 정보 로드 // (이 부분은 실제 서비스에서 컨트롤러에 메서드 추가 필요) } else if (widget.selectedEquipments != null && widget.selectedEquipments!.isNotEmpty) { // 다중 선택 장비 있음: 별도 초기화 필요시 컨트롤러에서 처리 } else if (widget.selectedEquipment != null) { _controller.initializeWithSelectedEquipment(widget.selectedEquipment!); } } @override void dispose() { _controller.dispose(); super.dispose(); } // 요약 테이블 위젯 - 다중 선택 장비에 대한 요약 테이블 Widget _buildSummaryTable(EquipmentOutFormController controller) { if (controller.selectedEquipments == null || controller.selectedEquipments!.isEmpty) { return const SizedBox.shrink(); } // 각 장비별로 전체 폭을 사용하는 리스트로 구현 return Container( width: double.infinity, // 전체 폭 사용 child: Card( elevation: 2, margin: EdgeInsets.zero, // margin 제거 child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '선택된 장비 목록 (${controller.selectedEquipments!.length}개)', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 12), // 리스트 헤더 Row( children: const [ Expanded( flex: 2, child: Text( '제조사', style: TextStyle(fontWeight: FontWeight.bold), ), ), Expanded( flex: 2, child: Text( '장비명', style: TextStyle(fontWeight: FontWeight.bold), ), ), Expanded( flex: 1, child: Text( '수량', style: TextStyle(fontWeight: FontWeight.bold), ), ), Expanded( flex: 2, child: Text( '워런티 시작일', style: TextStyle(fontWeight: FontWeight.bold), ), ), Expanded( flex: 2, child: Text( '워런티 종료일', style: TextStyle(fontWeight: FontWeight.bold), ), ), ], ), const Divider(), // 리스트 본문 Column( children: List.generate(controller.selectedEquipments!.length, ( index, ) { final equipmentData = controller.selectedEquipments![index]; final equipment = equipmentData['equipment'] as Equipment; // 워런티 날짜를 임시로 저장할 수 있도록 상태를 관리(컨트롤러에 리스트로 추가하거나, 여기서 임시로 관리) // 여기서는 equipment 객체의 필드를 직접 수정(실제 서비스에서는 별도 상태 관리 필요) return Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: Row( children: [ Expanded(flex: 2, child: Text(equipment.manufacturer)), Expanded(flex: 2, child: Text(equipment.name)), Expanded(flex: 1, child: Text('${equipment.quantity}')), Expanded( flex: 2, child: InkWell( onTap: () async { final picked = await showDatePicker( context: context, initialDate: equipment.warrantyStartDate ?? DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(2100), ); if (picked != null) { equipment.warrantyStartDate = picked; controller.notifyListeners(); } }, child: Container( padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 4, ), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(4), ), child: Text( _formatDate(equipment.warrantyStartDate), style: const TextStyle( decoration: TextDecoration.underline, color: Colors.blue, ), ), ), ), ), Expanded( flex: 2, child: InkWell( onTap: () async { final picked = await showDatePicker( context: context, initialDate: equipment.warrantyEndDate ?? DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(2100), ); if (picked != null) { equipment.warrantyEndDate = picked; controller.notifyListeners(); } }, child: Container( padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 4, ), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(4), ), child: Text( _formatDate(equipment.warrantyEndDate), style: const TextStyle( decoration: TextDecoration.underline, color: Colors.blue, ), ), ), ), ), ], ), ); }), ), ], ), ), ), ); } // 날짜 포맷 유틸리티 String _formatDate(DateTime? date) { if (date == null) return '정보 없음'; return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}'; } @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: _controller, child: Consumer( builder: (context, controller, child) { // 담당자가 없거나 첫 번째 회사에 대한 담당자가 '없음'인 경우 등록 버튼 비활성화 조건 final bool canSubmit = controller.selectedCompanies.isNotEmpty && controller.selectedCompanies[0] != null && controller.hasManagersPerCompany[0] && controller.filteredManagersPerCompany[0].first != '없음'; final int totalSelectedEquipments = controller.selectedEquipments?.length ?? 0; // 로딩 상태 처리 if (controller.isLoading) { return Scaffold( appBar: AppBar( title: const Text('장비 출고'), ), body: const Center( child: CircularProgressIndicator(), ), ); } // 에러 상태 처리 if (controller.errorMessage != null) { return Scaffold( appBar: AppBar( title: const Text('장비 출고'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, size: 64, color: Colors.red.shade400, ), const SizedBox(height: 16), Text( '오류가 발생했습니다', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 8), Text( controller.errorMessage!, style: TextStyle(color: Colors.grey.shade600), textAlign: TextAlign.center, ), const SizedBox(height: 24), ElevatedButton( onPressed: () { controller.clearError(); controller.loadDropdownData(); }, child: const Text('다시 시도'), ), ], ), ), ); } return Scaffold( appBar: AppBar( title: Text( controller.isEditMode ? '장비 출고 수정' : totalSelectedEquipments > 0 ? '장비 출고 등록 (${totalSelectedEquipments}개)' : '장비 출고 등록', ), ), body: Padding( padding: const EdgeInsets.all(16.0), child: Form( key: controller.formKey, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 장비 정보 요약 섹션 if (controller.selectedEquipments != null && controller.selectedEquipments!.isNotEmpty) _buildSummaryTable(controller) else if (controller.selectedEquipment != null) // 단일 장비 요약 카드도 전체 폭으로 맞춤 Container( width: double.infinity, child: EquipmentSingleSummaryCard( equipment: controller.selectedEquipment!, ), ) else const SizedBox.shrink(), // 요약 카드 아래 라디오 버튼 추가 const SizedBox(height: 12), // 전체 폭을 사용하는 라디오 버튼 Container(width: double.infinity, child: _buildOutTypeRadio(controller)), const SizedBox(height: 16), // 출고 정보 입력 섹션 (수정/등록) _buildOutgoingInfoSection(context, controller), // 비고 입력란 추가 const SizedBox(height: 16), FormFieldWrapper( label: '비고', isRequired: false, child: RemarkInput( controller: controller.remarkController, hint: '비고를 입력하세요', minLines: 4, ), ), const SizedBox(height: 24), // 담당자 없음 경고 메시지 if (controller.selectedCompanies.isNotEmpty && controller.selectedCompanies[0] != null && (!controller.hasManagersPerCompany[0] || controller.filteredManagersPerCompany[0].first == '없음')) Container( padding: const EdgeInsets.all(8), margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: Colors.red.shade100, borderRadius: BorderRadius.circular(4), border: Border.all(color: Colors.red.shade300), ), child: const Row( children: [ Icon(Icons.warning, color: Colors.red), SizedBox(width: 8), Expanded( child: Text( '선택한 회사에 등록된 담당자가 없습니다. 담당자를 먼저 등록해야 합니다.', style: TextStyle(color: Colors.red), ), ), ], ), ), // 저장 버튼 SizedBox( width: double.infinity, child: ElevatedButton( onPressed: canSubmit ? () { // 각 회사별 담당자를 첫 번째 항목으로 설정 for ( int i = 0; i < controller.selectedCompanies.length; i++ ) { if (controller.selectedCompanies[i] != null && controller.hasManagersPerCompany[i] && controller .filteredManagersPerCompany[i] .isNotEmpty && _controller .filteredManagersPerCompany[i] .first != '없음') { controller.selectedManagersPerCompany[i] = controller .filteredManagersPerCompany[i] .first; } } controller.saveEquipmentOut(context).then((success) { if (success) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('출고가 완료되었습니다.'), duration: Duration(seconds: 2), ), ); Navigator.pop(context, true); } }); } : null, style: canSubmit ? AppThemeTailwind.primaryButtonStyle : ElevatedButton.styleFrom( backgroundColor: Colors.grey.shade300, foregroundColor: Colors.grey.shade700, ), child: Padding( padding: const EdgeInsets.all(12.0), child: Text( controller.isEditMode ? '수정하기' : '등록하기', style: const TextStyle(fontSize: 16), ), ), ), ), ], ), ), ), ), ); }, ), ); } // 출고 정보 입력 섹션 위젯 (등록/수정 공통) Widget _buildOutgoingInfoSection(BuildContext context, EquipmentOutFormController controller) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('출고 정보', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 12), // 출고일 _buildDateField( context, controller, label: '출고일', date: controller.outDate, onDateChanged: (picked) { controller.outDate = picked; }, ), // 출고 회사 영역 헤더 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('출고 회사', style: TextStyle(fontWeight: FontWeight.bold)), TextButton.icon( onPressed: () { controller.addCompany(); }, icon: const Icon(Icons.add_circle_outline, size: 18), label: const Text('출고 회사 추가'), style: TextButton.styleFrom( padding: EdgeInsets.zero, minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), ], ), const SizedBox(height: 4), // 동적 출고 회사 드롭다운 목록 ...List.generate(controller.selectedCompanies.length, (index) { return Padding( padding: const EdgeInsets.only(bottom: 12.0), child: DropdownButtonFormField( value: controller.selectedCompanies[index], decoration: InputDecoration( hintText: index == 0 ? '출고할 회사를 선택하세요' : '추가된 출고할 회사를 선택하세요', // 이전 드롭다운에 값이 선택되지 않았으면 비활성화 enabled: index == 0 || controller.selectedCompanies[index - 1] != null, ), items: controller.availableCompaniesPerDropdown[index] .map( (item) => DropdownMenuItem( value: item.name, child: _buildCompanyDropdownItem(item.name, controller), ), ) .toList(), validator: (value) { if (index == 0 && (value == null || value.isEmpty)) { return '출고 회사를 선택해주세요'; } return null; }, onChanged: (index == 0 || controller.selectedCompanies[index - 1] != null) ? (value) { controller.selectedCompanies[index] = value; controller.filterManagersByCompanyAtIndex(index); controller.updateAvailableCompanies(); } : null, ), ); }), // 각 회사별 담당자 선택 목록 ...List.generate(controller.selectedCompanies.length, (index) { // 회사가 선택된 경우에만 담당자 표시 if (controller.selectedCompanies[index] != null) { // 회사 정보 가져오기 final companyInfo = controller.companiesWithBranches.firstWhere( (info) => info.name == controller.selectedCompanies[index], orElse: () => CompanyBranchInfo( id: 0, name: controller.selectedCompanies[index]!, originalName: controller.selectedCompanies[index]!, isMainCompany: true, companyId: 0, branchId: null, ), ); // 실제 회사/지점 정보를 ID로 가져오기 Company? company; Branch? branch; if (companyInfo.companyId != null) { company = controller.dataService.getCompanyById( companyInfo.companyId!, ); if (!companyInfo.isMainCompany && companyInfo.branchId != null && company != null) { final branches = company.branches; if (branches != null) { branch = branches.firstWhere( (b) => b.id == companyInfo.branchId, orElse: () => Branch( companyId: companyInfo.companyId!, name: companyInfo.originalName, ), ); } } } return Padding( padding: const EdgeInsets.only(bottom: 12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '담당자 정보 (${controller.selectedCompanies[index]})', style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 4), Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 15, ), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade400), borderRadius: BorderRadius.circular(4), ), child: company != null ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 본사/지점 정보 표시 if (companyInfo.isMainCompany && company.contactName != null && company.contactName!.isNotEmpty) Text( '${company.contactName} ${company.contactPosition ?? ""} ${company.contactPhone ?? ""} ${company.contactEmail ?? ""}', style: AppThemeTailwind.bodyStyle, ), if (!companyInfo.isMainCompany && branch != null && branch.contactName != null && branch.contactName!.isNotEmpty) Text( '${branch.contactName} ${branch.contactPosition ?? ""} ${branch.contactPhone ?? ""} ${branch.contactEmail ?? ""}', style: AppThemeTailwind.bodyStyle, ), const SizedBox(height: 8), // 담당자 목록에서 실제 담당자 정보만 표시하는 부분은 제거 ], ) : Text( '회사 정보를 불러올 수 없습니다.', style: TextStyle( color: Colors.red.shade400, fontStyle: FontStyle.italic, ), ), ), ], ), ); } else { return const SizedBox.shrink(); } }), // 유지 보수(라이센스) 선택 _buildDropdownField( label: '유지 보수', // 텍스트 변경 value: controller.selectedLicense, items: controller.licenses, hint: '유지 보수를 선택하세요', // 텍스트 변경 onChanged: (value) { controller.selectedLicense = value; }, validator: (value) { if (value == null || value.isEmpty) { return '유지 보수를 선택해주세요'; // 텍스트 변경 } return null; }, ), ], ); } // 날짜 선택 필드 위젯 Widget _buildDateField( BuildContext context, EquipmentOutFormController controller, { required String label, required DateTime date, required ValueChanged onDateChanged, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 4), InkWell( onTap: () async { final DateTime? picked = await showDatePicker( context: context, initialDate: date, firstDate: DateTime(2000), lastDate: DateTime(2100), ); if (picked != null && picked != date) { onDateChanged(picked); } }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 15), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade400), borderRadius: BorderRadius.circular(4), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( controller.formatDate(date), style: AppThemeTailwind.bodyStyle, ), const Icon(Icons.calendar_today, size: 20), ], ), ), ), const SizedBox(height: 12), ], ); } // 드롭다운 필드 위젯 Widget _buildDropdownField({ required String label, required String? value, required List items, required String hint, required ValueChanged? onChanged, required String? Function(String?) validator, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 4), DropdownButtonFormField( value: value, decoration: InputDecoration(hintText: hint), items: items .map( (item) => DropdownMenuItem( value: item, child: Text(item), ), ) .toList(), validator: validator, onChanged: onChanged, ), const SizedBox(height: 12), ], ); } // 회사 이름을 표시하는 위젯 (지점 포함) Widget _buildCompanyDropdownItem(String item, EquipmentOutFormController controller) { final TextStyle defaultStyle = TextStyle( color: Colors.black87, fontSize: 14, fontWeight: FontWeight.normal, ); // 컨트롤러에서 해당 항목에 대한 정보 확인 final companyInfoList = controller.companiesWithBranches .where((info) => info.name == item) .toList(); // 회사 정보가 존재하고 지점인 경우 if (companyInfoList.isNotEmpty && !companyInfoList[0].isMainCompany) { final companyInfo = companyInfoList[0]; final parentCompanyName = companyInfo.parentCompanyName ?? ''; final branchName = companyInfo.displayName ?? companyInfo.originalName; // Row 대신 RichText 사용 - 지점 표시 return RichText( text: TextSpan( style: defaultStyle, // 기본 스타일 설정 children: [ WidgetSpan( child: Icon( Icons.subdirectory_arrow_right, size: 16, color: Colors.grey, ), alignment: PlaceholderAlignment.middle, ), TextSpan(text: ' ', style: defaultStyle), TextSpan( text: parentCompanyName, // 회사명 style: defaultStyle, ), TextSpan(text: ' ', style: defaultStyle), TextSpan( text: branchName, // 지점명 style: const TextStyle( color: Colors.indigo, fontWeight: FontWeight.w500, fontSize: 14, ), ), ], ), overflow: TextOverflow.ellipsis, maxLines: 1, ); } // 일반 회사명 (본사) return RichText( text: TextSpan( style: defaultStyle, // 기본 스타일 설정 children: [ WidgetSpan( child: Icon(Icons.business, size: 16, color: Colors.black54), alignment: PlaceholderAlignment.middle, ), TextSpan(text: ' ', style: defaultStyle), TextSpan( text: item, style: defaultStyle.copyWith(fontWeight: FontWeight.w500), ), ], ), overflow: TextOverflow.ellipsis, maxLines: 1, ); } // 회사 ID에 따른 담당자 정보를 가져와 표시하는 위젯 목록 생성 List _getUsersForCompany(CompanyBranchInfo companyInfo) { final List userWidgets = []; // 판교지점 특별 처리 if (companyInfo.originalName == "판교지점" && companyInfo.parentCompanyName == "LG전자") { userWidgets.add( Text( '정수진 사원 010-4567-8901 jung.soojin@lg.com', style: AppThemeTailwind.bodyStyle, ), ); } return userWidgets; } // 출고/대여/폐기 라디오 버튼 위젯 Widget _buildOutTypeRadio(EquipmentOutFormController controller) { // 출고 유형 리스트 final List outTypes = ['출고', '대여', '폐기']; return Row( mainAxisAlignment: MainAxisAlignment.start, children: outTypes.map((type) { return Row( children: [ Radio( value: type, groupValue: controller.outType, // 컨트롤러에서 현재 선택값 관리 onChanged: (value) { controller.outType = value!; }, ), Text(type), ], ); }).toList(), ); } }