import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:intl/intl.dart'; import '../../services/auth_service.dart'; import '../../services/club_service.dart'; import '../../services/member_service.dart'; import '../../models/member_model.dart'; import '../../utils/enum_mappings.dart'; import '../../widgets/dialog_actions.dart'; import '../../theme/badges.dart'; class MembersScreen extends StatefulWidget { const MembersScreen({super.key}); @override State createState() => _MembersScreenState(); } class _MembersScreenState extends State { bool _isInit = false; String _searchQuery = ''; final TextEditingController _searchController = TextEditingController(); @override void dispose() { _searchController.dispose(); super.dispose(); } @override void didChangeDependencies() { super.didChangeDependencies(); if (!_isInit) { _loadMembers(); _isInit = true; } } Future _loadMembers() async { // 비동기 이후 컨텍스트 직접 참조를 피하기 위해 메신저를 미리 캡처 final messenger = ScaffoldMessenger.of(context); try { print('_loadMembers 호출됨'); final authService = Provider.of(context, listen: false); final clubService = Provider.of(context, listen: false); final memberService = Provider.of(context, listen: false); print('현재 클럽: ${clubService.currentClub?.name}'); print('토큰: ${authService.token}'); if (clubService.currentClub != null && authService.token != null) { print('멤버 서비스 초기화 시작'); memberService.initialize(authService.token!); memberService.setClubId(clubService.currentClub!.id); // 클럽 ID 설정 추가 print('멤버 서비스 초기화 완료'); print('회원 목록 가져오기 시작'); await memberService.fetchClubMembers(); print('회원 목록 가져오기 완료: ${memberService.members.length}개'); } else { print('클럽 또는 토큰이 없어서 회원 목록을 가져올 수 없습니다.'); } } catch (e) { print('회원 목록 가져오기 오류: $e'); messenger.showSnackBar( SnackBar(content: Text('회원 목록을 불러오는데 실패했습니다: $e')), ); } } List _getFilteredMembers(List members) { if (_searchQuery.isEmpty) { return members; } final query = _searchQuery.toLowerCase(); return members.where((member) { return member.name.toLowerCase().contains(query) || member.email.toLowerCase().contains(query); }).toList(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('회원'), actions: [ IconButton( tooltip: '회원 추가', icon: const Icon(Icons.person_add), onPressed: _showAddMemberDialog, ), ], ), body: Column( children: [ // 검색 바 Padding( padding: const EdgeInsets.all(16.0), child: TextField( controller: _searchController, decoration: InputDecoration( hintText: '회원 검색', prefixIcon: const Icon(Icons.search), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), contentPadding: const EdgeInsets.symmetric(vertical: 0), suffixIcon: _searchQuery.isNotEmpty ? IconButton( icon: const Icon(Icons.clear), onPressed: () { _searchController.clear(); setState(() { _searchQuery = ''; }); }, ) : null, ), onChanged: (value) { setState(() { _searchQuery = value; }); }, ), ), // 회원 목록 Expanded( child: Consumer( builder: (context, memberService, _) { if (memberService.isLoading) { return const Center(child: CircularProgressIndicator()); } final filteredMembers = _getFilteredMembers(memberService.members); if (filteredMembers.isEmpty) { return Center( child: Text( _searchQuery.isEmpty ? '등록된 회원이 없습니다' : '검색 결과가 없습니다', ), ); } return RefreshIndicator( onRefresh: _loadMembers, child: ListView.builder( padding: const EdgeInsets.only(bottom: 16), itemCount: filteredMembers.length, itemBuilder: (context, index) { final member = filteredMembers[index]; return _buildMemberCard(member); }, ), ); }, ), ), ], ), // FAB 제거: AppBar actions로 이동 ); } Widget _buildMemberCard(Member member) { final memberTypeText = _getMemberTypeDisplayName(member.memberType); return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: InkWell( onTap: () { // 회원 상세 정보 다이얼로그 표시 _showMemberDetailDialog(member); }, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(16.0), child: Row( children: [ // 회원 정보 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( member.name, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(width: 8), // 성별 아이콘 Icon( member.gender == 'female' ? Icons.female : Icons.male, size: 18, color: member.gender == 'female' ? Colors.pink : Colors.blue, ), // 핸디캡 if (member.handicap != null && member.handicap != 0) Container( margin: const EdgeInsets.only(left: 8), padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.orange.shade100, borderRadius: BorderRadius.circular(12), ), child: Text( (member.handicap! > 0) ? "+${member.handicap}" : member.handicap.toString(), style: TextStyle( fontSize: 12, color: Colors.orange.shade800, fontWeight: FontWeight.bold, ), ), ), // 평균 if (member.average != null) Container( margin: const EdgeInsets.only(left: 8), padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.indigo.shade100, borderRadius: BorderRadius.circular(12), ), child: Text( 'AVG ${member.average!.toStringAsFixed(1)}', style: TextStyle( fontSize: 12, color: Colors.indigo.shade800, fontWeight: FontWeight.w700, ), ), ), ], ), if (member.email.isNotEmpty || member.phone != null) const SizedBox(height: 4), if (member.phone != null) Text( member.phone!, style: TextStyle( fontSize: 14, color: Colors.grey.shade700, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 2), if (member.email.isNotEmpty) Text( member.email, style: TextStyle( fontSize: 14, color: Colors.grey.shade700, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), // 2열: 모든 뱃지 Wrap( spacing: 8, runSpacing: 4, crossAxisAlignment: WrapCrossAlignment.center, children: [ // 회원 유형 배지 BadgeStyles.memberType(memberTypeText), // 상태 배지 BadgeStyles.memberStatus(getStatusLabel(member.status)), ], ), // 3열: 가입일 if (member.joinDate != null) ...[ const SizedBox(height: 4), Text( '가입: ${formatDate(member.joinDate!)}', style: TextStyle( fontSize: 12, color: Colors.grey.shade600, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ], ), ), // 액션 버튼 PopupMenuButton( icon: const Icon(Icons.more_vert), onSelected: (value) { if (value == 'edit') { _showMemberDetailDialog(member); } else if (value == 'delete') { _showDeleteConfirmationDialog(member); } }, itemBuilder: (context) => [ const PopupMenuItem( value: 'delete', child: Row( children: [ Icon(Icons.delete, size: 20, color: Colors.red), SizedBox(width: 8), Text('삭제', style: TextStyle(color: Colors.red)), ], ), ), ], ), ], ), ), ), ); } // 회원 추가 다이얼로그 void _showAddMemberDialog() { final nameController = TextEditingController(); final emailController = TextEditingController(); final phoneController = TextEditingController(); final handicapController = TextEditingController(text: '0'); final joinDateController = TextEditingController(text: formatDate(DateTime.now())); // 선택형 필드를 위한 값 초기화 String selectedGender = 'male'; String selectedMemberType = 'regular'; String selectedStatus = 'active'; showDialog( context: context, builder: (dialogContext) => StatefulBuilder( builder: (dialogContext, setState) { return AlertDialog( title: const Text('회원 추가'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: nameController, decoration: const InputDecoration( labelText: '이름', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), TextField( controller: emailController, keyboardType: TextInputType.emailAddress, decoration: const InputDecoration( labelText: '이메일', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), TextField( controller: phoneController, keyboardType: TextInputType.phone, decoration: const InputDecoration( labelText: '전화번호 (선택사항)', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), TextField( controller: handicapController, keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: '핸디캡', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), TextField( controller: joinDateController, readOnly: true, decoration: const InputDecoration( labelText: '가입일', border: OutlineInputBorder(), suffixIcon: Icon(Icons.calendar_today), ), onTap: () async { final DateTime? picked = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(2101), ); if (picked != null) { setState(() { joinDateController.text = formatDate(picked); }); } }, ), const SizedBox(height: 16), DropdownButtonFormField( key: const Key('members_add_gender_dropdown'), value: selectedGender, decoration: const InputDecoration( labelText: '성별', border: OutlineInputBorder(), ), isExpanded: true, items: [ DropdownMenuItem(value: 'male', child: Text('남성', maxLines: 1, overflow: TextOverflow.ellipsis)), DropdownMenuItem(value: 'female', child: Text('여성', maxLines: 1, overflow: TextOverflow.ellipsis)), ], onChanged: (value) { if (value != null) { setState(() { selectedGender = value; }); } }, ), const SizedBox(height: 16), DropdownButtonFormField( key: const Key('members_add_member_type_dropdown'), value: selectedMemberType, decoration: const InputDecoration( labelText: '회원 유형', border: OutlineInputBorder(), ), isExpanded: true, items: [ DropdownMenuItem(value: 'regular', child: Text('정회원', maxLines: 1, overflow: TextOverflow.ellipsis)), DropdownMenuItem(value: 'associate', child: Text('준회원', maxLines: 1, overflow: TextOverflow.ellipsis)), DropdownMenuItem(value: 'guest', child: Text('게스트', maxLines: 1, overflow: TextOverflow.ellipsis)), ], onChanged: (value) { if (value != null) { setState(() { selectedMemberType = value; }); } }, ), const SizedBox(height: 16), DropdownButtonFormField( key: const Key('members_add_status_dropdown'), value: selectedStatus, decoration: const InputDecoration( labelText: '상태', border: OutlineInputBorder(), ), isExpanded: true, items: [ DropdownMenuItem(value: 'active', child: Text('활성', maxLines: 1, overflow: TextOverflow.ellipsis)), DropdownMenuItem(value: 'inactive', child: Text('비활성', maxLines: 1, overflow: TextOverflow.ellipsis)), ], onChanged: (value) { if (value != null) { setState(() { selectedStatus = value; }); } }, ), ], ), ), actions: DialogActions.confirm( onCancel: () => Navigator.of(dialogContext).pop(), onConfirm: () async { // await 이전에 컨텍스트 파생 객체 캡처 final navigator = Navigator.of(dialogContext); final messenger = ScaffoldMessenger.of(dialogContext); if (nameController.text.isEmpty) { messenger.showSnackBar( const SnackBar(content: Text('이름은 필수 입력 항목입니다')), ); return; } try { final memberService = Provider.of(context, listen: false); final clubService = Provider.of(context, listen: false); if (clubService.currentClub != null) { // 회원 추가 데이터 준비 final memberData = { 'name': nameController.text.trim(), 'clubId': clubService.currentClub!.id, 'gender': selectedGender, 'memberType': selectedMemberType, 'status': selectedStatus, 'handicap': int.tryParse(handicapController.text) ?? 0, 'isActive': selectedStatus == 'active', }; // 가입일 추가 try { final joinDate = parseDate(joinDateController.text); if (joinDate != null) { memberData['joinDate'] = joinDate.toIso8601String(); } } catch (e) { // 가입일 파싱 오류 무시 } // 이메일이 비어있으면 null로 설정, 그렇지 않으면 트림한 값 사용 memberData['email'] = emailController.text.trim().isEmpty ? null : emailController.text.trim(); // 전화번호가 비어있으면 null로 설정, 그렇지 않으면 트림한 값 사용 memberData['phone'] = phoneController.text.trim().isEmpty ? null : phoneController.text.trim(); await memberService.addMember(memberData); // 결과 알림 및 다이얼로그 닫기 navigator.pop(); messenger.showSnackBar( const SnackBar(content: Text('회원이 추가되었습니다')), ); } } catch (e) { messenger.showSnackBar( SnackBar(content: Text('회원 추가에 실패했습니다: $e')), ); } }, confirmText: '추가', ), ); }, ), ); } // 날짜 포맷팅 함수 String formatDate(DateTime? date) { if (date == null) return '정보 없음'; return DateFormat('yyyy-MM-dd').format(date); } // 날짜 파싱 함수 DateTime? parseDate(String? dateStr) { if (dateStr == null) return null; if (dateStr.isEmpty) return null; try { // 다양한 형식 지원 if (dateStr.contains('T')) { // ISO 형식 (예: 2023-01-01T00:00:00.000Z) return DateTime.parse(dateStr); } else if (dateStr.contains('-')) { // yyyy-MM-dd 형식 return DateFormat('yyyy-MM-dd').parse(dateStr); } return null; } catch (e) { return null; } } // 폼 필드 생성 헬퍼 메서드 // 폼 필드 빌더 헬퍼 함수 Widget buildFormField(String label, Widget field) { return Padding( padding: const EdgeInsets.only(bottom: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.grey, ), ), const SizedBox(height: 4), field, ], ), ); } // 성별 표시 이름 변환 String _getGenderDisplayName(String? gender) { return getGenderLabel(gender); } // 회원 유형 표시 이름 변환 String _getMemberTypeDisplayName(String? memberType) { return getMemberTypeLabel(memberType); } // 회원 상태에 따른 배경색 가져오기 Color getStatusColor(String status) { switch (status) { case 'active': return Colors.green.shade100; case 'inactive': return Colors.grey.shade200; case 'suspended': return Colors.red.shade100; case 'deleted': return Colors.grey.shade300; default: return Colors.grey.shade200; } } // 회원 상태에 따른 텍스트 색상 가져오기 Color getStatusTextColor(String status) { switch (status) { case 'active': return Colors.green.shade800; case 'inactive': return Colors.grey.shade700; case 'suspended': return Colors.red.shade800; case 'deleted': return Colors.grey.shade800; default: return Colors.grey.shade700; } } // 회원 상세 정보 다이얼로그 void _showMemberDetailDialog(Member member) { bool isEditing = false; // 폼 컨트롤러 초기화 final nameController = TextEditingController(text: member.name); final phoneController = TextEditingController(text: member.phone ?? ''); final emailController = TextEditingController(text: member.email); final handicapController = TextEditingController(text: member.handicap?.toString() ?? '0'); final joinDateController = TextEditingController(text: formatDate(member.joinDate)); // 선택형 필드를 위한 값 초기화 String selectedGender = member.gender ?? 'male'; String selectedMemberType = member.memberType ?? 'regular'; String selectedStatus = member.status ?? (member.isActive ? 'active' : 'inactive'); showDialog( context: context, builder: (dialogContext) => StatefulBuilder( builder: (dialogContext, setState) { return AlertDialog( title: Text(isEditing ? '회원 정보 수정' : '회원 정보'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // 회원 정보 헤더 (보기 모드에서만 표시) if (!isEditing) ...[ Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // 현재 회원 상태 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( member.name, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 4), Text( _getMemberTypeDisplayName(member.memberType), style: TextStyle( fontSize: 14, color: Colors.blue.shade700, fontWeight: FontWeight.w500, ), ), if (member.phone != null && member.phone!.isNotEmpty) ...[ const SizedBox(height: 4), Text( member.phone!, style: TextStyle( fontSize: 14, color: Colors.grey.shade700, ), ), ], if (member.email.isNotEmpty) ...[ const SizedBox(height: 2), Text( member.email, style: TextStyle( fontSize: 14, color: Colors.grey.shade700, ), ), ], ], ), ), ], ), const SizedBox(height: 16), const Divider(), ], // 폼 필드 Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 이름 필드 - 수정 모드에서만 표시 (상단에 이미 표시됨) if (isEditing) buildFormField('이름', TextField( controller: nameController, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), )), // 성별 필드 - 상단에 없으므로 항상 표시 buildFormField('성별', isEditing ? DropdownButtonFormField( key: const Key('members_edit_gender_dropdown'), value: selectedGender, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), isExpanded: true, items: [ DropdownMenuItem( value: 'male', child: Text('남성', maxLines: 1, overflow: TextOverflow.ellipsis), ), DropdownMenuItem( value: 'female', child: Text('여성', maxLines: 1, overflow: TextOverflow.ellipsis), ), ], onChanged: (value) { if (value != null) { selectedGender = value; } }, ) : Text(_getGenderDisplayName(member.gender), style: const TextStyle(fontSize: 16)) ), // 회원 유형 필드 - 수정 모드에서만 표시 (상단에 이미 표시됨) if (isEditing) buildFormField('회원 유형', DropdownButtonFormField( key: const Key('members_edit_member_type_dropdown'), value: selectedMemberType, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), isExpanded: true, items: [ DropdownMenuItem( value: 'regular', child: Text('정회원', maxLines: 1, overflow: TextOverflow.ellipsis), ), DropdownMenuItem( value: 'associate', child: Text('준회원', maxLines: 1, overflow: TextOverflow.ellipsis), ), DropdownMenuItem( value: 'guest', child: Text('게스트', maxLines: 1, overflow: TextOverflow.ellipsis), ), DropdownMenuItem( value: 'manager', child: Text('운영진', maxLines: 1, overflow: TextOverflow.ellipsis), ), DropdownMenuItem( value: 'owner', child: Text('모임장', maxLines: 1, overflow: TextOverflow.ellipsis), ), ], onChanged: (value) { if (value != null) { selectedMemberType = value; } }, )), // 전화번호 필드 - 수정 모드에서만 표시 (상단에 이미 표시됨) if (isEditing) buildFormField('전화번호', TextField( controller: phoneController, keyboardType: TextInputType.phone, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), )), // 이메일 필드 - 수정 모드에서만 표시 (상단에 이미 표시됨) if (isEditing) buildFormField('이메일', TextField( controller: emailController, keyboardType: TextInputType.emailAddress, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), )), // 핸디캡 필드 - 상단에 없으므로 항상 표시 buildFormField('핸디캡', isEditing ? TextField( controller: handicapController, keyboardType: TextInputType.number, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), ) : Text(member.handicap?.toString() ?? '0', style: const TextStyle(fontSize: 16)) ), // 상태 필드 - 수정 모드에서만 표시 (상단에 이미 표시됨) if (isEditing) buildFormField('상태', DropdownButtonFormField( key: const Key('members_edit_status_dropdown'), value: selectedStatus, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), isExpanded: true, items: [ DropdownMenuItem( value: 'active', child: Text('활성', maxLines: 1, overflow: TextOverflow.ellipsis), ), DropdownMenuItem( value: 'inactive', child: Text('비활성', maxLines: 1, overflow: TextOverflow.ellipsis), ), DropdownMenuItem( value: 'suspended', child: Text('정지', maxLines: 1, overflow: TextOverflow.ellipsis), ), ], onChanged: (value) { if (value != null) { selectedStatus = value; } }, )), // 가입일 필드 buildFormField('가입일', isEditing ? InkWell( onTap: () async { final DateTime? picked = await showDatePicker( context: dialogContext, initialDate: member.joinDate ?? DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime.now(), // locale은 앱 전체 설정을 사용합니다 ); if (picked != null) { setState(() { // 새로운 날짜로 업데이트 joinDateController.text = formatDate(picked); // copyWith 메서드를 사용하여 효율적으로 업데이트 member = member.copyWith( joinDate: picked, // 새로 선택한 날짜 ); }); } }, child: InputDecorator( decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), suffixIcon: Icon(Icons.calendar_today), ), child: Text(formatDate(member.joinDate), style: const TextStyle(fontSize: 16)), ), ) : Text(formatDate(member.joinDate), style: const TextStyle(fontSize: 16)) ), // 시스템 정보 (수정불가) if (!isEditing) ...[ const SizedBox(height: 16), const Divider(), const SizedBox(height: 8), const Text('시스템 정보', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), const SizedBox(height: 8), buildFormField('회원 ID', Text(member.id, style: const TextStyle(fontSize: 14, fontFamily: 'monospace'))), if (member.userId.isNotEmpty) buildFormField('유저 ID', Text(member.userId, style: const TextStyle(fontSize: 14, fontFamily: 'monospace'))), if (member.clubId.isNotEmpty) buildFormField('클럽 ID', Text(member.clubId, style: const TextStyle(fontSize: 14, fontFamily: 'monospace'))), ], ], ), ), ], ), ), actions: [ // 취소 버튼 TextButton( onPressed: () => Navigator.of(dialogContext).pop(), child: const Text('취소'), ), // 수정/저장 버튼 TextButton( onPressed: () { // 비동기 작업 전 컨텍스트 파생 객체 캡처 final navigator = Navigator.of(dialogContext); final messenger = ScaffoldMessenger.of(dialogContext); if (isEditing) { // 수정 모드에서 저장 버튼 클릭 시 if (nameController.text.trim().isEmpty) { messenger.showSnackBar( const SnackBar(content: Text('이름은 필수 입력 항목입니다.')), ); return; } // copyWith 메서드를 사용하여 회원 정보 업데이트 final updatedMember = member.copyWith( name: nameController.text.trim(), email: emailController.text.trim().isEmpty ? null : emailController.text.trim(), phone: phoneController.text.trim().isEmpty ? null : phoneController.text.trim(), memberType: selectedMemberType, gender: selectedGender, status: selectedStatus, handicap: int.tryParse(handicapController.text) ?? member.handicap, isActive: selectedStatus == 'active', updatedAt: DateTime.now(), // joinDate는 이미 날짜 선택기에서 업데이트되었으므로 여기서 다시 설정할 필요 없음 ); // 회원 정보 저장 // toJson 결과에서 clubId 제거하여 MemberService에서 설정한 clubId만 사용 final memberData = updatedMember.toJson(); memberData.remove('clubId'); // clubId 필드 제거 // 전화번호와 이메일 필드가 비어있을 때 null로 명시적 설정 if (phoneController.text.trim().isEmpty) { memberData['phone'] = null; } if (emailController.text.trim().isEmpty) { memberData['email'] = null; } // 가입일 필드 명시적으로 추가 (ISO 형식으로 변환) if (member.joinDate != null) { memberData['joinDate'] = member.joinDate!.toIso8601String(); } context.read().updateMember( updatedMember.id, memberData, ).then((_) { messenger.showSnackBar( const SnackBar(content: Text('회원 정보가 업데이트되었습니다.')), ); navigator.pop(); }).catchError((error) { messenger.showSnackBar( SnackBar(content: Text('오류: ${error.toString()}')), ); }); } else { // 보기 모드에서 수정 버튼 클릭 시 setState(() { isEditing = true; }); } }, child: Text(isEditing ? '저장' : '수정'), ), ], ); }, ), ); } // 회원 삭제 확인 다이얼로그 void _showDeleteConfirmationDialog(Member member) { showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text('회원 삭제'), content: Text('${member.name} 회원을 정말 삭제하시겠습니까?'), actions: DialogActions.confirm( onCancel: () => Navigator.of(dialogContext).pop(), onConfirm: () async { // await 이전에 컨텍스트 파생 객체 캡처 final navigator = Navigator.of(dialogContext); final messenger = ScaffoldMessenger.of(dialogContext); try { final memberService = Provider.of(context, listen: false); await memberService.deleteMember(member.id); navigator.pop(); messenger.showSnackBar( const SnackBar(content: Text('회원이 삭제되었습니다')), ); } catch (e) { messenger.showSnackBar( SnackBar(content: Text('회원 삭제에 실패했습니다: $e')), ); } }, destructive: true, confirmText: '삭제', ), ), ); } }