From 347861e5a1018fa0b20e9dcf2bf6d7ffc0e0899c Mon Sep 17 00:00:00 2001 From: Akira Date: Wed, 7 Aug 2024 14:24:05 +0900 Subject: [PATCH] Semi Final 7th Aug 2024 --- ios/Runner.xcodeproj/project.pbxproj | 24 ++-- lib/main.dart | 3 + lib/pages/camera/camera_page.dart | 9 +- lib/pages/drawer/drawer_page.dart | 21 ++- lib/pages/entry/entry_controller.dart | 9 +- lib/pages/entry/entry_detail_page.dart | 24 +++- lib/pages/entry/entry_list_page.dart | 10 +- lib/pages/entry/event_entries_controller.dart | 32 ++++- lib/pages/entry/event_entries_page.dart | 4 +- lib/pages/index/index_controller.dart | 35 ++--- lib/pages/index/index_page.dart | 50 +++++-- lib/pages/login/login_page.dart | 20 +++ lib/pages/register/register_page.dart | 3 +- lib/pages/team/member_controller.dart | 24 ++-- lib/pages/team/member_detail_page.dart | 129 +++++++++++------- lib/pages/team/team_detail_page.dart | 6 +- lib/services/api_service.dart | 10 +- lib/services/auth_service.dart | 5 +- lib/services/external_service.dart | 66 +++++---- lib/widgets/bottom_sheet_new.dart | 5 +- login_page.dart | 2 + pubspec.lock | 8 ++ pubspec.yaml | 3 +- 23 files changed, 342 insertions(+), 160 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 0553e89..7477237 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -398,11 +398,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 488; + CURRENT_PROJECT_VERSION = 493; DEVELOPMENT_TEAM = UMNEWT25JR; ENABLE_BITCODE = NO; - FLUTTER_BUILD_NAME = 4.8.8; - FLUTTER_BUILD_NUMBER = 488; + FLUTTER_BUILD_NAME = 4.8.13; + FLUTTER_BUILD_NUMBER = 493; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness"; @@ -411,7 +411,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.8.8; + MARKETING_VERSION = 4.8.13; PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -539,11 +539,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 488; + CURRENT_PROJECT_VERSION = 493; DEVELOPMENT_TEAM = UMNEWT25JR; ENABLE_BITCODE = NO; - FLUTTER_BUILD_NAME = 4.8.8; - FLUTTER_BUILD_NUMBER = 488; + FLUTTER_BUILD_NAME = 4.8.13; + FLUTTER_BUILD_NUMBER = 493; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness"; @@ -552,7 +552,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.8.8; + MARKETING_VERSION = 4.8.13; PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -571,11 +571,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 488; + CURRENT_PROJECT_VERSION = 493; DEVELOPMENT_TEAM = UMNEWT25JR; ENABLE_BITCODE = NO; - FLUTTER_BUILD_NAME = 4.8.8; - FLUTTER_BUILD_NUMBER = 488; + FLUTTER_BUILD_NAME = 4.8.13; + FLUTTER_BUILD_NUMBER = 493; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness"; @@ -584,7 +584,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.8.8; + MARKETING_VERSION = 4.8.13; PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/lib/main.dart b/lib/main.dart index 2c9ea41..e6554a3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,7 @@ import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; //import 'package:vm_service/vm_service.dart'; //import 'package:dart_vm_info/dart_vm_info.dart'; +import 'package:timezone/data/latest.dart' as tz; import 'package:rogapp/pages/settings/settings_controller.dart'; @@ -152,6 +153,8 @@ void main() async { */ try { + tz.initializeTimeZones(); + // ApiServiceを初期化 //await Get.putAsync(() => ApiService().init()); await initServices(); diff --git a/lib/pages/camera/camera_page.dart b/lib/pages/camera/camera_page.dart index bd07db0..a277bb3 100644 --- a/lib/pages/camera/camera_page.dart +++ b/lib/pages/camera/camera_page.dart @@ -239,6 +239,7 @@ class CameraPage extends StatelessWidget { style: ElevatedButton.styleFrom( shape: const CircleBorder(), padding: const EdgeInsets.all(20), + foregroundColor: Colors.white, backgroundColor: destinationController.photos.isEmpty ? Colors.red : Colors.grey[300], @@ -294,7 +295,10 @@ class CameraPage extends StatelessWidget { ? settingGoal.value == false ? ElevatedButton( style: - ElevatedButton.styleFrom(backgroundColor: Colors.red), + ElevatedButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.red + ), onPressed: () async { // print( // "----- user isss ${indexController.currentUser[0]} -----"); @@ -339,7 +343,7 @@ class CameraPage extends StatelessWidget { isgoal: true); } else { //print("---- status ${value['status']} ---- "); - Get.snackbar("目標が追加されていません", "please_try_again", + Get.snackbar(value["detail"], "ERROR", backgroundColor: Colors.green, colorText: Colors.white ); @@ -455,6 +459,7 @@ class CameraPage extends StatelessWidget { style: ElevatedButton.styleFrom(backgroundColor: Colors.red), onPressed: () async { // print( + // "##### current destination ${indexController.currentDestinationFeature[0].sub_loc_id} #######"); await destinationController.makeCheckin( indexController.currentDestinationFeature[0], diff --git a/lib/pages/drawer/drawer_page.dart b/lib/pages/drawer/drawer_page.dart index 8a89fe2..4049e80 100644 --- a/lib/pages/drawer/drawer_page.dart +++ b/lib/pages/drawer/drawer_page.dart @@ -255,18 +255,17 @@ class DrawerPage extends StatelessWidget { Get.back(); // ドロワーを閉じる Get.toNamed(Routes.SETTINGS); }, + ), + + //ListTile( + // leading: const Icon(Icons.developer_mode), + // title: const Text('open_settings'), + // onTap: () { + // Get.back(); // ドロワーを閉じる + // Get.toNamed('/debug'); // デバッグ画面に遷移 + // }, + //), - ), - /* - ListTile( - leading: const Icon(Icons.developer_mode), - title: const Text('open_settings'), - onTap: () { - Get.back(); // ドロワーを閉じる - Get.toNamed('/debug'); // デバッグ画面に遷移 - }, - ), - */ // ListTile( // leading: const Icon(Icons.router), diff --git a/lib/pages/entry/entry_controller.dart b/lib/pages/entry/entry_controller.dart index 003d78d..845f7a0 100644 --- a/lib/pages/entry/entry_controller.dart +++ b/lib/pages/entry/entry_controller.dart @@ -9,6 +9,8 @@ import 'package:rogapp/model/category.dart'; import 'package:rogapp/services/api_service.dart'; import '../index/index_controller.dart'; +import 'package:timezone/timezone.dart' as tz; +import 'package:timezone/data/latest.dart' as tz; class EntryController extends GetxController { late ApiService _apiService; @@ -92,8 +94,10 @@ class EntryController extends GetxController { 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 = 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?; @@ -201,6 +205,7 @@ class EntryController extends GetxController { selectedEvent.value!.id, selectedCategory.value!.id, selectedDate.value!, + currentEntry.value!.zekkenNumber, ); final index = entries.indexWhere((entry) => entry.id == updatedEntry.id); if (index != -1) { diff --git a/lib/pages/entry/entry_detail_page.dart b/lib/pages/entry/entry_detail_page.dart index 7f736c0..73e1577 100644 --- a/lib/pages/entry/entry_detail_page.dart +++ b/lib/pages/entry/entry_detail_page.dart @@ -8,6 +8,9 @@ import 'package:rogapp/model/category.dart'; import 'package:rogapp/model/team.dart'; import 'package:intl/intl.dart'; +import 'package:timezone/timezone.dart' as tz; +import 'package:timezone/data/latest.dart' as tz; + class EntryDetailPage extends GetView { @override Widget build(BuildContext context) { @@ -70,7 +73,7 @@ class EntryDetailPage extends GetView { title: Text('日付'), subtitle: Text( controller.selectedDate.value != null - ? DateFormat('yyyy-MM-dd').format(controller.selectedDate.value!) + ? DateFormat('yyyy-MM-dd').format(tz.TZDateTime.from(controller.selectedDate.value!, tz.getLocation('Asia/Tokyo'))) : '日付を選択してください', ), onTap: () async { @@ -78,14 +81,22 @@ class EntryDetailPage extends GetView { Get.snackbar('Error', 'Please select an event first'); return; } + final tz.TZDateTime now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo')); + final tz.TZDateTime eventStart = tz.TZDateTime.from(controller.selectedEvent.value!.startDatetime, tz.getLocation('Asia/Tokyo')); + final tz.TZDateTime eventEnd = tz.TZDateTime.from(controller.selectedEvent.value!.endDatetime, tz.getLocation('Asia/Tokyo')); + + final tz.TZDateTime initialDate = controller.selectedDate.value != null + ? tz.TZDateTime.from(controller.selectedDate.value!, tz.getLocation('Asia/Tokyo')) + : (now.isAfter(eventStart) ? now : eventStart); + final DateTime? picked = await showDatePicker( context: context, - initialDate: controller.selectedDate.value ?? controller.selectedEvent.value!.startDatetime, - firstDate: controller.selectedEvent.value!.startDatetime, - lastDate: controller.selectedEvent.value!.endDatetime, + initialDate: initialDate, + firstDate: eventStart, + lastDate: eventEnd, ); if (picked != null) { - controller.updateDate(picked); + controller.updateDate(tz.TZDateTime.from(picked, tz.getLocation('Asia/Tokyo'))); } }, ), @@ -96,6 +107,7 @@ class EntryDetailPage extends GetView { onPressed: () => controller.createEntry(), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, + foregroundColor: Colors.white, minimumSize: Size(double.infinity, 50), ), ) @@ -108,6 +120,7 @@ class EntryDetailPage extends GetView { onPressed: () => controller.deleteEntry(), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, + foregroundColor: Colors.white, minimumSize: Size(0, 50), ), ), @@ -119,6 +132,7 @@ class EntryDetailPage extends GetView { onPressed: () => controller.updateEntry(), style: ElevatedButton.styleFrom( backgroundColor: Colors.lightBlue, + foregroundColor: Colors.white, minimumSize: Size(0, 50), ), ), diff --git a/lib/pages/entry/entry_list_page.dart b/lib/pages/entry/entry_list_page.dart index c70c6e7..45c3404 100644 --- a/lib/pages/entry/entry_list_page.dart +++ b/lib/pages/entry/entry_list_page.dart @@ -5,6 +5,7 @@ import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:rogapp/pages/entry/entry_controller.dart'; import 'package:rogapp/routes/app_pages.dart'; +import 'package:timezone/timezone.dart' as tz; class EntryListPage extends GetView { @override @@ -12,6 +13,12 @@ class EntryListPage extends GetView { return Scaffold( appBar: AppBar( title: Text('エントリー管理'), + actions: [ + IconButton( + icon: Icon(Icons.add), + onPressed: () => Get.toNamed(AppPages.ENTRY_DETAIL, arguments: {'mode': 'new'}), + ), + ], ), body: Obx(() { if (controller.entries.isEmpty) { @@ -54,7 +61,8 @@ class EntryListPage extends GetView { if (date == null) { return '日時未設定'; } - return DateFormat('yyyy-MM-dd').format(date); + final jstDate = tz.TZDateTime.from(date, tz.getLocation('Asia/Tokyo')); + return DateFormat('yyyy-MM-dd').format(jstDate); } } diff --git a/lib/pages/entry/event_entries_controller.dart b/lib/pages/entry/event_entries_controller.dart index 24fc76c..c86f90c 100644 --- a/lib/pages/entry/event_entries_controller.dart +++ b/lib/pages/entry/event_entries_controller.dart @@ -4,6 +4,8 @@ import 'package:rogapp/pages/index/index_controller.dart'; import 'package:rogapp/pages/destination/destination_controller.dart'; import 'package:rogapp/services/api_service.dart'; import 'package:flutter/material.dart'; +import 'package:timezone/timezone.dart' as tz; +import 'package:timezone/data/latest.dart' as tz; class EventEntriesController extends GetxController { final ApiService _apiService = Get.find(); @@ -14,9 +16,12 @@ class EventEntriesController extends GetxController { final filteredEntries = [].obs; final showTodayEntries = true.obs; + static bool _timezoneInitialized = false; + @override void onInit() { super.onInit(); + _initializeTimezone(); // DestinationControllerが登録されていない場合に備えて、lazyPutを使用 Get.lazyPut(() => DestinationController(), fenix: true); _destinationController = Get.find(); @@ -24,6 +29,13 @@ class EventEntriesController extends GetxController { fetchEntries(); } + void _initializeTimezone() { + if (!_timezoneInitialized) { + tz.initializeTimeZones(); + _timezoneInitialized = true; + } + } + Future fetchEntries() async { try { final fetchedEntries = await _apiService.getEntries(); @@ -44,6 +56,16 @@ class EventEntriesController extends GetxController { } void filterEntriesForToday() { + final now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo')); + filteredEntries.assignAll(entries.where((entry) { + final entryDate = tz.TZDateTime.from(entry.date!, tz.getLocation('Asia/Tokyo')); + return entryDate.year == now.year && + entryDate.month == now.month && + entryDate.day == now.day; + })); + } + + void filterEntriesForToday_old() { final now = DateTime.now(); filteredEntries.assignAll(entries.where((entry) => entry.date?.year == now.year && @@ -66,10 +88,12 @@ class EventEntriesController extends GetxController { } Future joinEvent(Entry entry) async { - final now = DateTime.now(); - bool isToday = entry.date?.year == now.year && - entry.date?.month == now.month && - entry.date?.day == now.day; + //final now = DateTime.now(); + final now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo')); + final entryDate = tz.TZDateTime.from(entry.date!, tz.getLocation('Asia/Tokyo')); + bool isToday = entryDate.year == now.year && + entryDate.month == now.month && + entryDate.day == now.day; _indexController.setReferenceMode(!isToday); _indexController.setSelectedEventName(entry.event.eventName); diff --git a/lib/pages/entry/event_entries_page.dart b/lib/pages/entry/event_entries_page.dart index e4aaa91..44f7a37 100644 --- a/lib/pages/entry/event_entries_page.dart +++ b/lib/pages/entry/event_entries_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:rogapp/pages/entry/event_entries_controller.dart'; +import 'package:timezone/timezone.dart' as tz; class EventEntriesPage_old extends GetView { @override @@ -96,7 +97,8 @@ class EventEntriesPage extends GetView { if (date == null) { return '日時未設定'; } - return DateFormat('yyyy-MM-dd').format(date); + final jstDate = tz.TZDateTime.from(date, tz.getLocation('Asia/Tokyo')); + return DateFormat('yyyy-MM-dd').format(jstDate); } } diff --git a/lib/pages/index/index_controller.dart b/lib/pages/index/index_controller.dart index a8a5a42..2f5afa6 100644 --- a/lib/pages/index/index_controller.dart +++ b/lib/pages/index/index_controller.dart @@ -489,7 +489,23 @@ class IndexController extends GetxController with WidgetsBindingObserver { // void register(String email, String password, String password2, BuildContext context) { AuthService.register(email, password,password2).then((value) { - if (value.isNotEmpty) { + if (value is Map && value.containsKey("error")) { + String err_message = value["error"]; + debugPrint("ユーザー登録失敗:${email}, ${password},${err_message}"); + logManager.addOperationLog("User failed to register new account : ${email} , ${password} ,${err_message}."); + isLoading.value = false; + Get.snackbar( + 'user_registration_failed_please_try_again'.tr, // ユーザー登録に失敗しました。 + err_message, + backgroundColor: Colors.red, + colorText: Colors.white, + icon: const Icon(Icons.error, size: 40.0, color: Colors.blue), + snackPosition: SnackPosition.TOP, + duration: const Duration(seconds: 3), + //backgroundColor: Colors.yellow, + //icon:Image(image:AssetImage("assets/images/dora.png")) + ); + }else{ debugPrint("ユーザー登録成功:${email}, ${password}"); logManager.addOperationLog("User tried to register new account : ${email} , ${password} ."); @@ -508,22 +524,7 @@ class IndexController extends GetxController with WidgetsBindingObserver { ); //Navigator.pop(context); - Get.toNamed(AppPages.INDEX); - } else { - debugPrint("ユーザー登録失敗:${email}, ${password}"); - logManager.addOperationLog("User failed to register new account : ${email} , ${password} ."); - isLoading.value = false; - Get.snackbar( - 'failed'.tr, // 失敗 - 'user_registration_failed_please_try_again'.tr, // ユーザー登録に失敗しました。 - backgroundColor: Colors.red, - colorText: Colors.white, - icon: const Icon(Icons.error, size: 40.0, color: Colors.blue), - snackPosition: SnackPosition.TOP, - duration: const Duration(seconds: 3), - //backgroundColor: Colors.yellow, - //icon:Image(image:AssetImage("assets/images/dora.png")) - ); + Get.toNamed(AppPages.LOGIN); } }); } diff --git a/lib/pages/index/index_page.dart b/lib/pages/index/index_page.dart index b71ec73..bea6cd4 100644 --- a/lib/pages/index/index_page.dart +++ b/lib/pages/index/index_page.dart @@ -36,24 +36,50 @@ class _IndexPageState extends State { void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - //checkEntryAndShowHelper(); + //checkLoginAndShowDialog(); }); } - /* - void checkEntryAndShowHelper() async { - final hasEntry = await checkIfUserHasEntry(); // この関数は実装する必要があります - if (!hasEntry) { - showHelperDialog( - context, - 'イベントに参加するには、チーム登録・メンバー登録及びエントリーが必要になります。', - 'entry_helper', - showDoNotShowAgain: true, + void checkLoginAndShowDialog() { + if (indexController.currentUser.isEmpty) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AlertDialog( + title: Text('ログインが必要です'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('1) ログインされていません。ロゲに参加するにはログインが必要です。'), + SizedBox(height: 10), + Text('2) ログイン後、個人情報入力、チーム登録、エントリー登録を行なってください。'), + SizedBox(height: 10), + Text('3) エントリー登録は場所と日にちごとに行なってください。'), + ], + ), + actions: [ + TextButton( + child: Text('キャンセル'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ElevatedButton( + child: Text('ログイン'), + onPressed: () { + Navigator.of(context).pop(); + Get.toNamed(AppPages.LOGIN); + }, + ), + ], + ); + }, ); } } - - */ + // class IndexPage extends GetView { // IndexPage({Key? key}) : super(key: key); diff --git a/lib/pages/login/login_page.dart b/lib/pages/login/login_page.dart index c1ce842..e583d6e 100644 --- a/lib/pages/login/login_page.dart +++ b/lib/pages/login/login_page.dart @@ -5,6 +5,8 @@ import 'package:rogapp/routes/app_pages.dart'; import 'package:rogapp/widgets/helper_dialog.dart'; import 'package:rogapp/services/api_service.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + // 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。 // エラーメッセージをローカライズすることを検討してください。 // ログイン処理中にエラーが発生した場合のエラーハンドリングを追加することをお勧めします。 @@ -22,6 +24,7 @@ class _LoginPageState extends State { TextEditingController emailController = TextEditingController(); TextEditingController passwordController = TextEditingController(); bool _obscureText = true; + String _version = ''; // バージョン情報を保持する変数 @override void initState() { @@ -32,6 +35,15 @@ class _LoginPageState extends State { 'login_page' ); }); + _getVersionInfo(); // バージョン情報を取得 + } + + // バージョン情報を取得するメソッド + Future _getVersionInfo() async { + final PackageInfo packageInfo = await PackageInfo.fromPlatform(); + setState(() { + _version = packageInfo.version; + }); } void _showResetPasswordDialog() { @@ -124,6 +136,14 @@ class _LoginPageState extends State { const SizedBox( height: 5, ), + // バージョン情報を表示 + Text( + 'Version: $_version', + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + ), + ), ], ), Padding( diff --git a/lib/pages/register/register_page.dart b/lib/pages/register/register_page.dart index 0499e69..16f704d 100644 --- a/lib/pages/register/register_page.dart +++ b/lib/pages/register/register_page.dart @@ -89,6 +89,7 @@ class _RegisterPageState extends State { ElevatedButton( onPressed: _handleRegister, style: ElevatedButton.styleFrom( + foregroundColor: Colors.white, backgroundColor: Colors.redAccent, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)), minimumSize: const Size(double.infinity, 60), @@ -165,7 +166,7 @@ class _RegisterPageState extends State { context ); // 登録が成功したと仮定し、ログインページに遷移 - Get.offNamed(AppPages.LOGIN); + //Get.offNamed(AppPages.LOGIN); } catch (error) { _showErrorSnackbar("registration_error".tr, error.toString()); } finally { diff --git a/lib/pages/team/member_controller.dart b/lib/pages/team/member_controller.dart index a0aa29d..e5cfbca 100644 --- a/lib/pages/team/member_controller.dart +++ b/lib/pages/team/member_controller.dart @@ -91,6 +91,7 @@ class MemberController extends GetxController { lastname.value = value; } + Future saveMember() async { if (!_validateInputs()) return false; @@ -139,8 +140,8 @@ class MemberController extends GetxController { return '${lastname.value} ${firstname.value}'.trim(); } - Future updateMember() async { - if (member.value == null) return; + Future updateMember() async { + if (member.value == null) return false; int? memberId = member.value?.id; try { final updatedMember = await _apiService.updateTeamMember( @@ -152,15 +153,17 @@ class MemberController extends GetxController { female.value, ); member.value = updatedMember; + return true; } catch (e) { print('Error updating member: $e'); + return false; } } - Future deleteMember() async { + Future deleteMember() async { if (member.value == null || member.value!.id == null) { Get.snackbar('エラー', 'メンバー情報が不正です', snackPosition: SnackPosition.BOTTOM); - return; + return false; } try { @@ -168,11 +171,14 @@ class MemberController extends GetxController { await _apiService.deleteTeamMember(teamId, member.value!.id!); Get.snackbar('成功', 'メンバーが削除されました', snackPosition: SnackPosition.BOTTOM); member.value = null; + isLoading.value = false; + return true; + } catch (e) { print('Error deleting member: $e'); Get.snackbar('エラー', 'メンバーの削除に失敗しました: ${e.toString()}', snackPosition: SnackPosition.BOTTOM); - } finally { isLoading.value = false; + return false; } } @@ -251,10 +257,10 @@ class MemberController extends GetxController { yearsFromSchoolStart--; } - if (yearsFromSchoolStart < 0) return '未就学'; - if (yearsFromSchoolStart < 6) return '小${yearsFromSchoolStart + 1}'; - if (yearsFromSchoolStart < 9) return '中${yearsFromSchoolStart - 5}'; - if (yearsFromSchoolStart < 12) return '高${yearsFromSchoolStart - 8}'; + if (yearsFromSchoolStart < 7) return '未就学'; + if (yearsFromSchoolStart < 13) return '小${yearsFromSchoolStart - 6}'; + if (yearsFromSchoolStart < 16) return '中${yearsFromSchoolStart - 12}'; + if (yearsFromSchoolStart < 19) return '高${yearsFromSchoolStart - 15}'; return '成人'; } diff --git a/lib/pages/team/member_detail_page.dart b/lib/pages/team/member_detail_page.dart index d8d2e9a..699693d 100644 --- a/lib/pages/team/member_detail_page.dart +++ b/lib/pages/team/member_detail_page.dart @@ -7,6 +7,8 @@ import 'package:intl/intl.dart'; // この行を追加 import 'package:flutter_localizations/flutter_localizations.dart'; // 追加 import 'package:flutter/cupertino.dart'; +import 'package:rogapp/routes/app_pages.dart'; + class MemberDetailPage extends StatefulWidget { @override _MemberDetailPageState createState() => _MemberDetailPageState(); @@ -34,15 +36,18 @@ class _MemberDetailPageState extends State { } void _initializeControllers() { - _firstNameController = TextEditingController(text: controller.firstname.value); - _lastNameController = TextEditingController(text: controller.lastname.value); + _firstNameController = + TextEditingController(text: controller.firstname.value); + _lastNameController = + TextEditingController(text: controller.lastname.value); _emailController = TextEditingController(text: controller.email.value); controller.firstname.listen((value) { if (_firstNameController.text != value) { _firstNameController.value = TextEditingValue( text: value, - selection: TextSelection.fromPosition(TextPosition(offset: value.length)), + selection: TextSelection.fromPosition( + TextPosition(offset: value.length)), ); } }); @@ -51,7 +56,8 @@ class _MemberDetailPageState extends State { if (_lastNameController.text != value) { _lastNameController.value = TextEditingValue( text: value, - selection: TextSelection.fromPosition(TextPosition(offset: value.length)), + selection: TextSelection.fromPosition( + TextPosition(offset: value.length)), ); } }); @@ -60,12 +66,71 @@ class _MemberDetailPageState extends State { if (_emailController.text != value) { _emailController.value = TextEditingValue( text: value, - selection: TextSelection.fromPosition(TextPosition(offset: value.length)), + selection: TextSelection.fromPosition( + TextPosition(offset: value.length)), ); } }); } + Future _handleSaveAndNavigateBack() async { + bool success = await controller.saveMember(); + if (success) { + Get.until((route) => Get.currentRoute == AppPages.TEAM_DETAIL); + // スナックバーが表示されるのを待つ + await Future.delayed(Duration(seconds: 1)); + + // 現在のスナックバーを安全に閉じる + if (Get.isSnackbarOpen) { + await Get.closeCurrentSnackbar(); + } + + // リストページに戻る + //Get.until((route) => Get.currentRoute == '/team'); + // または、リストページの具体的なルート名がある場合は以下を使用 + // Get.offNamed('/team'); + } + } + + + Future _handleDeleteAndNavigateBack() async { + final confirmed = await Get.dialog( + AlertDialog( + title: Text('確認'), + content: Text('このメンバーを削除してもよろしいですか?'), + actions: [ + TextButton( + child: Text('キャンセル'), + onPressed: () => Get.back(result: false), + ), + TextButton( + child: Text('削除'), + onPressed: () => Get.back(result: true), + ), + ], + ), + ); + if (confirmed == true) { + bool success = await controller.deleteMember(); + if (success) { + // リストページに戻る + //Get.offNamed(result: true); + Get.until((route) => Get.currentRoute == AppPages.TEAM_DETAIL); + + // スナックバーが表示されるのを待つ + await Future.delayed(Duration(seconds: 1)); + + // 現在のスナックバーを安全に閉じる + if (Get.isSnackbarOpen) { + await Get.closeCurrentSnackbar(); + } + + + } + } + } + + @override Widget build(BuildContext context) { final mode = Get.arguments['mode'] as String; @@ -109,17 +174,9 @@ class _MemberDetailPageState extends State { children: [ if (mode == 'new') TextFormField( + controller: _emailController, + onChanged: (value) => controller.updateEmail(value), decoration: InputDecoration(labelText: 'メールアドレス'), - //onChanged: (value) => controller.email.value = value, - onChanged: (value) { - controller.email.value = value; - // カーソル位置を更新 - emailController.selection = TextSelection.fromPosition( - TextPosition(offset: value.length), - ); - }, - controller: emailController, - //controller: TextEditingController(text: controller.email.value), keyboardType: TextInputType.emailAddress, // メールアドレス用のキーボードを表示 autocorrect: false, // 自動修正を無効化 enableSuggestions: false, @@ -129,16 +186,20 @@ class _MemberDetailPageState extends State { else Text('メールアドレス: ${controller.email.value}'), - if (controller.email.value.isEmpty || mode == 'edit') ...[ + if (controller.email.value.isEmpty || controller.isDummyEmail || mode == 'edit') ...[ TextFormField( decoration: InputDecoration(labelText: '姓'), - onChanged: (value) => controller.lastname.value = value, - controller: TextEditingController(text: controller.lastname.value), + controller: _lastNameController, + onChanged: (value) => controller.updateLastName(value), + + //controller: TextEditingController(text: controller.lastname.value), ), TextFormField( decoration: InputDecoration(labelText: '名'), - onChanged: (value) => controller.firstname.value = value, - controller: TextEditingController(text: controller.firstname.value), + controller: _firstNameController, + //onChanged: (value) => controller.firstname.value = value, + onChanged: (value) => controller.updateFirstName(value), + //controller: TextEditingController(text: controller.firstname.value), ), // 生年月日 if (controller.isDummyEmail || !controller.isOver18()) @@ -178,28 +239,7 @@ class _MemberDetailPageState extends State { children: [ ElevatedButton( child: Text('削除'), - onPressed: () async { - final confirmed = await Get.dialog( - AlertDialog( - title: Text('確認'), - content: Text('このメンバーを削除してもよろしいですか?'), - actions: [ - TextButton( - child: Text('キャンセル'), - onPressed: () => Get.back(result: false), - ), - TextButton( - child: Text('削除'), - onPressed: () => Get.back(result: true), - ), - ], - ), - ); - if (confirmed == true) { - await controller.deleteMember(); - Get.back(result: true); - } - }, + onPressed: _handleDeleteAndNavigateBack, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, @@ -216,10 +256,7 @@ class _MemberDetailPageState extends State { ), ElevatedButton( child: Text('保存・招待'), - onPressed: () async { - await controller.saveMember(); - Get.back(result: true); - }, + onPressed: _handleSaveAndNavigateBack, style: ElevatedButton.styleFrom( backgroundColor: Colors.green, foregroundColor: Colors.white, diff --git a/lib/pages/team/team_detail_page.dart b/lib/pages/team/team_detail_page.dart index 28db560..b489e67 100644 --- a/lib/pages/team/team_detail_page.dart +++ b/lib/pages/team/team_detail_page.dart @@ -175,9 +175,9 @@ class _TeamDetailPageState extends State { AppPages.MEMBER_DETAIL, arguments: {'mode': 'edit', 'member': member, 'teamId': controller.selectedTeam.value?.id}, ); - if (result == true) { - await controller.fetchTeamMembers(controller.selectedTeam.value!.id); - } + + await controller.fetchTeamMembers(controller.selectedTeam.value!.id); + }, ); }, diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart index 67ebbd6..63bf91c 100644 --- a/lib/services/api_service.dart +++ b/lib/services/api_service.dart @@ -363,7 +363,7 @@ class ApiService extends GetxService{ }), ); - if (response.statusCode == 201) { + if (response.statusCode == 200 || response.statusCode == 201) { final decodedResponse = utf8.decode(response.bodyBytes); return User.fromJson(json.decode(decodedResponse)); } else { @@ -511,6 +511,8 @@ class ApiService extends GetxService{ return Entry.fromJson(json.decode(decodedResponse)); } else { + final decodedResponse = utf8.decode(response.bodyBytes); + print("decodedResponse = $decodedResponse"); throw Exception('Failed to create entry'); } } @@ -552,7 +554,7 @@ class ApiService extends GetxService{ } - Future updateEntry(int entryId, int teamId, int eventId, int categoryId, DateTime date) async { + Future updateEntry(int entryId, int teamId, int eventId, int categoryId, DateTime date,int zekken_number) async { init(); getToken(); @@ -572,6 +574,7 @@ class ApiService extends GetxService{ 'event': eventId, 'category': categoryId, 'date': formattedDate, + 'zekken_number': zekken_number, }), ); @@ -580,6 +583,9 @@ class ApiService extends GetxService{ return Entry.fromJson(json.decode(decodedResponse)); } else { + final decodedResponse = utf8.decode(response.bodyBytes); + final blk = json.decode(decodedResponse); + throw Exception('Failed to update entry'); } } diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart index ee2f40e..23804a4 100644 --- a/lib/services/auth_service.dart +++ b/lib/services/auth_service.dart @@ -164,9 +164,10 @@ class AuthService { }, body: jsonEncode({'email': email, 'password': password, 'password2': password2}), ); - //print(response.body); + cats = json.decode(utf8.decode(response.bodyBytes)); + print("result=$cats"); if (response.statusCode == 201) { - cats = json.decode(utf8.decode(response.bodyBytes)); + }else{ } return cats; } diff --git a/lib/services/external_service.dart b/lib/services/external_service.dart index 1b6b0aa..05bb7cb 100644 --- a/lib/services/external_service.dart +++ b/lib/services/external_service.dart @@ -202,6 +202,7 @@ class ExternalService { colorText: Colors.white ); } + } } else { Get.snackbar("サーバーエラーがおきました", "サーバーと通信できませんでした", @@ -273,7 +274,8 @@ class ExternalService { Get.find(); // チームIDを取得 - int teamId = indexController.currentUser[0]["user"]["team"]["id"]; + + //int teamId = indexController.currentUser[0]["user"]["team"]["id"]; debugPrint("== goal Rogaining =="); @@ -283,7 +285,7 @@ class ExternalService { id: 1, team_name: teamname, event_code: eventcode, - user_id: teamId, //userId, + user_id: userId, // 中身はteamid cp_number: -1, checkintime: DateTime.now().toUtc().microsecondsSinceEpoch, image: image, @@ -305,7 +307,7 @@ class ExternalService { }, // 'id', 'user', 'goalimage', 'goaltime', 'team_name', 'event_code','cp_number' body: jsonEncode({ - 'user': teamId.toString(), //userId.toString(), + 'user': userId.toString(), //userId.toString(), 'team_name': teamname, 'event_code': eventcode, 'goaltime': goalTime, @@ -318,34 +320,42 @@ class ExternalService { String url = '$serverUrl/gifuroge/goal_from_rogapp'; //print('++++++++$url'); if (response.statusCode == 201) { - Map res = json.decode(utf8.decode(response.bodyBytes)); - // print('----_res : $res ----'); - // print('---- image url ${res["goalimage"]} ----'); - final http.Response response2 = await http.post( - Uri.parse(url), - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - }, - body: jsonEncode({ + try { + Map res = json.decode(utf8.decode(response.bodyBytes)); + // print('----_res : $res ----'); + // print('---- image url ${res["goalimage"]} ----'); + final http.Response response2 = await http.post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + }, + body: jsonEncode({ + 'team_name': teamname, + 'event_code': eventcode, + 'goal_time': goalTime, + 'image': res["goalimage"].toString().replaceAll( + 'http://localhost:8100', serverUrl) + //'http://rogaining.sumasen.net') + }), + ); + String rec = jsonEncode({ 'team_name': teamname, 'event_code': eventcode, 'goal_time': goalTime, - 'image': res["goalimage"].toString().replaceAll( - 'http://localhost:8100', serverUrl) //'http://rogaining.sumasen.net') - }), - ); - String rec = jsonEncode({ - 'team_name': teamname, - 'event_code': eventcode, - 'goal_time': goalTime, - 'image': res["goalimage"] - .toString() - .replaceAll('http://localhost:8100', serverUrl) //'http://rogaining.sumasen.net') - }); - //print("-- json -- $rec"); - //print('----- response2 is $response2 --------'); - if (response2.statusCode == 200) { - res2 = json.decode(utf8.decode(response2.bodyBytes)); + 'image': res["goalimage"] + .toString() + .replaceAll('http://localhost:8100', serverUrl) + //'http://rogaining.sumasen.net') + }); + //print("-- json -- $rec"); + //print('----- response2 is $response2 --------'); + if (response2.statusCode == 200) { + res2 = json.decode(utf8.decode(response2.bodyBytes)); + } else { + res2 = json.decode(utf8.decode(response2.bodyBytes)); + } + } catch(e){ + print( "Error {$e}" ); } } //} diff --git a/lib/widgets/bottom_sheet_new.dart b/lib/widgets/bottom_sheet_new.dart index b5a945e..f663d35 100644 --- a/lib/widgets/bottom_sheet_new.dart +++ b/lib/widgets/bottom_sheet_new.dart @@ -250,7 +250,10 @@ class BottomSheetNew extends GetView { //goal return ElevatedButton( - style: ElevatedButton.styleFrom(backgroundColor: Colors.red), + style: ElevatedButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.red + ), onPressed: destinationController.rogainingCounted.value == true && destinationController.distanceToStart() <= 500 && (destination.cp == 0 || destination.cp == -2|| destination.cp == -1) && diff --git a/login_page.dart b/login_page.dart index b8464ef..f62b173 100644 --- a/login_page.dart +++ b/login_page.dart @@ -92,6 +92,8 @@ class _LoginPageState extends State { //LoginPage({Key? key}) : super(key: key); + + @override Widget build(BuildContext context) { return Scaffold( diff --git a/pubspec.lock b/pubspec.lock index 82bdaaa..0d4ca7c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1266,6 +1266,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + timezone: + dependency: "direct main" + description: + name: timezone + sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" + url: "https://pub.dev" + source: hosted + version: "0.9.4" transparent_image: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 15c2202..571fd43 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 4.8.6+486 +version: 4.8.13+493 environment: sdk: ">=3.2.0 <4.0.0" @@ -41,6 +41,7 @@ dependencies: geolocator: ^10.1.0 permission_handler: ^11.3.1 logging: ^1.0.2 + timezone: ^0.9.1 # flutter_dev_tools: ^0.0.2 # permission_handler: ^11.1.0 <== older