// lib/controllers/team_controller.dart import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:gifunavi/model/team.dart'; import 'package:gifunavi/model/category.dart'; import 'package:gifunavi/model/user.dart'; import 'package:gifunavi/model/entry.dart'; import 'package:gifunavi/services/api_service.dart'; import 'package:gifunavi/widgets/category_change_dialog.dart'; import 'package:gifunavi/routes/app_pages.dart'; import 'package:gifunavi/pages/entry/entry_controller.dart'; import 'package:gifunavi/model/event.dart'; class TeamController extends GetxController { late final ApiService _apiService; late final EntryController _entryController; final teams = [].obs; final categories = [].obs; final RxList teamMembers = [].obs; final teamEntries = [].obs; final selectedCategory = Rx(null); final selectedTeam = Rx(null); final currentUser = Rx(null); final teamName = ''.obs; final isLoading = false.obs; final error = RxString(''); @override void onInit() async { super.onInit(); try { _apiService = Get.find(); if (!Get.isRegistered()) { Get.put(EntryController()); } _entryController = Get.find(); await loadInitialData(); } catch (e) { error.value = e.toString(); print('Error in TeamController onInit: $e'); } } Future loadInitialData() async { try { isLoading.value = true; await Future.wait([ fetchCategories(), fetchTeams(), getCurrentUser(), ]); if (categories.isNotEmpty && selectedCategory.value == null) { selectedCategory.value = categories.first; } } catch (e) { error.value = e.toString(); } finally { isLoading.value = false; } } void setSelectedTeam(Team team) { selectedTeam.value = team; teamName.value = team.teamName; if (categories.isNotEmpty) { selectedCategory.value = categories.firstWhere( (category) => category.id == team.category.id, orElse: () => categories.first, ); } else { // カテゴリリストが空の場合、teamのカテゴリをそのまま使用 selectedCategory.value = team.category; } fetchTeamMembers(team.id); } void resetForm() { selectedTeam.value = null; teamName.value = ''; if (categories.isNotEmpty) { selectedCategory.value = categories.first; } else { selectedCategory.value = null; // カテゴリが空の場合、エラーメッセージをセット error.value = 'カテゴリデータが取得できませんでした。'; } teamMembers.clear(); } void cleanupForNavigation() { selectedTeam.value = null; teamName.value = ''; selectedCategory.value = categories.isNotEmpty ? categories.first : null; teamMembers.clear(); //teamMembersはクリアしない // 必要に応じて他のクリーンアップ処理を追加 } Future fetchTeams() async { try { isLoading.value = true; final fetchedTeams = await _apiService.getTeams(); teams.assignAll(fetchedTeams); } catch (e) { error.value = 'チームの取得に失敗しました: $e'; print('Error fetching teams: $e'); } finally { isLoading.value = false; } } bool checkIfUserHasEntryData(){ if (teams.isEmpty) { return false; }else { return true; } } Future fetchCategories() async { try { final fetchedCategories = await _apiService.getCategories(); categories.assignAll(fetchedCategories); print("Fetched categories: ${categories.length}"); // デバッグ用 } catch (e) { print('Error fetching categories: $e'); } } Future getCurrentUser() async { try { final user = await _apiService.getCurrentUser(); currentUser.value = user; } catch (e) { print('Error getting current user: $e'); } } Future createTeam(String teamName, int categoryId) async { final newTeam = await _apiService.createTeam(teamName, categoryId); // 自分自身をメンバーとして自動登録 await _apiService.createTeamMember( newTeam.id, currentUser.value?.email, currentUser.value!.firstname, currentUser.value!.lastname, currentUser.value?.dateOfBirth, currentUser.value?.female, ); return newTeam; } Future updateTeam(int teamId, String teamName, int categoryId) async { // APIサービスを使用してチームを更新 final updatedTeam = await _apiService.updateTeam(teamId, teamName, categoryId); return updatedTeam; } Future deleteTeam(int teamId) async { bool confirmDelete = await Get.dialog( AlertDialog( title: const Text('チーム削除の確認'), content: const Text('このチームとそのすべてのメンバーを削除しますか?この操作は取り消せません。'), actions: [ TextButton( child: const Text('キャンセル'), onPressed: () => Get.back(result: false), ), TextButton( child: const Text('削除'), onPressed: () => Get.back(result: true), ), ], ), ) ?? false; if (confirmDelete) { try { // まず、チームのメンバーを全て削除 await _apiService.deleteAllTeamMembers(teamId); // その後、チームを削除 await _apiService.deleteTeam(teamId); // ローカルのチームリストを更新 teams.removeWhere((team) => team.id == teamId); /* Get.snackbar( '成功', 'チームとそのメンバーが削除されました', backgroundColor: Colors.green, colorText: Colors.white, snackPosition: SnackPosition.BOTTOM, ); */ // チームリスト画面に戻る Get.back(); } catch (e) { print('Error deleting team and members: $e'); Get.snackbar('エラー', 'チームとメンバーの削除に失敗しました'); } } } Future fetchTeamMembers(int teamId) async { try { isLoading.value = true; final members = await _apiService.getTeamMembers(teamId); teamMembers.assignAll(members); teamMembers.refresh(); // 明示的に更新を通知 } catch (e) { error.value = 'メンバーの取得に失敗しました: $e'; print('Error fetching team members: $e'); } finally { isLoading.value = false; } } Future updateMember(teamId, User member) async { try { isLoading.value = true; await _apiService.updateTeamMember(teamId,member.id, member.firstname, member.lastname, member.dateOfBirth, member.female); await fetchTeamMembers(selectedTeam.value!.id); } catch (e) { error.value = 'メンバーの更新に失敗しました: $e'; print('Error updating member: $e'); } finally { isLoading.value = false; } } Future deleteMember(int memberId, int teamId) async { try { isLoading.value = true; await _apiService.deleteTeamMember(teamId,memberId); await fetchTeamMembers(teamId); } catch (e) { error.value = 'メンバーの削除に失敗しました: $e'; print('Error deleting member: $e'); } finally { isLoading.value = false; } } // Future addMember(User member, int teamId) async { // try { // isLoading.value = true; // await _apiService.createTeamMember(teamId, member.email, member.firstname, member.lastname, member.dateOfBirth); // await fetchTeamMembers(teamId); // } catch (e) { // error.value = 'メンバーの追加に失敗しました: $e'; // print('Error adding member: $e'); // } finally { // isLoading.value = false; // } // } void updateTeamName(String value) { teamName.value = value; } void updateCategory(NewCategory? value) { if (value != null) { selectedCategory.value = value; } } //void updateCategory(NewCategory? value) { // if (value != null) { // selectedCategory.value = categories.firstWhere( // (category) => category.id == value.id, // orElse: () => value, // ); // } //} Future saveTeam() async { try { isLoading.value = true; if (selectedCategory.value == null) { throw Exception('カテゴリを選択してください'); } if (selectedTeam.value == null) { await createTeam(teamName.value, selectedCategory.value!.id); } else { await updateTeam(selectedTeam.value!.id, teamName.value, selectedCategory.value!.id); } // サーバーから最新のデータを再取得 await fetchTeams(); update(); // UIを強制的に更新 } catch (e) { error.value = 'チームの保存に失敗しました: $e'; } finally { isLoading.value = false; } } Future deleteSelectedTeam() async { if (selectedTeam.value != null) { await deleteTeam(selectedTeam.value!.id); selectedTeam.value = null; } } List getFilteredCategories_old() { //List teamMembers = getCurrentTeamMembers(); return categories.where((category) { return isCategoryValid(category, teamMembers); }).toList(); } List getFilteredCategories() { if (teamMembers.isEmpty && currentUser.value != null) { // ソロの場合 String baseCategory = currentUser.value!.female ? 'ソロ女子' : 'ソロ男子'; return categories.where((c) => c.categoryName.startsWith(baseCategory)).toList(); } else { bool hasElementaryOrYounger = teamMembers.any(isElementarySchoolOrYounger); String baseCategory = hasElementaryOrYounger ? 'ファミリー' : '一般'; return categories.where((c) => c.categoryName.startsWith(baseCategory)).toList(); } } bool isElementarySchoolOrYounger(User user) { final now = DateTime.now(); final age = now.year - user.dateOfBirth!.year; return age <= 12; } bool isCategoryValid(NewCategory category, List teamMembers) { int maleCount = teamMembers.where((member) => !member.female).length; int femaleCount = teamMembers.where((member) => member.female).length; int totalCount = teamMembers.length; bool isValidGender = category.female ? (femaleCount == totalCount) : true; bool isValidMemberCount = totalCount == category.numOfMember; bool isValidFamily = category.family ? areAllMembersFamily(teamMembers) : true; return isValidGender && isValidMemberCount && isValidFamily; } bool areAllMembersFamily(List teamMembers) { // 家族かどうかを判断するロジック(例: 同じ姓を持つメンバーが2人以上いる場合は家族とみなす) Set familyNames = teamMembers.map((member) => member.lastname).toSet(); return familyNames.length < teamMembers.length; } Future updateTeamComposition() async { NewCategory? oldCategory = selectedCategory.value; String? oldTime = oldCategory?.time; // メンバーリストの最新状態を取得 await fetchTeamMembers(selectedTeam.value!.id); List eligibleCategories = []; if (teamMembers.isEmpty || teamMembers.length == 1) { if (currentUser.value != null) { String baseCategory = currentUser.value!.female ? 'ソロ女子' : 'ソロ男子'; eligibleCategories = categories.where((c) => c.baseCategory == baseCategory).toList(); } } else { bool hasElementaryOrYounger = teamMembers.any(isElementarySchoolOrYounger); String baseCategory = hasElementaryOrYounger ? 'ファミリー' : '一般'; eligibleCategories = categories.where((c) => c.baseCategory == baseCategory).toList(); } // 同じ時間のカテゴリを優先的に選択 NewCategory? newCategory = eligibleCategories.firstWhereOrNull((c) => c.time == oldTime); if (newCategory == null && eligibleCategories.isNotEmpty) { // 同じ時間のカテゴリがない場合、最初のカテゴリを選択 newCategory = eligibleCategories.first; // 警告メッセージを表示 Get.dialog( AlertDialog( title: Text('カテゴリ変更の通知'), content: Text('旧カテゴリの時間($oldTime)と一致するカテゴリがないため、${newCategory.time ?? "時間指定なし"}を選択しました。'), actions: [ TextButton( child: Text('OK'), onPressed: () => Get.back(), ), ], ), ); } if (newCategory != null && oldCategory != newCategory) { selectedCategory.value = newCategory; // チームのカテゴリを更新 if (selectedTeam.value != null) { try { final updatedTeam = await updateTeam(selectedTeam.value!.id, selectedTeam.value!.teamName, newCategory.id); selectedTeam.value = updatedTeam; // チームリストも更新 final index = teams.indexWhere((team) => team.id == updatedTeam.id); if (index != -1) { teams[index] = updatedTeam; } // エントリーの締め切りチェック bool hasClosedEntries = await checkForClosedEntries(selectedTeam.value!.id); if (hasClosedEntries) { Get.dialog( AlertDialog( title: Text('警告'), content: Text('締め切りを過ぎたエントリーがあります。カテゴリ変更が必要な場合は事務局にお問い合わせください。'), actions: [ TextButton( child: Text('OK'), onPressed: () => Get.back(), ), ], ), ); } else { await checkAndHandleCategoryChange(oldCategory!, newCategory); } } catch (e) { print('Error updating team category: $e'); Get.snackbar('エラー', 'チームのカテゴリ更新に失敗しました'); } } } } Future checkForClosedEntries(int teamId) async { try { final entries = await _apiService.getEntries(); return entries.any((entry) => !isEntryEditable(entry.event)); } catch (e) { print('Error checking for closed entries: $e'); return false; } } bool isEntryEditable(Event event) { return DateTime.now().isBefore(event.deadlineDateTime); } Future updateTeamComposition_old() async { NewCategory? oldCategory = selectedCategory.value; String? oldTime = oldCategory?.time; // メンバーリストの最新状態を取得 await fetchTeamMembers(selectedTeam.value!.id); List eligibleCategories = []; if (teamMembers.isEmpty) { if (currentUser.value != null) { String baseCategory = currentUser.value!.female ? 'ソロ女子' : 'ソロ男子'; eligibleCategories = categories.where((c) => c.baseCategory == baseCategory).toList(); } } else { bool hasElementaryOrYounger = teamMembers.any(isElementarySchoolOrYounger); String baseCategory = hasElementaryOrYounger ? 'ファミリー' : '一般'; eligibleCategories = categories.where((c) => c.baseCategory == baseCategory).toList(); } // 同じ時間のカテゴリを優先的に選択 NewCategory? newCategory = eligibleCategories.firstWhereOrNull((c) => c.time == oldTime); if (newCategory == null && eligibleCategories.isNotEmpty) { // 同じ時間のカテゴリがない場合、最初のカテゴリを選択 newCategory = eligibleCategories.first; // 警告メッセージを表示 Get.dialog( AlertDialog( title: Text('カテゴリ変更の通知'), content: Text('旧カテゴリの時間($oldTime)と一致するカテゴリがないため、${newCategory.time ?? "時間指定なし"}を選択しました。'), actions: [ TextButton( child: Text('OK'), onPressed: () => Get.back(), ), ], ), ); } if (newCategory != null && oldCategory != newCategory) { selectedCategory.value = newCategory; // チームのカテゴリを更新 if (selectedTeam.value != null) { try { final updatedTeam = await updateTeam(selectedTeam.value!.id, selectedTeam.value!.teamName, newCategory.id); selectedTeam.value = updatedTeam; // チームリストも更新 final index = teams.indexWhere((team) => team.id == updatedTeam.id); if (index != -1) { teams[index] = updatedTeam; } } catch (e) { print('Error updating team category: $e'); Get.snackbar('エラー', 'チームのカテゴリ更新に失敗しました'); } } await checkAndHandleCategoryChange(oldCategory!, newCategory); } } // メンバーの追加後に呼び出すメソッド Future addMember(User newMember) async { try { await _apiService.createTeamMember(selectedTeam.value!.id, newMember.email, newMember.firstname, newMember.lastname, newMember.dateOfBirth, newMember.female); await updateTeamComposition(); } catch (e) { print('Error adding member: $e'); Get.snackbar('エラー', 'メンバーの追加に失敗しました'); } } // メンバーの削除後に呼び出すメソッド Future removeMember(int memberId) async { try { await _apiService.deleteTeamMember(selectedTeam.value!.id, memberId); // selectedTeamからメンバーを削除 if (selectedTeam.value != null) { selectedTeam.value!.members.removeWhere((member) => member.id == memberId); selectedTeam.refresh(); } // メンバー削除後にチーム構成を更新 await updateTeamComposition(); // teamMembersを更新 await fetchTeamMembers(selectedTeam.value!.id); } catch (e) { print('Error removing member: $e'); Get.snackbar('エラー', 'メンバーの削除に失敗しました'); } } Future checkAndHandleCategoryChange(NewCategory oldCategory, NewCategory newCategory) async { try { if (selectedTeam.value == null) { print('No team selected'); return; } // エントリーの存在を確認 bool hasEntries = await checkIfTeamHasEntries(selectedTeam.value!.id); if (hasEntries) { bool shouldCreateNewTeam = await Get.dialog( CategoryChangeDialog( oldCategory: oldCategory.categoryName, newCategory: newCategory.categoryName, ), ) ?? false; if (shouldCreateNewTeam) { await createNewTeamWithCurrentMembers(); } else { await updateEntriesWithNewCategory(); } }else{ // エントリーが存在しない場合は、カテゴリの更新のみを行う await updateTeamCategory(newCategory); } } catch (e) { print('Error in checkAndHandleCategoryChange: $e'); Get.snackbar('エラー', 'カテゴリ変更の処理中にエラーが発生しました'); } } Future checkIfTeamHasEntries(int teamId) async { try { final entries = await _apiService.getTeamEntries(teamId); return entries.isNotEmpty; } catch (e) { print('Error checking if team has entries: $e'); return false; } } Future updateTeamCategory(NewCategory newCategory) async { try { if (selectedTeam.value != null) { final updatedTeam = await updateTeam(selectedTeam.value!.id, selectedTeam.value!.teamName, newCategory.id); selectedTeam.value = updatedTeam; // チームリストも更新 final index = teams.indexWhere((team) => team.id == updatedTeam.id); if (index != -1) { teams[index] = updatedTeam; } Get.snackbar('成功', 'チームのカテゴリを更新しました'); } } catch (e) { print('Error updating team category: $e'); Get.snackbar('エラー', 'チームのカテゴリ更新に失敗しました'); } } Future fetchTeamEntries(int teamId) async { try { final fetchedEntries = await _apiService.getEntries(); teamEntries.assignAll(fetchedEntries); } catch (e) { print('Error fetching team entries: $e'); teamEntries.clear(); // エラーが発生した場合、エントリーリストをクリア //Get.snackbar('エラー', 'チームのエントリー取得に失敗しました'); } } Future createNewTeamWithCurrentMembers() async { String newTeamName = '${selectedTeam.value!.teamName}_${DateTime.now().millisecondsSinceEpoch}'; try { Team newTeam = await _apiService.createTeam(newTeamName, selectedCategory.value!.id); for (var member in teamMembers) { await _apiService.createTeamMember( newTeam.id, member.email, member.firstname, member.lastname, member.dateOfBirth, member.female, ); } Get.offNamed(AppPages.TEAM_DETAIL, arguments: {'mode': 'edit', 'team': newTeam}); } catch (e) { print('Error creating new team: $e'); Get.snackbar('エラー', '新しいチームの作成に失敗しました'); } } Future updateEntriesWithNewCategory() async { try { for (var entry in teamEntries) { //await _apiService.updateEntry(entry.id, selectedCategory.value!.id); final updatedEntry = await _apiService.updateEntry( entry.id, entry.team.id, entry.event.id, selectedCategory.value!.id, entry.date!, entry.zekkenNumber, ); } Get.snackbar('成功', 'エントリーのカテゴリを更新しました'); } catch (e) { print('Error updating entries: $e'); Get.snackbar('エラー', 'エントリーの更新に失敗しました'); } } void updateCurrentUserGender(bool isFemale) { if (currentUser.value != null) { currentUser.value!.female = isFemale; updateTeamComposition(); } } }