import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:superport/data/models/equipment/equipment_dto.dart'; import 'package:superport/screens/equipment/controllers/equipment_outbound_controller.dart'; import 'package:superport/screens/common/widgets/standard_dropdown.dart'; import 'package:superport/screens/common/widgets/remark_input.dart'; class EquipmentOutboundDialog extends StatefulWidget { final List selectedEquipments; const EquipmentOutboundDialog({ super.key, required this.selectedEquipments, }); @override State createState() => _EquipmentOutboundDialogState(); } class _EquipmentOutboundDialogState extends State { late final EquipmentOutboundController _controller; @override void initState() { super.initState(); _controller = EquipmentOutboundController( selectedEquipments: widget.selectedEquipments, ); _controller.initialize(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: _controller, child: Consumer( builder: (context, controller, child) { return ShadDialog( title: Text('장비 출고 (${widget.selectedEquipments.length}개)'), actions: [ ShadButton.outline( onPressed: () => Navigator.of(context).pop(), child: const Text('취소'), ), ShadButton( onPressed: controller.canSubmit ? () async { print('[EquipmentOutboundDialog] 출고 버튼 클릭됨'); final success = await controller.processOutbound(); if (context.mounted) { if (success) { print('[EquipmentOutboundDialog] 출고 처리 성공, 다이얼로그 닫기'); Navigator.of(context).pop(true); // true를 반환하여 부모에서 새로고침 할 수 있도록 ShadToaster.of(context).show( const ShadToast( title: Text('출고 완료'), description: Text('장비 출고가 완료되었습니다.'), ), ); } else { print('[EquipmentOutboundDialog] 출고 처리 실패'); // 에러 메시지는 controller에서 이미 설정되므로 추가 토스트는 필요 없음 // 다이얼로그는 열린 상태로 유지하여 사용자가 에러 메시지를 볼 수 있도록 함 } } } : null, child: controller.isLoading ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('출고 처리'), ), ], child: Material( color: Colors.transparent, child: Container( width: 800, height: 600, padding: const EdgeInsets.all(24), child: controller.isLoading ? const Center(child: ShadProgress()) : SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 창고 정보 로딩 상태 표시 if (controller.isLoadingWarehouseInfo) Container( margin: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.blue.shade200), ), child: const Row( children: [ SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ), SizedBox(width: 12), Text('장비 창고 정보 로딩 중...'), ], ), ), // 창고 정보 로딩 오류 표시 if (controller.warehouseError != null) Container( margin: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.orange.shade50, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.orange.shade200), ), child: Row( children: [ Icon(Icons.warning, color: Colors.orange.shade600, size: 20), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '창고 정보 로딩 실패', style: TextStyle(fontWeight: FontWeight.w500), ), Text( '기존 장비 정보의 창고 데이터를 사용합니다.', style: TextStyle( fontSize: 12, color: Colors.orange.shade700, ), ), ], ), ), ], ), ), // 선택된 장비 목록 _buildEquipmentSummary(controller), const SizedBox(height: 24), // 출고 정보 입력 _buildOutboundForm(controller), ], ), ), ), ), ); }, ), ); } Widget _buildEquipmentSummary(EquipmentOutboundController controller) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '선택된 장비', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), // 장비별 상세 정보 카드 Container( constraints: const BoxConstraints(maxHeight: 300), // 스크롤 가능한 영역 child: SingleChildScrollView( child: Column( children: widget.selectedEquipments.map((equipment) { return Container( margin: const EdgeInsets.only(bottom: 12), child: ShadCard( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 첫번째 줄: 제조사, 모델명 Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('제조사', style: TextStyle(fontSize: 12, color: Colors.grey)), Text(equipment.vendorName ?? '-', style: const TextStyle(fontWeight: FontWeight.w500)), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('모델명', style: TextStyle(fontSize: 12, color: Colors.grey)), Text(equipment.modelName ?? '-', style: const TextStyle(fontWeight: FontWeight.w500)), ], ), ), ], ), const SizedBox(height: 12), // 두번째 줄: 시리얼번호, 바코드 Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('시리얼번호', style: TextStyle(fontSize: 12, color: Colors.grey)), Text(equipment.serialNumber ?? '-', style: const TextStyle(fontWeight: FontWeight.w500)), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('바코드', style: TextStyle(fontSize: 12, color: Colors.grey)), Text(equipment.barcode ?? '-', style: const TextStyle(fontWeight: FontWeight.w500)), ], ), ), ], ), const SizedBox(height: 12), // 세번째 줄: 구매가격, 등록일 Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('구매가격', style: TextStyle(fontSize: 12, color: Colors.grey)), Text(controller.formatPrice(equipment.purchasePrice), style: const TextStyle(fontWeight: FontWeight.w500)), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('등록일', style: TextStyle(fontSize: 12, color: Colors.grey)), Text( equipment.registeredAt != null ? controller.formatDate(equipment.registeredAt!) : '-', style: const TextStyle(fontWeight: FontWeight.w500) ), ], ), ), ], ), const SizedBox(height: 12), // 네번째 줄: 워런티 만료일 (수정 가능) Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('워런티 만료일', style: TextStyle(fontSize: 12, color: Colors.grey)), Row( children: [ Builder( builder: (context) { final equipmentId = equipment.id; if (equipmentId != null) { final warrantyDate = controller.getWarrantyDate(equipmentId); if (warrantyDate != null) { return Text( controller.formatDate(warrantyDate), style: const TextStyle(fontWeight: FontWeight.w500), ); } else if (equipment.warrantyEndedAt != null) { return Text( controller.formatDate(equipment.warrantyEndedAt), style: const TextStyle(fontWeight: FontWeight.w500), ); } } return const Text('미지정', style: TextStyle(fontWeight: FontWeight.w500)); }, ), const SizedBox(width: 8), InkWell( onTap: () async { final equipmentId = equipment.id; if (equipmentId != null) { final date = await showDatePicker( context: context, initialDate: controller.getWarrantyDate(equipmentId) ?? equipment.warrantyEndedAt ?? DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(2100), ); if (date != null) { controller.updateWarrantyDate(equipmentId, date); } } }, child: const Icon(Icons.edit, size: 16, color: Colors.blue), ), ], ), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('현재 창고', style: TextStyle(fontSize: 12, color: Colors.grey)), Builder( builder: (context) { // 개선된 창고 정보 조회 (Stock Status API 기반) final currentWarehouse = controller.getEquipmentCurrentWarehouse(equipment); final stockStatus = controller.getEquipmentStockStatus(equipment); final isFromStockApi = stockStatus != null; return Row( children: [ // 창고명 표시 Expanded( child: Text( currentWarehouse, style: TextStyle( fontWeight: FontWeight.w500, color: currentWarehouse == '위치 미확인' ? Colors.red : isFromStockApi ? Colors.green.shade700 // Stock API 기반 = 정확한 정보 : Colors.orange.shade700, // Equipment DTO 기반 = 참고 정보 ), ), ), // 데이터 소스 표시 아이콘 if (isFromStockApi) Tooltip( message: '실시간 재고 현황 기반', child: Icon( Icons.verified, size: 16, color: Colors.green.shade600, ), ) else if (currentWarehouse != '위치 미확인') Tooltip( message: '장비 등록 정보 기반 (참고용)', child: Icon( Icons.info_outline, size: 16, color: Colors.orange.shade600, ), ), ], ); }, ), // 재고 현황 추가 정보 (Stock Status API 사용 가능 시) Builder( builder: (context) { final stockStatus = controller.getEquipmentStockStatus(equipment); if (stockStatus != null && stockStatus.lastTransactionDate != null) { return Padding( padding: const EdgeInsets.only(top: 2), child: Text( '최종 이동: ${controller.formatDate(stockStatus.lastTransactionDate!)}', style: TextStyle( fontSize: 10, color: Colors.grey.shade600, ), ), ); } return const SizedBox.shrink(); }, ), ], ), ), ], ), // 비고가 있으면 표시 if (equipment.remark != null && equipment.remark!.isNotEmpty) ...[ const SizedBox(height: 12), const Divider(), const SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('장비 비고', style: TextStyle(fontSize: 12, color: Colors.grey)), const SizedBox(height: 4), Text(equipment.remark!, style: const TextStyle(fontWeight: FontWeight.w400)), ], ), ], ], ), ), ), ); }).toList(), ), ), ), ], ); } Widget _buildOutboundForm(EquipmentOutboundController controller) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '출고 정보', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(height: 16), // 거래일 Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('거래일 *', style: TextStyle(fontWeight: FontWeight.w500)), const SizedBox(height: 8), InkWell( onTap: () async { final date = await showDatePicker( context: context, initialDate: controller.transactionDate, firstDate: DateTime(2000), lastDate: DateTime.now(), ); if (date != null) { controller.transactionDate = date; } }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade400), borderRadius: BorderRadius.circular(6), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(controller.formatDate(controller.transactionDate)), const Icon(Icons.calendar_today, size: 18), ], ), ), ), ], ), const SizedBox(height: 16), // 출고 대상 회사 StandardIntDropdown( label: '출고 대상 회사', isRequired: true, items: controller.companies, isLoading: controller.isLoadingCompanies, error: controller.companyError, onRetry: () => controller.loadCompanies(), selectedValue: controller.selectedCompany, onChanged: (value) { controller.selectedCompany = value; }, itemBuilder: (item) => Text(item.name), selectedItemBuilder: (item) => Text(item.name), idExtractor: (item) => item.id ?? 0, ), const SizedBox(height: 16), // 비고 RemarkInput( controller: controller.remarkController, label: '비고', hint: '출고 관련 비고사항을 입력하세요', minLines: 3, ), // 에러 메시지 if (controller.errorMessage != null) Container( margin: const EdgeInsets.only(top: 16), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.red.shade50, borderRadius: BorderRadius.circular(6), border: Border.all(color: Colors.red.shade300), ), child: Row( children: [ Icon(Icons.error_outline, color: Colors.red.shade600, size: 20), const SizedBox(width: 8), Expanded( child: Text( controller.errorMessage!, style: TextStyle(color: Colors.red.shade600), ), ), ], ), ), ], ); } }