import 'package:flutter/material.dart'; import 'package:superport/screens/common/theme_shadcn.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:get_it/get_it.dart'; import '../../data/models/rent_dto.dart'; import '../equipment/controllers/equipment_history_controller.dart'; class RentFormDialog extends StatefulWidget { final RentDto? rent; final Future Function(RentRequestDto) onSubmit; const RentFormDialog({ super.key, this.rent, required this.onSubmit, }); @override State createState() => _RentFormDialogState(); } class _RentFormDialogState extends State { final _formKey = GlobalKey(); late final EquipmentHistoryController _historyController; // 백엔드 스키마에 맞는 필드만 유지 int? _selectedEquipmentHistoryId; DateTime _startDate = DateTime.now(); DateTime? _endDate; bool _isLoading = false; bool _isLoadingHistories = false; String? _historiesError; @override void initState() { super.initState(); _historyController = GetIt.instance(); if (widget.rent != null) { _initializeForm(widget.rent!); } _loadEquipmentHistories(); } Future _loadEquipmentHistories() async { setState(() { _isLoadingHistories = true; _historiesError = null; // 오류 상태 초기화 }); try { await _historyController.loadHistory(); setState(() => _historiesError = null); // 성공 시 오류 상태 클리어 } catch (e) { debugPrint('장비 이력 로딩 실패: $e'); setState(() => _historiesError = '장비 이력을 불러오는 중 오류가 발생했습니다: ${e.toString()}'); } finally { if (mounted) { setState(() => _isLoadingHistories = false); } } } // 재시도 기능 추가 (UserForm 패턴과 동일) void _retryLoadHistories() { _loadEquipmentHistories(); } @override void dispose() { super.dispose(); } void _initializeForm(RentDto rent) { _selectedEquipmentHistoryId = rent.equipmentHistoryId; _startDate = rent.startedAt; _endDate = rent.endedAt; } Future _selectStartDate() async { final date = await showDatePicker( context: context, initialDate: _startDate, firstDate: DateTime(2020), lastDate: DateTime(2030), ); if (date != null) { setState(() { _startDate = date; }); } } Future _selectEndDate() async { final date = await showDatePicker( context: context, initialDate: _endDate ?? DateTime.now().add(const Duration(days: 30)), firstDate: _startDate, lastDate: DateTime(2030), ); if (date != null) { setState(() { _endDate = date; }); } } Future _submit() async { if (_selectedEquipmentHistoryId == null || _endDate == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('장비 이력과 종료일을 선택해주세요')), ); return; } setState(() => _isLoading = true); try { final request = RentRequestDto( equipmentHistoryId: _selectedEquipmentHistoryId!, startedAt: _startDate, endedAt: _endDate!, ); await widget.onSubmit(request); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('오류가 발생했습니다: $e')), ); } } finally { if (mounted) { setState(() => _isLoading = false); } } } @override Widget build(BuildContext context) { return Dialog( child: Container( width: 500, constraints: const BoxConstraints(maxHeight: 600), padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ // 제목 Text( widget.rent != null ? '임대 계약 수정' : '새 임대 계약', style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox(height: 24), // 폼 (백엔드 스키마 3개 필드만 포함) Expanded( child: Form( key: _formKey, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 장비 이력 선택 (백엔드 필수 필드) Text('장비 선택 *', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), // 3단계 상태 처리 (UserForm 패턴 적용) _isLoadingHistories ? SizedBox( height: 56, child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: const [ SizedBox(width: 120, child: ShadProgress()), SizedBox(width: 8), Text('장비 이력을 불러오는 중...'), ], ), ), ) // 오류 발생 시 오류 컨테이너 표시 : _historiesError != null ? Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.red.shade50, border: Border.all(color: Colors.red.shade200), borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.error, color: Colors.red.shade600, size: 20), const SizedBox(width: 8), const Text('장비 이력 로딩 실패', style: TextStyle(fontWeight: FontWeight.w500)), ], ), const SizedBox(height: 8), Text( _historiesError!, style: TextStyle(color: Colors.red.shade700, fontSize: 14), ), const SizedBox(height: 12), ShadButton( onPressed: _retryLoadHistories, child: const Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.refresh, size: 16), SizedBox(width: 4), Text('다시 시도'), ], ), ), ], ), ) // 정상 상태: 드롭다운 표시 : ShadSelect( placeholder: const Text('장비를 선택하세요'), options: _historyController.historyList.map((history) { return ShadOption( value: history.id!, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( '${history.equipment?.serialNumber ?? "Serial N/A"} - ${history.equipment?.modelName ?? "Model N/A"}', style: const TextStyle(fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis, ), Text( '거래: ${history.transactionType} | 수량: ${history.quantity} | ${history.transactedAt.toString().split(' ')[0]}', style: const TextStyle(fontSize: 12, color: Colors.grey), overflow: TextOverflow.ellipsis, ), ], ), ); }).toList(), selectedOptionBuilder: (context, value) { if (_historyController.historyList.isEmpty) { return const Text('이력 없음'); } final selectedHistory = _historyController.historyList .firstWhere( (h) => h.id == value, orElse: () => _historyController.historyList.first, ); return Text( '${selectedHistory.equipment?.serialNumber ?? "Serial N/A"} - ${selectedHistory.equipment?.modelName ?? "Model N/A"}', overflow: TextOverflow.ellipsis, ); }, onChanged: (value) { setState(() { _selectedEquipmentHistoryId = value; }); }, initialValue: _selectedEquipmentHistoryId, ), const SizedBox(height: 20), // 임대 기간 (백엔드 필수 필드) Text('임대 기간', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 12), Row( children: [ Expanded( child: InkWell( onTap: _selectStartDate, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(4), ), child: Row( children: [ const Icon(Icons.calendar_today), const SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('시작일 *', style: TextStyle(fontSize: 12, color: Colors.grey)), Text( '${_startDate.year}-${_startDate.month.toString().padLeft(2, '0')}-${_startDate.day.toString().padLeft(2, '0')}', style: const TextStyle(fontSize: 16), ), ], ), ], ), ), ), ), const SizedBox(width: 16), Expanded( child: InkWell( onTap: _selectEndDate, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(4), ), child: Row( children: [ const Icon(Icons.calendar_today), const SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('종료일 *', style: TextStyle(fontSize: 12, color: Colors.grey)), Text( _endDate != null ? '${_endDate!.year}-${_endDate!.month.toString().padLeft(2, '0')}-${_endDate!.day.toString().padLeft(2, '0')}' : '날짜 선택', style: TextStyle( fontSize: 16, color: _endDate != null ? ShadcnTheme.foreground : ShadcnTheme.mutedForeground, ), ), ], ), ], ), ), ), ), ], ), const SizedBox(height: 16), // 임대 기간 표시 if (_endDate != null) Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.blue.shade200), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.info, color: Colors.blue), const SizedBox(width: 8), Text( '임대 기간: ${_endDate!.difference(_startDate).inDays + 1}일', style: const TextStyle(fontWeight: FontWeight.bold), ), ], ), ), ], ), ), ), ), const SizedBox(height: 24), // 버튼 Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: _isLoading ? null : () => Navigator.of(context).pop(), child: const Text('취소'), ), const SizedBox(width: 8), ElevatedButton( onPressed: _isLoading ? null : _submit, child: _isLoading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : Text(widget.rent != null ? '수정' : '생성'), ), ], ), ], ), ), ); } }