Compare commits
7 Commits
9c98d3ed53
...
mapsix
| Author | SHA1 | Date | |
|---|---|---|---|
| f9a2bae9a9 | |||
| bdf6dd3c04 | |||
| 347861e5a1 | |||
| 66ade1fe09 | |||
| 1e0af0b06b | |||
| ee007795b9 | |||
| 3d7a5ae0c1 |
15597
all_combined.txt
15597
all_combined.txt
File diff suppressed because it is too large
Load Diff
BIN
assets/images/QR_certificate.png
Normal file
BIN
assets/images/QR_certificate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
BIN
assets/images/QR_gifuroge_stage1.png
Normal file
BIN
assets/images/QR_gifuroge_stage1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 518 B |
@ -11,9 +11,11 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- flutter_keyboard_visibility (0.0.1):
|
- flutter_keyboard_visibility (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FMDB (2.7.10):
|
- FMDB (2.7.12):
|
||||||
- FMDB/standard (= 2.7.10)
|
- FMDB/standard (= 2.7.12)
|
||||||
- FMDB/standard (2.7.10)
|
- FMDB/Core (2.7.12)
|
||||||
|
- FMDB/standard (2.7.12):
|
||||||
|
- FMDB/Core
|
||||||
- geolocator_apple (1.2.0):
|
- geolocator_apple (1.2.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- image_gallery_saver (2.0.2):
|
- image_gallery_saver (2.0.2):
|
||||||
@ -35,7 +37,7 @@ PODS:
|
|||||||
- qr_code_scanner (0.2.0):
|
- qr_code_scanner (0.2.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- MTBBarcodeScanner
|
- MTBBarcodeScanner
|
||||||
- ReachabilitySwift (5.2.1)
|
- ReachabilitySwift (5.2.3)
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@ -121,7 +123,7 @@ SPEC CHECKSUMS:
|
|||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
flutter_compass: cbbd285cea1584c7ac9c4e0c3e1f17cbea55e855
|
flutter_compass: cbbd285cea1584c7ac9c4e0c3e1f17cbea55e855
|
||||||
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
|
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
|
||||||
FMDB: eae540775bf7d0c87a5af926ae37af69effe5a19
|
FMDB: 728731dd336af3936ce00f91d9d8495f5718a0e6
|
||||||
geolocator_apple: 9157311f654584b9bb72686c55fc02a97b73f461
|
geolocator_apple: 9157311f654584b9bb72686c55fc02a97b73f461
|
||||||
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||||
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
||||||
@ -132,7 +134,7 @@ SPEC CHECKSUMS:
|
|||||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||||
pointer_interceptor_ios: 9280618c0b2eeb80081a343924aa8ad756c21375
|
pointer_interceptor_ios: 9280618c0b2eeb80081a343924aa8ad756c21375
|
||||||
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
|
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
|
||||||
ReachabilitySwift: 5ae15e16814b5f9ef568963fb2c87aeb49158c66
|
ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979
|
||||||
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
|
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
|
||||||
sqflite: 50a33e1d72bd59ee092a519a35d107502757ebed
|
sqflite: 50a33e1d72bd59ee092a519a35d107502757ebed
|
||||||
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
|
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 54;
|
objectVersion = 60;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@ -398,11 +398,11 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 480;
|
CURRENT_PROJECT_VERSION = 497;
|
||||||
DEVELOPMENT_TEAM = UMNEWT25JR;
|
DEVELOPMENT_TEAM = UMNEWT25JR;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FLUTTER_BUILD_NAME = 4.8.0;
|
FLUTTER_BUILD_NAME = 4.8.17;
|
||||||
FLUTTER_BUILD_NUMBER = 480;
|
FLUTTER_BUILD_NUMBER = 497;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ";
|
INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ";
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness";
|
||||||
@ -411,7 +411,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 4.8.0;
|
MARKETING_VERSION = 4.8.17;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -539,11 +539,11 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 480;
|
CURRENT_PROJECT_VERSION = 497;
|
||||||
DEVELOPMENT_TEAM = UMNEWT25JR;
|
DEVELOPMENT_TEAM = UMNEWT25JR;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FLUTTER_BUILD_NAME = 4.8.0;
|
FLUTTER_BUILD_NAME = 4.8.17;
|
||||||
FLUTTER_BUILD_NUMBER = 480;
|
FLUTTER_BUILD_NUMBER = 497;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ";
|
INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ";
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness";
|
||||||
@ -552,7 +552,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 4.8.0;
|
MARKETING_VERSION = 4.8.17;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -571,11 +571,11 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 480;
|
CURRENT_PROJECT_VERSION = 497;
|
||||||
DEVELOPMENT_TEAM = UMNEWT25JR;
|
DEVELOPMENT_TEAM = UMNEWT25JR;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FLUTTER_BUILD_NAME = 4.8.0;
|
FLUTTER_BUILD_NAME = 4.8.17;
|
||||||
FLUTTER_BUILD_NUMBER = 480;
|
FLUTTER_BUILD_NUMBER = 497;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ";
|
INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ";
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness";
|
||||||
@ -584,7 +584,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 4.8.0;
|
MARKETING_VERSION = 4.8.17;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi;
|
PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import 'package:geolocator/geolocator.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
//import 'package:vm_service/vm_service.dart';
|
//import 'package:vm_service/vm_service.dart';
|
||||||
//import 'package:dart_vm_info/dart_vm_info.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';
|
import 'package:rogapp/pages/settings/settings_controller.dart';
|
||||||
|
|
||||||
@ -152,6 +153,8 @@ void main() async {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
tz.initializeTimeZones();
|
||||||
|
|
||||||
// ApiServiceを初期化
|
// ApiServiceを初期化
|
||||||
//await Get.putAsync(() => ApiService().init());
|
//await Get.putAsync(() => ApiService().init());
|
||||||
await initServices();
|
await initServices();
|
||||||
|
|||||||
@ -34,13 +34,28 @@ class NewCategory {
|
|||||||
id: json['id'] ?? 0,
|
id: json['id'] ?? 0,
|
||||||
categoryName: json['category_name'] ?? 'Unknown Category',
|
categoryName: json['category_name'] ?? 'Unknown Category',
|
||||||
categoryNumber: json['category_number'] ?? 0,
|
categoryNumber: json['category_number'] ?? 0,
|
||||||
duration: Duration(seconds: json['duration'] ?? 0),
|
duration: parseDuration(json['duration']),
|
||||||
numOfMember: json['num_of_member'] ?? 1,
|
numOfMember: json['num_of_member'] ?? 1,
|
||||||
family: json['family'] ?? false,
|
family: json['family'] ?? false,
|
||||||
female: json['female'] ?? false,
|
female: json['female'] ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Duration parseDuration(String s) {
|
||||||
|
int hours = 0;
|
||||||
|
int minutes = 0;
|
||||||
|
int micros;
|
||||||
|
List<String> parts = s.split(':');
|
||||||
|
if (parts.length > 2) {
|
||||||
|
hours = int.parse(parts[parts.length - 3]);
|
||||||
|
}
|
||||||
|
if (parts.length > 1) {
|
||||||
|
minutes = int.parse(parts[parts.length - 2]);
|
||||||
|
}
|
||||||
|
micros = (double.parse(parts[parts.length - 1]) * 1000000).round();
|
||||||
|
return Duration(hours: hours, minutes: minutes, microseconds: micros);
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'id': id,
|
'id': id,
|
||||||
|
|||||||
@ -10,6 +10,7 @@ class Entry {
|
|||||||
final Event event;
|
final Event event;
|
||||||
final NewCategory category;
|
final NewCategory category;
|
||||||
final DateTime? date;
|
final DateTime? date;
|
||||||
|
final int zekkenNumber; // 新しく追加
|
||||||
final String owner;
|
final String owner;
|
||||||
|
|
||||||
Entry({
|
Entry({
|
||||||
@ -18,6 +19,7 @@ class Entry {
|
|||||||
required this.event,
|
required this.event,
|
||||||
required this.category,
|
required this.category,
|
||||||
required this.date,
|
required this.date,
|
||||||
|
required this.zekkenNumber,
|
||||||
required this.owner,
|
required this.owner,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -30,6 +32,7 @@ class Entry {
|
|||||||
date: json['date'] != null
|
date: json['date'] != null
|
||||||
? DateTime.tryParse(json['date'])
|
? DateTime.tryParse(json['date'])
|
||||||
: null,
|
: null,
|
||||||
|
zekkenNumber: json['zekken_number'], // 新しく追加
|
||||||
owner: json['owner'] is Map ? json['owner']['name'] ?? '' : json['owner'] ?? '',
|
owner: json['owner'] is Map ? json['owner']['name'] ?? '' : json['owner'] ?? '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -41,6 +44,7 @@ class Entry {
|
|||||||
'event': event.toJson(),
|
'event': event.toJson(),
|
||||||
'category': category.toJson(),
|
'category': category.toJson(),
|
||||||
'date': date?.toIso8601String(),
|
'date': date?.toIso8601String(),
|
||||||
|
'zekken_number': zekkenNumber, // 新しく追加
|
||||||
'owner': owner,
|
'owner': owner,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import 'user.dart';
|
|||||||
|
|
||||||
class Team {
|
class Team {
|
||||||
final int id;
|
final int id;
|
||||||
final String zekkenNumber;
|
// final String zekkenNumber;
|
||||||
final String teamName;
|
final String teamName;
|
||||||
final NewCategory category;
|
final NewCategory category;
|
||||||
final User owner;
|
final User owner;
|
||||||
@ -14,7 +14,7 @@ class Team {
|
|||||||
|
|
||||||
Team({
|
Team({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.zekkenNumber,
|
// required this.zekkenNumber,
|
||||||
required this.teamName,
|
required this.teamName,
|
||||||
required this.category,
|
required this.category,
|
||||||
required this.owner,
|
required this.owner,
|
||||||
@ -23,7 +23,7 @@ class Team {
|
|||||||
factory Team.fromJson(Map<String, dynamic> json) {
|
factory Team.fromJson(Map<String, dynamic> json) {
|
||||||
return Team(
|
return Team(
|
||||||
id: json['id'] ?? 0,
|
id: json['id'] ?? 0,
|
||||||
zekkenNumber: json['zekken_number'] ?? 'Unknown',
|
//zekkenNumber: json['zekken_number'] ?? 'Unknown',
|
||||||
teamName: json['team_name'] ?? 'Unknown Team',
|
teamName: json['team_name'] ?? 'Unknown Team',
|
||||||
category: json['category'] != null
|
category: json['category'] != null
|
||||||
? NewCategory.fromJson(json['category'])
|
? NewCategory.fromJson(json['category'])
|
||||||
@ -37,7 +37,7 @@ class Team {
|
|||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'id': id,
|
'id': id,
|
||||||
'zekken_number': zekkenNumber,
|
//'zekken_number': zekkenNumber,
|
||||||
'team_name': teamName,
|
'team_name': teamName,
|
||||||
'category': category.toJson(),
|
'category': category.toJson(),
|
||||||
'owner': owner.toJson(),
|
'owner': owner.toJson(),
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert'; // この行を追加または確認
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:rogapp/model/destination.dart';
|
import 'package:rogapp/model/destination.dart';
|
||||||
import 'package:rogapp/pages/destination/destination_controller.dart';
|
import 'package:rogapp/pages/destination/destination_controller.dart';
|
||||||
import 'package:rogapp/pages/index/index_controller.dart';
|
import 'package:rogapp/pages/index/index_controller.dart';
|
||||||
@ -10,6 +14,9 @@ import 'package:rogapp/services/external_service.dart';
|
|||||||
import 'package:rogapp/utils/const.dart';
|
import 'package:rogapp/utils/const.dart';
|
||||||
import 'package:qr_code_scanner/qr_code_scanner.dart';
|
import 'package:qr_code_scanner/qr_code_scanner.dart';
|
||||||
|
|
||||||
|
import 'package:http/http.dart' as http; // この行を追加
|
||||||
|
|
||||||
|
|
||||||
// 関数 getTagText は、特定の条件に基づいて文字列から特定の部分を抽出し、返却するためのものです。
|
// 関数 getTagText は、特定の条件に基づいて文字列から特定の部分を抽出し、返却するためのものです。
|
||||||
// 関数は2つのパラメータを受け取り、条件分岐を通じて結果を返します。
|
// 関数は2つのパラメータを受け取り、条件分岐を通じて結果を返します。
|
||||||
//
|
//
|
||||||
@ -232,6 +239,7 @@ class CameraPage extends StatelessWidget {
|
|||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
shape: const CircleBorder(),
|
shape: const CircleBorder(),
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
backgroundColor: destinationController.photos.isEmpty
|
backgroundColor: destinationController.photos.isEmpty
|
||||||
? Colors.red
|
? Colors.red
|
||||||
: Colors.grey[300],
|
: Colors.grey[300],
|
||||||
@ -287,7 +295,10 @@ class CameraPage extends StatelessWidget {
|
|||||||
? settingGoal.value == false
|
? settingGoal.value == false
|
||||||
? ElevatedButton(
|
? ElevatedButton(
|
||||||
style:
|
style:
|
||||||
ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
ElevatedButton.styleFrom(
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
backgroundColor: Colors.red
|
||||||
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
// print(
|
// print(
|
||||||
// "----- user isss ${indexController.currentUser[0]} -----");
|
// "----- user isss ${indexController.currentUser[0]} -----");
|
||||||
@ -332,7 +343,7 @@ class CameraPage extends StatelessWidget {
|
|||||||
isgoal: true);
|
isgoal: true);
|
||||||
} else {
|
} else {
|
||||||
//print("---- status ${value['status']} ---- ");
|
//print("---- status ${value['status']} ---- ");
|
||||||
Get.snackbar("目標が追加されていません", "please_try_again",
|
Get.snackbar(value["detail"], "ERROR",
|
||||||
backgroundColor: Colors.green,
|
backgroundColor: Colors.green,
|
||||||
colorText: Colors.white
|
colorText: Colors.white
|
||||||
);
|
);
|
||||||
@ -352,7 +363,7 @@ class CameraPage extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
} else if (destinationController.isInRog.value &&
|
} else if ((destinationController.isInRog.value || (destination.buy_point != null && destination.buy_point! > 0)) &&
|
||||||
dbDest?.checkedin != null &&
|
dbDest?.checkedin != null &&
|
||||||
destination.cp != -1 &&
|
destination.cp != -1 &&
|
||||||
dbDest?.checkedin == true) {
|
dbDest?.checkedin == true) {
|
||||||
@ -392,7 +403,7 @@ class CameraPage extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
} else if (destinationController.isInRog.value &&
|
} else if ((destinationController.isInRog.value || (destination.buy_point != null && destination.buy_point! > 0)) &&
|
||||||
dbDest?.checkedin != null &&
|
dbDest?.checkedin != null &&
|
||||||
destination.cp != -1 &&
|
destination.cp != -1 &&
|
||||||
destination.use_qr_code == true &&
|
destination.use_qr_code == true &&
|
||||||
@ -448,6 +459,7 @@ class CameraPage extends StatelessWidget {
|
|||||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
// print(
|
// print(
|
||||||
|
|
||||||
// "##### current destination ${indexController.currentDestinationFeature[0].sub_loc_id} #######");
|
// "##### current destination ${indexController.currentDestinationFeature[0].sub_loc_id} #######");
|
||||||
await destinationController.makeCheckin(
|
await destinationController.makeCheckin(
|
||||||
indexController.currentDestinationFeature[0],
|
indexController.currentDestinationFeature[0],
|
||||||
@ -489,10 +501,13 @@ class CameraPage extends StatelessWidget {
|
|||||||
if (buyPointPhoto == true) {
|
if (buyPointPhoto == true) {
|
||||||
// buyPointPhotoがtrueの場合は、BuyPointCameraウィジェットを返します。
|
// buyPointPhotoがtrueの場合は、BuyPointCameraウィジェットを返します。
|
||||||
//print("--- buy point camera ${destination.toString()}");
|
//print("--- buy point camera ${destination.toString()}");
|
||||||
return BuyPointCamera(destination: destination);
|
//return BuyPointCamera(destination: destination);
|
||||||
|
|
||||||
|
return SwitchableBuyPointCamera(destination: destination);
|
||||||
|
|
||||||
//}else if(destination.use_qr_code){
|
//}else if(destination.use_qr_code){
|
||||||
// return QRCodeScannerPage(destination: destination);
|
// return QRCodeScannerPage(destination: destination);
|
||||||
} else if (destinationController.isInRog.value) {
|
} else if (destinationController.isInRog.value || (destination.buy_point != null && destination.buy_point! > 0)) {
|
||||||
// isInRogがtrueの場合は、カメラページのUIを構築します。
|
// isInRogがtrueの場合は、カメラページのUIを構築します。
|
||||||
// AppBarには、目的地の情報を表示します。
|
// AppBarには、目的地の情報を表示します。
|
||||||
// ボディには、目的地の画像、タグ、アクションボタンを表示します。
|
// ボディには、目的地の画像、タグ、アクションボタンを表示します。
|
||||||
@ -613,101 +628,83 @@ class StartRogaining extends StatelessWidget {
|
|||||||
// 完了ボタンをタップすると、購入ポイントの処理が行われます。
|
// 完了ボタンをタップすると、購入ポイントの処理が行われます。
|
||||||
// 購入なしボタンをタップすると、購入ポイントがキャンセルされます。
|
// 購入なしボタンをタップすると、購入ポイントがキャンセルされます。
|
||||||
//
|
//
|
||||||
class BuyPointCamera extends StatelessWidget {
|
class SwitchableBuyPointCamera extends StatefulWidget {
|
||||||
BuyPointCamera({Key? key, required this.destination}) : super(key: key);
|
final Destination destination;
|
||||||
|
|
||||||
Destination destination;
|
const SwitchableBuyPointCamera({Key? key, required this.destination}) : super(key: key);
|
||||||
|
|
||||||
DestinationController destinationController =
|
@override
|
||||||
Get.find<DestinationController>();
|
_SwitchableBuyPointCameraState createState() => _SwitchableBuyPointCameraState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SwitchableBuyPointCameraState extends State<SwitchableBuyPointCamera> {
|
||||||
|
bool isQRMode = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
final screenHeight = MediaQuery.of(context).size.height;
|
||||||
|
final qrViewWidth = screenWidth * 2 / 3;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
title: Text(
|
title: Text("${widget.destination.sub_loc_id} : ${widget.destination.name}"),
|
||||||
"${destination.sub_loc_id} : ${destination.name}",
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SafeArea(
|
||||||
child: Column(
|
child: Stack(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
if (isQRMode)
|
||||||
padding: const EdgeInsets.all(8.0),
|
Column(
|
||||||
child: Center(
|
|
||||||
child: Obx(
|
|
||||||
() => Container(
|
|
||||||
width: MediaQuery.of(context).size.width,
|
|
||||||
height: 370,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
// 要修正:getReceiptImage関数の戻り値がnullの場合のエラーハンドリングが不十分です。適切なデフォルト画像を表示するなどの処理を追加してください。
|
|
||||||
//
|
|
||||||
image: getReceiptImage(), fit: BoxFit.cover)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(getTagText(true, destination.tags)),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 16.0,
|
|
||||||
runSpacing: 8.0,
|
|
||||||
children: [
|
children: [
|
||||||
Obx(() => ElevatedButton(
|
SizedBox(height: screenHeight * 0.1),
|
||||||
onPressed: () {
|
Center(
|
||||||
destinationController.openCamera(context, destination);
|
child: SizedBox(
|
||||||
},
|
width: qrViewWidth,
|
||||||
style: ElevatedButton.styleFrom(
|
height: qrViewWidth,
|
||||||
shape: const CircleBorder(),
|
child: BuyPointCamera_QR(destination: widget.destination),
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
backgroundColor: destinationController.photos.isEmpty
|
|
||||||
? Colors.red
|
|
||||||
: Colors.grey[300],
|
|
||||||
),
|
),
|
||||||
child: destinationController.photos.isEmpty
|
),
|
||||||
? const Text("撮影",
|
Expanded(
|
||||||
style: TextStyle(color: Colors.white))
|
child: Align(
|
||||||
: const Text("再撮影",
|
alignment: Alignment.topCenter,
|
||||||
style: TextStyle(color: Colors.black)),
|
child: Padding(
|
||||||
)),
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
ElevatedButton(
|
child: Text(
|
||||||
onPressed: () async {
|
"岐阜ロゲQRコードにかざしてください。",
|
||||||
await destinationController.cancelBuyPoint(destination);
|
style: TextStyle(fontSize: 16),
|
||||||
Navigator.of(Get.context!).pop();
|
),
|
||||||
destinationController.rogainingCounted.value = true;
|
),
|
||||||
destinationController.skipGps = false;
|
),
|
||||||
destinationController.isPhotoShoot.value = false;
|
),
|
||||||
},
|
|
||||||
child: const Text("買い物なし")),
|
|
||||||
Obx(() => destinationController.photos.isNotEmpty
|
|
||||||
? ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Colors.red),
|
|
||||||
onPressed: () async {
|
|
||||||
await destinationController.makeBuyPoint(
|
|
||||||
destination,
|
|
||||||
destinationController.photos[0].path);
|
|
||||||
Get.back();
|
|
||||||
destinationController.rogainingCounted.value = true;
|
|
||||||
destinationController.skipGps = false;
|
|
||||||
destinationController.isPhotoShoot.value = false;
|
|
||||||
Get.snackbar("お買い物加点を行いました",
|
|
||||||
"${destination.sub_loc_id} : ${destination.name}",
|
|
||||||
backgroundColor: Colors.green,
|
|
||||||
colorText: Colors.white);
|
|
||||||
},
|
|
||||||
child: const Text("完了",
|
|
||||||
style: TextStyle(color: Colors.white)))
|
|
||||||
: Container())
|
|
||||||
],
|
],
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Positioned.fill(
|
||||||
|
child: BuyPointCamera(destination: widget.destination),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 16,
|
||||||
|
bottom: 16,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.7),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(isQRMode ? "カメラへ" : "QRへ"),
|
||||||
|
Switch(
|
||||||
|
value: isQRMode,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
isQRMode = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -718,130 +715,220 @@ class BuyPointCamera extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
class BuyPointCamera extends StatelessWidget {
|
class BuyPointCamera extends StatelessWidget {
|
||||||
BuyPointCamera({Key? key, required this.destination}) : super(key: key);
|
BuyPointCamera({Key? key, required this.destination}) : super(key: key);
|
||||||
|
|
||||||
Destination destination;
|
Destination destination;
|
||||||
|
|
||||||
DestinationController destinationController =
|
DestinationController destinationController =
|
||||||
Get.find<DestinationController>();
|
Get.find<DestinationController>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
//print("in camera purchase 1 ${destinationController.isInRog.value}");
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
automaticallyImplyLeading: false,
|
|
||||||
title: Text(
|
|
||||||
"${destination.sub_loc_id} : ${destination.name}",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => Container(
|
() =>
|
||||||
width: MediaQuery.of(context).size.width,
|
Container(
|
||||||
height: 370,
|
width: MediaQuery
|
||||||
decoration: BoxDecoration(
|
.of(context)
|
||||||
image: DecorationImage(
|
.size
|
||||||
// 要修正:getReceiptImage関数の戻り値がnullの場合のエラーハンドリングが不十分です。適切なデフォルト画像を表示するなどの処理を追加してください。
|
.width,
|
||||||
//
|
height: 370,
|
||||||
image: getReceiptImage(), fit: BoxFit.cover)),
|
decoration: BoxDecoration(
|
||||||
),
|
image: DecorationImage(
|
||||||
|
// 要修正:getReceiptImage関数の戻り値がnullの場合のエラーハンドリングが不十分です。適切なデフォルト画像を表示するなどの処理を追加してください。
|
||||||
|
//
|
||||||
|
image: getReceiptImage(), fit: BoxFit.cover)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(getTagText(true, destination.tags)),
|
child: Text(getTagText(true, destination.tags)),
|
||||||
),
|
),
|
||||||
Row(
|
Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Wrap(
|
||||||
children: [
|
spacing: 16.0,
|
||||||
Obx(() => Row(
|
runSpacing: 8.0,
|
||||||
mainAxisSize: MainAxisSize.min,
|
children: [
|
||||||
children: [
|
Obx(() =>
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// print(
|
destinationController.openCamera(context, destination);
|
||||||
// "in camera purchase 2 ${destinationController.isInRog.value}");
|
},
|
||||||
destinationController.openCamera(
|
style: ElevatedButton.styleFrom(
|
||||||
context, destination);
|
shape: const CircleBorder(),
|
||||||
},
|
padding: const EdgeInsets.all(20),
|
||||||
child: destinationController.photos.isNotEmpty
|
backgroundColor: destinationController.photos.isEmpty
|
||||||
? const Text("再撮影")
|
? Colors.red
|
||||||
: const Text("撮影")),
|
: Colors.grey[300],
|
||||||
const SizedBox(
|
|
||||||
width: 10,
|
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
child: destinationController.photos.isEmpty
|
||||||
onPressed: () async {
|
? const Text("撮影",
|
||||||
await destinationController
|
style: TextStyle(color: Colors.white))
|
||||||
.cancelBuyPoint(destination);
|
: const Text("再撮影",
|
||||||
Navigator.of(Get.context!).pop();
|
style: TextStyle(color: Colors.black)),
|
||||||
//Get.back();
|
)),
|
||||||
destinationController.rogainingCounted.value = true;
|
ElevatedButton(
|
||||||
destinationController.skipGps = false;
|
onPressed: () async {
|
||||||
destinationController.isPhotoShoot.value = false;
|
await destinationController.cancelBuyPoint(destination);
|
||||||
},
|
Navigator.of(Get.context!).pop();
|
||||||
child: const Text("買い物なし"))
|
destinationController.rogainingCounted.value = true;
|
||||||
],
|
destinationController.skipGps = false;
|
||||||
)),
|
destinationController.isPhotoShoot.value = false;
|
||||||
Obx(() => destinationController.photos.isNotEmpty
|
},
|
||||||
? Row(
|
child: const Text("買い物なし")),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
Obx(() =>
|
||||||
children: [
|
destinationController.photos.isNotEmpty
|
||||||
// ElevatedButton(
|
? ElevatedButton(
|
||||||
// style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
// backgroundColor: Colors.red),
|
backgroundColor: Colors.red),
|
||||||
// onPressed: () async {},
|
onPressed: () async {
|
||||||
// child: const Text("買物なし")),
|
await destinationController.makeBuyPoint(
|
||||||
// const SizedBox(
|
destination,
|
||||||
// width: 10,
|
destinationController.photos[0].path);
|
||||||
// ),
|
Get.back();
|
||||||
ElevatedButton(
|
destinationController.rogainingCounted.value = true;
|
||||||
style: ElevatedButton.styleFrom(
|
destinationController.skipGps = false;
|
||||||
backgroundColor: Colors.red),
|
destinationController.isPhotoShoot.value = false;
|
||||||
onPressed: () async {
|
Get.snackbar("お買い物加点を行いました",
|
||||||
// print(
|
"${destination.sub_loc_id} : ${destination.name}",
|
||||||
// "in camera purchase 3 ${destinationController.isInRog.value}");
|
backgroundColor: Colors.green,
|
||||||
await destinationController.makeBuyPoint(
|
colorText: Colors.white);
|
||||||
destination,
|
},
|
||||||
destinationController.photos[0].path);
|
child: const Text("完了",
|
||||||
Get.back();
|
style: TextStyle(color: Colors.white)))
|
||||||
// print(
|
: Container())
|
||||||
// "in camera purchase 4 ${destinationController.isInRog.value}");
|
],
|
||||||
destinationController.rogainingCounted.value =
|
),
|
||||||
true;
|
|
||||||
destinationController.skipGps = false;
|
|
||||||
destinationController.isPhotoShoot.value = false;
|
|
||||||
Get.snackbar("お買い物加点を行いました",
|
|
||||||
"${destination.sub_loc_id} : ${destination.name}",
|
|
||||||
backgroundColor: Colors.green,
|
|
||||||
colorText: Colors.white
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const Text("完了"))
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Container())
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
class BuyPointCamera_QR extends StatefulWidget {
|
||||||
|
final Destination destination;
|
||||||
|
|
||||||
|
const BuyPointCamera_QR({Key? key, required this.destination}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_BuyPointCamera_QRState createState() => _BuyPointCamera_QRState();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class _BuyPointCamera_QRState extends State<BuyPointCamera_QR> {
|
||||||
|
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
|
||||||
|
QRViewController? controller;
|
||||||
|
bool isQRScanned = false;
|
||||||
|
|
||||||
|
final DestinationController destinationController = Get.find<DestinationController>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return QRView(
|
||||||
|
key: qrKey,
|
||||||
|
onQRViewCreated: _onQRViewCreated,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onQRViewCreated(QRViewController controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
controller.scannedDataStream.listen((scanData) {
|
||||||
|
if (!isQRScanned && scanData.code != null && scanData.code!.startsWith('https://rogaining.sumasen.net/api/activate_buy_point/')) {
|
||||||
|
isQRScanned = true;
|
||||||
|
_processBuyPoint();
|
||||||
|
//_activateBuyPoint(scanData.code!);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getImageFilePathFromAssets(String assetPath) async {
|
||||||
|
final byteData = await rootBundle.load(assetPath);
|
||||||
|
final buffer = byteData.buffer;
|
||||||
|
Directory tempDir = await getTemporaryDirectory();
|
||||||
|
String tempPath = tempDir.path;
|
||||||
|
var filePath = '$tempPath/temp_qr_receipt.png';
|
||||||
|
return (await File(filePath).writeAsBytes(
|
||||||
|
buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes)
|
||||||
|
)).path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _processBuyPoint() async {
|
||||||
|
// アセットの画像をテンポラリファイルにコピー
|
||||||
|
String predefinedImagePath = await getImageFilePathFromAssets('assets/images/QR_certificate.png');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await destinationController.makeBuyPoint(widget.destination, predefinedImagePath);
|
||||||
|
Get.snackbar('成功', 'お買い物ポイントが有効化されました');
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
} catch (e) {
|
||||||
|
Get.snackbar('エラー', 'お買い物ポイントの有効化に失敗しました');
|
||||||
|
} finally {
|
||||||
|
isQRScanned = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _activateBuyPoint(String qrCode) async {
|
||||||
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
|
||||||
|
final userId = indexController.currentUser[0]["user"]["id"];
|
||||||
|
final token = indexController.currentUser[0]["token"];
|
||||||
|
final teamName = indexController.currentUser[0]["user"]['team_name'];
|
||||||
|
final eventCode = indexController.currentUser[0]["user"]["event_code"];
|
||||||
|
//final cpNumber = destinationController.currentDestinationFeature[0].cp;
|
||||||
|
final cpNumber = widget.destination.cp;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse('https://rogaining.sumasen.net/api/activate_buy_point/'),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Token $token',
|
||||||
|
},
|
||||||
|
body: jsonEncode({
|
||||||
|
'user_id': userId,
|
||||||
|
'team_name': teamName,
|
||||||
|
'event_code': eventCode,
|
||||||
|
'cp_number': cpNumber,
|
||||||
|
'qr_code': qrCode,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
Get.snackbar('成功', 'お買い物ポイントが有効化されました');
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
} else {
|
||||||
|
Get.snackbar('エラー', 'お買い物ポイントの有効化に失敗しました');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Get.snackbar('エラー', 'ネットワークエラーが発生しました');
|
||||||
|
} finally {
|
||||||
|
isQRScanned = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
controller?.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class QRCodeScannerPage extends StatefulWidget {
|
class QRCodeScannerPage extends StatefulWidget {
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import 'package:rogapp/model/gps_data.dart';
|
|||||||
import 'package:rogapp/pages/camera/camera_page.dart';
|
import 'package:rogapp/pages/camera/camera_page.dart';
|
||||||
import 'package:rogapp/pages/camera/custom_camera_view.dart';
|
import 'package:rogapp/pages/camera/custom_camera_view.dart';
|
||||||
import 'package:rogapp/pages/index/index_controller.dart';
|
import 'package:rogapp/pages/index/index_controller.dart';
|
||||||
|
import 'package:rogapp/pages/team/team_controller.dart';
|
||||||
import 'package:rogapp/routes/app_pages.dart';
|
import 'package:rogapp/routes/app_pages.dart';
|
||||||
import 'package:rogapp/services/DatabaseService.dart';
|
import 'package:rogapp/services/DatabaseService.dart';
|
||||||
import 'package:rogapp/services/destination_service.dart';
|
import 'package:rogapp/services/destination_service.dart';
|
||||||
@ -44,6 +45,7 @@ import 'package:rogapp/pages/permission/permission.dart';
|
|||||||
class DestinationController extends GetxController {
|
class DestinationController extends GetxController {
|
||||||
late LocationSettings locationSettings; // 位置情報の設定を保持する変数です。
|
late LocationSettings locationSettings; // 位置情報の設定を保持する変数です。
|
||||||
|
|
||||||
|
//late TeamController teamController = TeamController();
|
||||||
//Timer? _GPStimer; // GPSタイマーを保持する変数です。
|
//Timer? _GPStimer; // GPSタイマーを保持する変数です。
|
||||||
|
|
||||||
var destinationCount = 0.obs; // 目的地の数を保持するReactive変数です。
|
var destinationCount = 0.obs; // 目的地の数を保持するReactive変数です。
|
||||||
@ -161,11 +163,11 @@ class DestinationController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
//==== Akira .. GPS信号シミュレーション用 ======= ここまで
|
//==== Akira .. GPS信号シミュレーション用 ======= ここまで
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
// ルートをクリアする関数です。
|
// ルートをクリアする関数です。
|
||||||
void clearRoute() {
|
void clearRoute() {
|
||||||
indexController.routePoints.clear();
|
indexController.routePoints.clear();
|
||||||
@ -429,7 +431,7 @@ class DestinationController extends GetxController {
|
|||||||
debugPrint("* 目的地がない場合 ==> 検知半径=-1の場合");
|
debugPrint("* 目的地がない場合 ==> 検知半径=-1の場合");
|
||||||
|
|
||||||
// print("----- in location popup cp - ${d.cp}----");
|
// print("----- in location popup cp - ${d.cp}----");
|
||||||
if ((d.cp == -1 || d.cp==0 ) && DateTime.now().difference(lastGoalAt).inHours >= 24) {
|
if ((d.cp == -1 || d.cp==0 ) && DateTime.now().difference(lastGoalAt).inHours >= 10) {
|
||||||
debugPrint("**1: 開始CPで、最後にゴールしてから24時間経過していれば、");
|
debugPrint("**1: 開始CPで、最後にゴールしてから24時間経過していれば、");
|
||||||
|
|
||||||
chekcs = 1;
|
chekcs = 1;
|
||||||
@ -671,11 +673,11 @@ class DestinationController extends GetxController {
|
|||||||
|
|
||||||
} else if (isInRog.value == false &&
|
} else if (isInRog.value == false &&
|
||||||
indexController.rogMode.value == 1 &&
|
indexController.rogMode.value == 1 &&
|
||||||
DateTime.now().difference(lastGoalAt).inHours >= 24) {
|
DateTime.now().difference(lastGoalAt).inHours >= 10) {
|
||||||
//start
|
//start
|
||||||
//print("---- in start -----");
|
//print("---- in start -----");
|
||||||
|
|
||||||
debugPrint("**5 スタートの場合で最後のゴールから24時間経過している場合");
|
debugPrint("**5 スタートの場合で最後のゴールから10時間経過している場合");
|
||||||
|
|
||||||
|
|
||||||
chekcs = 6; // start point
|
chekcs = 6; // start point
|
||||||
@ -823,7 +825,7 @@ class DestinationController extends GetxController {
|
|||||||
//print("is rog ---- ${is_in_rog.value} ----");
|
//print("is rog ---- ${is_in_rog.value} ----");
|
||||||
if (d.hidden_location != null &&
|
if (d.hidden_location != null &&
|
||||||
d.hidden_location == 0 &&
|
d.hidden_location == 0 &&
|
||||||
isInRog.value == true &&
|
(isInRog.value == true || (d.buy_point != null && d.buy_point! > 0)) &&
|
||||||
d.cp != -1 && d.cp != 0 && d.cp != -2) {
|
d.cp != -1 && d.cp != 0 && d.cp != -2) {
|
||||||
chekcs = 3;
|
chekcs = 3;
|
||||||
photos.clear();
|
photos.clear();
|
||||||
@ -1028,7 +1030,7 @@ class DestinationController extends GetxController {
|
|||||||
print("An error occurred: $e");
|
print("An error occurred: $e");
|
||||||
// await checkForCheckin();
|
// await checkForCheckin();
|
||||||
} finally {
|
} finally {
|
||||||
await Future.delayed(const Duration(seconds: 5)); // 一定時間待機してから再帰呼び出し
|
await Future.delayed(const Duration(seconds: 1)); // 一定時間待機してから再帰呼び出し
|
||||||
//print("--- End of checkForCheckin function, calling recursively ---");
|
//print("--- End of checkForCheckin function, calling recursively ---");
|
||||||
unawaited( checkForCheckin() );
|
unawaited( checkForCheckin() );
|
||||||
}
|
}
|
||||||
@ -1156,9 +1158,13 @@ class DestinationController extends GetxController {
|
|||||||
//await _saveImageFromPath(imageurl);
|
//await _saveImageFromPath(imageurl);
|
||||||
await _saveImageToGallery(imageurl);
|
await _saveImageToGallery(imageurl);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (indexController.currentUser.isNotEmpty) {
|
if (indexController.currentUser.isNotEmpty) {
|
||||||
double cpNum = destination.cp!;
|
double cpNum = destination.cp!;
|
||||||
|
|
||||||
|
//int teamId = indexController.teamId.value; // teamIdを使用
|
||||||
|
|
||||||
int userId = indexController.currentUser[0]["user"]["id"];
|
int userId = indexController.currentUser[0]["user"]["id"];
|
||||||
//print("--- Pressed -----");
|
//print("--- Pressed -----");
|
||||||
String team = indexController.currentUser[0]["user"]['team_name'];
|
String team = indexController.currentUser[0]["user"]['team_name'];
|
||||||
@ -1222,6 +1228,11 @@ class DestinationController extends GetxController {
|
|||||||
if (indexController.currentUser.isNotEmpty) {
|
if (indexController.currentUser.isNotEmpty) {
|
||||||
double cpNum = destination.cp!;
|
double cpNum = destination.cp!;
|
||||||
|
|
||||||
|
//int teamId = indexController.teamId.value; // teamIdを使用
|
||||||
|
//Team team0 = teamController.teams[0];
|
||||||
|
//print("team={team0}");
|
||||||
|
|
||||||
|
|
||||||
int userId = indexController.currentUser[0]["user"]["id"];
|
int userId = indexController.currentUser[0]["user"]["id"];
|
||||||
//print("--- Pressed -----");
|
//print("--- Pressed -----");
|
||||||
String team = indexController.currentUser[0]["user"]['team_name'];
|
String team = indexController.currentUser[0]["user"]['team_name'];
|
||||||
@ -1238,7 +1249,7 @@ class DestinationController extends GetxController {
|
|||||||
// print("------ checkin event $eventCode ------");
|
// print("------ checkin event $eventCode ------");
|
||||||
ExternalService()
|
ExternalService()
|
||||||
.makeCheckpoint(
|
.makeCheckpoint(
|
||||||
userId,
|
userId, // teamIdを使用
|
||||||
token,
|
token,
|
||||||
formattedDate,
|
formattedDate,
|
||||||
team,
|
team,
|
||||||
@ -1628,7 +1639,7 @@ class DestinationController extends GetxController {
|
|||||||
// 地図のイベントリスナーを設定
|
// 地図のイベントリスナーを設定
|
||||||
indexController.mapController.mapEventStream.listen((MapEvent mapEvent) {
|
indexController.mapController.mapEventStream.listen((MapEvent mapEvent) {
|
||||||
if (mapEvent is MapEventMoveEnd) {
|
if (mapEvent is MapEventMoveEnd) {
|
||||||
indexController.loadLocationsBound();
|
indexController.loadLocationsBound(indexController.currentUser[0]["user"]["event_code"]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1653,7 +1664,7 @@ class DestinationController extends GetxController {
|
|||||||
);
|
);
|
||||||
indexController.currentBound.clear();
|
indexController.currentBound.clear();
|
||||||
indexController.currentBound.add(bnds);
|
indexController.currentBound.add(bnds);
|
||||||
indexController.loadLocationsBound();
|
indexController.loadLocationsBound(indexController.currentUser[0]["user"]["event_code"]);
|
||||||
centerMapToCurrentLocation();
|
centerMapToCurrentLocation();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1716,6 +1727,8 @@ class DestinationController extends GetxController {
|
|||||||
//print('----- %%%%%%%%%%%%%%%%%%%%% ----- $val');
|
//print('----- %%%%%%%%%%%%%%%%%%%%% ----- $val');
|
||||||
Map<String, dynamic> res = {};
|
Map<String, dynamic> res = {};
|
||||||
if (val == "wifi" || val == "mobile") {
|
if (val == "wifi" || val == "mobile") {
|
||||||
|
//int teamId = indexController.teamId.value; // teamIdを使用
|
||||||
|
|
||||||
String token = indexController.currentUser[0]["token"];
|
String token = indexController.currentUser[0]["token"];
|
||||||
DatabaseHelper db = DatabaseHelper.instance;
|
DatabaseHelper db = DatabaseHelper.instance;
|
||||||
db.allRogianing().then((value) {
|
db.allRogianing().then((value) {
|
||||||
@ -1725,7 +1738,7 @@ class DestinationController extends GetxController {
|
|||||||
} else if (e.rog_action_type == 1) {
|
} else if (e.rog_action_type == 1) {
|
||||||
var datetime = DateTime.fromMicrosecondsSinceEpoch(e.checkintime!);
|
var datetime = DateTime.fromMicrosecondsSinceEpoch(e.checkintime!);
|
||||||
res = await ExternalService().makeCheckpoint(
|
res = await ExternalService().makeCheckpoint(
|
||||||
e.user_id!,
|
e.user_id!, // teamId???
|
||||||
token,
|
token,
|
||||||
getFormatedTime(datetime),
|
getFormatedTime(datetime),
|
||||||
e.team_name!,
|
e.team_name!,
|
||||||
@ -1735,7 +1748,7 @@ class DestinationController extends GetxController {
|
|||||||
} else if (e.rog_action_type == 2) {
|
} else if (e.rog_action_type == 2) {
|
||||||
var datetime = DateTime.fromMicrosecondsSinceEpoch(e.checkintime!);
|
var datetime = DateTime.fromMicrosecondsSinceEpoch(e.checkintime!);
|
||||||
res = await ExternalService().makeGoal(
|
res = await ExternalService().makeGoal(
|
||||||
e.user_id!,
|
e.user_id!, // // teamId???
|
||||||
token,
|
token,
|
||||||
e.team_name!,
|
e.team_name!,
|
||||||
e.image!,
|
e.image!,
|
||||||
|
|||||||
@ -170,7 +170,7 @@ class DestinationMapPage extends StatelessWidget {
|
|||||||
indexController.currentBound.clear();
|
indexController.currentBound.clear();
|
||||||
indexController.currentBound.add(bounds);
|
indexController.currentBound.add(bounds);
|
||||||
if (indexController.currentUser.isEmpty) {
|
if (indexController.currentUser.isEmpty) {
|
||||||
indexController.loadLocationsBound();
|
indexController.loadLocationsBound(indexController.currentUser[0]["user"]["event_code"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -79,6 +79,40 @@ class DrawerPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.group),
|
||||||
|
title: Text('チーム管理'),
|
||||||
|
onTap: () {
|
||||||
|
Get.back();
|
||||||
|
Get.toNamed(AppPages.TEAM_LIST);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.event),
|
||||||
|
title: Text('エントリー管理'),
|
||||||
|
onTap: () {
|
||||||
|
Get.back();
|
||||||
|
Get.toNamed(AppPages.ENTRY_LIST);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.event),
|
||||||
|
title: Text('イベント参加'),
|
||||||
|
onTap: () {
|
||||||
|
Get.back(); // ドロワーを閉じる
|
||||||
|
Get.toNamed(AppPages.EVENT_ENTRY);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.person),
|
||||||
|
title: Text("個人情報の修正"),
|
||||||
|
onTap: () {
|
||||||
|
Get.back(); // Close the drawer
|
||||||
|
Get.toNamed(AppPages.USER_DETAILS_EDIT);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
Obx(() => indexController.currentUser.isEmpty
|
Obx(() => indexController.currentUser.isEmpty
|
||||||
? ListTile(
|
? ListTile(
|
||||||
leading: const Icon(Icons.login),
|
leading: const Icon(Icons.login),
|
||||||
@ -206,22 +240,7 @@ class DrawerPage extends StatelessWidget {
|
|||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
),
|
),
|
||||||
ListTile(
|
|
||||||
leading: Icon(Icons.group),
|
|
||||||
title: Text('チーム管理'),
|
|
||||||
onTap: () {
|
|
||||||
Get.back();
|
|
||||||
Get.toNamed(AppPages.TEAM_LIST);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: Icon(Icons.event),
|
|
||||||
title: Text('エントリー管理'),
|
|
||||||
onTap: () {
|
|
||||||
Get.back();
|
|
||||||
Get.toNamed(AppPages.ENTRY_LIST);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.privacy_tip),
|
leading: const Icon(Icons.privacy_tip),
|
||||||
title: Text("privacy".tr),
|
title: Text("privacy".tr),
|
||||||
@ -236,16 +255,18 @@ class DrawerPage extends StatelessWidget {
|
|||||||
Get.back(); // ドロワーを閉じる
|
Get.back(); // ドロワーを閉じる
|
||||||
Get.toNamed(Routes.SETTINGS);
|
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(
|
// ListTile(
|
||||||
// leading: const Icon(Icons.router),
|
// leading: const Icon(Icons.router),
|
||||||
// title: Text("my_route".tr),
|
// title: Text("my_route".tr),
|
||||||
|
|||||||
@ -8,6 +8,10 @@ import 'package:rogapp/model/team.dart';
|
|||||||
import 'package:rogapp/model/category.dart';
|
import 'package:rogapp/model/category.dart';
|
||||||
import 'package:rogapp/services/api_service.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 {
|
class EntryController extends GetxController {
|
||||||
late ApiService _apiService;
|
late ApiService _apiService;
|
||||||
|
|
||||||
@ -69,6 +73,7 @@ class EntryController extends GetxController {
|
|||||||
|
|
||||||
|
|
||||||
void initializeEditMode(Entry entry) {
|
void initializeEditMode(Entry entry) {
|
||||||
|
currentEntry.value = entry;
|
||||||
selectedEvent.value = entry.event;
|
selectedEvent.value = entry.event;
|
||||||
selectedTeam.value = entry.team;
|
selectedTeam.value = entry.team;
|
||||||
selectedCategory.value = entry.category;
|
selectedCategory.value = entry.category;
|
||||||
@ -89,8 +94,10 @@ class EntryController extends GetxController {
|
|||||||
|
|
||||||
void updateTeam(Team? value) => selectedTeam.value = value;
|
void updateTeam(Team? value) => selectedTeam.value = value;
|
||||||
void updateCategory(NewCategory? value) => selectedCategory.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){
|
void updateDate(DateTime value){
|
||||||
selectedDate.value = DateFormat('yyyy-MM-dd').format(value!) as DateTime?;
|
selectedDate.value = DateFormat('yyyy-MM-dd').format(value!) as DateTime?;
|
||||||
@ -154,28 +161,51 @@ class EntryController extends GetxController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
|
// Get zekken number
|
||||||
|
final updatedCategory = await _apiService.getZekkenNumber(selectedCategory.value!.id);
|
||||||
|
final zekkenNumber = updatedCategory.categoryNumber.toString();
|
||||||
|
|
||||||
final newEntry = await _apiService.createEntry(
|
final newEntry = await _apiService.createEntry(
|
||||||
selectedTeam.value!.id,
|
selectedTeam.value!.id,
|
||||||
selectedEvent.value!.id,
|
selectedEvent.value!.id,
|
||||||
selectedCategory.value!.id,
|
selectedCategory.value!.id,
|
||||||
selectedDate.value!,
|
selectedDate.value!,
|
||||||
|
zekkenNumber,
|
||||||
);
|
);
|
||||||
entries.add(newEntry);
|
entries.add(newEntry);
|
||||||
Get.back();
|
Get.back();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error creating entry: $e');
|
print('Error creating entry: $e');
|
||||||
Get.snackbar('Error', 'Failed to create entry');
|
Get.snackbar('Error', 'Failed to create entry');
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateEntryAndRefreshMap() async {
|
||||||
|
await updateEntry();
|
||||||
|
|
||||||
|
// エントリーが正常に更新された後、マップをリフレッシュ
|
||||||
|
final indexController = Get.find<IndexController>();
|
||||||
|
final eventCode = currentEntry.value?.event.eventName ?? '';
|
||||||
|
indexController.reloadMap(eventCode);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> updateEntry() async {
|
Future<void> updateEntry() async {
|
||||||
if (currentEntry.value == null) return;
|
if (currentEntry.value == null) {
|
||||||
|
Get.snackbar('Error', 'No entry selected for update');
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
final updatedEntry = await _apiService.updateEntry(
|
final updatedEntry = await _apiService.updateEntry(
|
||||||
currentEntry.value!.id,
|
currentEntry.value!.id,
|
||||||
|
currentEntry.value!.team.id,
|
||||||
selectedEvent.value!.id,
|
selectedEvent.value!.id,
|
||||||
selectedCategory.value!.id,
|
selectedCategory.value!.id,
|
||||||
selectedDate.value!,
|
selectedDate.value!,
|
||||||
|
currentEntry.value!.zekkenNumber,
|
||||||
);
|
);
|
||||||
final index = entries.indexWhere((entry) => entry.id == updatedEntry.id);
|
final index = entries.indexWhere((entry) => entry.id == updatedEntry.id);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
@ -185,18 +215,26 @@ class EntryController extends GetxController {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error updating entry: $e');
|
print('Error updating entry: $e');
|
||||||
Get.snackbar('Error', 'Failed to update entry');
|
Get.snackbar('Error', 'Failed to update entry');
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteEntry() async {
|
Future<void> deleteEntry() async {
|
||||||
if (currentEntry.value == null) return;
|
if (currentEntry.value == null) {
|
||||||
|
Get.snackbar('Error', 'No entry selected for deletion');
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
await _apiService.deleteEntry(currentEntry.value!.id);
|
await _apiService.deleteEntry(currentEntry.value!.id);
|
||||||
entries.removeWhere((entry) => entry.id == currentEntry.value!.id);
|
entries.removeWhere((entry) => entry.id == currentEntry.value!.id);
|
||||||
Get.back();
|
Get.back();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error deleting entry: $e');
|
print('Error deleting entry: $e');
|
||||||
Get.snackbar('Error', 'Failed to delete entry');
|
Get.snackbar('Error', 'Failed to delete entry');
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,9 @@ import 'package:rogapp/model/category.dart';
|
|||||||
import 'package:rogapp/model/team.dart';
|
import 'package:rogapp/model/team.dart';
|
||||||
import 'package:intl/intl.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<EntryController> {
|
class EntryDetailPage extends GetView<EntryController> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -70,7 +73,7 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
title: Text('日付'),
|
title: Text('日付'),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
controller.selectedDate.value != null
|
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 {
|
onTap: () async {
|
||||||
@ -78,33 +81,66 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
Get.snackbar('Error', 'Please select an event first');
|
Get.snackbar('Error', 'Please select an event first');
|
||||||
return;
|
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 tz.TZDateTime firstDate = now.isAfter(eventStart) ? now : eventStart;
|
||||||
|
|
||||||
final DateTime? picked = await showDatePicker(
|
final DateTime? picked = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: controller.selectedDate.value ?? controller.selectedEvent.value!.startDatetime,
|
initialDate: initialDate.isAfter(firstDate) ? initialDate : firstDate,
|
||||||
firstDate: controller.selectedEvent.value!.startDatetime,
|
firstDate: firstDate,
|
||||||
lastDate: controller.selectedEvent.value!.endDatetime,
|
lastDate: eventEnd,
|
||||||
);
|
);
|
||||||
if (picked != null) {
|
if (picked != null) {
|
||||||
controller.updateDate(picked);
|
controller.updateDate(tz.TZDateTime.from(picked, tz.getLocation('Asia/Tokyo')));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 32),
|
SizedBox(height: 32),
|
||||||
ElevatedButton(
|
if (mode == 'new')
|
||||||
child: Text(mode == 'new' ? 'エントリーを作成' : 'エントリーを更新'),
|
|
||||||
onPressed: () {
|
|
||||||
if (mode == 'new') {
|
|
||||||
controller.createEntry();
|
|
||||||
} else {
|
|
||||||
controller.updateEntry();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (mode == 'edit' && controller.isOwner())
|
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
child: Text('エントリーを削除'),
|
child: Text('エントリーを作成'),
|
||||||
onPressed: () => controller.deleteEntry(),
|
onPressed: () => controller.createEntry(),
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
minimumSize: Size(double.infinity, 50),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Text('エントリーを削除'),
|
||||||
|
onPressed: () => controller.deleteEntry(),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
minimumSize: Size(0, 50),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: Text('エントリーを更新'),
|
||||||
|
onPressed: () => controller.updateEntry(),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.lightBlue,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
minimumSize: Size(0, 50),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:rogapp/pages/entry/entry_controller.dart';
|
import 'package:rogapp/pages/entry/entry_controller.dart';
|
||||||
import 'package:rogapp/routes/app_pages.dart';
|
import 'package:rogapp/routes/app_pages.dart';
|
||||||
|
import 'package:timezone/timezone.dart' as tz;
|
||||||
|
|
||||||
class EntryListPage extends GetView<EntryController> {
|
class EntryListPage extends GetView<EntryController> {
|
||||||
@override
|
@override
|
||||||
@ -18,18 +20,93 @@ class EntryListPage extends GetView<EntryController> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Obx(() => ListView.builder(
|
body: Obx(() {
|
||||||
itemCount: controller.entries.length,
|
if (controller.entries.isEmpty) {
|
||||||
itemBuilder: (context, index) {
|
return Center(
|
||||||
final entry = controller.entries[index];
|
child: Text('表示するエントリーがありません。'),
|
||||||
return ListTile(
|
|
||||||
title: Text(entry.event?.eventName ?? 'イベント未設定'),
|
|
||||||
subtitle: Text('${entry.team?.teamName ?? 'チーム未設定'} - ${entry.category?.categoryName ?? 'カテゴリ未設定'}'),
|
|
||||||
trailing: Text(entry.date?.toString().substring(0, 10) ?? '日付未設定'),
|
|
||||||
onTap: () => Get.toNamed(AppPages.ENTRY_DETAIL, arguments: {'mode': 'edit', 'entry': entry}),
|
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
)),
|
return ListView.builder(
|
||||||
|
itemCount: controller.entries.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final entry = controller.entries[index];
|
||||||
|
return ListTile(
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text('${_formatDate(entry.date)}: ${entry.event.eventName}'),
|
||||||
|
),
|
||||||
|
Text(entry.team.teamName, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subtitle: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text('カテゴリー: ${entry.category.categoryName}'),
|
||||||
|
),
|
||||||
|
Text('ゼッケン: ${entry.zekkenNumber ?? "未設定"}'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () =>
|
||||||
|
Get.toNamed(AppPages.ENTRY_DETAIL,
|
||||||
|
arguments: {'mode': 'edit', 'entry': entry}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _formatDate(DateTime? date) {
|
||||||
|
if (date == null) {
|
||||||
|
return '日時未設定';
|
||||||
|
}
|
||||||
|
final jstDate = tz.TZDateTime.from(date, tz.getLocation('Asia/Tokyo'));
|
||||||
|
return DateFormat('yyyy-MM-dd').format(jstDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EntryListPage_old extends GetView<EntryController> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
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.isLoading.value) {
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
// エントリーを日付昇順にソート
|
||||||
|
final sortedEntries = controller.entries.toList()
|
||||||
|
..sort((a, b) => (a.date ?? DateTime(0)).compareTo(b.date ?? DateTime(0)));
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: sortedEntries.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final entry = sortedEntries[index];
|
||||||
|
return ListTile(
|
||||||
|
title: Text(entry.event?.eventName ?? 'イベント未設定'),
|
||||||
|
subtitle: Text(
|
||||||
|
'${entry.team?.teamName ?? 'チーム未設定'} - ${entry.category
|
||||||
|
?.categoryName ?? 'カテゴリ未設定'}'),
|
||||||
|
trailing: Text(
|
||||||
|
entry.date?.toString().substring(0, 10) ?? '日付未設定'),
|
||||||
|
onTap: () =>
|
||||||
|
Get.toNamed(AppPages.ENTRY_DETAIL,
|
||||||
|
arguments: {'mode': 'edit', 'entry': entry}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
9
lib/pages/entry/event_entries_binding.dart
Normal file
9
lib/pages/entry/event_entries_binding.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rogapp/pages/entry/event_entries_controller.dart';
|
||||||
|
|
||||||
|
class EventEntriesBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
Get.lazyPut<EventEntriesController>(() => EventEntriesController());
|
||||||
|
}
|
||||||
|
}
|
||||||
129
lib/pages/entry/event_entries_controller.dart
Normal file
129
lib/pages/entry/event_entries_controller.dart
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rogapp/model/entry.dart';
|
||||||
|
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<ApiService>();
|
||||||
|
final IndexController _indexController = Get.find<IndexController>();
|
||||||
|
late final DestinationController _destinationController;
|
||||||
|
|
||||||
|
final entries = <Entry>[].obs;
|
||||||
|
final filteredEntries = <Entry>[].obs;
|
||||||
|
final showTodayEntries = true.obs;
|
||||||
|
|
||||||
|
static bool _timezoneInitialized = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
_initializeTimezone();
|
||||||
|
// DestinationControllerが登録されていない場合に備えて、lazyPutを使用
|
||||||
|
Get.lazyPut<DestinationController>(() => DestinationController(), fenix: true);
|
||||||
|
_destinationController = Get.find<DestinationController>();
|
||||||
|
|
||||||
|
fetchEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initializeTimezone() {
|
||||||
|
if (!_timezoneInitialized) {
|
||||||
|
tz.initializeTimeZones();
|
||||||
|
_timezoneInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetchEntries() async {
|
||||||
|
try {
|
||||||
|
final fetchedEntries = await _apiService.getEntries();
|
||||||
|
entries.assignAll(fetchedEntries);
|
||||||
|
filterEntries();
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching entries: $e');
|
||||||
|
// エラー処理を追加
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void filterEntries() {
|
||||||
|
if (showTodayEntries.value) {
|
||||||
|
filterEntriesForToday();
|
||||||
|
} else {
|
||||||
|
filteredEntries.assignAll(entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 &&
|
||||||
|
entry.date?.month == now.month &&
|
||||||
|
entry.date?.day == now.day
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggleShowTodayEntries() {
|
||||||
|
showTodayEntries.toggle();
|
||||||
|
filterEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshMap() {
|
||||||
|
final tk = _indexController.currentUser[0]["token"];
|
||||||
|
if (tk != null) {
|
||||||
|
|
||||||
|
_destinationController.fixMapBound(tk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> joinEvent(Entry entry) async {
|
||||||
|
//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);
|
||||||
|
|
||||||
|
final userid = _indexController.currentUser[0]["user"]["id"];
|
||||||
|
|
||||||
|
await _apiService.updateUserInfo(userid,entry);
|
||||||
|
|
||||||
|
_indexController.currentUser[0]["user"]["event_date"] = entryDate; // 追加2024-8-9
|
||||||
|
_indexController.currentUser[0]["user"]["event_code"] = entry.event.eventName;
|
||||||
|
_indexController.currentUser[0]["user"]["team_name"] = entry.team.teamName;
|
||||||
|
_indexController.currentUser[0]["user"]["group"] = entry.team.category.categoryName;
|
||||||
|
_indexController.currentUser[0]["user"]["zekken_number"] = entry.zekkenNumber;
|
||||||
|
|
||||||
|
Get.back(); // エントリー一覧ページを閉じる
|
||||||
|
//_indexController.isLoading.value = true;
|
||||||
|
_indexController.reloadMap(entry.event.eventName);
|
||||||
|
|
||||||
|
refreshMap();
|
||||||
|
|
||||||
|
if (isToday) {
|
||||||
|
Get.snackbar('成功', 'イベントに参加しました。',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
colorText: Colors.white);
|
||||||
|
} else {
|
||||||
|
Get.snackbar('参照モード', '過去または未来のイベントを参照しています。ロゲの開始やチェックインはできません。',
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
backgroundColor: Colors.orange,
|
||||||
|
colorText: Colors.white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
104
lib/pages/entry/event_entries_page.dart
Normal file
104
lib/pages/entry/event_entries_page.dart
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
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<EventEntriesController> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: Text('イベント参加')),
|
||||||
|
body: Obx(() => ListView.builder(
|
||||||
|
itemCount: controller.entries.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final entry = controller.entries[index];
|
||||||
|
return ListTile(
|
||||||
|
title: Text(entry.event.eventName),
|
||||||
|
subtitle: Text('${entry.category.categoryName} - ${entry.date}'),
|
||||||
|
onTap: () => controller.joinEvent(entry),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EventEntriesPage extends GetView<EventEntriesController> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Obx(() => Text(controller.showTodayEntries.value ? 'イベント参加' : 'イベント参照')),
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Obx(() => Text(
|
||||||
|
controller.showTodayEntries.value ? '本日のエントリー' : 'すべてのエントリー',
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
|
)),
|
||||||
|
Obx(() => Switch(
|
||||||
|
value: !controller.showTodayEntries.value,
|
||||||
|
onChanged: (value) {
|
||||||
|
controller.toggleShowTodayEntries();
|
||||||
|
},
|
||||||
|
activeColor: Colors.blue,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() {
|
||||||
|
if (controller.filteredEntries.isEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: Text('表示するエントリーがありません。'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: controller.filteredEntries.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final entry = controller.filteredEntries[index];
|
||||||
|
return ListTile(
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text('${_formatDate(entry.date)}: ${entry.event.eventName}'),
|
||||||
|
),
|
||||||
|
Text(entry.team.teamName, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subtitle: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text('カテゴリー: ${entry.category.categoryName}'),
|
||||||
|
),
|
||||||
|
Text('ゼッケン: ${entry.zekkenNumber ?? "未設定"}'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
await controller.joinEvent(entry);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDate(DateTime? date) {
|
||||||
|
if (date == null) {
|
||||||
|
return '日時未設定';
|
||||||
|
}
|
||||||
|
final jstDate = tz.TZDateTime.from(date, tz.getLocation('Asia/Tokyo'));
|
||||||
|
return DateFormat('yyyy-MM-dd').format(jstDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -10,7 +10,9 @@ import 'package:geolocator/geolocator.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:rogapp/model/destination.dart';
|
import 'package:rogapp/model/destination.dart';
|
||||||
|
import 'package:rogapp/model/entry.dart';
|
||||||
import 'package:rogapp/pages/destination/destination_controller.dart';
|
import 'package:rogapp/pages/destination/destination_controller.dart';
|
||||||
|
import 'package:rogapp/pages/team/team_controller.dart';
|
||||||
import 'package:rogapp/routes/app_pages.dart';
|
import 'package:rogapp/routes/app_pages.dart';
|
||||||
import 'package:rogapp/services/auth_service.dart';
|
import 'package:rogapp/services/auth_service.dart';
|
||||||
import 'package:rogapp/services/location_service.dart';
|
import 'package:rogapp/services/location_service.dart';
|
||||||
@ -19,10 +21,17 @@ import 'package:rogapp/widgets/debug_widget.dart';
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import 'package:rogapp/services/api_service.dart';
|
import 'package:rogapp/services/api_service.dart';
|
||||||
|
import 'package:rogapp/model/user.dart';
|
||||||
|
|
||||||
import '../../main.dart';
|
import 'package:rogapp/model/rog.dart';
|
||||||
|
|
||||||
|
|
||||||
|
import 'package:rogapp/main.dart';
|
||||||
|
|
||||||
|
import 'package:rogapp/widgets/helper_dialog.dart';
|
||||||
|
import 'package:timezone/timezone.dart' as tz;
|
||||||
|
import 'package:timezone/data/latest.dart' as tz;
|
||||||
|
|
||||||
class IndexController extends GetxController with WidgetsBindingObserver {
|
class IndexController extends GetxController with WidgetsBindingObserver {
|
||||||
List<GeoJSONFeatureCollection> locations = <GeoJSONFeatureCollection>[].obs;
|
List<GeoJSONFeatureCollection> locations = <GeoJSONFeatureCollection>[].obs;
|
||||||
List<GeoJSONFeature> currentFeature = <GeoJSONFeature>[].obs;
|
List<GeoJSONFeature> currentFeature = <GeoJSONFeature>[].obs;
|
||||||
@ -59,6 +68,10 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
|
|
||||||
String? userToken;
|
String? userToken;
|
||||||
|
|
||||||
|
//late final ApiService _apiService;
|
||||||
|
final ApiService _apiService = Get.find<ApiService>();
|
||||||
|
final DatabaseHelper _dbHelper = DatabaseHelper.instance;
|
||||||
|
|
||||||
// mode = 0 is map mode, mode = 1 list mode
|
// mode = 0 is map mode, mode = 1 list mode
|
||||||
var mode = 0.obs;
|
var mode = 0.obs;
|
||||||
|
|
||||||
@ -74,11 +87,44 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
String areaDropdownValue = "-1";
|
String areaDropdownValue = "-1";
|
||||||
String cateogory = "-all-";
|
String cateogory = "-all-";
|
||||||
|
|
||||||
|
final selectedEventName = 'add_location'.tr.obs;
|
||||||
|
|
||||||
|
void setSelectedEventName(String eventName) {
|
||||||
|
selectedEventName.value = eventName;
|
||||||
|
}
|
||||||
|
|
||||||
ConnectivityResult connectionStatus = ConnectivityResult.none;
|
ConnectivityResult connectionStatus = ConnectivityResult.none;
|
||||||
var connectionStatusName = "".obs;
|
var connectionStatusName = "".obs;
|
||||||
final Connectivity _connectivity = Connectivity();
|
final Connectivity _connectivity = Connectivity();
|
||||||
late StreamSubscription<ConnectivityResult> _connectivitySubscription;
|
late StreamSubscription<ConnectivityResult> _connectivitySubscription;
|
||||||
|
|
||||||
|
final Rx<DateTime> lastUserUpdateTime = DateTime.now().obs;
|
||||||
|
|
||||||
|
RxInt teamId = RxInt(-1); // チームIDを保存するための変数
|
||||||
|
|
||||||
|
//late TeamController teamController = TeamController();
|
||||||
|
/*
|
||||||
|
void updateUserInfo(Map<String, dynamic> newUserInfo) {
|
||||||
|
currentUser.clear();
|
||||||
|
currentUser.add(newUserInfo);
|
||||||
|
lastUserUpdateTime.value = DateTime.now();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
final isReferenceMode = false.obs;
|
||||||
|
|
||||||
|
void setReferenceMode(bool value) {
|
||||||
|
isReferenceMode.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canStartRoge() {
|
||||||
|
return !isReferenceMode.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canCheckin() {
|
||||||
|
return !isReferenceMode.value;
|
||||||
|
}
|
||||||
|
|
||||||
void toggleMode() {
|
void toggleMode() {
|
||||||
if (mode.value == 0) {
|
if (mode.value == 0) {
|
||||||
mode += 1;
|
mode += 1;
|
||||||
@ -196,12 +242,16 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
|
|
||||||
print('IndexController onInit called'); // デバッグ用の出力を追加
|
print('IndexController onInit called'); // デバッグ用の出力を追加
|
||||||
|
|
||||||
|
tz.initializeTimeZones();
|
||||||
|
//teamController = Get.find<TeamController>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initializeApiService() async {
|
Future<void> initializeApiService() async {
|
||||||
if (currentUser.isNotEmpty) {
|
if (currentUser.isNotEmpty) {
|
||||||
// 既にログインしている場合
|
// 既にログインしている場合
|
||||||
await Get.putAsync(() => ApiService().init());
|
await Get.putAsync(() => ApiService().init());
|
||||||
|
//await Get.putAsync(() => ApiService().init());
|
||||||
// 必要に応じて追加の初期化処理
|
// 必要に応じて追加の初期化処理
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,6 +388,12 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
// ログイン成功後、api_serviceを初期化
|
// ログイン成功後、api_serviceを初期化
|
||||||
await Get.putAsync(() => ApiService().init());
|
await Get.putAsync(() => ApiService().init());
|
||||||
|
|
||||||
|
// ユーザー情報の完全性をチェック
|
||||||
|
if (await checkUserInfoComplete()) {
|
||||||
|
Get.offAllNamed(AppPages.INDEX);
|
||||||
|
} else {
|
||||||
|
Get.offAllNamed(AppPages.USER_DETAILS_EDIT);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
logManager.addOperationLog("User failed login : ${email} , ${password}.");
|
logManager.addOperationLog("User failed login : ${email} , ${password}.");
|
||||||
@ -357,6 +413,14 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> checkUserInfoComplete() async {
|
||||||
|
final user = await ApiService.to.getCurrentUser();
|
||||||
|
return user.firstname.isNotEmpty &&
|
||||||
|
user.lastname.isNotEmpty &&
|
||||||
|
user.dateOfBirth != null &&
|
||||||
|
user.female != null;
|
||||||
|
}
|
||||||
|
|
||||||
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
|
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
|
||||||
//
|
//
|
||||||
void changePassword(
|
void changePassword(
|
||||||
@ -429,32 +493,44 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
|
|
||||||
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
|
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
|
||||||
//
|
//
|
||||||
void register(String email, String password, BuildContext context) {
|
void register(String email, String password, String password2, BuildContext context) {
|
||||||
AuthService.register(email, password).then((value) {
|
AuthService.register(email, password,password2).then((value) {
|
||||||
if (value.isNotEmpty) {
|
if (value is Map && value.containsKey("error")) {
|
||||||
debugPrint("ユーザー登録成功:${email}, ${password}");
|
String err_message = value["error"];
|
||||||
logManager.addOperationLog("User tried to register new account : ${email} , ${password} .");
|
debugPrint("ユーザー登録失敗:${email}, ${password},${err_message}");
|
||||||
|
logManager.addOperationLog("User failed to register new account : ${email} , ${password} ,${err_message}.");
|
||||||
currentUser.clear();
|
|
||||||
currentUser.add(value);
|
|
||||||
isLoading.value = false;
|
|
||||||
Navigator.pop(context);
|
|
||||||
Get.toNamed(AppPages.INDEX);
|
|
||||||
} else {
|
|
||||||
debugPrint("ユーザー登録失敗:${email}, ${password}");
|
|
||||||
logManager.addOperationLog("User failed to register new account : ${email} , ${password} .");
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
Get.snackbar(
|
Get.snackbar(
|
||||||
'failed'.tr, // 失敗
|
|
||||||
'user_registration_failed_please_try_again'.tr, // ユーザー登録に失敗しました。
|
'user_registration_failed_please_try_again'.tr, // ユーザー登録に失敗しました。
|
||||||
backgroundColor: Colors.red,
|
err_message,
|
||||||
colorText: Colors.white,
|
backgroundColor: Colors.red,
|
||||||
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue),
|
colorText: Colors.white,
|
||||||
|
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue),
|
||||||
snackPosition: SnackPosition.TOP,
|
snackPosition: SnackPosition.TOP,
|
||||||
duration: const Duration(seconds: 3),
|
duration: const Duration(seconds: 3),
|
||||||
//backgroundColor: Colors.yellow,
|
//backgroundColor: Colors.yellow,
|
||||||
//icon:Image(image:AssetImage("assets/images/dora.png"))
|
//icon:Image(image:AssetImage("assets/images/dora.png"))
|
||||||
);
|
);
|
||||||
|
}else{
|
||||||
|
debugPrint("ユーザー登録成功:${email}, ${password}");
|
||||||
|
logManager.addOperationLog("User tried to register new account : ${email} , ${password} .");
|
||||||
|
|
||||||
|
currentUser.clear();
|
||||||
|
//currentUser.add(value);
|
||||||
|
isLoading.value = false;
|
||||||
|
|
||||||
|
// ユーザー登録成功メッセージを表示
|
||||||
|
Get.snackbar(
|
||||||
|
'success'.tr,
|
||||||
|
'user_registration_successful'.tr,
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
colorText: Colors.white,
|
||||||
|
snackPosition: SnackPosition.TOP,
|
||||||
|
duration: const Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
|
||||||
|
//Navigator.pop(context);
|
||||||
|
Get.toNamed(AppPages.LOGIN);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -485,23 +561,98 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void changeUser(Map<String, dynamic> value, {bool replace = true}) {
|
void changeUser(Map<String, dynamic> value, {bool replace = true}) async{
|
||||||
currentUser.clear();
|
currentUser.clear();
|
||||||
currentUser.add(value);
|
currentUser.add(value);
|
||||||
if (replace) {
|
if (replace) {
|
||||||
saveToDevice(currentUser[0]["token"]);
|
saveToDevice(currentUser[0]["token"]);
|
||||||
}
|
}
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
loadLocationsBound();
|
|
||||||
|
// ユーザーのイベント情報を取得
|
||||||
|
await fetchUserEventInfo();
|
||||||
|
|
||||||
|
loadLocationsBound( currentUser[0]["user"]["event_code"]);
|
||||||
if (currentUser.isNotEmpty) {
|
if (currentUser.isNotEmpty) {
|
||||||
rogMode.value = 0;
|
rogMode.value = 0;
|
||||||
restoreGame();
|
restoreGame();
|
||||||
|
|
||||||
|
// チームデータを取得
|
||||||
|
await fetchTeamData();
|
||||||
} else {
|
} else {
|
||||||
rogMode.value = 1;
|
rogMode.value = 1;
|
||||||
}
|
}
|
||||||
Get.toNamed(AppPages.INDEX);
|
Get.toNamed(AppPages.INDEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> fetchUserEventInfo() async {
|
||||||
|
try {
|
||||||
|
final List<Entry> entries = await _apiService.getEntries();
|
||||||
|
|
||||||
|
if (entries.isNotEmpty) {
|
||||||
|
final Entry latestEntry = entries.last;
|
||||||
|
|
||||||
|
final tokyo = tz.getLocation('Asia/Tokyo');
|
||||||
|
final eventDate = latestEntry.date!.toUtc();
|
||||||
|
//final eventDate = tz.TZDateTime.from(utcDate, tokyo);
|
||||||
|
|
||||||
|
final eventDateOnly = tz.TZDateTime(tokyo, eventDate.year, eventDate.month, eventDate.day);
|
||||||
|
|
||||||
|
currentUser[0]['user']['event_date'] = eventDateOnly.toIso8601String().split('T')[0];
|
||||||
|
currentUser[0]['user']['event_code'] = latestEntry.event.eventName;
|
||||||
|
currentUser[0]['user']['team_name'] = latestEntry.team.teamName;
|
||||||
|
currentUser[0]['user']['group'] = latestEntry.team.category.categoryName;
|
||||||
|
currentUser[0]['user']['zekken_number'] = latestEntry.zekkenNumber;
|
||||||
|
|
||||||
|
// 最後のゴール日時を取得
|
||||||
|
final lastGoalTime = await getLastGoalTime();
|
||||||
|
currentUser[0]['user']['last_goal_time'] = lastGoalTime?.toIso8601String();
|
||||||
|
|
||||||
|
print('Updated user event info: ${currentUser[0]['user']}');
|
||||||
|
} else {
|
||||||
|
print('No entries found for the user');
|
||||||
|
_clearUserEventInfo();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching user event info: $e');
|
||||||
|
_clearUserEventInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DateTime?> getLastGoalTime() async {
|
||||||
|
try {
|
||||||
|
final userId = currentUser[0]['user']['id'];
|
||||||
|
return await _apiService.getLastGoalTime(userId);
|
||||||
|
} catch (e) {
|
||||||
|
print('Error getting last goal time: $e');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _clearUserEventInfo() {
|
||||||
|
currentUser[0]['user']['event_date'] = null;
|
||||||
|
currentUser[0]['user']['event_code'] = null;
|
||||||
|
currentUser[0]['user']['team_name'] = null;
|
||||||
|
currentUser[0]['user']['group'] = null;
|
||||||
|
currentUser[0]['user']['zekken_number'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetchTeamData() async {
|
||||||
|
try {
|
||||||
|
Get.put(TeamController());
|
||||||
|
// \"TeamController\" not found. You need to call \"Get.put(TeamController())\" or \"Get.lazyPut(()=>TeamController())\"
|
||||||
|
final teamController = Get.find<TeamController>();
|
||||||
|
await teamController.fetchTeams();
|
||||||
|
if (teamController.teams.isNotEmpty) {
|
||||||
|
teamId.value = teamController.teams.first.id;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("Error fetching team data: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
loadUserDetailsForToken(String token) async {
|
loadUserDetailsForToken(String token) async {
|
||||||
AuthService.userForToken(token).then((value) {
|
AuthService.userForToken(token).then((value) {
|
||||||
print("----token val-- $value ------");
|
print("----token val-- $value ------");
|
||||||
@ -577,7 +728,7 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
// 要検討:Future.delayedを使用して非同期処理を待たずに先に進むようにしていますが、
|
// 要検討:Future.delayedを使用して非同期処理を待たずに先に進むようにしていますが、
|
||||||
// これによってメモリリークが発生する可能性があります。非同期処理の結果を適切に処理することを検討してください。
|
// これによってメモリリークが発生する可能性があります。非同期処理の結果を適切に処理することを検討してください。
|
||||||
//
|
//
|
||||||
void loadLocationsBound() async {
|
void loadLocationsBound(String eventCode) async {
|
||||||
if (isCustomAreaSelected.value == true) {
|
if (isCustomAreaSelected.value == true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -609,12 +760,13 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
currentBound.clear();
|
currentBound.clear();
|
||||||
currentBound.add(bounds);
|
currentBound.add(bounds);
|
||||||
|
|
||||||
isLoading.value = true; // ローディング状態をtrueに設定
|
//isLoading.value = true; // ローディング状態をtrueに設定
|
||||||
|
|
||||||
//print("bounds --- (${bounds.southWest.latitude},${bounds.southWest.longitude}),(${bounds.northWest.latitude},${bounds.northWest.longitude}),(${bounds.northEast.latitude},${bounds.northEast.longitude}),(${bounds.southEast.latitude},${bounds.southEast.longitude})");
|
//print("bounds --- (${bounds.southWest.latitude},${bounds.southWest.longitude}),(${bounds.northWest.latitude},${bounds.northWest.longitude}),(${bounds.northEast.latitude},${bounds.northEast.longitude}),(${bounds.southEast.latitude},${bounds.southEast.longitude})");
|
||||||
|
|
||||||
// 要検討:APIからのレスポンスがnullの場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
|
// 要検討:APIからのレスポンスがnullの場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
|
||||||
try {
|
try {
|
||||||
|
final eventCode = currentUser[0]["user"]["event_code"];
|
||||||
final value = await LocationService.loadLocationsBound(
|
final value = await LocationService.loadLocationsBound(
|
||||||
bounds.southWest.latitude,
|
bounds.southWest.latitude,
|
||||||
bounds.southWest.longitude,
|
bounds.southWest.longitude,
|
||||||
@ -624,7 +776,8 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
bounds.northEast.longitude,
|
bounds.northEast.longitude,
|
||||||
bounds.southEast.latitude,
|
bounds.southEast.latitude,
|
||||||
bounds.southEast.longitude,
|
bounds.southEast.longitude,
|
||||||
cat
|
cat,
|
||||||
|
eventCode
|
||||||
);
|
);
|
||||||
/*
|
/*
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
@ -728,4 +881,30 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void reloadMap( String eventCode ) {
|
||||||
|
// マップをリロードするロジックを実装
|
||||||
|
// 例: 現在の位置情報を再取得し、マップを更新する
|
||||||
|
loadLocationsBound( eventCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> checkEntryData() async {
|
||||||
|
// エントリーデータの有無をチェックするロジック
|
||||||
|
final teamController = TeamController();
|
||||||
|
bool hasEntryData = teamController.checkIfUserHasEntryData();
|
||||||
|
if (!hasEntryData) {
|
||||||
|
await showHelperDialog(
|
||||||
|
'イベントに参加するには、チーム登録・メンバー登録及びエントリーが必要になります。',
|
||||||
|
'entry_check'
|
||||||
|
);
|
||||||
|
// ドロワーを表示するロジック
|
||||||
|
Get.toNamed('/drawer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCurrentUser(User updatedUser) {
|
||||||
|
currentUser[0]['user'] = updatedUser.toJson();
|
||||||
|
update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,8 +23,66 @@ import 'package:rogapp/utils/location_controller.dart';
|
|||||||
|
|
||||||
// IndexPageクラスは、GetView<IndexController>を継承したStatelessWidgetです。このクラスは、アプリのメインページを表すウィジェットです。
|
// IndexPageクラスは、GetView<IndexController>を継承したStatelessWidgetです。このクラスは、アプリのメインページを表すウィジェットです。
|
||||||
//
|
//
|
||||||
class IndexPage extends GetView<IndexController> {
|
import 'package:rogapp/widgets/helper_dialog.dart';
|
||||||
IndexPage({Key? key}) : super(key: key);
|
|
||||||
|
class IndexPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_IndexPageState createState() => _IndexPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _IndexPageState extends State<IndexPage> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
//checkLoginAndShowDialog();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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<IndexController> {
|
||||||
|
// IndexPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
// IndexControllerとDestinationControllerのインスタンスを取得しています。
|
// IndexControllerとDestinationControllerのインスタンスを取得しています。
|
||||||
//
|
//
|
||||||
@ -46,7 +104,8 @@ class IndexPage extends GetView<IndexController> {
|
|||||||
//
|
//
|
||||||
drawer: DrawerPage(),
|
drawer: DrawerPage(),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("add_location".tr),
|
title: Obx(() => Text(indexController.selectedEventName.value)),
|
||||||
|
//title: Text("add_location".tr),
|
||||||
actions: [
|
actions: [
|
||||||
// IconButton(
|
// IconButton(
|
||||||
// onPressed: () {
|
// onPressed: () {
|
||||||
|
|||||||
@ -2,18 +2,107 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rogapp/pages/index/index_controller.dart';
|
import 'package:rogapp/pages/index/index_controller.dart';
|
||||||
import 'package:rogapp/routes/app_pages.dart';
|
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';
|
||||||
|
|
||||||
// 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。
|
// 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。
|
||||||
// エラーメッセージをローカライズすることを検討してください。
|
// エラーメッセージをローカライズすることを検討してください。
|
||||||
// ログイン処理中にエラーが発生した場合のエラーハンドリングを追加することをお勧めします。
|
// ログイン処理中にエラーが発生した場合のエラーハンドリングを追加することをお勧めします。
|
||||||
//
|
//
|
||||||
class LoginPage extends StatelessWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_LoginPageState createState() => _LoginPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LoginPageState extends State<LoginPage> {
|
||||||
|
//class LoginPage extends StatelessWidget {
|
||||||
final IndexController indexController = Get.find<IndexController>();
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
final ApiService apiService = Get.find<ApiService>();
|
||||||
|
|
||||||
TextEditingController emailController = TextEditingController();
|
TextEditingController emailController = TextEditingController();
|
||||||
TextEditingController passwordController = TextEditingController();
|
TextEditingController passwordController = TextEditingController();
|
||||||
|
bool _obscureText = true;
|
||||||
|
String _version = ''; // バージョン情報を保持する変数
|
||||||
|
|
||||||
LoginPage({Key? key}) : super(key: key);
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
showHelperDialog(
|
||||||
|
'参加するにはユーザー登録が必要です。サインアップからユーザー登録してください。',
|
||||||
|
'login_page'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
_getVersionInfo(); // バージョン情報を取得
|
||||||
|
}
|
||||||
|
|
||||||
|
// バージョン情報を取得するメソッド
|
||||||
|
Future<void> _getVersionInfo() async {
|
||||||
|
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||||
|
setState(() {
|
||||||
|
_version = packageInfo.version;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showResetPasswordDialog() {
|
||||||
|
TextEditingController resetEmailController = TextEditingController();
|
||||||
|
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: Text('パスワードのリセット'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text('パスワードをリセットするメールアドレスを入力してください。'),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
TextField(
|
||||||
|
controller: resetEmailController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'メールアドレス',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('キャンセル'),
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: Text('リセット'),
|
||||||
|
onPressed: () async {
|
||||||
|
if (resetEmailController.text.isNotEmpty) {
|
||||||
|
bool success = await apiService.resetPassword(resetEmailController.text);
|
||||||
|
Get.back();
|
||||||
|
if (success) {
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: Text('パスワードリセット'),
|
||||||
|
content: Text('パスワードリセットメールを送信しました。メールのリンクからパスワードを設定してください。'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('OK'),
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Get.snackbar('エラー', 'パスワードリセットに失敗しました。もう一度お試しください。',
|
||||||
|
snackPosition: SnackPosition.BOTTOM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//LoginPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -25,226 +114,251 @@ class LoginPage extends StatelessWidget {
|
|||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
),
|
),
|
||||||
body: indexController.currentUser.isEmpty
|
body: GestureDetector(
|
||||||
? SizedBox(
|
onTap: () => FocusScope.of(context).unfocus(),
|
||||||
width: double.infinity,
|
child: indexController.currentUser.isEmpty
|
||||||
|
? SizedBox(
|
||||||
child: Column(
|
width: double.infinity,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Container(
|
||||||
children: [
|
height: MediaQuery.of(context).size.height / 6,
|
||||||
Container(
|
decoration: const BoxDecoration(
|
||||||
height: MediaQuery.of(context).size.height / 6,
|
image: DecorationImage(
|
||||||
decoration: const BoxDecoration(
|
image: AssetImage(
|
||||||
image: DecorationImage(
|
'assets/images/login_image.jpg'))),
|
||||||
image: AssetImage(
|
|
||||||
'assets/images/login_image.jpg'))),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 5,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
Padding(
|
const SizedBox(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
height: 5,
|
||||||
child: Column(
|
),
|
||||||
children: [
|
// バージョン情報を表示
|
||||||
makeInput(
|
Text(
|
||||||
label: "email".tr, controller: emailController),
|
'Version: $_version',
|
||||||
makeInput(
|
style: TextStyle(
|
||||||
label: "password".tr,
|
fontSize: 12,
|
||||||
controller: passwordController,
|
color: Colors.grey[600],
|
||||||
obsureText: true),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
],
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
),
|
||||||
child: Container(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 3, left: 3),
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
decoration: BoxDecoration(
|
child: Column(
|
||||||
borderRadius: BorderRadius.circular(40),
|
children: [
|
||||||
),
|
makeInput(
|
||||||
child: Obx(
|
label: "email".tr, controller: emailController),
|
||||||
(() => indexController.isLoading.value == true
|
makePasswordInput(
|
||||||
? MaterialButton(
|
label: "password".tr,
|
||||||
minWidth: double.infinity,
|
controller: passwordController,
|
||||||
height: 60,
|
obscureText: _obscureText,
|
||||||
onPressed: () {},
|
onToggleVisibility: () {
|
||||||
color: Colors.grey[400],
|
setState(() {
|
||||||
shape: RoundedRectangleBorder(
|
_obscureText = !_obscureText;
|
||||||
borderRadius:
|
});
|
||||||
BorderRadius.circular(40)),
|
}),
|
||||||
child: const CircularProgressIndicator(),
|
],
|
||||||
)
|
),
|
||||||
: Column(
|
),
|
||||||
children: [
|
Padding(
|
||||||
MaterialButton(
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
minWidth: double.infinity,
|
child: Container(
|
||||||
height: 40,
|
padding: const EdgeInsets.only(top: 3, left: 3),
|
||||||
onPressed: () async {
|
decoration: BoxDecoration(
|
||||||
if (emailController.text.isEmpty ||
|
borderRadius: BorderRadius.circular(40),
|
||||||
passwordController
|
),
|
||||||
.text.isEmpty) {
|
child: Obx(
|
||||||
Get.snackbar(
|
(() => indexController.isLoading.value == true
|
||||||
"no_values".tr,
|
? MaterialButton(
|
||||||
"email_and_password_required"
|
minWidth: double.infinity,
|
||||||
.tr,
|
height: 60,
|
||||||
backgroundColor: Colors.red,
|
onPressed: () {},
|
||||||
colorText: Colors.white,
|
color: Colors.grey[400],
|
||||||
icon: const Icon(
|
shape: RoundedRectangleBorder(
|
||||||
Icons
|
borderRadius:
|
||||||
.assistant_photo_outlined,
|
BorderRadius.circular(40)),
|
||||||
size: 40.0,
|
child: const CircularProgressIndicator(),
|
||||||
color: Colors.blue),
|
)
|
||||||
snackPosition:
|
: Column(
|
||||||
SnackPosition.TOP,
|
children: [
|
||||||
duration: const Duration(
|
MaterialButton(
|
||||||
seconds: 3),
|
minWidth: double.infinity,
|
||||||
// backgroundColor: Colors.yellow,
|
height: 40,
|
||||||
//icon:Image(image:AssetImage("assets/images/dora.png"))
|
onPressed: () async {
|
||||||
);
|
if (emailController.text.isEmpty ||
|
||||||
return;
|
passwordController
|
||||||
}
|
.text.isEmpty) {
|
||||||
indexController.isLoading.value =
|
Get.snackbar(
|
||||||
true;
|
"no_values".tr,
|
||||||
indexController.login(
|
"email_and_password_required"
|
||||||
emailController.text,
|
.tr,
|
||||||
passwordController.text,
|
backgroundColor: Colors.red,
|
||||||
context);
|
colorText: Colors.white,
|
||||||
},
|
icon: const Icon(
|
||||||
color: Colors.indigoAccent[400],
|
Icons
|
||||||
shape: RoundedRectangleBorder(
|
.assistant_photo_outlined,
|
||||||
borderRadius:
|
size: 40.0,
|
||||||
BorderRadius.circular(40)),
|
color: Colors.blue),
|
||||||
child: Text(
|
snackPosition:
|
||||||
"login".tr,
|
SnackPosition.TOP,
|
||||||
style: const TextStyle(
|
duration: const Duration(
|
||||||
fontWeight: FontWeight.w600,
|
seconds: 3),
|
||||||
fontSize: 16,
|
);
|
||||||
color: Colors.white70),
|
return;
|
||||||
),
|
}
|
||||||
),
|
indexController.isLoading.value =
|
||||||
const SizedBox(
|
true;
|
||||||
height: 5.0,
|
indexController.login(
|
||||||
),
|
emailController.text,
|
||||||
MaterialButton(
|
passwordController.text,
|
||||||
minWidth: double.infinity,
|
context);
|
||||||
height: 36,
|
},
|
||||||
onPressed: () {
|
color: Colors.indigoAccent[400],
|
||||||
Get.toNamed(AppPages.REGISTER);
|
shape: RoundedRectangleBorder(
|
||||||
},
|
borderRadius:
|
||||||
color: Colors.redAccent,
|
BorderRadius.circular(40)),
|
||||||
shape: RoundedRectangleBorder(
|
child: Text(
|
||||||
borderRadius:
|
"login".tr,
|
||||||
BorderRadius.circular(40)),
|
style: const TextStyle(
|
||||||
child: Text(
|
fontWeight: FontWeight.w600,
|
||||||
"sign_up".tr,
|
fontSize: 16,
|
||||||
style: const TextStyle(
|
color: Colors.white70),
|
||||||
fontWeight: FontWeight.w600,
|
),
|
||||||
fontSize: 16,
|
),
|
||||||
color: Colors.white70),
|
const SizedBox(
|
||||||
),
|
height: 5.0,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
MaterialButton(
|
||||||
height: 2.0,
|
minWidth: double.infinity,
|
||||||
),
|
height: 36,
|
||||||
MaterialButton(
|
onPressed: () {
|
||||||
minWidth: double.infinity,
|
Get.toNamed(AppPages.REGISTER);
|
||||||
height: 36,
|
},
|
||||||
onPressed: () {
|
color: Colors.redAccent,
|
||||||
Get.back();
|
shape: RoundedRectangleBorder(
|
||||||
},
|
borderRadius:
|
||||||
color: Colors.grey,
|
BorderRadius.circular(40)),
|
||||||
shape: RoundedRectangleBorder(
|
child: Text(
|
||||||
borderRadius:
|
"sign_up".tr,
|
||||||
BorderRadius.circular(40)),
|
style: const TextStyle(
|
||||||
child: Text(
|
fontWeight: FontWeight.w600,
|
||||||
"cancel".tr,
|
fontSize: 16,
|
||||||
style: const TextStyle(
|
color: Colors.white70),
|
||||||
fontWeight: FontWeight.w600,
|
),
|
||||||
fontSize: 16,
|
),
|
||||||
color: Colors.white70),
|
],
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
)),
|
)),
|
||||||
const SizedBox(
|
),
|
||||||
height: 3,
|
)),
|
||||||
|
const SizedBox(
|
||||||
|
height: 3,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: _showResetPasswordDialog,
|
||||||
|
child: Text(
|
||||||
|
"forgot_password".tr,
|
||||||
|
style: TextStyle(color: Colors.blue),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Row(
|
],
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
),
|
||||||
children: [
|
Row(
|
||||||
Flexible(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
child: Padding(
|
children: [
|
||||||
padding: const EdgeInsets.all(8.0),
|
Flexible(
|
||||||
child: Text(
|
child: Padding(
|
||||||
"rogaining_user_need_tosign_up".tr,
|
padding: const EdgeInsets.all(8.0),
|
||||||
style: const TextStyle(
|
child: Text(
|
||||||
overflow: TextOverflow.ellipsis,
|
"app_developed_by_gifu_dx".tr,
|
||||||
fontSize: 10.0
|
style: const TextStyle(
|
||||||
),
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
fontSize: 10.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
"※第8回と第9回は、岐阜県の令和5年度「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"app_developed_by_gifu_dx".tr,
|
|
||||||
style: const TextStyle(
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
fontSize: 10.0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
"※第8回と第9回は、岐阜県の令和5年度「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています",
|
|
||||||
style: TextStyle(
|
|
||||||
//overflow: TextOverflow.ellipsis,
|
|
||||||
fontSize:
|
|
||||||
10.0, // Consider adjusting the font size if the text is too small.
|
|
||||||
// Removed overflow: TextOverflow.ellipsis to allow text wrapping.
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
],
|
||||||
)
|
),
|
||||||
: TextButton(
|
)
|
||||||
onPressed: () {
|
: TextButton(
|
||||||
indexController.currentUser.clear();
|
onPressed: () {
|
||||||
},
|
indexController.logout();
|
||||||
child: const Text("Already Logged in, Click to logout"),
|
Get.offAllNamed(AppPages.LOGIN);
|
||||||
),
|
},
|
||||||
|
child: const Text("Already Logged in, Click to logout"),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget makePasswordInput({
|
||||||
|
required String label,
|
||||||
|
required TextEditingController controller,
|
||||||
|
required bool obscureText,
|
||||||
|
required VoidCallback onToggleVisibility,
|
||||||
|
}) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
TextField(
|
||||||
|
controller: controller,
|
||||||
|
obscureText: obscureText,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||||
|
),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
obscureText ? Icons.visibility : Icons.visibility_off,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
onPressed: onToggleVisibility,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30.0)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget makeInput(
|
Widget makeInput(
|
||||||
{label, required TextEditingController controller, obsureText = false}) {
|
{label, required TextEditingController controller, obsureText = false}) {
|
||||||
return Column(
|
return Column(
|
||||||
|
|||||||
381
lib/pages/login/login_page.dart_backup
Normal file
381
lib/pages/login/login_page.dart_backup
Normal file
@ -0,0 +1,381 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rogapp/pages/index/index_controller.dart';
|
||||||
|
import 'package:rogapp/routes/app_pages.dart';
|
||||||
|
import 'package:rogapp/widgets/helper_dialog.dart';
|
||||||
|
import 'package:rogapp/services/api_service.dart';
|
||||||
|
|
||||||
|
// 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。
|
||||||
|
// エラーメッセージをローカライズすることを検討してください。
|
||||||
|
// ログイン処理中にエラーが発生した場合のエラーハンドリングを追加することをお勧めします。
|
||||||
|
//
|
||||||
|
class LoginPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_LoginPageState createState() => _LoginPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LoginPageState extends State<LoginPage> {
|
||||||
|
//class LoginPage extends StatelessWidget {
|
||||||
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
final ApiService apiService = Get.find<ApiService>();
|
||||||
|
|
||||||
|
TextEditingController emailController = TextEditingController();
|
||||||
|
TextEditingController passwordController = TextEditingController();
|
||||||
|
bool _obscureText = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
showHelperDialog(
|
||||||
|
'参加するにはユーザー登録が必要です。サインアップからユーザー登録してください。',
|
||||||
|
'login_page'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showResetPasswordDialog() {
|
||||||
|
TextEditingController resetEmailController = TextEditingController();
|
||||||
|
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: Text('パスワードのリセット'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text('パスワードをリセットするメールアドレスを入力してください。'),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
TextField(
|
||||||
|
controller: resetEmailController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'メールアドレス',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('キャンセル'),
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: Text('リセット'),
|
||||||
|
onPressed: () async {
|
||||||
|
if (resetEmailController.text.isNotEmpty) {
|
||||||
|
bool success = await apiService.resetPassword(resetEmailController.text);
|
||||||
|
Get.back();
|
||||||
|
if (success) {
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: Text('パスワードリセット'),
|
||||||
|
content: Text('パスワードリセットメールを送信しました。メールのリンクからパスワードを設定してください。'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('OK'),
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Get.snackbar('エラー', 'パスワードリセットに失敗しました。もう一度お試しください。',
|
||||||
|
snackPosition: SnackPosition.BOTTOM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//LoginPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
resizeToAvoidBottomInset: false,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
appBar: AppBar(
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
),
|
||||||
|
body: indexController.currentUser.isEmpty
|
||||||
|
? SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: MediaQuery.of(context).size.height / 6,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage(
|
||||||
|
'assets/images/login_image.jpg'))),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
makeInput(
|
||||||
|
label: "email".tr, controller: emailController),
|
||||||
|
makePasswordInput(
|
||||||
|
label: "password".tr,
|
||||||
|
controller: passwordController,
|
||||||
|
obscureText: _obscureText,
|
||||||
|
onToggleVisibility: () {
|
||||||
|
setState(() {
|
||||||
|
_obscureText = !_obscureText;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.only(top: 3, left: 3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(40),
|
||||||
|
),
|
||||||
|
child: Obx(
|
||||||
|
(() => indexController.isLoading.value == true
|
||||||
|
? MaterialButton(
|
||||||
|
minWidth: double.infinity,
|
||||||
|
height: 60,
|
||||||
|
onPressed: () {},
|
||||||
|
color: Colors.grey[400],
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(40)),
|
||||||
|
child: const CircularProgressIndicator(),
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
children: [
|
||||||
|
MaterialButton(
|
||||||
|
minWidth: double.infinity,
|
||||||
|
height: 40,
|
||||||
|
onPressed: () async {
|
||||||
|
if (emailController.text.isEmpty ||
|
||||||
|
passwordController
|
||||||
|
.text.isEmpty) {
|
||||||
|
Get.snackbar(
|
||||||
|
"no_values".tr,
|
||||||
|
"email_and_password_required"
|
||||||
|
.tr,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons
|
||||||
|
.assistant_photo_outlined,
|
||||||
|
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"))
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
indexController.isLoading.value =
|
||||||
|
true;
|
||||||
|
indexController.login(
|
||||||
|
emailController.text,
|
||||||
|
passwordController.text,
|
||||||
|
context);
|
||||||
|
},
|
||||||
|
color: Colors.indigoAccent[400],
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(40)),
|
||||||
|
child: Text(
|
||||||
|
"login".tr,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white70),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5.0,
|
||||||
|
),
|
||||||
|
MaterialButton(
|
||||||
|
minWidth: double.infinity,
|
||||||
|
height: 36,
|
||||||
|
onPressed: () {
|
||||||
|
Get.toNamed(AppPages.REGISTER);
|
||||||
|
},
|
||||||
|
color: Colors.redAccent,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(40)),
|
||||||
|
child: Text(
|
||||||
|
"sign_up".tr,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white70),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
const SizedBox(
|
||||||
|
height: 3,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: _showResetPasswordDialog,
|
||||||
|
child: Text(
|
||||||
|
"forgot_password".tr,
|
||||||
|
style: TextStyle(color: Colors.blue),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
"app_developed_by_gifu_dx".tr,
|
||||||
|
style: const TextStyle(
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
fontSize: 10.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
"※第8回と第9回は、岐阜県の令和5年度「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています",
|
||||||
|
style: TextStyle(
|
||||||
|
//overflow: TextOverflow.ellipsis,
|
||||||
|
fontSize:
|
||||||
|
10.0, // Consider adjusting the font size if the text is too small.
|
||||||
|
// Removed overflow: TextOverflow.ellipsis to allow text wrapping.
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
: TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
indexController.logout();
|
||||||
|
Get.offAllNamed(AppPages.LOGIN);
|
||||||
|
},
|
||||||
|
child: const Text("Already Logged in, Click to logout"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makePasswordInput({
|
||||||
|
required String label,
|
||||||
|
required TextEditingController controller,
|
||||||
|
required bool obscureText,
|
||||||
|
required VoidCallback onToggleVisibility,
|
||||||
|
}) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
TextField(
|
||||||
|
controller: controller,
|
||||||
|
obscureText: obscureText,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||||
|
),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
obscureText ? Icons.visibility : Icons.visibility_off,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
onPressed: onToggleVisibility,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30.0)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeInput(
|
||||||
|
{label, required TextEditingController controller, obsureText = false}) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: controller,
|
||||||
|
obscureText: obsureText,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: (Colors.grey[400])!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: (Colors.grey[400])!),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 30.0,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -207,7 +207,8 @@ class LoginPopupPage extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
: TextButton(
|
: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
indexController.currentUser.clear();
|
indexController.logout();
|
||||||
|
Get.offAllNamed(AppPages.LOGIN);
|
||||||
},
|
},
|
||||||
child: const Text("Already Logged in, Click to logout"),
|
child: const Text("Already Logged in, Click to logout"),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -2,15 +2,33 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rogapp/pages/index/index_controller.dart';
|
import 'package:rogapp/pages/index/index_controller.dart';
|
||||||
import 'package:rogapp/routes/app_pages.dart';
|
import 'package:rogapp/routes/app_pages.dart';
|
||||||
|
import 'package:rogapp/widgets/helper_dialog.dart';
|
||||||
|
|
||||||
class RegisterPage extends StatelessWidget {
|
class RegisterPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_RegisterPageState createState() => _RegisterPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RegisterPageState extends State<RegisterPage> {
|
||||||
final IndexController indexController = Get.find<IndexController>();
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
|
||||||
TextEditingController emailController = TextEditingController();
|
final TextEditingController emailController = TextEditingController();
|
||||||
TextEditingController passwordController = TextEditingController();
|
final TextEditingController passwordController = TextEditingController();
|
||||||
TextEditingController confirmPasswordController = TextEditingController();
|
final TextEditingController confirmPasswordController = TextEditingController();
|
||||||
|
|
||||||
RegisterPage({Key? key}) : super(key: key);
|
bool _obscurePassword = true;
|
||||||
|
bool _obscureConfirmPassword = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
showHelperDialog(
|
||||||
|
'登録メールにアクティベーションメールが送信されます。メールにあるリンクをタップすると正式登録になります。',
|
||||||
|
'register_page'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -21,150 +39,74 @@ class RegisterPage extends StatelessWidget {
|
|||||||
elevation: 0,
|
elevation: 0,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
onPressed: () {
|
onPressed: () => Navigator.pop(context),
|
||||||
Navigator.pop(context);
|
icon: const Icon(Icons.arrow_back_ios, size: 20, color: Colors.black),
|
||||||
},
|
),
|
||||||
icon: const Icon(
|
|
||||||
Icons.arrow_back_ios,
|
|
||||||
size: 20,
|
|
||||||
color: Colors.black,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: SizedBox(
|
child: Container(
|
||||||
height: MediaQuery.of(context).size.height,
|
height: MediaQuery.of(context).size.height,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Text(
|
||||||
children: [
|
"sign_up".tr,
|
||||||
Column(
|
style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"sign_up".tr, // "サインアップ"
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 30,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"create_account".tr, // アカウントを無料で作成します。
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
color: Colors.grey[700],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 30,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
makeInput(label: "email".tr, controller: emailController), // メールアドレス
|
|
||||||
makeInput(
|
|
||||||
label: "password".tr,
|
|
||||||
controller: passwordController,
|
|
||||||
obsureText: true), // パスワード
|
|
||||||
makeInput(
|
|
||||||
label: "confirm_password".tr,
|
|
||||||
controller: confirmPasswordController,
|
|
||||||
obsureText: true) // パスワード再確認
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.only(top: 3, left: 3),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(40),
|
|
||||||
border: const Border(
|
|
||||||
bottom: BorderSide(color: Colors.black),
|
|
||||||
top: BorderSide(color: Colors.black),
|
|
||||||
right: BorderSide(color: Colors.black),
|
|
||||||
left: BorderSide(color: Colors.black))),
|
|
||||||
child: MaterialButton(
|
|
||||||
minWidth: double.infinity,
|
|
||||||
height: 60,
|
|
||||||
onPressed: () {
|
|
||||||
if (passwordController.text !=
|
|
||||||
confirmPasswordController.text) {
|
|
||||||
Get.snackbar(
|
|
||||||
"no_match".tr,
|
|
||||||
"password_does_not_match".tr,
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
colorText: Colors.white,
|
|
||||||
icon: const Icon(Icons.assistant_photo_outlined,
|
|
||||||
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"))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (emailController.text.isEmpty ||
|
|
||||||
passwordController.text.isEmpty) {
|
|
||||||
Get.snackbar(
|
|
||||||
"no_values".tr,
|
|
||||||
"email_and_password_required".tr,
|
|
||||||
backgroundColor: Colors.red,
|
|
||||||
colorText: Colors.white,
|
|
||||||
icon: const Icon(Icons.assistant_photo_outlined,
|
|
||||||
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"))
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
indexController.isLoading.value = true;
|
|
||||||
indexController.register(emailController.text,
|
|
||||||
passwordController.text, context);
|
|
||||||
},
|
|
||||||
color: Colors.redAccent,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(40)),
|
|
||||||
child: Text(
|
|
||||||
"sign_up".tr,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Flexible(child: Text("already_have_account".tr)),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Get.toNamed(AppPages.LOGIN);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
"login".tr,
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w600, fontSize: 18),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
"create_account".tr,
|
||||||
|
style: TextStyle(fontSize: 15, color: Colors.grey[700]),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
makeInput(label: "email".tr, controller: emailController),
|
||||||
|
//makeInput(label: "password".tr, controller: passwordController, obsureText: true),
|
||||||
|
//makeInput(label: "confirm_password".tr, controller: confirmPasswordController, obsureText: true),
|
||||||
|
makePasswordInput(
|
||||||
|
label: "password".tr,
|
||||||
|
controller: passwordController,
|
||||||
|
obscureText: _obscurePassword,
|
||||||
|
onToggleVisibility: () {
|
||||||
|
setState(() {
|
||||||
|
_obscurePassword = !_obscurePassword;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
makePasswordInput(
|
||||||
|
label: "confirm_password".tr,
|
||||||
|
controller: confirmPasswordController,
|
||||||
|
obscureText: _obscureConfirmPassword,
|
||||||
|
onToggleVisibility: () {
|
||||||
|
setState(() {
|
||||||
|
_obscureConfirmPassword = !_obscureConfirmPassword;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _handleRegister,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
backgroundColor: Colors.redAccent,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)),
|
||||||
|
minimumSize: const Size(double.infinity, 60),
|
||||||
|
),
|
||||||
|
child: Text("sign_up".tr, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(child: Text("already_have_account".tr)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Get.toNamed(AppPages.LOGIN),
|
||||||
|
child: Text("login".tr, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 18)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -172,40 +114,96 @@ class RegisterPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget makePasswordInput({
|
||||||
|
required String label,
|
||||||
|
required TextEditingController controller,
|
||||||
|
required bool obscureText,
|
||||||
|
required VoidCallback onToggleVisibility,
|
||||||
|
}) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(label, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87)),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
TextField(
|
||||||
|
controller: controller,
|
||||||
|
obscureText: obscureText,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||||
|
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey[400]!)),
|
||||||
|
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey[400]!)),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
obscureText ? Icons.visibility : Icons.visibility_off,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
onPressed: onToggleVisibility,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleRegister() {
|
||||||
|
if (passwordController.text != confirmPasswordController.text) {
|
||||||
|
_showErrorSnackbar("no_match".tr, "password_does_not_match".tr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (emailController.text.isEmpty || passwordController.text.isEmpty) {
|
||||||
|
_showErrorSnackbar("no_values".tr, "email_and_password_required".tr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
indexController.isLoading.value = true;
|
||||||
|
try {
|
||||||
|
indexController.register(
|
||||||
|
emailController.text,
|
||||||
|
passwordController.text,
|
||||||
|
confirmPasswordController.text,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
// 登録が成功したと仮定し、ログインページに遷移
|
||||||
|
//Get.offNamed(AppPages.LOGIN);
|
||||||
|
} catch (error) {
|
||||||
|
_showErrorSnackbar("registration_error".tr, error.toString());
|
||||||
|
} finally {
|
||||||
|
indexController.isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showErrorSnackbar(String title, String message) {
|
||||||
|
Get.snackbar(
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
icon: const Icon(Icons.error_outline, size: 40.0, color: Colors.white),
|
||||||
|
snackPosition: SnackPosition.TOP,
|
||||||
|
duration: const Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget makeInput(
|
Widget makeInput({required String label, required TextEditingController controller, bool obsureText = false}) {
|
||||||
{label, required TextEditingController controller, obsureText = false}) {
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(label, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87)),
|
||||||
label,
|
const SizedBox(height: 5),
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 5,
|
|
||||||
),
|
|
||||||
TextField(
|
TextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
obscureText: obsureText,
|
obscureText: obsureText,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
contentPadding:
|
contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||||
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey[400]!)),
|
||||||
enabledBorder: OutlineInputBorder(
|
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey[400]!)),
|
||||||
borderSide: BorderSide(
|
|
||||||
color: (Colors.grey[400])!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: (Colors.grey[400])!),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(height: 20),
|
||||||
height: 30,
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
182
lib/pages/register/user_detail_page.dart
Normal file
182
lib/pages/register/user_detail_page.dart
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rogapp/model/user.dart';
|
||||||
|
import 'package:rogapp/routes/app_pages.dart';
|
||||||
|
import 'package:rogapp/services/api_service.dart';
|
||||||
|
import 'package:rogapp/pages/index/index_controller.dart';
|
||||||
|
|
||||||
|
class UserDetailsEditPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_UserDetailsEditPageState createState() => _UserDetailsEditPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _UserDetailsEditPageState extends State<UserDetailsEditPage> {
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
late User _user;
|
||||||
|
final TextEditingController _firstnameController = TextEditingController();
|
||||||
|
final TextEditingController _lastnameController = TextEditingController();
|
||||||
|
final TextEditingController _dateOfBirthController = TextEditingController();
|
||||||
|
late bool _female;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_user = User.fromJson(indexController.currentUser[0]['user']);
|
||||||
|
_firstnameController.text = _user.firstname;
|
||||||
|
_lastnameController.text = _user.lastname;
|
||||||
|
_dateOfBirthController.text = _user.dateOfBirth != null
|
||||||
|
? '${_user.dateOfBirth!.year}/${_user.dateOfBirth!.month.toString().padLeft(2, '0')}/${_user.dateOfBirth!.day.toString().padLeft(2, '0')}'
|
||||||
|
: '';
|
||||||
|
_female = _user.female;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('個人情報の修正'),
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
),
|
||||||
|
body: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
controller: _lastnameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: '姓',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return '姓を入力してください';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: _firstnameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: '名',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return '名を入力してください';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
controller: _dateOfBirthController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: '生年月日 (YYYY/MM/DD)',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
hintText: 'YYYY/MM/DD',
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.datetime,
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return '生年月日を入力してください';
|
||||||
|
}
|
||||||
|
if (!RegExp(r'^\d{4}/\d{2}/\d{2}$').hasMatch(value)) {
|
||||||
|
return '正しい形式で入力してください (YYYY/MM/DD)';
|
||||||
|
}
|
||||||
|
final date = DateTime.tryParse(value.replaceAll('/', '-'));
|
||||||
|
if (date == null) {
|
||||||
|
return '有効な日付を入力してください';
|
||||||
|
}
|
||||||
|
if (date.isAfter(DateTime.now())) {
|
||||||
|
return '未来の日付は入力できません';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
SwitchListTile(
|
||||||
|
title: Text('性別'),
|
||||||
|
subtitle: Text(_female ? '女性' : '男性'),
|
||||||
|
value: _female,
|
||||||
|
onChanged: (bool value) {
|
||||||
|
setState(() {
|
||||||
|
_female = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
TextFormField(
|
||||||
|
initialValue: _user.email,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'メールアドレス',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
SwitchListTile(
|
||||||
|
title: Text('アクティブ状態'),
|
||||||
|
value: _user.isActive,
|
||||||
|
onChanged: null,
|
||||||
|
),
|
||||||
|
SizedBox(height: 32),
|
||||||
|
ElevatedButton(
|
||||||
|
child: Text('更新'),
|
||||||
|
onPressed: _updateUserDetails,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateUserDetails() async {
|
||||||
|
if (_formKey.currentState!.validate()) {
|
||||||
|
final dateOfBirth = DateTime.tryParse(_dateOfBirthController.text.replaceAll('/', '-'));
|
||||||
|
if (dateOfBirth == null || dateOfBirth.isAfter(DateTime.now())) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('生年月日が無効です', style: TextStyle(color: Colors.red))),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
User updatedUser = User(
|
||||||
|
id: _user.id,
|
||||||
|
email: _user.email,
|
||||||
|
firstname: _firstnameController.text,
|
||||||
|
lastname: _lastnameController.text,
|
||||||
|
dateOfBirth: dateOfBirth,
|
||||||
|
female: _female,
|
||||||
|
isActive: _user.isActive,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
bool success = await ApiService.updateUserDetail(updatedUser, indexController.currentUser[0]['token']);
|
||||||
|
if (success) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('個人情報が更新されました')),
|
||||||
|
);
|
||||||
|
indexController.updateCurrentUser(updatedUser);
|
||||||
|
Get.offAllNamed(AppPages.INDEX);
|
||||||
|
//Get.back();
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('更新に失敗しました', style: TextStyle(color: Colors.red))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('エラーが発生しました: $e', style: TextStyle(color: Colors.red))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -91,6 +91,7 @@ class MemberController extends GetxController {
|
|||||||
lastname.value = value;
|
lastname.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<bool> saveMember() async {
|
Future<bool> saveMember() async {
|
||||||
if (!_validateInputs()) return false;
|
if (!_validateInputs()) return false;
|
||||||
|
|
||||||
@ -139,8 +140,8 @@ class MemberController extends GetxController {
|
|||||||
return '${lastname.value} ${firstname.value}'.trim();
|
return '${lastname.value} ${firstname.value}'.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateMember() async {
|
Future<bool> updateMember() async {
|
||||||
if (member.value == null) return;
|
if (member.value == null) return false;
|
||||||
int? memberId = member.value?.id;
|
int? memberId = member.value?.id;
|
||||||
try {
|
try {
|
||||||
final updatedMember = await _apiService.updateTeamMember(
|
final updatedMember = await _apiService.updateTeamMember(
|
||||||
@ -152,15 +153,17 @@ class MemberController extends GetxController {
|
|||||||
female.value,
|
female.value,
|
||||||
);
|
);
|
||||||
member.value = updatedMember;
|
member.value = updatedMember;
|
||||||
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error updating member: $e');
|
print('Error updating member: $e');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteMember() async {
|
Future<bool> deleteMember() async {
|
||||||
if (member.value == null || member.value!.id == null) {
|
if (member.value == null || member.value!.id == null) {
|
||||||
Get.snackbar('エラー', 'メンバー情報が不正です', snackPosition: SnackPosition.BOTTOM);
|
Get.snackbar('エラー', 'メンバー情報が不正です', snackPosition: SnackPosition.BOTTOM);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -168,11 +171,14 @@ class MemberController extends GetxController {
|
|||||||
await _apiService.deleteTeamMember(teamId, member.value!.id!);
|
await _apiService.deleteTeamMember(teamId, member.value!.id!);
|
||||||
Get.snackbar('成功', 'メンバーが削除されました', snackPosition: SnackPosition.BOTTOM);
|
Get.snackbar('成功', 'メンバーが削除されました', snackPosition: SnackPosition.BOTTOM);
|
||||||
member.value = null;
|
member.value = null;
|
||||||
|
isLoading.value = false;
|
||||||
|
return true;
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error deleting member: $e');
|
print('Error deleting member: $e');
|
||||||
Get.snackbar('エラー', 'メンバーの削除に失敗しました: ${e.toString()}', snackPosition: SnackPosition.BOTTOM);
|
Get.snackbar('エラー', 'メンバーの削除に失敗しました: ${e.toString()}', snackPosition: SnackPosition.BOTTOM);
|
||||||
} finally {
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,10 +257,10 @@ class MemberController extends GetxController {
|
|||||||
yearsFromSchoolStart--;
|
yearsFromSchoolStart--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (yearsFromSchoolStart < 0) return '未就学';
|
if (yearsFromSchoolStart < 7) return '未就学';
|
||||||
if (yearsFromSchoolStart < 6) return '小${yearsFromSchoolStart + 1}';
|
if (yearsFromSchoolStart < 13) return '小${yearsFromSchoolStart - 6}';
|
||||||
if (yearsFromSchoolStart < 9) return '中${yearsFromSchoolStart - 5}';
|
if (yearsFromSchoolStart < 16) return '中${yearsFromSchoolStart - 12}';
|
||||||
if (yearsFromSchoolStart < 12) return '高${yearsFromSchoolStart - 8}';
|
if (yearsFromSchoolStart < 19) return '高${yearsFromSchoolStart - 15}';
|
||||||
return '成人';
|
return '成人';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import 'package:intl/intl.dart'; // この行を追加
|
|||||||
import 'package:flutter_localizations/flutter_localizations.dart'; // 追加
|
import 'package:flutter_localizations/flutter_localizations.dart'; // 追加
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
import 'package:rogapp/routes/app_pages.dart';
|
||||||
|
|
||||||
class MemberDetailPage extends StatefulWidget {
|
class MemberDetailPage extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_MemberDetailPageState createState() => _MemberDetailPageState();
|
_MemberDetailPageState createState() => _MemberDetailPageState();
|
||||||
@ -34,15 +36,18 @@ class _MemberDetailPageState extends State<MemberDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _initializeControllers() {
|
void _initializeControllers() {
|
||||||
_firstNameController = TextEditingController(text: controller.firstname.value);
|
_firstNameController =
|
||||||
_lastNameController = TextEditingController(text: controller.lastname.value);
|
TextEditingController(text: controller.firstname.value);
|
||||||
|
_lastNameController =
|
||||||
|
TextEditingController(text: controller.lastname.value);
|
||||||
_emailController = TextEditingController(text: controller.email.value);
|
_emailController = TextEditingController(text: controller.email.value);
|
||||||
|
|
||||||
controller.firstname.listen((value) {
|
controller.firstname.listen((value) {
|
||||||
if (_firstNameController.text != value) {
|
if (_firstNameController.text != value) {
|
||||||
_firstNameController.value = TextEditingValue(
|
_firstNameController.value = TextEditingValue(
|
||||||
text: value,
|
text: value,
|
||||||
selection: TextSelection.fromPosition(TextPosition(offset: value.length)),
|
selection: TextSelection.fromPosition(
|
||||||
|
TextPosition(offset: value.length)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -51,7 +56,8 @@ class _MemberDetailPageState extends State<MemberDetailPage> {
|
|||||||
if (_lastNameController.text != value) {
|
if (_lastNameController.text != value) {
|
||||||
_lastNameController.value = TextEditingValue(
|
_lastNameController.value = TextEditingValue(
|
||||||
text: value,
|
text: value,
|
||||||
selection: TextSelection.fromPosition(TextPosition(offset: value.length)),
|
selection: TextSelection.fromPosition(
|
||||||
|
TextPosition(offset: value.length)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -60,12 +66,71 @@ class _MemberDetailPageState extends State<MemberDetailPage> {
|
|||||||
if (_emailController.text != value) {
|
if (_emailController.text != value) {
|
||||||
_emailController.value = TextEditingValue(
|
_emailController.value = TextEditingValue(
|
||||||
text: value,
|
text: value,
|
||||||
selection: TextSelection.fromPosition(TextPosition(offset: value.length)),
|
selection: TextSelection.fromPosition(
|
||||||
|
TextPosition(offset: value.length)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _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<void> _handleDeleteAndNavigateBack() async {
|
||||||
|
final confirmed = await Get.dialog<bool>(
|
||||||
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final mode = Get.arguments['mode'] as String;
|
final mode = Get.arguments['mode'] as String;
|
||||||
@ -85,121 +150,125 @@ class _MemberDetailPageState extends State<MemberDetailPage> {
|
|||||||
home:Scaffold(
|
home:Scaffold(
|
||||||
*/
|
*/
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(mode == 'new' ? 'メンバー追加' : 'メンバー詳細'),
|
title: Text(mode == 'new' ? 'メンバー追加' : 'メンバー詳細'),
|
||||||
actions: [
|
),
|
||||||
IconButton(
|
body: Obx(() {
|
||||||
icon: Icon(Icons.save),
|
if (controller.isLoading.value) {
|
||||||
onPressed: () async {
|
return Center(child: CircularProgressIndicator());
|
||||||
await controller.saveMember();
|
}
|
||||||
Get.back(result: true);
|
// TextEditingControllerをObxの中で作成
|
||||||
},
|
final emailController = TextEditingController(text: controller.email.value);
|
||||||
),
|
// カーソル位置を保持
|
||||||
],
|
emailController.selection = TextSelection.fromPosition(
|
||||||
),
|
TextPosition(offset: controller.email.value.length),
|
||||||
body: Obx(() {
|
|
||||||
if (controller.isLoading.value) {
|
|
||||||
return Center(child: CircularProgressIndicator());
|
|
||||||
}
|
|
||||||
|
|
||||||
return SingleChildScrollView(
|
|
||||||
padding: EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (mode == 'new')
|
|
||||||
TextField(
|
|
||||||
decoration: InputDecoration(labelText: 'メールアドレス'),
|
|
||||||
onChanged: (value) => controller.email.value = value,
|
|
||||||
controller: TextEditingController(text: controller.email.value),
|
|
||||||
)
|
|
||||||
else if (controller.isDummyEmail)
|
|
||||||
Text('メールアドレス: (メアド無し)')
|
|
||||||
else
|
|
||||||
Text('メールアドレス: ${controller.email.value}'),
|
|
||||||
|
|
||||||
if (controller.email.value.isEmpty || mode == 'edit') ...[
|
|
||||||
TextField(
|
|
||||||
decoration: InputDecoration(labelText: '姓'),
|
|
||||||
onChanged: (value) => controller.lastname.value = value,
|
|
||||||
controller: TextEditingController(text: controller.lastname.value),
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
decoration: InputDecoration(labelText: '名'),
|
|
||||||
onChanged: (value) => controller.firstname.value = value,
|
|
||||||
controller: TextEditingController(text: controller.firstname.value),
|
|
||||||
),
|
|
||||||
// 生年月日
|
|
||||||
if (controller.isDummyEmail || !controller.isOver18())
|
|
||||||
ListTile(
|
|
||||||
title: Text('生年月日'),
|
|
||||||
subtitle: Text(controller.dateOfBirth.value != null
|
|
||||||
? '${DateFormat('yyyy年MM月dd日').format(controller.dateOfBirth.value!)} (${controller.getAgeAndGrade()})'
|
|
||||||
: '未設定'),
|
|
||||||
onTap: () async {
|
|
||||||
final date = await showDatePicker(
|
|
||||||
context: context,
|
|
||||||
initialDate: controller.dateOfBirth.value ?? DateTime.now(),
|
|
||||||
firstDate: DateTime(1900),
|
|
||||||
lastDate: DateTime.now(),
|
|
||||||
//locale: const Locale('ja', 'JP'),
|
|
||||||
);
|
|
||||||
if (date != null) controller.dateOfBirth.value = date;
|
|
||||||
},
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Text('18歳以上'),
|
|
||||||
|
|
||||||
SwitchListTile(
|
|
||||||
title: Text('性別'),
|
|
||||||
subtitle: Text(controller.female.value ? '女性' : '男性'),
|
|
||||||
value: controller.female.value,
|
|
||||||
onChanged: (value) => controller.female.value = value,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
// 招待メール再送信ボタン(通常のEmailで未承認の場合のみ)
|
|
||||||
if (!controller.isDummyEmail && !controller.isApproved)
|
|
||||||
ElevatedButton(
|
|
||||||
child: Text('招待メールを再送信'),
|
|
||||||
onPressed: () => controller.resendInvitation(),
|
|
||||||
),
|
|
||||||
|
|
||||||
// メンバー削除ボタン
|
|
||||||
ElevatedButton(
|
|
||||||
child: Text('メンバーから削除'),
|
|
||||||
onPressed: () async {
|
|
||||||
final confirmed = await Get.dialog<bool>(
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (mode == 'new')
|
||||||
|
TextFormField(
|
||||||
|
controller: _emailController,
|
||||||
|
onChanged: (value) => controller.updateEmail(value),
|
||||||
|
decoration: InputDecoration(labelText: 'メールアドレス'),
|
||||||
|
keyboardType: TextInputType.emailAddress, // メールアドレス用のキーボードを表示
|
||||||
|
autocorrect: false, // 自動修正を無効化
|
||||||
|
enableSuggestions: false,
|
||||||
|
)
|
||||||
|
else if (controller.isDummyEmail)
|
||||||
|
Text('メールアドレス: (メアド無し)')
|
||||||
|
else
|
||||||
|
Text('メールアドレス: ${controller.email.value}'),
|
||||||
|
|
||||||
|
if (controller.email.value.isEmpty || controller.isDummyEmail || mode == 'edit') ...[
|
||||||
|
TextFormField(
|
||||||
|
decoration: InputDecoration(labelText: '姓'),
|
||||||
|
controller: _lastNameController,
|
||||||
|
onChanged: (value) => controller.updateLastName(value),
|
||||||
|
|
||||||
|
//controller: TextEditingController(text: controller.lastname.value),
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
decoration: InputDecoration(labelText: '名'),
|
||||||
|
controller: _firstNameController,
|
||||||
|
//onChanged: (value) => controller.firstname.value = value,
|
||||||
|
onChanged: (value) => controller.updateFirstName(value),
|
||||||
|
//controller: TextEditingController(text: controller.firstname.value),
|
||||||
|
),
|
||||||
|
// 生年月日
|
||||||
|
if (controller.isDummyEmail || !controller.isOver18())
|
||||||
|
ListTile(
|
||||||
|
title: Text('生年月日'),
|
||||||
|
subtitle: Text(controller.dateOfBirth.value != null
|
||||||
|
? '${DateFormat('yyyy年MM月dd日').format(controller.dateOfBirth.value!)} (${controller.getAgeAndGrade()})'
|
||||||
|
: '未設定'),
|
||||||
|
onTap: () async {
|
||||||
|
final date = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: controller.dateOfBirth.value ?? DateTime.now(),
|
||||||
|
firstDate: DateTime(1900),
|
||||||
|
lastDate: DateTime.now(),
|
||||||
|
//locale: const Locale('ja', 'JP'),
|
||||||
|
);
|
||||||
|
if (date != null) controller.dateOfBirth.value = date;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Text('18歳以上'),
|
||||||
|
|
||||||
|
SwitchListTile(
|
||||||
|
title: Text('性別'),
|
||||||
|
subtitle: Text(controller.female.value ? '女性' : '男性'),
|
||||||
|
value: controller.female.value,
|
||||||
|
onChanged: (value) => controller.female.value = value,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
child: Text('削除'),
|
||||||
|
onPressed: _handleDeleteAndNavigateBack,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!controller.isDummyEmail && !controller.isApproved)
|
||||||
|
ElevatedButton(
|
||||||
|
child: Text('招待再送信'),
|
||||||
|
onPressed: () => controller.resendInvitation(),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
foregroundColor: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: Text('保存・招待'),
|
||||||
|
onPressed: _handleSaveAndNavigateBack,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// lib/controllers/team_controller.dart
|
// lib/controllers/team_controller.dart
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rogapp/model/team.dart';
|
import 'package:rogapp/model/team.dart';
|
||||||
import 'package:rogapp/model/category.dart';
|
import 'package:rogapp/model/category.dart';
|
||||||
@ -27,6 +28,7 @@ class TeamController extends GetxController {
|
|||||||
super.onInit();
|
super.onInit();
|
||||||
try {
|
try {
|
||||||
_apiService = Get.find<ApiService>();
|
_apiService = Get.find<ApiService>();
|
||||||
|
isLoading.value = true;
|
||||||
|
|
||||||
await fetchCategories();
|
await fetchCategories();
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
@ -67,8 +69,13 @@ class TeamController extends GetxController {
|
|||||||
void resetForm() {
|
void resetForm() {
|
||||||
selectedTeam.value = null;
|
selectedTeam.value = null;
|
||||||
teamName.value = '';
|
teamName.value = '';
|
||||||
selectedCategory.value = categories.isNotEmpty ? categories.first : null;
|
if (categories.isNotEmpty) {
|
||||||
teamMembers.clear();
|
selectedCategory.value = categories.first;
|
||||||
|
} else {
|
||||||
|
selectedCategory.value = null;
|
||||||
|
// カテゴリが空の場合、エラーメッセージをセット
|
||||||
|
error.value = 'カテゴリデータが取得できませんでした。';
|
||||||
|
} teamMembers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanupForNavigation() {
|
void cleanupForNavigation() {
|
||||||
@ -93,6 +100,14 @@ class TeamController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkIfUserHasEntryData(){
|
||||||
|
if (teams.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> fetchCategories() async {
|
Future<void> fetchCategories() async {
|
||||||
try {
|
try {
|
||||||
final fetchedCategories = await _apiService.getCategories();
|
final fetchedCategories = await _apiService.getCategories();
|
||||||
@ -115,6 +130,17 @@ class TeamController extends GetxController {
|
|||||||
|
|
||||||
Future<Team> createTeam(String teamName, int categoryId) async {
|
Future<Team> createTeam(String teamName, int categoryId) async {
|
||||||
final newTeam = await _apiService.createTeam(teamName, categoryId);
|
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;
|
return newTeam;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,11 +151,51 @@ class TeamController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteTeam(int teamId) async {
|
Future<void> deleteTeam(int teamId) async {
|
||||||
try {
|
bool confirmDelete = await Get.dialog(
|
||||||
await _apiService.deleteTeam(teamId);
|
AlertDialog(
|
||||||
teams.removeWhere((team) => team.id == teamId);
|
title: Text('チーム削除の確認'),
|
||||||
} catch (e) {
|
content: Text('このチームとそのすべてのメンバーを削除しますか?この操作は取り消せません。'),
|
||||||
print('Error deleting team: $e');
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('キャンセル'),
|
||||||
|
onPressed: () => Get.back(result: false),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: 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('エラー', 'チームとメンバーの削除に失敗しました');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,4 +303,30 @@ class TeamController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<NewCategory> getFilteredCategories() {
|
||||||
|
//List<User> teamMembers = getCurrentTeamMembers();
|
||||||
|
return categories.where((category) {
|
||||||
|
return isCategoryValid(category, teamMembers);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCategoryValid(NewCategory category, List<User> 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<User> teamMembers) {
|
||||||
|
// 家族かどうかを判断するロジック(例: 同じ姓を持つメンバーが2人以上いる場合は家族とみなす)
|
||||||
|
Set<String> familyNames = teamMembers.map((member) => member.lastname).toSet();
|
||||||
|
return familyNames.length < teamMembers.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -126,20 +126,26 @@ class _TeamDetailPageState extends State<TeamDetailPage> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
DropdownButtonFormField<NewCategory>(
|
Obx(() {
|
||||||
decoration: InputDecoration(labelText: 'カテゴリ'),
|
if (controller.categories.isEmpty) {
|
||||||
value: controller.selectedCategory.value,
|
return Text('カテゴリデータを読み込めませんでした。', style: TextStyle(color: Colors.red));
|
||||||
items: controller.categories.map((category) => DropdownMenuItem(
|
}
|
||||||
value: category,
|
return DropdownButtonFormField<NewCategory>(
|
||||||
child: Text(category.categoryName),
|
decoration: InputDecoration(labelText: 'カテゴリ'),
|
||||||
)).toList(),
|
value: controller.selectedCategory.value,
|
||||||
onChanged: (value) => controller.updateCategory(value),
|
items: controller.categories.map((category) => DropdownMenuItem(
|
||||||
),
|
value: category,
|
||||||
if (mode.value == 'edit')
|
child: Text(category.categoryName),
|
||||||
Padding(
|
)).toList(),
|
||||||
padding: EdgeInsets.symmetric(vertical: 16),
|
/*
|
||||||
child: Text('ゼッケン番号: ${controller.selectedTeam.value?.zekkenNumber ?? ""}'),
|
items: controller.getFilteredCategories().map((category) => DropdownMenuItem(
|
||||||
),
|
value: category,
|
||||||
|
child: Text(category.categoryName),
|
||||||
|
)).toList(),
|
||||||
|
*/
|
||||||
|
onChanged: (value) => controller.updateCategory(value),
|
||||||
|
);
|
||||||
|
}),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 16),
|
padding: EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Text('所有者: ${controller.currentUser.value?.email ?? "未設定"}'),
|
child: Text('所有者: ${controller.currentUser.value?.email ?? "未設定"}'),
|
||||||
@ -160,7 +166,7 @@ class _TeamDetailPageState extends State<TeamDetailPage> {
|
|||||||
? '${member.lastname} ${member.firstname}'
|
? '${member.lastname} ${member.firstname}'
|
||||||
: member.isActive
|
: member.isActive
|
||||||
? '${member.lastname} ${member.firstname}'
|
? '${member.lastname} ${member.firstname}'
|
||||||
: '${member.email?.split('@')[0] ?? ''}(未承認)';
|
: '${member.email?.split('@')[0] ?? ''}'; //(未承認)';
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(displayName),
|
title: Text(displayName),
|
||||||
subtitle: isDummyEmail ? Text('Email未設定') : null,
|
subtitle: isDummyEmail ? Text('Email未設定') : null,
|
||||||
@ -169,9 +175,9 @@ class _TeamDetailPageState extends State<TeamDetailPage> {
|
|||||||
AppPages.MEMBER_DETAIL,
|
AppPages.MEMBER_DETAIL,
|
||||||
arguments: {'mode': 'edit', 'member': member, 'teamId': controller.selectedTeam.value?.id},
|
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);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -35,7 +35,7 @@ class TeamListPage extends GetWidget<TeamController> {
|
|||||||
final team = controller.teams[index];
|
final team = controller.teams[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(team.teamName),
|
title: Text(team.teamName),
|
||||||
subtitle: Text('${team.category.categoryName} - ${team.zekkenNumber}'),
|
subtitle: Text('${team.category.categoryName} '),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await Get.toNamed(
|
await Get.toNamed(
|
||||||
AppPages.TEAM_DETAIL,
|
AppPages.TEAM_DETAIL,
|
||||||
|
|||||||
@ -37,6 +37,10 @@ import 'package:rogapp/pages/entry/entry_list_page.dart';
|
|||||||
import 'package:rogapp/pages/entry/entry_detail_page.dart';
|
import 'package:rogapp/pages/entry/entry_detail_page.dart';
|
||||||
import 'package:rogapp/pages/entry/entry_binding.dart';
|
import 'package:rogapp/pages/entry/entry_binding.dart';
|
||||||
|
|
||||||
|
import 'package:rogapp/pages/entry/event_entries_page.dart';
|
||||||
|
import 'package:rogapp/pages/entry/event_entries_binding.dart';
|
||||||
|
import 'package:rogapp/pages/register/user_detail_page.dart';
|
||||||
|
|
||||||
part 'app_routes.dart';
|
part 'app_routes.dart';
|
||||||
|
|
||||||
class AppPages {
|
class AppPages {
|
||||||
@ -70,6 +74,9 @@ class AppPages {
|
|||||||
static const MEMBER_DETAIL = Routes.MEMBER_DETAIL;
|
static const MEMBER_DETAIL = Routes.MEMBER_DETAIL;
|
||||||
static const ENTRY_LIST = Routes.ENTRY_LIST;
|
static const ENTRY_LIST = Routes.ENTRY_LIST;
|
||||||
static const ENTRY_DETAIL = Routes.ENTRY_DETAIL;
|
static const ENTRY_DETAIL = Routes.ENTRY_DETAIL;
|
||||||
|
static const EVENT_ENTRY = Routes.EVENT_ENTRIES;
|
||||||
|
static const USER_DETAILS_EDIT = Routes.USER_DETAILS_EDIT;
|
||||||
|
|
||||||
|
|
||||||
static final routes = [
|
static final routes = [
|
||||||
GetPage(
|
GetPage(
|
||||||
@ -166,7 +173,15 @@ class AppPages {
|
|||||||
page: () => EntryDetailPage(),
|
page: () => EntryDetailPage(),
|
||||||
binding: EntryBinding(),
|
binding: EntryBinding(),
|
||||||
),
|
),
|
||||||
|
GetPage(
|
||||||
|
name: Routes.EVENT_ENTRIES,
|
||||||
|
page: () => EventEntriesPage(),
|
||||||
|
binding: EventEntriesBinding(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: Routes.USER_DETAILS_EDIT,
|
||||||
|
page: () => UserDetailsEditPage(),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,4 +34,8 @@ abstract class Routes {
|
|||||||
static const MEMBER_DETAIL = '/member-detail';
|
static const MEMBER_DETAIL = '/member-detail';
|
||||||
static const ENTRY_LIST = '/entry-list';
|
static const ENTRY_LIST = '/entry-list';
|
||||||
static const ENTRY_DETAIL = '/entry-detail';
|
static const ENTRY_DETAIL = '/entry-detail';
|
||||||
|
|
||||||
|
static const EVENT_ENTRIES = '/event-entries';
|
||||||
|
static const USER_DETAILS_EDIT = '/user-details-edit';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,13 +75,13 @@ class ApiService extends GetxService{
|
|||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
// UTF-8でデコード
|
// UTF-8でデコード
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||||
print('User Response body: $decodedResponse');
|
//print('User Response body: $decodedResponse');
|
||||||
List<dynamic> teamsJson = json.decode(decodedResponse);
|
List<dynamic> teamsJson = json.decode(decodedResponse);
|
||||||
|
|
||||||
List<Team> teams = [];
|
List<Team> teams = [];
|
||||||
for (var teamJson in teamsJson) {
|
for (var teamJson in teamsJson) {
|
||||||
print('\nTeam Data:');
|
//print('\nTeam Data:');
|
||||||
_printDataComparison(teamJson, Team);
|
//_printDataComparison(teamJson, Team);
|
||||||
teams.add(Team.fromJson(teamJson));
|
teams.add(Team.fromJson(teamJson));
|
||||||
}
|
}
|
||||||
return teams;
|
return teams;
|
||||||
@ -114,9 +114,14 @@ class ApiService extends GetxService{
|
|||||||
|
|
||||||
List<NewCategory> categories = [];
|
List<NewCategory> categories = [];
|
||||||
for (var categoryJson in categoriesJson) {
|
for (var categoryJson in categoriesJson) {
|
||||||
print('\nCategory Data:');
|
try {
|
||||||
_printDataComparison(categoryJson, NewCategory);
|
//print('\nCategory Data:');
|
||||||
categories.add(NewCategory.fromJson(categoryJson));
|
//_printDataComparison(categoryJson, NewCategory);
|
||||||
|
categories.add(NewCategory.fromJson(categoryJson));
|
||||||
|
}catch(e){
|
||||||
|
print('Error parsing category: $e');
|
||||||
|
print('Problematic JSON: $categoryJson');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return categories;
|
return categories;
|
||||||
@ -131,6 +136,26 @@ class ApiService extends GetxService{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<NewCategory> getZekkenNumber(int categoryId) async {
|
||||||
|
try {
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse('$baseUrl/categories-viewset/$categoryId/get_zekken_number/'),
|
||||||
|
headers: {'Authorization': 'Token $token',"Content-Type": "application/json; charset=UTF-8"},
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||||
|
print('User Response body: $decodedResponse');
|
||||||
|
final categoriesJson = json.decode(decodedResponse);
|
||||||
|
|
||||||
|
return NewCategory.fromJson(categoriesJson);
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to increment category number');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Error incrementing category number: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<User> getCurrentUser() async {
|
Future<User> getCurrentUser() async {
|
||||||
init();
|
init();
|
||||||
getToken();
|
getToken();
|
||||||
@ -143,11 +168,11 @@ class ApiService extends GetxService{
|
|||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||||
print('User Response body: $decodedResponse');
|
//print('User Response body: $decodedResponse');
|
||||||
final jsonData = json.decode(decodedResponse);
|
final jsonData = json.decode(decodedResponse);
|
||||||
|
|
||||||
print('\nUser Data Comparison:');
|
//print('\nUser Data Comparison:');
|
||||||
_printDataComparison(jsonData, User);
|
//_printDataComparison(jsonData, User);
|
||||||
|
|
||||||
return User.fromJson(jsonData);
|
return User.fromJson(jsonData);
|
||||||
} else {
|
} else {
|
||||||
@ -277,7 +302,11 @@ class ApiService extends GetxService{
|
|||||||
headers: {'Authorization': 'Token $token','Content-Type': 'application/json; charset=UTF-8'},
|
headers: {'Authorization': 'Token $token','Content-Type': 'application/json; charset=UTF-8'},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode != 204) {
|
if( response.statusCode == 400) {
|
||||||
|
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||||
|
print('User Response body: $decodedResponse');
|
||||||
|
throw Exception('まだメンバーが残っているので、チームを削除できません。');
|
||||||
|
}else if (response.statusCode != 204) {
|
||||||
throw Exception('Failed to delete team');
|
throw Exception('Failed to delete team');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,10 +331,18 @@ class ApiService extends GetxService{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<User> createTeamMember(int teamId, String? email, String firstname, String lastname, DateTime? dateOfBirth,bool? female) async {
|
Future<User> createTeamMember(int teamId, String? email, String? firstname, String? lastname, DateTime? dateOfBirth,bool? female) async {
|
||||||
init();
|
init();
|
||||||
getToken();
|
getToken();
|
||||||
|
|
||||||
|
// emailが値を持っている場合の処理
|
||||||
|
if (email != null && email.isNotEmpty) {
|
||||||
|
firstname ??= "dummy";
|
||||||
|
lastname ??= "dummy";
|
||||||
|
dateOfBirth ??= DateTime.now();
|
||||||
|
female ??= false;
|
||||||
|
}
|
||||||
|
|
||||||
String? formattedDateOfBirth;
|
String? formattedDateOfBirth;
|
||||||
if (dateOfBirth != null) {
|
if (dateOfBirth != null) {
|
||||||
formattedDateOfBirth = DateFormat('yyyy-MM-dd').format(dateOfBirth);
|
formattedDateOfBirth = DateFormat('yyyy-MM-dd').format(dateOfBirth);
|
||||||
@ -326,7 +363,7 @@ class ApiService extends GetxService{
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 201) {
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||||
return User.fromJson(json.decode(decodedResponse));
|
return User.fromJson(json.decode(decodedResponse));
|
||||||
} else {
|
} else {
|
||||||
@ -379,6 +416,17 @@ class ApiService extends GetxService{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteAllTeamMembers(int teamId) async {
|
||||||
|
final response = await http.delete(
|
||||||
|
Uri.parse('$baseUrl/teams/$teamId/members/destroy_all/?confirm=true'),
|
||||||
|
headers: {'Authorization': 'Token $token'},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw Exception('Failed to delete team members');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> resendMemberInvitation(int memberId) async {
|
Future<void> resendMemberInvitation(int memberId) async {
|
||||||
init();
|
init();
|
||||||
getToken();
|
getToken();
|
||||||
@ -434,7 +482,7 @@ class ApiService extends GetxService{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Entry> createEntry(int teamId, int eventId, int categoryId, DateTime date) async {
|
Future<Entry> createEntry(int teamId, int eventId, int categoryId, DateTime date,String zekkenNumber) async {
|
||||||
init();
|
init();
|
||||||
getToken();
|
getToken();
|
||||||
|
|
||||||
@ -454,6 +502,7 @@ class ApiService extends GetxService{
|
|||||||
'event': eventId,
|
'event': eventId,
|
||||||
'category': categoryId,
|
'category': categoryId,
|
||||||
'date': formattedDate,
|
'date': formattedDate,
|
||||||
|
'zekken_number':zekkenNumber,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -462,11 +511,50 @@ class ApiService extends GetxService{
|
|||||||
|
|
||||||
return Entry.fromJson(json.decode(decodedResponse));
|
return Entry.fromJson(json.decode(decodedResponse));
|
||||||
} else {
|
} else {
|
||||||
|
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||||
|
print("decodedResponse = $decodedResponse");
|
||||||
throw Exception('Failed to create entry');
|
throw Exception('Failed to create entry');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Entry> updateEntry(int entryId, int eventId, int categoryId, DateTime date) async {
|
Future<void> updateUserInfo(int userId, Entry entry) async {
|
||||||
|
init();
|
||||||
|
getToken();
|
||||||
|
|
||||||
|
final entryId = entry.id;
|
||||||
|
|
||||||
|
DateTime? date = entry.date;
|
||||||
|
String? formattedDate;
|
||||||
|
if (date != null) {
|
||||||
|
formattedDate = DateFormat('yyyy-MM-dd').format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
final response = await http.put(
|
||||||
|
Uri.parse('$baseUrl/userinfo/$userId/'),
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Token $token',
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
},
|
||||||
|
body: json.encode({
|
||||||
|
'zekken_number': entry.zekkenNumber,
|
||||||
|
'event_code': entry.event.eventName,
|
||||||
|
'group': entry.team.category.categoryName,
|
||||||
|
'team_name': entry.team.teamName,
|
||||||
|
'date': formattedDate,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||||
|
final updatedUserInfo = json.decode(decodedResponse);
|
||||||
|
//Get.find<IndexController>().updateUserInfo(updatedUserInfo);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to update entry');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Future<Entry> updateEntry(int entryId, int teamId, int eventId, int categoryId, DateTime date,int zekken_number) async {
|
||||||
init();
|
init();
|
||||||
getToken();
|
getToken();
|
||||||
|
|
||||||
@ -482,9 +570,11 @@ class ApiService extends GetxService{
|
|||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
},
|
},
|
||||||
body: json.encode({
|
body: json.encode({
|
||||||
|
'team': teamId,
|
||||||
'event': eventId,
|
'event': eventId,
|
||||||
'category': categoryId,
|
'category': categoryId,
|
||||||
'date': formattedDate,
|
'date': formattedDate,
|
||||||
|
'zekken_number': zekken_number,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -493,6 +583,9 @@ class ApiService extends GetxService{
|
|||||||
|
|
||||||
return Entry.fromJson(json.decode(decodedResponse));
|
return Entry.fromJson(json.decode(decodedResponse));
|
||||||
} else {
|
} else {
|
||||||
|
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||||
|
final blk = json.decode(decodedResponse);
|
||||||
|
|
||||||
throw Exception('Failed to update entry');
|
throw Exception('Failed to update entry');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,4 +603,91 @@ class ApiService extends GetxService{
|
|||||||
throw Exception('Failed to delete entry');
|
throw Exception('Failed to delete entry');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<bool> updateUserDetail(User user, String token) async {
|
||||||
|
String serverUrl = ConstValues.currentServer();
|
||||||
|
int? userid = user.id;
|
||||||
|
String url = '$serverUrl/api/userdetail/$userid/';
|
||||||
|
|
||||||
|
try {
|
||||||
|
String? formattedDate;
|
||||||
|
if (user.dateOfBirth != null) {
|
||||||
|
formattedDate = DateFormat('yyyy-MM-dd').format(user.dateOfBirth!);
|
||||||
|
}
|
||||||
|
final http.Response response = await http.put(
|
||||||
|
Uri.parse(url),
|
||||||
|
headers: <String, String>{
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
'Authorization': 'Token $token'
|
||||||
|
},
|
||||||
|
body: jsonEncode({
|
||||||
|
'firstname': user.firstname,
|
||||||
|
'lastname': user.lastname,
|
||||||
|
'date_of_birth': formattedDate,
|
||||||
|
'female': user.female,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
print('Update failed with status code: ${response.statusCode}');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error in updateUserDetail: $e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> resetPassword(String email) async {
|
||||||
|
init();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse('$baseUrl/password-reset/'),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
},
|
||||||
|
body: json.encode({
|
||||||
|
'email': email,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
print('Password reset failed with status code: ${response.statusCode}');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error in resetPassword: $e');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DateTime?> getLastGoalTime(int userId) async {
|
||||||
|
try {
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse('$baseUrl/users/$userId/last-goal/'),
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Token $token',
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final decodedResponse = json.decode(utf8.decode(response.bodyBytes));
|
||||||
|
if (decodedResponse['last_goal_time'] != null) {
|
||||||
|
return DateTime.parse(decodedResponse['last_goal_time']).toLocal();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print('Failed to get last goal time. Status code: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error in getLastGoalTime: $e');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -5,6 +5,9 @@ import 'package:get/get.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../utils/const.dart';
|
import '../utils/const.dart';
|
||||||
|
//import 'package:rogapp/services/team_service.dart';
|
||||||
|
//import 'package:rogapp/services/member_service.dart';
|
||||||
|
|
||||||
|
|
||||||
class AuthService {
|
class AuthService {
|
||||||
Future<AuthUser?> userLogin(String email, String password) async {
|
Future<AuthUser?> userLogin(String email, String password) async {
|
||||||
@ -129,10 +132,27 @@ class AuthService {
|
|||||||
return cats;
|
return cats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ユーザー登録
|
// ユーザー登録
|
||||||
//
|
//
|
||||||
|
/*
|
||||||
|
Future<void> registerUser(String email, String password, bool isFemale) async {
|
||||||
|
final user = await register(email, password);
|
||||||
|
if (user != null) {
|
||||||
|
final _teamController = TeamController();
|
||||||
|
_teamController.createTeam(String teamName, int categoryId) ;
|
||||||
|
final teamService = TeamService();
|
||||||
|
final memberService = MemberService();
|
||||||
|
|
||||||
|
final team = await teamService.createSoloTeam(user.id, isFemale);
|
||||||
|
await memberService.addMember(team.id, user.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
static Future<Map<String, dynamic>> register(
|
static Future<Map<String, dynamic>> register(
|
||||||
String email, String password) async {
|
String email, String password, String password2) async {
|
||||||
Map<String, dynamic> cats = {};
|
Map<String, dynamic> cats = {};
|
||||||
String serverUrl = ConstValues.currentServer();
|
String serverUrl = ConstValues.currentServer();
|
||||||
String url = '$serverUrl/api/register/';
|
String url = '$serverUrl/api/register/';
|
||||||
@ -142,11 +162,12 @@ class AuthService {
|
|||||||
headers: <String, String>{
|
headers: <String, String>{
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
},
|
},
|
||||||
body: jsonEncode(<String, String>{'email': email, 'password': password}),
|
body: jsonEncode(<String, String>{'email': email, 'password': password, 'password2': password2}),
|
||||||
);
|
);
|
||||||
//print(response.body);
|
cats = json.decode(utf8.decode(response.bodyBytes));
|
||||||
if (response.statusCode == 200) {
|
print("result=$cats");
|
||||||
cats = json.decode(utf8.decode(response.bodyBytes));
|
if (response.statusCode == 201) {
|
||||||
|
}else{
|
||||||
}
|
}
|
||||||
return cats;
|
return cats;
|
||||||
}
|
}
|
||||||
@ -168,6 +189,7 @@ class AuthService {
|
|||||||
return cats;
|
return cats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Future<List<dynamic>?> userDetails(int userid) async {
|
static Future<List<dynamic>?> userDetails(int userid) async {
|
||||||
List<dynamic> cats = [];
|
List<dynamic> cats = [];
|
||||||
String serverUrl = ConstValues.currentServer();
|
String serverUrl = ConstValues.currentServer();
|
||||||
|
|||||||
@ -4,8 +4,10 @@ import 'package:get/get.dart';
|
|||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:rogapp/model/rog.dart';
|
import 'package:rogapp/model/rog.dart';
|
||||||
|
import 'package:rogapp/model/team.dart';
|
||||||
import 'package:rogapp/pages/destination/destination_controller.dart';
|
import 'package:rogapp/pages/destination/destination_controller.dart';
|
||||||
import 'package:rogapp/pages/index/index_controller.dart';
|
import 'package:rogapp/pages/index/index_controller.dart';
|
||||||
|
import 'package:rogapp/pages/team/team_controller.dart';
|
||||||
import 'package:rogapp/utils/database_gps.dart';
|
import 'package:rogapp/utils/database_gps.dart';
|
||||||
import 'package:rogapp/utils/database_helper.dart';
|
import 'package:rogapp/utils/database_helper.dart';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
@ -32,11 +34,19 @@ class ExternalService {
|
|||||||
|
|
||||||
Future<Map<String, dynamic>> startRogaining() async {
|
Future<Map<String, dynamic>> startRogaining() async {
|
||||||
final IndexController indexController = Get.find<IndexController>();
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
//final TeamController teamController = Get.find<TeamController>();
|
||||||
|
|
||||||
debugPrint("== startRogaining ==");
|
debugPrint("== startRogaining ==");
|
||||||
|
|
||||||
Map<String, dynamic> res = {};
|
Map<String, dynamic> res = {};
|
||||||
|
|
||||||
|
//final teamController = TeamController();
|
||||||
|
|
||||||
|
//Team team0 = teamController.teams[0];
|
||||||
|
//print("team={team0}");
|
||||||
|
|
||||||
|
//int teamId = indexController.currentUser[0]["user"]["team"]["id"];
|
||||||
|
|
||||||
int userId = indexController.currentUser[0]["user"]["id"];
|
int userId = indexController.currentUser[0]["user"]["id"];
|
||||||
//print("--- Pressed -----");
|
//print("--- Pressed -----");
|
||||||
String team = indexController.currentUser[0]["user"]['team_name'];
|
String team = indexController.currentUser[0]["user"]['team_name'];
|
||||||
@ -60,8 +70,9 @@ class ExternalService {
|
|||||||
} else {
|
} else {
|
||||||
debugPrint("== startRogaining processing==");
|
debugPrint("== startRogaining processing==");
|
||||||
|
|
||||||
String url = 'https://rogaining.sumasen.net/gifuroge/start_from_rogapp';
|
String serverUrl = ConstValues.currentServer();
|
||||||
//print('++++++++$url');
|
String url = '$serverUrl/gifuroge/start_from_rogapp';
|
||||||
|
print('++++++++$url');
|
||||||
final http.Response response = await http.post(
|
final http.Response response = await http.post(
|
||||||
Uri.parse(url),
|
Uri.parse(url),
|
||||||
headers: <String, String>{
|
headers: <String, String>{
|
||||||
@ -82,7 +93,7 @@ class ExternalService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> makeCheckpoint(
|
Future<Map<String, dynamic>> makeCheckpoint(
|
||||||
int userId,
|
int userId, // 中身はteamId
|
||||||
String token,
|
String token,
|
||||||
String checkinTime,
|
String checkinTime,
|
||||||
String teamname,
|
String teamname,
|
||||||
@ -93,9 +104,17 @@ class ExternalService {
|
|||||||
// print("~~~~ cp is $cp ~~~~");
|
// print("~~~~ cp is $cp ~~~~");
|
||||||
//print("--cpcp-- ${cp}");
|
//print("--cpcp-- ${cp}");
|
||||||
Map<String, dynamic> res = {};
|
Map<String, dynamic> res = {};
|
||||||
String url = 'https://rogaining.sumasen.net/gifuroge/checkin_from_rogapp';
|
String serverUrl = ConstValues.currentServer();
|
||||||
|
String url = '$serverUrl/gifuroge/checkin_from_rogapp';
|
||||||
//print('++++++++$url');
|
//print('++++++++$url');
|
||||||
final IndexController indexController = Get.find<IndexController>();
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
//final TeamController teamController = Get.find<TeamController>();
|
||||||
|
|
||||||
|
|
||||||
|
// Team team0 = indexController.teamController.teams[0];
|
||||||
|
// print("team={team0}");
|
||||||
|
|
||||||
|
//int teamId = indexController.teamController.teams[0];
|
||||||
|
|
||||||
if (imageurl != null) {
|
if (imageurl != null) {
|
||||||
if (indexController.connectionStatusName.value != "wifi" &&
|
if (indexController.connectionStatusName.value != "wifi" &&
|
||||||
@ -160,7 +179,7 @@ class ExternalService {
|
|||||||
'cp_number': cp.toString(),
|
'cp_number': cp.toString(),
|
||||||
'event_code': eventcode,
|
'event_code': eventcode,
|
||||||
'image': res["checkinimage"].toString().replaceAll(
|
'image': res["checkinimage"].toString().replaceAll(
|
||||||
'http://localhost:8100', 'http://rogaining.sumasen.net')
|
'http://localhost:8100', serverUrl) //'http://rogaining.sumasen.net')
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
var vv = jsonEncode(<String, String>{
|
var vv = jsonEncode(<String, String>{
|
||||||
@ -168,7 +187,7 @@ class ExternalService {
|
|||||||
'cp_number': cp.toString(),
|
'cp_number': cp.toString(),
|
||||||
'event_code': eventcode,
|
'event_code': eventcode,
|
||||||
'image': res["checkinimage"].toString().replaceAll(
|
'image': res["checkinimage"].toString().replaceAll(
|
||||||
'http://localhost:8100', 'http://rogaining.sumasen.net')
|
'http://localhost:8100', serverUrl) //'http://rogaining.sumasen.net')
|
||||||
});
|
});
|
||||||
//print("~~~~ api 2 values $vv ~~~~");
|
//print("~~~~ api 2 values $vv ~~~~");
|
||||||
//print("--json-- $vv");
|
//print("--json-- $vv");
|
||||||
@ -183,6 +202,7 @@ class ExternalService {
|
|||||||
colorText: Colors.white
|
colorText: Colors.white
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Get.snackbar("サーバーエラーがおきました", "サーバーと通信できませんでした",
|
Get.snackbar("サーバーエラーがおきました", "サーバーと通信できませんでした",
|
||||||
@ -253,6 +273,10 @@ class ExternalService {
|
|||||||
final DestinationController destinationController =
|
final DestinationController destinationController =
|
||||||
Get.find<DestinationController>();
|
Get.find<DestinationController>();
|
||||||
|
|
||||||
|
// チームIDを取得
|
||||||
|
|
||||||
|
//int teamId = indexController.currentUser[0]["user"]["team"]["id"];
|
||||||
|
|
||||||
debugPrint("== goal Rogaining ==");
|
debugPrint("== goal Rogaining ==");
|
||||||
|
|
||||||
//if(indexController.connectionStatusName != "wifi" && indexController.connectionStatusName != "mobile"){
|
//if(indexController.connectionStatusName != "wifi" && indexController.connectionStatusName != "mobile"){
|
||||||
@ -261,7 +285,7 @@ class ExternalService {
|
|||||||
id: 1,
|
id: 1,
|
||||||
team_name: teamname,
|
team_name: teamname,
|
||||||
event_code: eventcode,
|
event_code: eventcode,
|
||||||
user_id: userId,
|
user_id: userId, // 中身はteamid
|
||||||
cp_number: -1,
|
cp_number: -1,
|
||||||
checkintime: DateTime.now().toUtc().microsecondsSinceEpoch,
|
checkintime: DateTime.now().toUtc().microsecondsSinceEpoch,
|
||||||
image: image,
|
image: image,
|
||||||
@ -283,7 +307,7 @@ class ExternalService {
|
|||||||
},
|
},
|
||||||
// 'id', 'user', 'goalimage', 'goaltime', 'team_name', 'event_code','cp_number'
|
// 'id', 'user', 'goalimage', 'goaltime', 'team_name', 'event_code','cp_number'
|
||||||
body: jsonEncode(<String, String>{
|
body: jsonEncode(<String, String>{
|
||||||
'user': userId.toString(),
|
'user': userId.toString(), //userId.toString(),
|
||||||
'team_name': teamname,
|
'team_name': teamname,
|
||||||
'event_code': eventcode,
|
'event_code': eventcode,
|
||||||
'goaltime': goalTime,
|
'goaltime': goalTime,
|
||||||
@ -292,37 +316,46 @@ class ExternalService {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
String url = 'https://rogaining.sumasen.net/gifuroge/goal_from_rogapp';
|
//String serverUrl = ConstValues.currentServer();
|
||||||
|
String url = '$serverUrl/gifuroge/goal_from_rogapp';
|
||||||
//print('++++++++$url');
|
//print('++++++++$url');
|
||||||
if (response.statusCode == 201) {
|
if (response.statusCode == 201) {
|
||||||
Map<String, dynamic> res = json.decode(utf8.decode(response.bodyBytes));
|
try {
|
||||||
// print('----_res : $res ----');
|
Map<String, dynamic> res = json.decode(utf8.decode(response.bodyBytes));
|
||||||
// print('---- image url ${res["goalimage"]} ----');
|
// print('----_res : $res ----');
|
||||||
final http.Response response2 = await http.post(
|
// print('---- image url ${res["goalimage"]} ----');
|
||||||
Uri.parse(url),
|
final http.Response response2 = await http.post(
|
||||||
headers: <String, String>{
|
Uri.parse(url),
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
headers: <String, String>{
|
||||||
},
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
body: jsonEncode(<String, String>{
|
},
|
||||||
|
body: jsonEncode(<String, String>{
|
||||||
|
'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(<String, String>{
|
||||||
'team_name': teamname,
|
'team_name': teamname,
|
||||||
'event_code': eventcode,
|
'event_code': eventcode,
|
||||||
'goal_time': goalTime,
|
'goal_time': goalTime,
|
||||||
'image': res["goalimage"].toString().replaceAll(
|
'image': res["goalimage"]
|
||||||
'http://localhost:8100', 'http://rogaining.sumasen.net')
|
.toString()
|
||||||
}),
|
.replaceAll('http://localhost:8100', serverUrl)
|
||||||
);
|
//'http://rogaining.sumasen.net')
|
||||||
String rec = jsonEncode(<String, String>{
|
});
|
||||||
'team_name': teamname,
|
//print("-- json -- $rec");
|
||||||
'event_code': eventcode,
|
//print('----- response2 is $response2 --------');
|
||||||
'goal_time': goalTime,
|
if (response2.statusCode == 200) {
|
||||||
'image': res["goalimage"]
|
res2 = json.decode(utf8.decode(response2.bodyBytes));
|
||||||
.toString()
|
} else {
|
||||||
.replaceAll('http://localhost:8100', 'http://rogaining.sumasen.net')
|
res2 = json.decode(utf8.decode(response2.bodyBytes));
|
||||||
});
|
}
|
||||||
//print("-- json -- $rec");
|
} catch(e){
|
||||||
//print('----- response2 is $response2 --------');
|
print( "Error {$e}" );
|
||||||
if (response2.statusCode == 200) {
|
|
||||||
res2 = json.decode(utf8.decode(response2.bodyBytes));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
@ -343,8 +376,8 @@ class ExternalService {
|
|||||||
indexController.connectionStatusName.value != "mobile") {
|
indexController.connectionStatusName.value != "mobile") {
|
||||||
return Future.value(false);
|
return Future.value(false);
|
||||||
} else {
|
} else {
|
||||||
String url =
|
String serverUrl = ConstValues.currentServer();
|
||||||
'https://rogaining.sumasen.net/gifuroge/remove_checkin_from_rogapp';
|
String url = '$serverUrl/gifuroge/remove_checkin_from_rogapp';
|
||||||
//print('++++++++$url');
|
//print('++++++++$url');
|
||||||
final http.Response response = await http.post(
|
final http.Response response = await http.post(
|
||||||
Uri.parse(url),
|
Uri.parse(url),
|
||||||
@ -413,8 +446,8 @@ class ExternalService {
|
|||||||
|
|
||||||
//print("calling push gps step 2 ${payload}");
|
//print("calling push gps step 2 ${payload}");
|
||||||
|
|
||||||
String urlS =
|
String serverUrl = ConstValues.currentServer();
|
||||||
'https://rogaining.sumasen.net/gifuroge/get_waypoint_datas_from_rogapp';
|
String urlS = '$serverUrl/gifuroge/get_waypoint_datas_from_rogapp';
|
||||||
//print('++++++++$url');
|
//print('++++++++$url');
|
||||||
var url = Uri.parse(urlS); // Replace with your server URL
|
var url = Uri.parse(urlS); // Replace with your server URL
|
||||||
var response = await http.post(
|
var response = await http.post(
|
||||||
@ -440,8 +473,8 @@ class ExternalService {
|
|||||||
static Future<Map<String, dynamic>> usersEventCode(
|
static Future<Map<String, dynamic>> usersEventCode(
|
||||||
String teamcode, String password) async {
|
String teamcode, String password) async {
|
||||||
Map<String, dynamic> res = {};
|
Map<String, dynamic> res = {};
|
||||||
String url =
|
String serverUrl = ConstValues.currentServer();
|
||||||
"https://rogaining.sumasen.net/gifuroge/check_event_code?zekken_number=$teamcode&password=$password";
|
String url = "$serverUrl/gifuroge/check_event_code?zekken_number=$teamcode&password=$password";
|
||||||
//print('++++++++$url');
|
//print('++++++++$url');
|
||||||
final http.Response response =
|
final http.Response response =
|
||||||
await http.get(Uri.parse(url), headers: <String, String>{
|
await http.get(Uri.parse(url), headers: <String, String>{
|
||||||
|
|||||||
@ -82,59 +82,93 @@ class LocationService {
|
|||||||
double lon3,
|
double lon3,
|
||||||
double lat4,
|
double lat4,
|
||||||
double lon4,
|
double lon4,
|
||||||
String cat) async {
|
String cat,
|
||||||
|
String event_code) async {
|
||||||
//print("-------- in location for bound -------------");
|
//print("-------- in location for bound -------------");
|
||||||
final IndexController indexController = Get.find<IndexController>();
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
String url = "";
|
final updateTime = indexController.lastUserUpdateTime.value;
|
||||||
String serverUrl = ConstValues.currentServer();
|
|
||||||
if (cat.isNotEmpty) {
|
|
||||||
if (indexController.currentUser.isNotEmpty) {
|
|
||||||
bool rog = indexController.currentUser[0]['user']['is_rogaining'];
|
|
||||||
String r = rog == true ? 'True' : 'False';
|
|
||||||
var grp = indexController.currentUser[0]['user']['event_code'];
|
|
||||||
url =
|
|
||||||
'$serverUrl/api/inbound?rog=$r&grp=$grp&ln1=$lon1&la1=$lat1&ln2=$lon2&la2=$lat2&ln3=$lon3&la3=$lat3&ln4=$lon4&la4=$lat4&cat=$cat';
|
|
||||||
} else {
|
|
||||||
url =
|
|
||||||
'$serverUrl/api/inbound?ln1=$lon1&la1=$lat1&ln2=$lon2&la2=$lat2&ln3=$lon3&la3=$lat3&ln4=$lon4&la4=$lat4&cat=$cat';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (indexController.currentUser.isNotEmpty) {
|
|
||||||
bool rog = indexController.currentUser[0]['user']['is_rogaining'];
|
|
||||||
String r = rog == true ? 'True' : 'False';
|
|
||||||
var grp = indexController.currentUser[0]['user']['event_code'];
|
|
||||||
//print("-------- requested user group $grp -------------");
|
|
||||||
url =
|
|
||||||
'$serverUrl/api/inbound?rog=$r&grp=$grp&ln1=$lon1&la1=$lat1&ln2=$lon2&la2=$lat2&ln3=$lon3&la3=$lat3&ln4=$lon4&la4=$lat4';
|
|
||||||
} else {
|
|
||||||
url =
|
|
||||||
'$serverUrl/api/inbound?ln1=$lon1&la1=$lat1&ln2=$lon2&la2=$lat2&ln3=$lon3&la3=$lat3&ln4=$lon4&la4=$lat4';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//print('++++++++$url');
|
|
||||||
final response = await http.get(
|
|
||||||
Uri.parse(url),
|
|
||||||
headers: <String, String>{
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.statusCode == 500) {
|
// ユーザー情報の更新を最大5秒間待つ
|
||||||
return null; //featuresFromGeoJson(utf8.decode(response.bodyBytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
try {
|
||||||
DestinationController destinationController =
|
/*
|
||||||
Get.find<DestinationController>();
|
// ユーザー情報の更新を最大5秒間待つ
|
||||||
GeoJSONFeatureCollection cc =
|
final newUpdateTime = await indexController.lastUserUpdateTime.stream
|
||||||
GeoJSONFeatureCollection.fromJSON(utf8.decode(response.bodyBytes));
|
.firstWhere(
|
||||||
if (cc.features.isEmpty) {
|
(time) => time.isAfter(updateTime),
|
||||||
|
orElse: () => updateTime,
|
||||||
|
).timeout(Duration(seconds: 5));
|
||||||
|
|
||||||
|
if (newUpdateTime == updateTime) {
|
||||||
|
print('ユーザー情報の更新がタイムアウトしました');
|
||||||
|
// タイムアウト時の処理(例:エラー表示やリトライ)
|
||||||
return null;
|
return null;
|
||||||
} else {
|
|
||||||
//print("---- feature got from server is ${cc.collection[0].properties} ------");
|
|
||||||
return cc;
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
await indexController.lastUserUpdateTime.stream.firstWhere(
|
||||||
|
(time) => time.isAfter(updateTime),
|
||||||
|
orElse: () => updateTime,
|
||||||
|
).timeout(Duration(seconds: 2), onTimeout: () => updateTime);
|
||||||
|
*/
|
||||||
|
|
||||||
|
String url = "";
|
||||||
|
String serverUrl = ConstValues.currentServer();
|
||||||
|
if (cat.isNotEmpty) {
|
||||||
|
if (indexController.currentUser.isNotEmpty) {
|
||||||
|
bool rog = indexController.currentUser[0]['user']['is_rogaining'];
|
||||||
|
String r = rog == true ? 'True' : 'False';
|
||||||
|
var grp = event_code; //indexController.currentUser[0]['user']['event_code'];
|
||||||
|
print("Group=$grp");
|
||||||
|
url =
|
||||||
|
'$serverUrl/api/inbound2?rog=$r&grp=$grp&ln1=$lon1&la1=$lat1&ln2=$lon2&la2=$lat2&ln3=$lon3&la3=$lat3&ln4=$lon4&la4=$lat4&cat=$cat';
|
||||||
|
} else {
|
||||||
|
url =
|
||||||
|
'$serverUrl/api/inbound2?ln1=$lon1&la1=$lat1&ln2=$lon2&la2=$lat2&ln3=$lon3&la3=$lat3&ln4=$lon4&la4=$lat4&cat=$cat';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (indexController.currentUser.isNotEmpty) {
|
||||||
|
bool rog = indexController.currentUser[0]['user']['is_rogaining'];
|
||||||
|
String r = rog == true ? 'True' : 'False';
|
||||||
|
var grp = indexController.currentUser[0]['user']['event_code'];
|
||||||
|
print("-------- requested user group $grp -------------");
|
||||||
|
url =
|
||||||
|
'$serverUrl/api/inbound2?rog=$r&grp=$grp&ln1=$lon1&la1=$lat1&ln2=$lon2&la2=$lat2&ln3=$lon3&la3=$lat3&ln4=$lon4&la4=$lat4';
|
||||||
|
} else {
|
||||||
|
url =
|
||||||
|
'$serverUrl/api/inbound2?ln1=$lon1&la1=$lat1&ln2=$lon2&la2=$lat2&ln3=$lon3&la3=$lat3&ln4=$lon4&la4=$lat4';
|
||||||
|
}
|
||||||
|
print('++++++++$url');
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse(url),
|
||||||
|
headers: <String, String>{
|
||||||
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 500) {
|
||||||
|
return null; //featuresFromGeoJson(utf8.decode(response.bodyBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
DestinationController destinationController =
|
||||||
|
Get.find<DestinationController>();
|
||||||
|
|
||||||
|
GeoJSONFeatureCollection cc =
|
||||||
|
GeoJSONFeatureCollection.fromJSON(utf8.decode(response.bodyBytes));
|
||||||
|
if (cc.features.isEmpty) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
//print("---- feature got from server is ${cc.collection[0].properties} ------");
|
||||||
|
return cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
print("Error: $e");
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
|
|
||||||
class ConstValues{
|
class ConstValues{
|
||||||
static const container_svr = "http://container.intranet.sumasen.net:8100";
|
//static const container_svr = "http://container.intranet.sumasen.net:8100";
|
||||||
static const server_uri = "https://rogaining.intranet.sumasen.net";
|
//static const server_uri = "https://rogaining.intranet.sumasen.net";
|
||||||
|
static const container_svr = "http://container.sumasen.net:8100";
|
||||||
|
static const server_uri = "https://rogaining.sumasen.net";
|
||||||
static const dev_server = "http://localhost:8100";
|
static const dev_server = "http://localhost:8100";
|
||||||
static const dev_ip_server = "http://192.168.8.100:8100";
|
static const dev_ip_server = "http://192.168.8.100:8100";
|
||||||
static const dev_home_ip_server = "http://172.20.10.9:8100";
|
static const dev_home_ip_server = "http://172.20.10.9:8100";
|
||||||
|
|||||||
@ -213,6 +213,9 @@ class StringValues extends Translations{
|
|||||||
'reset_explain': 'All data has been reset. You should tap start rogaining to start game.',
|
'reset_explain': 'All data has been reset. You should tap start rogaining to start game.',
|
||||||
'no_match': 'No match!',
|
'no_match': 'No match!',
|
||||||
'password_does_not_match':'The passwords you entered were not match.',
|
'password_does_not_match':'The passwords you entered were not match.',
|
||||||
|
'forgot_password':'Forgot password',
|
||||||
|
'user_registration_successful':'Sent activation mail to you. Pls click activation link on the email.',
|
||||||
|
|
||||||
},
|
},
|
||||||
'ja_JP': {
|
'ja_JP': {
|
||||||
'drawer_title':'ロゲイニング参加者はログイン するとチェックポイントが参照 できます',
|
'drawer_title':'ロゲイニング参加者はログイン するとチェックポイントが参照 できます',
|
||||||
@ -311,7 +314,7 @@ class StringValues extends Translations{
|
|||||||
'already_have_account': 'すでにアカウントをお持ちですか?',
|
'already_have_account': 'すでにアカウントをお持ちですか?',
|
||||||
'sign_up': 'サインアップ',
|
'sign_up': 'サインアップ',
|
||||||
'create_account': 'アカウントを無料で作成します',
|
'create_account': 'アカウントを無料で作成します',
|
||||||
'confirm_password': 'パスワードを認証する',
|
'confirm_password': '確認用パスワード',
|
||||||
'cancel_checkin': 'チェックイン取消',
|
'cancel_checkin': 'チェックイン取消',
|
||||||
'go_here': 'ルート表示',
|
'go_here': 'ルート表示',
|
||||||
'cancel_route':'ルート消去',
|
'cancel_route':'ルート消去',
|
||||||
@ -428,6 +431,8 @@ class StringValues extends Translations{
|
|||||||
'reset_explain': 'すべてリセットされました。ロゲ開始から再開して下さい。',
|
'reset_explain': 'すべてリセットされました。ロゲ開始から再開して下さい。',
|
||||||
'no_match': '不一致',
|
'no_match': '不一致',
|
||||||
'password_does_not_match':'入力したパスワードが一致しません',
|
'password_does_not_match':'入力したパスワードが一致しません',
|
||||||
|
'forgot_password':'パスワードを忘れた場合',
|
||||||
|
'user_registration_successful':'ユーザー認証のメールをお届けしました。メール上のリンクをクリックして正式登録してください。',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,10 @@ import 'package:rogapp/widgets/bottom_sheet_controller.dart';
|
|||||||
import 'package:rogapp/widgets/debug_widget.dart';
|
import 'package:rogapp/widgets/debug_widget.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
import 'package:timezone/timezone.dart' as tz;
|
||||||
|
import 'package:timezone/data/latest.dart' as tz;
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
// BottomSheetNewは、StatelessWidgetを継承したクラスで、目的地の詳細情報を表示するボトムシートのUIを構築します。
|
// BottomSheetNewは、StatelessWidgetを継承したクラスで、目的地の詳細情報を表示するボトムシートのUIを構築します。
|
||||||
// コンストラクタでは、destination(目的地オブジェクト)とisAlreadyCheckedIn(すでにチェックイン済みかどうかのフラグ)を受け取ります。
|
// コンストラクタでは、destination(目的地オブジェクト)とisAlreadyCheckedIn(すでにチェックイン済みかどうかのフラグ)を受け取ります。
|
||||||
// buildメソッドでは、detailsSheetメソッドを呼び出して、目的地の詳細情報を表示します。
|
// buildメソッドでは、detailsSheetメソッドを呼び出して、目的地の詳細情報を表示します。
|
||||||
@ -38,6 +42,15 @@ class BottomSheetNew extends GetView<BottomSheetController> {
|
|||||||
|
|
||||||
final RxBool isButtonDisabled = false.obs;
|
final RxBool isButtonDisabled = false.obs;
|
||||||
|
|
||||||
|
static bool _timezoneInitialized = false;
|
||||||
|
|
||||||
|
void _initializeTimezone() {
|
||||||
|
if (!_timezoneInitialized) {
|
||||||
|
tz.initializeTimeZones();
|
||||||
|
_timezoneInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 目的地の画像を取得するためのメソッドです。
|
// 目的地の画像を取得するためのメソッドです。
|
||||||
// indexController.rogModeの値に基づいて、適切な画像を返します。画像が見つからない場合は、デフォルトの画像を返します。
|
// indexController.rogModeの値に基づいて、適切な画像を返します。画像が見つからない場合は、デフォルトの画像を返します。
|
||||||
//
|
//
|
||||||
@ -146,6 +159,7 @@ class BottomSheetNew extends GetView<BottomSheetController> {
|
|||||||
// ボタンがタップされたときの処理も含まれています。
|
// ボタンがタップされたときの処理も含まれています。
|
||||||
//
|
//
|
||||||
Widget getActionButton(BuildContext context, Destination destination) {
|
Widget getActionButton(BuildContext context, Destination destination) {
|
||||||
|
_initializeTimezone(); // タイムゾーンの初期化
|
||||||
/*
|
/*
|
||||||
debugPrint("getActionButton ${destinationController.rogainingCounted.value}");
|
debugPrint("getActionButton ${destinationController.rogainingCounted.value}");
|
||||||
debugPrint("getActionButton ${destinationController.distanceToStart()}");
|
debugPrint("getActionButton ${destinationController.distanceToStart()}");
|
||||||
@ -181,6 +195,51 @@ class BottomSheetNew extends GetView<BottomSheetController> {
|
|||||||
onPressed: destinationController.isInRog.value
|
onPressed: destinationController.isInRog.value
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
|
// Check if the event is for today
|
||||||
|
bool isEventToday = await checkIfEventIsToday();
|
||||||
|
if (!isEventToday) {
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: Text("警告"),
|
||||||
|
content: Text("参加したエントリーは別日のものですので、ロゲの開始はできません。当日のエントリーを選択するか、エントリーを今日に変更してからロゲ開始を行ってください。"),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text("OK"),
|
||||||
|
onPressed: () {
|
||||||
|
Get.back(); // Close the dialog
|
||||||
|
Get.back(); // Close the bottom sheet
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user has already completed a rogaining event today
|
||||||
|
bool hasCompletedToday = await checkIfCompletedToday();
|
||||||
|
if (hasCompletedToday) {
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: Text("警告"),
|
||||||
|
content: Text("すでにロゲの参加を行いゴールをしています。ロゲは1日1回に制限されています。ご了承ください。"),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text("OK"),
|
||||||
|
onPressed: () {
|
||||||
|
Get.back(); // Close the dialog
|
||||||
|
Get.back(); // Close the bottom sheet
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
destinationController.isInRog.value = true;
|
destinationController.isInRog.value = true;
|
||||||
|
|
||||||
|
|
||||||
@ -216,6 +275,7 @@ class BottomSheetNew extends GetView<BottomSheetController> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
saveGameState();
|
saveGameState();
|
||||||
|
//int teamId = indexController.teamId.value; // teamIdを使用
|
||||||
await ExternalService().startRogaining();
|
await ExternalService().startRogaining();
|
||||||
Get.back();
|
Get.back();
|
||||||
Get.back();// Close the dialog and potentially navigate away
|
Get.back();// Close the dialog and potentially navigate away
|
||||||
@ -249,12 +309,17 @@ class BottomSheetNew extends GetView<BottomSheetController> {
|
|||||||
//goal
|
//goal
|
||||||
|
|
||||||
return ElevatedButton(
|
return ElevatedButton(
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
style: ElevatedButton.styleFrom(
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
backgroundColor: Colors.red
|
||||||
|
),
|
||||||
onPressed: destinationController.rogainingCounted.value == true &&
|
onPressed: destinationController.rogainingCounted.value == true &&
|
||||||
destinationController.distanceToStart() <= 500 &&
|
destinationController.distanceToStart() <= 500 &&
|
||||||
(destination.cp == 0 || destination.cp == -2|| destination.cp == -1) &&
|
(destination.cp == 0 || destination.cp == -2|| destination.cp == -1) &&
|
||||||
DestinationController.ready_for_goal == true
|
DestinationController.ready_for_goal == true
|
||||||
? () async {
|
? () async {
|
||||||
|
|
||||||
|
|
||||||
destinationController.isAtGoal.value = true;
|
destinationController.isAtGoal.value = true;
|
||||||
destinationController.photos.clear();
|
destinationController.photos.clear();
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
@ -317,7 +382,7 @@ class BottomSheetNew extends GetView<BottomSheetController> {
|
|||||||
? "in_game".tr
|
? "in_game".tr
|
||||||
: isAlreadyCheckedIn == true
|
: isAlreadyCheckedIn == true
|
||||||
? "in_game".tr
|
? "in_game".tr
|
||||||
: destinationController.isInRog.value == true
|
: (destinationController.isInRog.value == true || (destination.buy_point != null && destination.buy_point! > 0))
|
||||||
? "checkin".tr
|
? "checkin".tr
|
||||||
: "rogaining_not_started".tr,
|
: "rogaining_not_started".tr,
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.onSecondary),
|
style: TextStyle(color: Theme.of(context).colorScheme.onSecondary),
|
||||||
@ -327,6 +392,62 @@ class BottomSheetNew extends GetView<BottomSheetController> {
|
|||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add these new methods to check event date and completion status
|
||||||
|
Future<bool> checkIfEventIsToday() async {
|
||||||
|
try {
|
||||||
|
final now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo'));
|
||||||
|
|
||||||
|
// Null チェックと安全な操作を追加
|
||||||
|
final userEventDate = indexController.currentUser.isNotEmpty &&
|
||||||
|
indexController.currentUser[0] != null &&
|
||||||
|
indexController.currentUser[0]["user"] != null
|
||||||
|
? indexController.currentUser[0]["user"]["event_date"]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (userEventDate == null || userEventDate.toString().isEmpty) {
|
||||||
|
print('Event date is null or empty');
|
||||||
|
return false; // イベント日付が設定されていない場合は false を返す
|
||||||
|
}
|
||||||
|
|
||||||
|
final eventDate = tz.TZDateTime.from(
|
||||||
|
DateTime.parse(userEventDate.toString()),
|
||||||
|
tz.getLocation('Asia/Tokyo')
|
||||||
|
);
|
||||||
|
|
||||||
|
return eventDate.year == now.year &&
|
||||||
|
eventDate.month == now.month &&
|
||||||
|
eventDate.day == now.day;
|
||||||
|
} catch (e) {
|
||||||
|
print('Error in checkIfEventIsToday: $e');
|
||||||
|
// エラーが発生した場合はダイアログを表示
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: Text('エラー'),
|
||||||
|
content: Text('イベント日付の確認中にエラーが発生しました。\nアプリを再起動するか、管理者にお問い合わせください。'),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text('OK'),
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return false; // エラーが発生した場合は false を返す
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> checkIfCompletedToday() async {
|
||||||
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
final lastGoalTime = await indexController.getLastGoalTime();
|
||||||
|
if (lastGoalTime == null) return false;
|
||||||
|
|
||||||
|
final now = DateTime.now();
|
||||||
|
return lastGoalTime.year == now.year &&
|
||||||
|
lastGoalTime.month == now.month &&
|
||||||
|
lastGoalTime.day == now.day;
|
||||||
|
}
|
||||||
|
|
||||||
// 継承元のbuild をオーバーライドし、detailsSheetメソッドを呼び出して、目的地の詳細情報を表示します。
|
// 継承元のbuild をオーバーライドし、detailsSheetメソッドを呼び出して、目的地の詳細情報を表示します。
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -443,7 +564,7 @@ class BottomSheetNew extends GetView<BottomSheetController> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
//checkin or remove checkin
|
//checkin or remove checkin
|
||||||
destinationController.isInRog.value == true
|
(destinationController.isInRog.value == true || (destination.buy_point != null && destination.buy_point! > 0))
|
||||||
&& (distanceToDest <=
|
&& (distanceToDest <=
|
||||||
destinationController.getForcedChckinDistance(destination) || destination.checkin_radious==-1 )
|
destinationController.getForcedChckinDistance(destination) || destination.checkin_radious==-1 )
|
||||||
&& destination.cp != 0 && destination.cp != -1 && destination.cp != -2
|
&& destination.cp != 0 && destination.cp != -1 && destination.cp != -2
|
||||||
|
|||||||
73
lib/widgets/helper_dialog.dart
Normal file
73
lib/widgets/helper_dialog.dart
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// lib/widgets/helper_dialog.dart
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class HelperDialog extends StatefulWidget {
|
||||||
|
final String message;
|
||||||
|
final String screenKey;
|
||||||
|
|
||||||
|
const HelperDialog({Key? key, required this.message, required this.screenKey}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_HelperDialogState createState() => _HelperDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HelperDialogState extends State<HelperDialog> {
|
||||||
|
bool _doNotShowAgain = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.help_outline, color: Colors.blue),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text('ヘルプ'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(widget.message),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Checkbox(
|
||||||
|
value: _doNotShowAgain,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_doNotShowAgain = value!;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Text('この画面を二度と表示しない'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('OK'),
|
||||||
|
onPressed: () async {
|
||||||
|
if (_doNotShowAgain) {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setBool('helper_${widget.screenKey}', false);
|
||||||
|
}
|
||||||
|
Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ヘルパー画面を表示する関数
|
||||||
|
Future<void> showHelperDialog(String message, String screenKey) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final showHelper = prefs.getBool('helper_$screenKey') ?? true;
|
||||||
|
if (showHelper) {
|
||||||
|
Get.dialog(HelperDialog(message: message, screenKey: screenKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
383
login_page.dart
Normal file
383
login_page.dart
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:rogapp/pages/index/index_controller.dart';
|
||||||
|
import 'package:rogapp/routes/app_pages.dart';
|
||||||
|
import 'package:rogapp/widgets/helper_dialog.dart';
|
||||||
|
import 'package:rogapp/services/api_service.dart';
|
||||||
|
|
||||||
|
// 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。
|
||||||
|
// エラーメッセージをローカライズすることを検討してください。
|
||||||
|
// ログイン処理中にエラーが発生した場合のエラーハンドリングを追加することをお勧めします。
|
||||||
|
//
|
||||||
|
class LoginPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_LoginPageState createState() => _LoginPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LoginPageState extends State<LoginPage> {
|
||||||
|
//class LoginPage extends StatelessWidget {
|
||||||
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
final ApiService apiService = Get.find<ApiService>();
|
||||||
|
|
||||||
|
TextEditingController emailController = TextEditingController();
|
||||||
|
TextEditingController passwordController = TextEditingController();
|
||||||
|
bool _obscureText = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
showHelperDialog(
|
||||||
|
'参加するにはユーザー登録が必要です。サインアップからユーザー登録してください。',
|
||||||
|
'login_page'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showResetPasswordDialog() {
|
||||||
|
TextEditingController resetEmailController = TextEditingController();
|
||||||
|
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: Text('パスワードのリセット'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text('パスワードをリセットするメールアドレスを入力してください。'),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
TextField(
|
||||||
|
controller: resetEmailController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'メールアドレス',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('キャンセル'),
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: Text('リセット'),
|
||||||
|
onPressed: () async {
|
||||||
|
if (resetEmailController.text.isNotEmpty) {
|
||||||
|
bool success = await apiService.resetPassword(resetEmailController.text);
|
||||||
|
Get.back();
|
||||||
|
if (success) {
|
||||||
|
Get.dialog(
|
||||||
|
AlertDialog(
|
||||||
|
title: Text('パスワードリセット'),
|
||||||
|
content: Text('パスワードリセットメールを送信しました。メールのリンクからパスワードを設定してください。'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: Text('OK'),
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Get.snackbar('エラー', 'パスワードリセットに失敗しました。もう一度お試しください。',
|
||||||
|
snackPosition: SnackPosition.BOTTOM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//LoginPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
resizeToAvoidBottomInset: false,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
appBar: AppBar(
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
),
|
||||||
|
body: indexController.currentUser.isEmpty
|
||||||
|
? SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: MediaQuery.of(context).size.height / 6,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage(
|
||||||
|
'assets/images/login_image.jpg'))),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
makeInput(
|
||||||
|
label: "email".tr, controller: emailController),
|
||||||
|
makePasswordInput(
|
||||||
|
label: "password".tr,
|
||||||
|
controller: passwordController,
|
||||||
|
obscureText: _obscureText,
|
||||||
|
onToggleVisibility: () {
|
||||||
|
setState(() {
|
||||||
|
_obscureText = !_obscureText;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.only(top: 3, left: 3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(40),
|
||||||
|
),
|
||||||
|
child: Obx(
|
||||||
|
(() => indexController.isLoading.value == true
|
||||||
|
? MaterialButton(
|
||||||
|
minWidth: double.infinity,
|
||||||
|
height: 60,
|
||||||
|
onPressed: () {},
|
||||||
|
color: Colors.grey[400],
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(40)),
|
||||||
|
child: const CircularProgressIndicator(),
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
children: [
|
||||||
|
MaterialButton(
|
||||||
|
minWidth: double.infinity,
|
||||||
|
height: 40,
|
||||||
|
onPressed: () async {
|
||||||
|
if (emailController.text.isEmpty ||
|
||||||
|
passwordController
|
||||||
|
.text.isEmpty) {
|
||||||
|
Get.snackbar(
|
||||||
|
"no_values".tr,
|
||||||
|
"email_and_password_required"
|
||||||
|
.tr,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons
|
||||||
|
.assistant_photo_outlined,
|
||||||
|
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"))
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
indexController.isLoading.value =
|
||||||
|
true;
|
||||||
|
indexController.login(
|
||||||
|
emailController.text,
|
||||||
|
passwordController.text,
|
||||||
|
context);
|
||||||
|
},
|
||||||
|
color: Colors.indigoAccent[400],
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(40)),
|
||||||
|
child: Text(
|
||||||
|
"login".tr,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white70),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5.0,
|
||||||
|
),
|
||||||
|
MaterialButton(
|
||||||
|
minWidth: double.infinity,
|
||||||
|
height: 36,
|
||||||
|
onPressed: () {
|
||||||
|
Get.toNamed(AppPages.REGISTER);
|
||||||
|
},
|
||||||
|
color: Colors.redAccent,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(40)),
|
||||||
|
child: Text(
|
||||||
|
"sign_up".tr,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white70),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
const SizedBox(
|
||||||
|
height: 3,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: _showResetPasswordDialog,
|
||||||
|
child: Text(
|
||||||
|
"forgot_password".tr,
|
||||||
|
style: TextStyle(color: Colors.blue),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
"app_developed_by_gifu_dx".tr,
|
||||||
|
style: const TextStyle(
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
fontSize: 10.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
"※第8回と第9回は、岐阜県の令和5年度「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています",
|
||||||
|
style: TextStyle(
|
||||||
|
//overflow: TextOverflow.ellipsis,
|
||||||
|
fontSize:
|
||||||
|
10.0, // Consider adjusting the font size if the text is too small.
|
||||||
|
// Removed overflow: TextOverflow.ellipsis to allow text wrapping.
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
)
|
||||||
|
: TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
indexController.logout();
|
||||||
|
Get.offAllNamed(AppPages.LOGIN);
|
||||||
|
},
|
||||||
|
child: const Text("Already Logged in, Click to logout"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makePasswordInput({
|
||||||
|
required String label,
|
||||||
|
required TextEditingController controller,
|
||||||
|
required bool obscureText,
|
||||||
|
required VoidCallback onToggleVisibility,
|
||||||
|
}) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
TextField(
|
||||||
|
controller: controller,
|
||||||
|
obscureText: obscureText,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||||
|
),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
obscureText ? Icons.visibility : Icons.visibility_off,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
onPressed: onToggleVisibility,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30.0)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeInput(
|
||||||
|
{label, required TextEditingController controller, obsureText = false}) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: controller,
|
||||||
|
obscureText: obsureText,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: (Colors.grey[400])!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(color: (Colors.grey[400])!),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 30.0,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1266,6 +1266,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
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:
|
transparent_image:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -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.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 4.8.2+483
|
version: 4.8.17+497
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.2.0 <4.0.0"
|
sdk: ">=3.2.0 <4.0.0"
|
||||||
@ -41,6 +41,7 @@ dependencies:
|
|||||||
geolocator: ^10.1.0
|
geolocator: ^10.1.0
|
||||||
permission_handler: ^11.3.1
|
permission_handler: ^11.3.1
|
||||||
logging: ^1.0.2
|
logging: ^1.0.2
|
||||||
|
timezone: ^0.9.1
|
||||||
|
|
||||||
# flutter_dev_tools: ^0.0.2
|
# flutter_dev_tools: ^0.0.2
|
||||||
# permission_handler: ^11.1.0 <== older
|
# permission_handler: ^11.1.0 <== older
|
||||||
|
|||||||
Reference in New Issue
Block a user