import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../models/member_model.dart'; import '../../services/auth_service.dart'; import '../../services/club_service.dart'; import '../../services/member_service.dart'; import '../../utils/enum_mappings.dart'; import '../../widgets/owner_dropdown.dart'; class ClubSettingsScreen extends StatefulWidget { static const routeName = '/club-settings'; const ClubSettingsScreen({super.key, this.onOwnerItemsBuilt, this.initialMembers}); // 테스트 전용: 모임장 드롭다운 항목 개수 리포트 콜백 final ValueChanged? onOwnerItemsBuilt; // 테스트 전용: 초기 멤버 목록을 직접 주입하여 로딩을 우회 final List? initialMembers; @override State createState() => _ClubSettingsScreenState(); } class _ClubSettingsScreenState extends State { final _formKey = GlobalKey(); final _nameController = TextEditingController(); final _descriptionController = TextEditingController(); final _locationController = TextEditingController(); final _femaleHandicapController = TextEditingController(); String? _selectedAverageCalculationPeriod; String? _selectedOwnerId; List _members = []; bool _isLoading = true; bool _hasEditPermission = false; @override void initState() { super.initState(); if (widget.initialMembers != null) { // 테스트 경로: 초기 데이터 주입 _members = List.from(widget.initialMembers!); _isLoading = false; _hasEditPermission = true; // 테스트에서는 편집 가능 상태로 둠 } else { // 첫 프레임 이후에 로드하여 InheritedWidget 접근 안전 보장 WidgetsBinding.instance.addPostFrameCallback((_) => _loadData()); } } @override void dispose() { _nameController.dispose(); _descriptionController.dispose(); _locationController.dispose(); _femaleHandicapController.dispose(); super.dispose(); } Future _loadData() async { setState(() { _isLoading = true; }); // await 이전에 authService 캡처 (이후 권한 계산에 사용) final authService = Provider.of(context, listen: false); try { // 클럽 정보 로드 final clubService = Provider.of(context, listen: false); final club = clubService.currentClub; if (club != null) { _nameController.text = club.name; _descriptionController.text = club.description ?? ''; _locationController.text = club.location ?? ''; _femaleHandicapController.text = club.femaleHandicap?.toString() ?? ''; _selectedAverageCalculationPeriod = club.averageCalculationPeriod ?? 'total'; _selectedOwnerId = club.ownerId; } // 회원 목록 로드 final memberService = Provider.of(context, listen: false); await memberService.fetchClubMembers(); setState(() { _members = memberService.members; }); // 권한 확인 final currentUserId = authService.currentUser?.id; // 현재 사용자가 클럽 소유자이거나 관리자인지 확인 if (currentUserId != null) { final currentMember = _members.firstWhere( (member) => member.userId == currentUserId, orElse: () => Member( id: '', userId: '', name: '', memberType: '', clubId: club?.id ?? '', email: '', isActive: true, ), ); setState(() { _hasEditPermission = club?.ownerId == currentUserId || // 클럽 소유자 currentMember.memberType == 'manager' || // 클럽 매니저 authService.currentUser?.role == 'admin'; // 시스템 관리자 }); } } catch (error) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('데이터 로드 중 오류가 발생했습니다: $error')), ); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } Future _saveClubInfo() async { if (!_formKey.currentState!.validate()) { return; } setState(() { _isLoading = true; }); // await 이전에 messenger 캡처 final messenger = ScaffoldMessenger.of(context); try { final clubService = Provider.of(context, listen: false); final club = clubService.currentClub; if (club != null) { // 기본 업데이트 데이터 준비 final Map updates = { 'clubId': club.id, 'name': _nameController.text.trim(), }; // 빈 문자열이 아닌 경우에만 추가 (null 값 명시적 처리) if (_descriptionController.text.trim().isNotEmpty) { updates['description'] = _descriptionController.text.trim(); } else if (_descriptionController.text.trim().isEmpty && club.description != null) { // 기존에 값이 있었는데 지금 비어있으면 명시적으로 null 설정 updates['description'] = null; } if (_locationController.text.trim().isNotEmpty) { updates['location'] = _locationController.text.trim(); } else if (_locationController.text.trim().isEmpty && club.location != null) { updates['location'] = null; } if (_femaleHandicapController.text.trim().isNotEmpty) { updates['femaleHandicap'] = int.parse( _femaleHandicapController.text.trim(), ); } if (_selectedAverageCalculationPeriod != null) { updates['averageCalculationPeriod'] = _selectedAverageCalculationPeriod; } if (_selectedOwnerId != null) { updates['ownerId'] = _selectedOwnerId; } // 모임장 변경 시에만 이메일 정보 추가 if (_selectedOwnerId != club.ownerId) { // 모임장이 변경된 경우 해당 회원의 이메일 정보 찾기 final selectedMember = _members.firstWhere( (member) => member.userId == _selectedOwnerId, orElse: () => Member( id: '', userId: '', name: '', memberType: '', clubId: club.id, email: '', isActive: true, ), ); // 이메일이 있는 경우에만 추가 (빈 문자열이나 null은 전송하지 않음) if (selectedMember.email.isNotEmpty) { updates['ownerEmail'] = selectedMember.email; } } await clubService.updateClub(club.id, updates); messenger.showSnackBar( const SnackBar(content: Text('클럽 정보가 성공적으로 저장되었습니다')), ); } } catch (error) { messenger.showSnackBar( SnackBar(content: Text('클럽 정보 저장 중 오류가 발생했습니다: $error')), ); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('클럽 설정')), body: _isLoading ? const Center(child: CircularProgressIndicator()) : Column( children: [ if (!_hasEditPermission) Container( width: double.infinity, padding: const EdgeInsets.all(16.0), color: Colors.amber.shade100, child: const Text( '권한 알림: 클럽 설정을 변경하려면 클럽 소유자 또는 관리자 권한이 필요합니다.', style: TextStyle(fontWeight: FontWeight.bold), ), ), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 클럽 이름 TextFormField( controller: _nameController, decoration: const InputDecoration( labelText: '클럽 이름', border: OutlineInputBorder(), ), validator: (value) { if (value == null || value.trim().isEmpty) { return '클럽 이름을 입력해주세요'; } return null; }, enabled: _hasEditPermission, ), // 설명 const SizedBox(height: 16), TextFormField( controller: _descriptionController, decoration: const InputDecoration( labelText: '설명', border: OutlineInputBorder(), ), maxLines: 3, enabled: _hasEditPermission, ), // 위치 const SizedBox(height: 16), TextFormField( controller: _locationController, decoration: const InputDecoration( labelText: '위치', border: OutlineInputBorder(), ), enabled: _hasEditPermission, ), // 여성 기본 핸디캡 const SizedBox(height: 16), TextFormField( controller: _femaleHandicapController, decoration: const InputDecoration( labelText: '여성 기본 핸디캡', border: OutlineInputBorder(), ), keyboardType: TextInputType.number, validator: (value) { if (value != null && value.isNotEmpty) { final handicap = int.tryParse(value); if (handicap == null) { return '숫자를 입력해주세요'; } if (handicap < 0) { return '0 이상의 값을 입력해주세요'; } } return null; }, enabled: _hasEditPermission, ), // 평균 산정 기준 const SizedBox(height: 16), DropdownButtonFormField( key: const Key('club_settings_avg_period_dropdown'), decoration: const InputDecoration( labelText: '평균 산정 기준', border: OutlineInputBorder(), ), value: _selectedAverageCalculationPeriod, isExpanded: true, items: averageCalculationPeriodOptions.entries .map( (entry) => DropdownMenuItem( value: entry.key, child: Text( entry.value, maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ) .toList(), onChanged: _hasEditPermission ? (value) { setState(() { _selectedAverageCalculationPeriod = value; }); } : null, ), // 모임장 설정 const SizedBox(height: 16), OwnerDropdown( key: const Key('owner_dropdown'), members: _members, value: _selectedOwnerId, enabled: _hasEditPermission, onChanged: (value) { if (!_hasEditPermission) return; setState(() { _selectedOwnerId = value; }); }, onItemsBuilt: widget.onOwnerItemsBuilt, ), // 저장 버튼 const SizedBox(height: 24), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _hasEditPermission ? _saveClubInfo : null, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric( vertical: 16.0, ), ), child: const Text('저장'), ), ), ], ), ), ), ), ], ), ); } }