import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:superport/models/license_model.dart'; import 'package:superport/screens/license/controllers/license_form_controller.dart'; import 'package:superport/screens/common/theme_tailwind.dart'; import 'package:superport/screens/common/templates/form_layout_template.dart'; import 'package:superport/screens/common/custom_widgets.dart' hide FormFieldWrapper; import 'package:superport/services/mock_data_service.dart'; import 'package:superport/utils/validators.dart'; import 'package:intl/intl.dart'; import 'package:superport/core/config/environment.dart' as env; // 유지보수 등록/수정 화면 (UI만 담당, 상태/로직 분리) class MaintenanceFormScreen extends StatefulWidget { final int? maintenanceId; final bool isExtension; // 연장 모드 여부 const MaintenanceFormScreen({ Key? key, this.maintenanceId, this.isExtension = false, }) : super(key: key); @override _MaintenanceFormScreenState createState() => _MaintenanceFormScreenState(); } class _MaintenanceFormScreenState extends State { late final LicenseFormController _controller; // 방문주기 드롭다운 옵션 final List _visitCycleOptions = [ '미방문', '장애시 지원', '월', '격월', '분기', '반기', '년', ]; // 점검형태 라디오 옵션 final List _inspectionTypeOptions = ['방문', '원격']; String _selectedVisitCycle = '미방문'; String _selectedInspectionType = '방문'; int _durationMonths = 12; @override void initState() { super.initState(); // API 모드 확인 final useApi = env.Environment.useApi; debugPrint('📌 라이선스 폼 초기화 - API 모드: $useApi'); _controller = LicenseFormController( useApi: useApi, dataService: useApi ? null : MockDataService(), licenseId: widget.maintenanceId, isExtension: widget.isExtension, ); // 컨트롤러 변경 리스너 등록 (데이터 로드 전에 등록!) _controller.addListener(_handleControllerUpdate); // 수정 모드 또는 연장 모드일 때 if (widget.maintenanceId != null) { // 초기 데이터 로드 WidgetsBinding.instance.addPostFrameCallback((_) async { if (widget.isExtension) { // 연장 모드: 기존 데이터를 로드하되 새로운 라이선스로 생성 _controller.isEditMode = false; await _controller.loadLicenseForExtension(); } else { // 수정 모드: 기존 라이선스 수정 _controller.isEditMode = true; await _controller.loadLicense(); } // 데이터 로드 후 UI 업데이트 if (mounted) { setState(() { // 로드된 데이터로 상태 업데이트 _selectedVisitCycle = _controller.visitCycle; _durationMonths = _controller.durationMonths; // 폼 필드들은 컨트롤러의 TextEditingController를 통해 자동 업데이트됨 }); } }); } } @override void dispose() { _controller.removeListener(_handleControllerUpdate); _controller.dispose(); super.dispose(); } void _handleControllerUpdate() { if (mounted) { setState(() {}); } } // 저장 메소드 Future _onSave() async { if (_controller.formKey.currentState!.validate()) { _controller.formKey.currentState!.save(); await _controller.saveLicense(); if (mounted) { String message = widget.isExtension ? '유지보수가 연장되었습니다' : (_controller.isEditMode ? '유지보수가 수정되었습니다' : '유지보수가 등록되었습니다'); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: AppThemeTailwind.success, ), ); Navigator.pop(context, true); } } } @override Widget build(BuildContext context) { // 유지보수 명은 유지보수기간, 방문주기, 점검형태를 결합해서 표기 final String maintenanceName = '${_durationMonths}개월,${_selectedVisitCycle},${_selectedInspectionType}'; return FormLayoutTemplate( title: widget.isExtension ? '유지보수 연장' : (_controller.isEditMode ? '유지보수 수정' : '유지보수 등록'), onSave: _onSave, saveButtonText: widget.isExtension ? '연장 완료' : (_controller.isEditMode ? '수정 완료' : '등록 완료'), child: _controller.isLoading ? const Center( child: CircularProgressIndicator(), ) : Form( key: _controller.formKey, child: SingleChildScrollView( padding: const EdgeInsets.all(UIConstants.formPadding), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 기본 정보 섹션 FormSection( title: '기본 정보', subtitle: '유지보수의 기본 정보를 입력하세요', children: [ // 제품명 FormFieldWrapper( label: '제품명', required: true, child: TextFormField( controller: _controller.productNameController, decoration: const InputDecoration( hintText: '제품명을 입력하세요', border: OutlineInputBorder(), ), validator: (value) => validateRequired(value, '제품명'), ), ), // 라이선스 키 FormFieldWrapper( label: '라이선스 키', required: true, child: TextFormField( controller: _controller.licenseKeyController, decoration: const InputDecoration( hintText: '라이선스 키를 입력하세요', border: OutlineInputBorder(), ), validator: (value) => validateRequired(value, '라이선스 키'), ), ), // 벤더 FormFieldWrapper( label: '벤더', required: true, child: TextFormField( controller: _controller.vendorController, decoration: const InputDecoration( hintText: '벤더명을 입력하세요', border: OutlineInputBorder(), ), validator: (value) => validateRequired(value, '벤더'), ), ), // 현위치 FormFieldWrapper( label: '현위치', required: true, child: TextFormField( controller: _controller.locationController, decoration: const InputDecoration( hintText: '현재 위치를 입력하세요', border: OutlineInputBorder(), ), validator: (value) => validateRequired(value, '현위치'), ), ), // 할당 사용자 FormFieldWrapper( label: '할당 사용자', child: TextFormField( controller: _controller.assignedUserController, decoration: const InputDecoration( hintText: '할당된 사용자를 입력하세요', border: OutlineInputBorder(), ), ), ), // 상태 FormFieldWrapper( label: '상태', required: true, child: DropdownButtonFormField( value: _controller.status, items: ['활성', '비활성', '만료'].map((status) => DropdownMenuItem( value: status, child: Text(status), ), ).toList(), onChanged: (value) => setState(() => _controller.status = value!), decoration: const InputDecoration( border: OutlineInputBorder(), ), validator: (value) => validateRequired(value, '상태'), ), ), // 구매일 FormFieldWrapper( label: '구매일', required: true, child: InkWell( onTap: () async { final date = await showDatePicker( context: context, initialDate: _controller.purchaseDate ?? DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(2100), ); if (date != null) { setState(() => _controller.purchaseDate = date); } }, child: InputDecorator( decoration: const InputDecoration( border: OutlineInputBorder(), suffixIcon: Icon(Icons.calendar_today), ), child: Text( _controller.purchaseDate != null ? DateFormat('yyyy-MM-dd').format(_controller.purchaseDate!) : '구매일을 선택하세요', ), ), ), ), // 만료일 FormFieldWrapper( label: '만료일', required: true, child: InkWell( onTap: () async { final date = await showDatePicker( context: context, initialDate: _controller.expiryDate ?? DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(2100), ); if (date != null) { setState(() => _controller.expiryDate = date); } }, child: InputDecorator( decoration: const InputDecoration( border: OutlineInputBorder(), suffixIcon: Icon(Icons.calendar_today), ), child: Text( _controller.expiryDate != null ? DateFormat('yyyy-MM-dd').format(_controller.expiryDate!) : '만료일을 선택하세요', ), ), ), ), // 남은 일수 (자동 계산) if (_controller.expiryDate != null) FormFieldWrapper( label: '남은 일수', child: Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), decoration: BoxDecoration( border: Border.all(color: UIConstants.borderColor), borderRadius: BorderRadius.circular(UIConstants.borderRadius), color: UIConstants.backgroundColor, ), child: Text( '${_controller.expiryDate!.difference(DateTime.now()).inDays}일', style: TextStyle( fontSize: 16, color: _controller.expiryDate!.difference(DateTime.now()).inDays < 30 ? Colors.red : _controller.expiryDate!.difference(DateTime.now()).inDays < 90 ? Colors.orange : Colors.green, fontWeight: FontWeight.bold, ), ), ), ), ], ), const SizedBox(height: 16), // 유지보수 설정 섹션 FormSection( title: '유지보수 설정', subtitle: '유지보수 기간 및 방문 주기를 설정하세요', children: [ // 유지보수 명 표기 (입력 불가, 자동 생성) FormFieldWrapper( label: '유지보수 명', hint: '유지보수 기간, 방문 주기, 점검 형태를 조합하여 자동 생성됩니다', child: Container( width: double.infinity, padding: const EdgeInsets.symmetric( vertical: 12, horizontal: 12, ), decoration: BoxDecoration( border: Border.all(color: UIConstants.borderColor), borderRadius: BorderRadius.circular(UIConstants.borderRadius), color: UIConstants.backgroundColor, ), child: Text( maintenanceName, style: const TextStyle(fontSize: 16), ), ), ), // 유지보수 기간 (개월) _buildTextField( label: '유지보수 기간 (개월)', initialValue: _durationMonths.toString(), hintText: '유지보수 기간을 입력하세요', suffixText: '개월', keyboardType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.digitsOnly], validator: (value) => validateNumber(value, '유지보수 기간'), onChanged: (value) { setState(() { _durationMonths = int.tryParse(value ?? '') ?? 0; }); }, ), // 방문 주기 (드롭다운) Padding( padding: const EdgeInsets.only(bottom: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '방문 주기', style: TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 4), DropdownButtonFormField( value: _selectedVisitCycle, items: _visitCycleOptions .map( (option) => DropdownMenuItem( value: option, child: Text(option), ), ) .toList(), onChanged: (value) { setState(() { _selectedVisitCycle = value!; }); }, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric( horizontal: 8, vertical: 0, ), ), validator: (value) => value == null || value.isEmpty ? '방문 주기를 선택하세요' : null, ), ], ), ), // 점검 형태 (라디오버튼) FormFieldWrapper( label: '점검 형태', required: true, child: Row( children: _inspectionTypeOptions.map((type) { return Expanded( child: RadioListTile( title: Text(type), value: type, groupValue: _selectedInspectionType, onChanged: (value) { setState(() { _selectedInspectionType = value!; }); }, contentPadding: EdgeInsets.zero, dense: true, ), ); }).toList(), ), ), ], // FormSection children ), // FormSection 끝 ], // Column children ), // SingleChildScrollView child ), // Form child ), // FormLayoutTemplate child ); } // 공통 텍스트 필드 위젯 (onSaved → onChanged로 변경) Widget _buildTextField({ required String label, required String initialValue, required String hintText, String? suffixText, TextInputType? keyboardType, List? inputFormatters, required String? Function(String?) validator, required void Function(String?) onChanged, }) { return Padding( padding: const EdgeInsets.only(bottom: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), const SizedBox(height: 4), TextFormField( initialValue: initialValue, decoration: InputDecoration( hintText: hintText, suffixText: suffixText, ), keyboardType: keyboardType, inputFormatters: inputFormatters, validator: validator, onChanged: onChanged, ), ], ), ); } }