Files
bowlingManager/mobile/lib/screens/club/club_settings_screen.dart
T
2025-08-01 16:45:26 +09:00

368 lines
14 KiB
Dart

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';
class ClubSettingsScreen extends StatefulWidget {
static const routeName = '/club-settings';
const ClubSettingsScreen({super.key});
@override
_ClubSettingsScreenState createState() => _ClubSettingsScreenState();
}
class _ClubSettingsScreenState extends State<ClubSettingsScreen> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _descriptionController = TextEditingController();
final _locationController = TextEditingController();
final _femaleHandicapController = TextEditingController();
String? _selectedAverageCalculationPeriod;
String? _selectedOwnerId;
List<Member> _members = [];
bool _isLoading = true;
bool _hasEditPermission = false;
@override
void initState() {
super.initState();
_loadData();
}
@override
void dispose() {
_nameController.dispose();
_descriptionController.dispose();
_locationController.dispose();
_femaleHandicapController.dispose();
super.dispose();
}
Future<void> _loadData() async {
setState(() {
_isLoading = true;
});
try {
// 클럽 정보 로드
final clubService = Provider.of<ClubService>(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<MemberService>(context, listen: false);
await memberService.fetchClubMembers();
setState(() {
_members = memberService.members;
});
// 권한 확인
final authService = Provider.of<AuthService>(context, listen: false);
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) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('데이터 로드 중 오류가 발생했습니다: $error')));
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
Future<void> _saveClubInfo() async {
if (!_formKey.currentState!.validate()) {
return;
}
setState(() {
_isLoading = true;
});
try {
final clubService = Provider.of<ClubService>(context, listen: false);
final club = clubService.currentClub;
if (club != null) {
// 기본 업데이트 데이터 준비
final Map<String, dynamic> 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);
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('클럽 정보가 성공적으로 저장되었습니다')));
}
}
} catch (error) {
if (context.mounted) {
ScaffoldMessenger.of(context).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<String>(
decoration: const InputDecoration(
labelText: '평균 산정 기준',
border: OutlineInputBorder(),
),
value: _selectedAverageCalculationPeriod,
items: averageCalculationPeriodOptions.entries
.map(
(entry) => DropdownMenuItem<String>(
value: entry.key,
child: Text(entry.value),
),
)
.toList(),
onChanged: _hasEditPermission
? (value) {
setState(() {
_selectedAverageCalculationPeriod = value;
});
}
: null,
),
// 모임장 설정
const SizedBox(height: 16),
DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: '모임장 설정',
border: OutlineInputBorder(),
),
value: _selectedOwnerId,
items: _members.map((member) {
return DropdownMenuItem<String>(
value: member.userId,
child: Text(
'${member.name} (${getMemberTypeLabel(member.memberType)})',
),
);
}).toList(),
onChanged: _hasEditPermission
? (value) {
setState(() {
_selectedOwnerId = value;
});
}
: null,
),
// 저장 버튼
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('저장'),
),
),
],
),
),
),
),
],
),
);
}
}