회원목록
This commit is contained in:
@@ -0,0 +1,367 @@
|
||||
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('저장'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user