// lib/entry/entry_controller.dart import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:gifunavi/model/entry.dart'; import 'package:gifunavi/model/event.dart'; import 'package:gifunavi/model/team.dart'; import 'package:gifunavi/model/category.dart'; import 'package:gifunavi/services/api_service.dart'; import 'package:gifunavi/pages/index/index_controller.dart'; import 'package:timezone/timezone.dart' as tz; import '../../model/user.dart'; class EntryController extends GetxController { late ApiService _apiService; final entries = [].obs; final events = [].obs; final teams = [].obs; final categories = [].obs; final selectedEvent = Rx(null); final selectedTeam = Rx(null); final selectedCategory = Rx(null); final selectedDate = Rx(null); final currentEntry = Rx(null); final isLoading = true.obs; final activeEvents = [].obs; //有効なイベントリスト final teamMembers = [].obs; final hasError = false.obs; final errorMessage = "".obs; @override void onInit() async { super.onInit(); await initializeApiService(); await loadInitialData(); } Future initializeApiService() async { try { _apiService = await Get.putAsync(() => ApiService().init()); } catch (e) { print('Error initializing ApiService: $e'); Get.snackbar('Error', 'APIサービスの初期化に失敗しました'); } } Future loadInitialData() async { try { isLoading.value = true; await Future.wait([ fetchEntries(), fetchEvents(), fetchTeams(), fetchCategories(), ]); updateActiveEvents(); // イベント取得後にアクティブなイベントを更新 if (Get.arguments != null && Get.arguments['entry'] != null) { currentEntry.value = Get.arguments['entry']; initializeEditMode(currentEntry.value!); } else { // 新規作成モードの場合、最初のイベントを選択 if (activeEvents.isNotEmpty) { selectedEvent.value = activeEvents.first; selectedDate.value = activeEvents.first.startDatetime; } } } catch(e) { print('Error initializing data: $e'); // エラー状態を設定 hasError.value = true; Get.snackbar('Error', '初期データの読み込みに失敗しました'); } finally { isLoading.value = false; } } void updateActiveEvents() { final now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo')); activeEvents.assignAll(events.where((event) => event.deadlineDateTime.isAfter(now))); } void initializeEditMode(Entry entry) { currentEntry.value = entry; selectedEvent.value = entry.event; selectedTeam.value = entry.team; selectedCategory.value = entry.category; selectedDate.value = entry.date; } void updateEvent(Event? value) { selectedEvent.value = value; if (value != null) { // イベント変更時に日付を調整 if (selectedDate.value == null || selectedDate.value!.isBefore(value.startDatetime) || selectedDate.value!.isAfter(value.endDatetime)) { selectedDate.value = value.startDatetime; } } } Future fetchTeamMembers(int teamId) async { try { final members = await _apiService.getTeamMembers(teamId); teamMembers.assignAll(members); } catch (e) { print('Error fetching team members: $e'); Get.snackbar('Error', 'Failed to fetch team members'); } } List getFilteredCategories() { if (selectedTeam.value == null) return []; if (teamMembers.isEmpty) { // ソロの場合 String baseCategory = selectedTeam.value!.members.first.female ? 'ソロ女子' : 'ソロ男子'; return categories.where((c) => c.categoryName.startsWith(baseCategory)).toList(); } else if (teamMembers.length == 1) { // チームメンバーが1人の場合(ソロ) String baseCategory = teamMembers.first.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; } void updateTeam(Team? value) async { selectedTeam.value = value; if (value != null) { await fetchTeamMembers(value.id); final filteredCategories = getFilteredCategories(); if (filteredCategories.isNotEmpty) { selectedCategory.value = filteredCategories.first; } else { selectedCategory.value = null; } } else { teamMembers.clear(); selectedCategory.value = null; } } void updateTeam_old(Team? value) { selectedTeam.value = value; if (value != null) { selectedCategory.value = value.category; } } //void updateTeam(Team? value) => selectedTeam.value = value; void updateCategory(NewCategory? value) => selectedCategory.value = value; //void updateDate(DateTime value) => selectedDate.value = value; void updateDate(DateTime value) { selectedDate.value = tz.TZDateTime.from(value, tz.getLocation('Asia/Tokyo')); } /* void updateDate(DateTime value){ selectedDate.value = DateFormat('yyyy-MM-dd').format(value!) as DateTime?; } */ void _initializeEntryData() { if (currentEntry.value != null) { selectedEvent.value = currentEntry.value!.event; selectedTeam.value = currentEntry.value!.team; selectedCategory.value = currentEntry.value!.category; selectedDate.value = currentEntry.value!.date; } } Future fetchEntries() async { try { final fetchedEntries = await _apiService.getEntries(); entries.assignAll(fetchedEntries); } catch (e) { print('Error fetching entries: $e'); Get.snackbar('Error', 'Failed to fetch entries'); } } Future fetchEvents() async { try { final fetchedEvents = await _apiService.getEvents(); events.assignAll(fetchedEvents.map((event) { // end_dateの7日前を締め切りとして設定 final deadlineDateTime = event.endDatetime.subtract(const Duration(days: 7)); return Event( id: event.id, eventName: event.eventName, startDatetime: event.startDatetime, endDatetime: event.endDatetime, deadlineDateTime: deadlineDateTime, ); }).toList()); updateActiveEvents(); } catch (e) { print('Error fetching events: $e'); Get.snackbar('Error', 'Failed to fetch events'); } } Future fetchEvents_old() async { try { final fetchedEvents = await _apiService.getEvents(); events.assignAll(fetchedEvents); } catch (e) { print('Error fetching events: $e'); Get.snackbar('Error', 'Failed to fetch events'); } } Future fetchTeams() async { try { final fetchedTeams = await _apiService.getTeams(); teams.assignAll(fetchedTeams); } catch (e) { print('Error fetching teams: $e'); Get.snackbar('Error', 'Failed to fetch team'); } } Future fetchCategories() async { try { final fetchedCategories = await _apiService.getCategories(); categories.assignAll(fetchedCategories); } catch (e) { print('Error fetching categories: $e'); Get.snackbar('Error', 'Failed to fetch categories'); } } Future createEntry() async { if (selectedEvent.value == null || selectedTeam.value == null || selectedCategory.value == null || selectedDate.value == null) { Get.snackbar('Error', 'Please fill all fields'); return; } try { isLoading.value = true; // Get zekken number final updatedCategory = await _apiService.getZekkenNumber(selectedCategory.value!.id); final zekkenNumber = updatedCategory.categoryNumber.toString(); // selectedDate.value に 9時間を加えてJSTのオフセットを適用 final jstDate = selectedDate.value!.add(const Duration(hours: 9)); final newEntry = await _apiService.createEntry( selectedTeam.value!.id, selectedEvent.value!.id, selectedCategory.value!.id, jstDate, // JSTオフセットが適用された日付を使用 zekkenNumber, ); entries.add(newEntry); Get.back(); } catch (e) { print('Error creating entry: $e'); Get.snackbar('Error', '$e'); } finally { isLoading.value = false; } } Future updateEntryAndRefreshMap() async { await updateEntry(); // エントリーが正常に更新された後、マップをリフレッシュ final indexController = Get.find(); final eventCode = currentEntry.value?.event.eventName ?? ''; indexController.reloadMap(eventCode); } bool isEntryEditable(Event event) { return DateTime.now().isBefore(event.deadlineDateTime); } Future updateEntry() async { if (currentEntry.value == null) { Get.snackbar('Error', 'No entry selected for update'); return; } if (!isEntryEditable(currentEntry.value!.event)) { Get.dialog( AlertDialog( title: Text('エントリー変更不可'), content: Text('締め切りを過ぎているため、エントリーの変更はできません。変更が必要な場合は事務局にお問い合わせください。'), actions: [ TextButton( child: Text('OK'), onPressed: () => Get.back(), ), ], ), ); return; } try { isLoading.value = true; final updatedEntry = await _apiService.updateEntry( currentEntry.value!.id, currentEntry.value!.team.id, selectedEvent.value!.id, selectedCategory.value!.id, selectedDate.value!, currentEntry.value!.zekkenNumber, ); final index = entries.indexWhere((entry) => entry.id == updatedEntry.id); if (index != -1) { entries[index] = updatedEntry; } Get.back(); } catch (e) { print('Error updating entry: $e'); Get.snackbar('Error', 'Failed to update entry'); } finally { isLoading.value = false; } } Future updateEntryCategory(int entryId, int newCategoryId) async { try { //await _apiService.updateEntryCategory(entryId, newCategoryId); final updatedEntry = await _apiService.updateEntry( currentEntry.value!.id, currentEntry.value!.team.id, selectedEvent.value!.id, newCategoryId, currentEntry.value!.date!, currentEntry.value!.zekkenNumber, ); await fetchEntries(); } catch (e) { print('Error updating entry category: $e'); Get.snackbar('エラー', 'エントリーのカテゴリ更新に失敗しました'); } } Future deleteEntry() async { if (currentEntry.value == null) { Get.snackbar('Error', 'No entry selected for deletion'); return; } try { isLoading.value = true; await _apiService.deleteEntry(currentEntry.value!.id); entries.removeWhere((entry) => entry.id == currentEntry.value!.id); Get.back(); } catch (e) { print('Error deleting entry: $e'); Get.snackbar('Error', 'Failed to delete entry'); } finally { isLoading.value = false; } } bool isOwner() { // Implement logic to check if the current user is the owner of the entry return true; // Placeholder } }