Team,Member, Entryの登録まで完了
This commit is contained in:
@ -9,7 +9,7 @@ class Entry {
|
||||
final Team team;
|
||||
final Event event;
|
||||
final NewCategory category;
|
||||
final DateTime date;
|
||||
final DateTime? date;
|
||||
final String owner;
|
||||
|
||||
Entry({
|
||||
@ -27,8 +27,10 @@ class Entry {
|
||||
team: Team.fromJson(json['team']),
|
||||
event: Event.fromJson(json['event']),
|
||||
category: NewCategory.fromJson(json['category']),
|
||||
date: DateTime.parse(json['date']),
|
||||
owner: json['owner'],
|
||||
date: json['date'] != null
|
||||
? DateTime.tryParse(json['date'])
|
||||
: null,
|
||||
owner: json['owner'] is Map ? json['owner']['name'] ?? '' : json['owner'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@ -38,7 +40,7 @@ class Entry {
|
||||
'team': team.toJson(),
|
||||
'event': event.toJson(),
|
||||
'category': category.toJson(),
|
||||
'date': date.toIso8601String(),
|
||||
'date': date?.toIso8601String(),
|
||||
'owner': owner,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// lib/entry/entry_controller.dart
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:rogapp/model/entry.dart';
|
||||
import 'package:rogapp/model/event.dart';
|
||||
import 'package:rogapp/model/team.dart';
|
||||
@ -8,7 +9,7 @@ import 'package:rogapp/model/category.dart';
|
||||
import 'package:rogapp/services/api_service.dart';
|
||||
|
||||
class EntryController extends GetxController {
|
||||
late final ApiService _apiService;
|
||||
late ApiService _apiService;
|
||||
|
||||
final entries = <Entry>[].obs;
|
||||
final events = <Event>[].obs;
|
||||
@ -21,14 +22,27 @@ class EntryController extends GetxController {
|
||||
final selectedDate = Rx<DateTime?>(null);
|
||||
|
||||
final currentEntry = Rx<Entry?>(null);
|
||||
final isLoading = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() async {
|
||||
super.onInit();
|
||||
await Get.putAsync(() => ApiService().init());
|
||||
_apiService = Get.find<ApiService>();
|
||||
await initializeApiService();
|
||||
await loadInitialData();
|
||||
}
|
||||
|
||||
Future<void> initializeApiService() async {
|
||||
try {
|
||||
_apiService = await Get.putAsync(() => ApiService().init());
|
||||
} catch (e) {
|
||||
print('Error initializing ApiService: $e');
|
||||
Get.snackbar('Error', 'Failed to initialize API service');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadInitialData() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
await Future.wait([
|
||||
fetchEntries(),
|
||||
fetchEvents(),
|
||||
@ -37,14 +51,53 @@ class EntryController extends GetxController {
|
||||
]);
|
||||
if (Get.arguments != null && Get.arguments['entry'] != null) {
|
||||
currentEntry.value = Get.arguments['entry'];
|
||||
_initializeEntryData();
|
||||
initializeEditMode(currentEntry.value!);
|
||||
} else {
|
||||
// 新規作成モードの場合、最初のイベントを選択
|
||||
if (events.isNotEmpty) {
|
||||
selectedEvent.value = events.first;
|
||||
selectedDate.value = events.first.startDatetime;
|
||||
}
|
||||
}
|
||||
Get.putAsync(() => ApiService().init());
|
||||
} catch(e) {
|
||||
print('Error initializing data: $e');
|
||||
Get.snackbar('Error', 'Failed to load initial data');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void initializeEditMode(Entry entry) {
|
||||
selectedEvent.value = entry.event;
|
||||
selectedTeam.value = entry.team;
|
||||
selectedCategory.value = entry.category;
|
||||
selectedDate.value = entry.date;
|
||||
}
|
||||
|
||||
void updateEvent(Event? value) {
|
||||
selectedEvent.value = value;
|
||||
if (value != null) {
|
||||
// イベント変更時に日付を調整
|
||||
if (selectedDate.value == null ||
|
||||
selectedDate.value!.isBefore(value.startDatetime) ||
|
||||
selectedDate.value!.isAfter(value.endDatetime)) {
|
||||
selectedDate.value = value.startDatetime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateTeam(Team? value) => selectedTeam.value = value;
|
||||
void updateCategory(NewCategory? value) => selectedCategory.value = value;
|
||||
void updateDate(DateTime value) => selectedDate.value = value;
|
||||
|
||||
/*
|
||||
void updateDate(DateTime value){
|
||||
selectedDate.value = DateFormat('yyyy-MM-dd').format(value!) as DateTime?;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
void _initializeEntryData() {
|
||||
if (currentEntry.value != null) {
|
||||
selectedEvent.value = currentEntry.value!.event;
|
||||
@ -60,6 +113,7 @@ class EntryController extends GetxController {
|
||||
entries.assignAll(fetchedEntries);
|
||||
} catch (e) {
|
||||
print('Error fetching entries: $e');
|
||||
Get.snackbar('Error', 'Failed to fetch entries');
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,6 +123,7 @@ class EntryController extends GetxController {
|
||||
events.assignAll(fetchedEvents);
|
||||
} catch (e) {
|
||||
print('Error fetching events: $e');
|
||||
Get.snackbar('Error', 'Failed to fetch events');
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,6 +133,7 @@ class EntryController extends GetxController {
|
||||
teams.assignAll(fetchedTeams);
|
||||
} catch (e) {
|
||||
print('Error fetching teams: $e');
|
||||
Get.snackbar('Error', 'Failed to fetch team');
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,6 +143,7 @@ class EntryController extends GetxController {
|
||||
categories.assignAll(fetchedCategories);
|
||||
} catch (e) {
|
||||
print('Error fetching categories: $e');
|
||||
Get.snackbar('Error', 'Failed to fetch categories');
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,10 +200,6 @@ class EntryController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
void updateEvent(Event? value) => selectedEvent.value = value;
|
||||
void updateTeam(Team? value) => selectedTeam.value = value;
|
||||
void updateCategory(NewCategory? value) => selectedCategory.value = value;
|
||||
void updateDate(DateTime value) => selectedDate.value = value;
|
||||
|
||||
bool isOwner() {
|
||||
// Implement logic to check if the current user is the owner of the entry
|
||||
|
||||
@ -3,6 +3,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rogapp/pages/entry/entry_controller.dart';
|
||||
import 'package:rogapp/model/event.dart';
|
||||
import 'package:rogapp/model/category.dart';
|
||||
import 'package:rogapp/model/team.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class EntryDetailPage extends GetView<EntryController> {
|
||||
@override
|
||||
@ -11,52 +15,121 @@ class EntryDetailPage extends GetView<EntryController> {
|
||||
final mode = Get.arguments['mode'] as String? ?? 'new';
|
||||
final entry = Get.arguments['entry'];
|
||||
|
||||
if (mode == 'edit' && entry != null) {
|
||||
controller.initializeEditMode(entry);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(mode == 'new' ? 'エントリー登録' : 'エントリー詳細'),
|
||||
),
|
||||
body: Padding(
|
||||
body: Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Obx(() => Column(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
DropdownButtonFormField(
|
||||
decoration: InputDecoration(labelText: 'イベント'),
|
||||
value: controller.selectedEvent.value,
|
||||
items: controller.events.map((event) => DropdownMenuItem(
|
||||
value: event,
|
||||
child: Text(event.eventName),
|
||||
)).toList(),
|
||||
onChanged: (value) => controller.updateEvent(value),
|
||||
_buildDropdown<Event>(
|
||||
label: 'イベント',
|
||||
items: controller.events,
|
||||
selectedId: controller.selectedEvent.value?.id,
|
||||
onChanged: (eventId) => controller.updateEvent(
|
||||
controller.events.firstWhere((e) => e.id == eventId)
|
||||
),
|
||||
DropdownButtonFormField(
|
||||
decoration: InputDecoration(labelText: 'チーム'),
|
||||
value: controller.selectedTeam.value,
|
||||
items: controller.teams.map((team) => DropdownMenuItem(
|
||||
value: team,
|
||||
child: Text(team.teamName),
|
||||
)).toList(),
|
||||
onChanged: (value) => controller.updateTeam(value),
|
||||
getDisplayName: (event) => event.eventName,
|
||||
getId: (event) => event.id,
|
||||
),
|
||||
DropdownButtonFormField(
|
||||
decoration: InputDecoration(labelText: 'カテゴリ'),
|
||||
value: controller.selectedCategory.value,
|
||||
items: controller.categories.map((category) => DropdownMenuItem(
|
||||
value: category,
|
||||
child: Text(category.categoryName),
|
||||
)).toList(),
|
||||
onChanged: (value) => controller.updateCategory(value),
|
||||
SizedBox(height: 16),
|
||||
_buildDropdown<Team>(
|
||||
label: 'チーム',
|
||||
items: controller.teams,
|
||||
selectedId: controller.selectedTeam.value?.id,
|
||||
onChanged: (teamId) => controller.updateTeam(
|
||||
controller.teams.firstWhere((t) => t.id == teamId)
|
||||
),
|
||||
getDisplayName: (team) => team.teamName,
|
||||
getId: (team) => team.id,
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
_buildDropdown<NewCategory>(
|
||||
label: 'カテゴリ',
|
||||
items: controller.categories,
|
||||
selectedId: controller.selectedCategory.value?.id,
|
||||
onChanged: (categoryId) => controller.updateCategory(
|
||||
controller.categories.firstWhere((c) => c.id == categoryId)
|
||||
),
|
||||
getDisplayName: (category) => category.categoryName,
|
||||
getId: (category) => category.id,
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
ListTile(
|
||||
title: Text('日付'),
|
||||
subtitle: Text(
|
||||
controller.selectedDate.value != null
|
||||
? DateFormat('yyyy-MM-dd').format(controller.selectedDate.value!)
|
||||
: '日付を選択してください',
|
||||
),
|
||||
onTap: () async {
|
||||
if (controller.selectedEvent.value == null) {
|
||||
Get.snackbar('Error', 'Please select an event first');
|
||||
return;
|
||||
}
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: controller.selectedDate.value ?? controller.selectedEvent.value!.startDatetime,
|
||||
firstDate: controller.selectedEvent.value!.startDatetime,
|
||||
lastDate: controller.selectedEvent.value!.endDatetime,
|
||||
);
|
||||
if (picked != null) {
|
||||
controller.updateDate(picked);
|
||||
}
|
||||
},
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
ElevatedButton(
|
||||
child: Text(mode == 'new' ? 'エントリーを作成' : 'エントリーを更新'),
|
||||
onPressed: () {
|
||||
if (mode == 'new') {
|
||||
controller.createEntry();
|
||||
} else {
|
||||
controller.updateEntry();
|
||||
}
|
||||
},
|
||||
),
|
||||
// 日付選択ウィジェットを追加
|
||||
if (mode == 'edit' && controller.isOwner())
|
||||
ElevatedButton(
|
||||
child: Text('エントリーを削除'),
|
||||
onPressed: () => controller.deleteEntry(),
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDropdown<T>({
|
||||
required String label,
|
||||
required List<T> items,
|
||||
required int? selectedId,
|
||||
required void Function(int?) onChanged,
|
||||
required String Function(T) getDisplayName,
|
||||
required int Function(T) getId,
|
||||
}) {
|
||||
return DropdownButtonFormField<int>(
|
||||
decoration: InputDecoration(labelText: label),
|
||||
value: selectedId,
|
||||
items: items.map((item) => DropdownMenuItem<int>(
|
||||
value: getId(item),
|
||||
child: Text(getDisplayName(item)),
|
||||
)).toList(),
|
||||
onChanged: onChanged,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -2,31 +2,52 @@
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rogapp/model/user.dart';
|
||||
import 'package:rogapp/pages/team/team_controller.dart';
|
||||
import 'package:rogapp/services/api_service.dart';
|
||||
|
||||
class MemberController extends GetxController {
|
||||
late final ApiService _apiService;
|
||||
|
||||
final selectedMember = Rx<User?>(null);
|
||||
final int teamId = 0;
|
||||
int teamId = 0;
|
||||
final member = Rx<User?>(null);
|
||||
final email = ''.obs;
|
||||
final firstname = ''.obs;
|
||||
final lastname = ''.obs;
|
||||
final female = false.obs;
|
||||
final dateOfBirth = Rx<DateTime?>(null);
|
||||
final isLoading = true.obs; // isLoadingプロパティを追加
|
||||
final isLoading = false.obs; // isLoadingプロパティを追加
|
||||
final isActive = false.obs;
|
||||
|
||||
//MemberController(this._apiService);
|
||||
|
||||
@override
|
||||
void onInit() async{
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
await Get.putAsync(() => ApiService().init());
|
||||
_apiService = Get.find<ApiService>();
|
||||
await loadInitialData();
|
||||
ever(member, (_) => _initializeMemberData());
|
||||
loadInitialData();
|
||||
}
|
||||
|
||||
if (Get.arguments != null && Get.arguments['member'] != null) {
|
||||
bool get isDummyEmail => email.value.startsWith('dummy_');
|
||||
bool get isApproved => !email.value.startsWith('dummy_') && member.value?.isActive == true;
|
||||
|
||||
Future<void> loadInitialData() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
if (Get.arguments != null) {
|
||||
if (Get.arguments['member'] != null) {
|
||||
member.value = Get.arguments['member'];
|
||||
_initializeMemberData();
|
||||
}
|
||||
if (Get.arguments['teamId'] != null) {
|
||||
teamId = Get.arguments['teamId'];
|
||||
}
|
||||
}
|
||||
// 他の必要な初期データの取得をここで行う
|
||||
} catch (e) {
|
||||
print('Error loading initial data: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,23 +61,26 @@ class MemberController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadInitialData() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
// 必要な初期データの取得をここで行う
|
||||
// 例: await fetchTeamMembers();
|
||||
} catch (e) {
|
||||
print('Error loading initial data: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setSelectedMember(User member) {
|
||||
selectedMember.value = member;
|
||||
firstname.value = member.firstname;
|
||||
lastname.value = member.lastname;
|
||||
this.member.value = member;
|
||||
email.value = member.email ?? '';
|
||||
firstname.value = member.firstname ?? '';
|
||||
lastname.value = member.lastname ?? '';
|
||||
dateOfBirth.value = member.dateOfBirth;
|
||||
female.value = member.female ?? false;
|
||||
isActive.value = member.isActive ?? false;
|
||||
}
|
||||
|
||||
bool _validateInputs() {
|
||||
if (email.value.isNotEmpty && !isDummyEmail) {
|
||||
return true; // Emailのみの場合は有効
|
||||
}
|
||||
if (firstname.value.isEmpty || lastname.value.isEmpty || dateOfBirth.value == null || female.value == null) {
|
||||
Get.snackbar('エラー', 'Emailが空の場合、姓名と生年月日及び性別は必須です', snackPosition: SnackPosition.BOTTOM);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void updateFirstName(String value) {
|
||||
@ -67,32 +91,52 @@ class MemberController extends GetxController {
|
||||
lastname.value = value;
|
||||
}
|
||||
|
||||
Future<void> saveMember() async {
|
||||
Future<bool> saveMember() async {
|
||||
if (!_validateInputs()) return false;
|
||||
|
||||
try {
|
||||
isLoading.value = true;
|
||||
// メンバー保存のロジックをここに実装
|
||||
// 例: await _apiService.updateMember(selectedMember.value!.id, firstName.value, lastName.value);
|
||||
User updatedMember;
|
||||
if (member.value == null || member.value!.id == null) {
|
||||
// 新規メンバー作成
|
||||
updatedMember = await _apiService.createTeamMember(
|
||||
teamId,
|
||||
isDummyEmail ? null : email.value, // dummy_メールの場合はnullを送信
|
||||
firstname.value,
|
||||
lastname.value,
|
||||
dateOfBirth.value,
|
||||
female.value,
|
||||
);
|
||||
} else {
|
||||
// 既存メンバー更新
|
||||
updatedMember = await _apiService.updateTeamMember(
|
||||
teamId,
|
||||
member.value!.id!,
|
||||
firstname.value,
|
||||
lastname.value,
|
||||
dateOfBirth.value,
|
||||
female.value,
|
||||
);
|
||||
}
|
||||
member.value = updatedMember;
|
||||
Get.snackbar('成功', 'メンバーが保存されました', snackPosition: SnackPosition.BOTTOM);
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Error saving member: $e');
|
||||
// エラーハンドリング(例:ユーザーへの通知)
|
||||
Get.snackbar('エラー', 'メンバーの保存に失敗しました: ${e.toString()}', snackPosition: SnackPosition.BOTTOM);
|
||||
return false;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createMember(int teamId) async {
|
||||
try {
|
||||
final newMember = await _apiService.createTeamMember(
|
||||
teamId,
|
||||
email.value,
|
||||
firstname.value,
|
||||
lastname.value,
|
||||
dateOfBirth.value,
|
||||
);
|
||||
member.value = newMember;
|
||||
} catch (e) {
|
||||
print('Error creating member: $e');
|
||||
|
||||
String getDisplayName() {
|
||||
if (!isActive.value && !isDummyEmail) {
|
||||
final displayName = email.value.split('@')[0];
|
||||
return '$displayName(未承認)';
|
||||
}
|
||||
return '${lastname.value} ${firstname.value}'.trim();
|
||||
}
|
||||
|
||||
Future<void> updateMember() async {
|
||||
@ -105,6 +149,7 @@ class MemberController extends GetxController {
|
||||
firstname.value,
|
||||
lastname.value,
|
||||
dateOfBirth.value,
|
||||
female.value,
|
||||
);
|
||||
member.value = updatedMember;
|
||||
} catch (e) {
|
||||
@ -112,6 +157,26 @@ class MemberController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteMember() async {
|
||||
if (member.value == null || member.value!.id == null) {
|
||||
Get.snackbar('エラー', 'メンバー情報が不正です', snackPosition: SnackPosition.BOTTOM);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
isLoading.value = true;
|
||||
await _apiService.deleteTeamMember(teamId, member.value!.id!);
|
||||
Get.snackbar('成功', 'メンバーが削除されました', snackPosition: SnackPosition.BOTTOM);
|
||||
member.value = null;
|
||||
} catch (e) {
|
||||
print('Error deleting member: $e');
|
||||
Get.snackbar('エラー', 'メンバーの削除に失敗しました: ${e.toString()}', snackPosition: SnackPosition.BOTTOM);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Future<void> deleteMember() async {
|
||||
if (member.value == null) return;
|
||||
int? memberId = member.value?.id;
|
||||
@ -124,7 +189,14 @@ class MemberController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
Future<void> resendInvitation() async {
|
||||
if (isDummyEmail) {
|
||||
Get.snackbar('エラー', 'ダミーメールアドレスには招待メールを送信できません', snackPosition: SnackPosition.BOTTOM);
|
||||
return;
|
||||
}
|
||||
|
||||
if (member.value == null || member.value!.email == null) return;
|
||||
int? memberId = member.value?.id;
|
||||
try {
|
||||
@ -147,4 +219,52 @@ class MemberController extends GetxController {
|
||||
if (member.value!.isActive) return '承認済';
|
||||
return '招待中';
|
||||
}
|
||||
|
||||
int calculateAge() {
|
||||
if (dateOfBirth.value == null) return 0;
|
||||
final today = DateTime.now();
|
||||
int age = today.year - dateOfBirth.value!.year;
|
||||
if (today.month < dateOfBirth.value!.month ||
|
||||
(today.month == dateOfBirth.value!.month && today.day < dateOfBirth.value!.day)) {
|
||||
age--;
|
||||
}
|
||||
return age;
|
||||
}
|
||||
|
||||
String calculateGrade() {
|
||||
if (dateOfBirth.value == null) return '不明';
|
||||
|
||||
final today = DateTime.now();
|
||||
final birthDate = dateOfBirth.value!;
|
||||
|
||||
// 今年の4月1日
|
||||
final thisYearSchoolStart = DateTime(today.year, 4, 1);
|
||||
|
||||
// 生まれた年の翌年の4月1日(学齢期の始まり)
|
||||
final schoolStartDate = DateTime(birthDate.year + 1, 4, 1);
|
||||
|
||||
// 学齢期の開始からの年数
|
||||
int yearsFromSchoolStart = today.year - schoolStartDate.year;
|
||||
|
||||
// 今年の4月1日より前なら1年引く
|
||||
if (today.isBefore(thisYearSchoolStart)) {
|
||||
yearsFromSchoolStart--;
|
||||
}
|
||||
|
||||
if (yearsFromSchoolStart < 0) return '未就学';
|
||||
if (yearsFromSchoolStart < 6) return '小${yearsFromSchoolStart + 1}';
|
||||
if (yearsFromSchoolStart < 9) return '中${yearsFromSchoolStart - 5}';
|
||||
if (yearsFromSchoolStart < 12) return '高${yearsFromSchoolStart - 8}';
|
||||
return '成人';
|
||||
}
|
||||
|
||||
String getAgeAndGrade() {
|
||||
final age = calculateAge();
|
||||
final grade = calculateGrade();
|
||||
return '$age歳/$grade';
|
||||
}
|
||||
|
||||
bool isOver18() {
|
||||
return calculateAge() >= 18;
|
||||
}
|
||||
}
|
||||
@ -3,22 +3,87 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rogapp/pages/team/member_controller.dart';
|
||||
import 'package:intl/intl.dart'; // この行を追加
|
||||
import 'package:flutter_localizations/flutter_localizations.dart'; // 追加
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class MemberDetailPage extends StatefulWidget {
|
||||
@override
|
||||
_MemberDetailPageState createState() => _MemberDetailPageState();
|
||||
}
|
||||
|
||||
class _MemberDetailPageState extends State<MemberDetailPage> {
|
||||
final MemberController controller = Get.find<MemberController>();
|
||||
late TextEditingController _firstNameController;
|
||||
late TextEditingController _lastNameController;
|
||||
late TextEditingController _emailController;
|
||||
|
||||
class MemberDetailPage extends GetView<MemberController> {
|
||||
final TextEditingController _firstNameController = TextEditingController();
|
||||
final TextEditingController _lastNameController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mode = Get.arguments['mode'];
|
||||
final member = Get.arguments['member'];
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeControllers();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final mode = Get.arguments['mode'];
|
||||
final member = Get.arguments['member'];
|
||||
if (mode == 'edit' && member != null) {
|
||||
controller.setSelectedMember(member);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _initializeControllers() {
|
||||
_firstNameController = TextEditingController(text: controller.firstname.value);
|
||||
_lastNameController = TextEditingController(text: controller.lastname.value);
|
||||
_emailController = TextEditingController(text: controller.email.value);
|
||||
|
||||
controller.firstname.listen((value) {
|
||||
if (_firstNameController.text != value) {
|
||||
_firstNameController.value = TextEditingValue(
|
||||
text: value,
|
||||
selection: TextSelection.fromPosition(TextPosition(offset: value.length)),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
controller.lastname.listen((value) {
|
||||
if (_lastNameController.text != value) {
|
||||
_lastNameController.value = TextEditingValue(
|
||||
text: value,
|
||||
selection: TextSelection.fromPosition(TextPosition(offset: value.length)),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
controller.email.listen((value) {
|
||||
if (_emailController.text != value) {
|
||||
_emailController.value = TextEditingValue(
|
||||
text: value,
|
||||
selection: TextSelection.fromPosition(TextPosition(offset: value.length)),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mode = Get.arguments['mode'] as String;
|
||||
//final member = Get.arguments['member'];
|
||||
final teamId = Get.arguments['teamId'] as int;
|
||||
|
||||
/*
|
||||
return MaterialApp( // MaterialApp をここに追加
|
||||
localizationsDelegates: [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: [
|
||||
const Locale('ja', 'JP'),
|
||||
],
|
||||
home:Scaffold(
|
||||
*/
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(mode == 'new' ? 'メンバー追加' : 'メンバー詳細'),
|
||||
@ -27,68 +92,121 @@ class MemberDetailPage extends GetView<MemberController> {
|
||||
icon: Icon(Icons.save),
|
||||
onPressed: () async {
|
||||
await controller.saveMember();
|
||||
Get.back();
|
||||
Get.back(result: true);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Obx(()
|
||||
{
|
||||
body: Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
_firstNameController.value = _firstNameController.value.copyWith(
|
||||
text: controller.firstname.value,
|
||||
selection: TextSelection.collapsed(
|
||||
offset: controller.firstname.value.length),
|
||||
);
|
||||
_lastNameController.value = _lastNameController.value.copyWith(
|
||||
text: controller.lastname.value,
|
||||
selection: TextSelection.collapsed(
|
||||
offset: controller.lastname.value.length),
|
||||
);
|
||||
|
||||
return Padding(
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (mode == 'edit' && member.email != null)
|
||||
Text('Email: ${member.email}'),
|
||||
if (mode == 'new' || (mode == 'edit' && member.email == null))
|
||||
if (mode == 'new')
|
||||
TextField(
|
||||
decoration: InputDecoration(labelText: 'Email'),
|
||||
onChanged: (value) => controller.updateEmail(value),
|
||||
),
|
||||
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: '姓'),
|
||||
controller: _lastNameController,
|
||||
onChanged: (value) => controller.updateLastName(value),
|
||||
onChanged: (value) => controller.lastname.value = value,
|
||||
controller: TextEditingController(text: controller.lastname.value),
|
||||
),
|
||||
TextField(
|
||||
decoration: InputDecoration(labelText: '名'),
|
||||
controller: _firstNameController,
|
||||
onChanged: (value) => controller.updateFirstName(value),
|
||||
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歳以上'),
|
||||
|
||||
// 誕生日選択ウィジェットを追加
|
||||
if (mode == 'edit')
|
||||
Text('ステータス: ${controller.getMemberStatus()}'),
|
||||
if (mode == 'edit' && controller.getMemberStatus() == '招待中')
|
||||
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(),
|
||||
),
|
||||
if (mode == 'edit')
|
||||
|
||||
// メンバー削除ボタン
|
||||
ElevatedButton(
|
||||
child: Text('メンバーから除外'),
|
||||
onPressed: () => controller.deleteMember(),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
|
||||
}),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_firstNameController.dispose();
|
||||
_lastNameController.dispose();
|
||||
_emailController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@ -71,6 +71,15 @@ class TeamController extends GetxController {
|
||||
teamMembers.clear();
|
||||
}
|
||||
|
||||
void cleanupForNavigation() {
|
||||
selectedTeam.value = null;
|
||||
teamName.value = '';
|
||||
selectedCategory.value = categories.isNotEmpty ? categories.first : null;
|
||||
teamMembers.clear();
|
||||
//teamMembersはクリアしない
|
||||
// 必要に応じて他のクリーンアップ処理を追加
|
||||
}
|
||||
|
||||
Future<void> fetchTeams() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
@ -126,25 +135,75 @@ class TeamController extends GetxController {
|
||||
|
||||
Future<void> fetchTeamMembers(int teamId) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
final members = await _apiService.getTeamMembers(teamId);
|
||||
teamMembers.assignAll(members);
|
||||
} catch (e) {
|
||||
error.value = 'メンバーの取得に失敗しました: $e';
|
||||
print('Error fetching team members: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateMember(teamId, User member) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
await _apiService.updateTeamMember(teamId,member.id, member.firstname, member.lastname, member.dateOfBirth, member.female);
|
||||
await fetchTeamMembers(selectedTeam.value!.id);
|
||||
} catch (e) {
|
||||
error.value = 'メンバーの更新に失敗しました: $e';
|
||||
print('Error updating member: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteMember(int memberId, int teamId) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
await _apiService.deleteTeamMember(teamId,memberId);
|
||||
await fetchTeamMembers(teamId);
|
||||
} catch (e) {
|
||||
error.value = 'メンバーの削除に失敗しました: $e';
|
||||
print('Error deleting member: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Future<void> addMember(User member, int teamId) async {
|
||||
// try {
|
||||
// isLoading.value = true;
|
||||
// await _apiService.createTeamMember(teamId, member.email, member.firstname, member.lastname, member.dateOfBirth);
|
||||
// await fetchTeamMembers(teamId);
|
||||
// } catch (e) {
|
||||
// error.value = 'メンバーの追加に失敗しました: $e';
|
||||
// print('Error adding member: $e');
|
||||
// } finally {
|
||||
// isLoading.value = false;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
void updateTeamName(String value) {
|
||||
teamName.value = value;
|
||||
}
|
||||
|
||||
void updateCategory(NewCategory? value) {
|
||||
if (value != null) {
|
||||
selectedCategory.value = categories.firstWhere(
|
||||
(category) => category.id == value.id,
|
||||
orElse: () => value,
|
||||
);
|
||||
selectedCategory.value = value;
|
||||
}
|
||||
}
|
||||
//void updateCategory(NewCategory? value) {
|
||||
// if (value != null) {
|
||||
// selectedCategory.value = categories.firstWhere(
|
||||
// (category) => category.id == value.id,
|
||||
// orElse: () => value,
|
||||
// );
|
||||
// }
|
||||
//}
|
||||
|
||||
Future<void> saveTeam() async {
|
||||
try {
|
||||
@ -161,18 +220,10 @@ class TeamController extends GetxController {
|
||||
|
||||
// サーバーから最新のデータを再取得
|
||||
await fetchTeams();
|
||||
|
||||
// 選択中のチームを更新
|
||||
if (selectedTeam.value != null) {
|
||||
selectedTeam.value = teams.firstWhere((t) => t.id == selectedTeam.value!.id);
|
||||
teamName.value = selectedTeam.value!.teamName;
|
||||
selectedCategory.value = selectedTeam.value!.category;
|
||||
}
|
||||
|
||||
update(); // UIを強制的に更新
|
||||
} catch (e) {
|
||||
error.value = 'チームの保存に失敗しました: $e';
|
||||
print("Team save error: $e");
|
||||
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
@ -7,21 +7,76 @@ import 'package:rogapp/routes/app_pages.dart';
|
||||
import 'package:rogapp/model/team.dart';
|
||||
import 'package:rogapp/model/category.dart';
|
||||
|
||||
class TeamDetailPage extends GetView<TeamController> {
|
||||
class TeamDetailPage extends StatefulWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mode = Get.arguments['mode'] as String;
|
||||
final team = Get.arguments['team'] as Team?;
|
||||
_TeamDetailPageState createState() => _TeamDetailPageState();
|
||||
}
|
||||
|
||||
if (mode == 'edit' && team != null) {
|
||||
controller.setSelectedTeam(team);
|
||||
class _TeamDetailPageState extends State<TeamDetailPage> {
|
||||
late TeamController controller;
|
||||
late TextEditingController _teamNameController = TextEditingController();
|
||||
final RxString mode = ''.obs;
|
||||
final Rx<Team?> team = Rx<Team?>(null);
|
||||
Worker? _teamNameWorker;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = Get.find<TeamController>();
|
||||
_teamNameController = TextEditingController();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_initializeData();
|
||||
});
|
||||
}
|
||||
|
||||
void _initializeData() {
|
||||
final args = Get.arguments;
|
||||
if (args != null && args is Map<String, dynamic>) {
|
||||
mode.value = args['mode'] as String? ?? '';
|
||||
team.value = args['team'] as Team?;
|
||||
|
||||
if (mode.value == 'edit' && team.value != null) {
|
||||
controller.setSelectedTeam(team.value!);
|
||||
} else {
|
||||
controller.resetForm();
|
||||
}
|
||||
} else {
|
||||
// 引数がない場合は新規作成モードとして扱う
|
||||
mode.value = 'new';
|
||||
controller.resetForm();
|
||||
}
|
||||
|
||||
_teamNameController.text = controller.teamName.value;
|
||||
|
||||
// Use ever instead of direct listener
|
||||
_teamNameWorker = ever(controller.teamName, (String value) {
|
||||
if (_teamNameController.text != value) {
|
||||
_teamNameController.text = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_teamNameWorker?.dispose();
|
||||
_teamNameController.dispose();
|
||||
//controller.resetForm(); // Reset the form when leaving the page
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(mode == 'new' ? '新規チーム作成' : 'チーム詳細'),
|
||||
title: Text(Get.arguments['mode'] == 'new' ? '新規チーム作成' : 'チーム詳細'),
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
controller.cleanupForNavigation();
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.save),
|
||||
@ -34,8 +89,9 @@ class TeamDetailPage extends GetView<TeamController> {
|
||||
}
|
||||
},
|
||||
),
|
||||
if (mode == 'edit')
|
||||
IconButton(
|
||||
Obx(() {
|
||||
if (mode.value == 'edit') {
|
||||
return IconButton(
|
||||
icon: Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
try {
|
||||
@ -45,11 +101,14 @@ class TeamDetailPage extends GetView<TeamController> {
|
||||
Get.snackbar('エラー', e.toString(), snackPosition: SnackPosition.BOTTOM);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
}),
|
||||
],
|
||||
),
|
||||
body: GetBuilder<TeamController>(
|
||||
builder: (controller) {
|
||||
body: Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
@ -61,10 +120,11 @@ class TeamDetailPage extends GetView<TeamController> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextFormField(
|
||||
initialValue: controller.teamName.value,
|
||||
decoration: InputDecoration(labelText: 'チーム名'),
|
||||
controller: _teamNameController,
|
||||
onChanged: (value) => controller.updateTeamName(value),
|
||||
),
|
||||
|
||||
SizedBox(height: 16),
|
||||
DropdownButtonFormField<NewCategory>(
|
||||
decoration: InputDecoration(labelText: 'カテゴリ'),
|
||||
@ -75,7 +135,7 @@ class TeamDetailPage extends GetView<TeamController> {
|
||||
)).toList(),
|
||||
onChanged: (value) => controller.updateCategory(value),
|
||||
),
|
||||
if (mode == 'edit')
|
||||
if (mode.value == 'edit')
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Text('ゼッケン番号: ${controller.selectedTeam.value?.zekkenNumber ?? ""}'),
|
||||
@ -84,6 +144,8 @@ class TeamDetailPage extends GetView<TeamController> {
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Text('所有者: ${controller.currentUser.value?.email ?? "未設定"}'),
|
||||
),
|
||||
|
||||
if (mode.value == 'edit') ...[
|
||||
SizedBox(height: 24),
|
||||
Text('メンバーリスト', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
SizedBox(height: 8),
|
||||
@ -93,34 +155,55 @@ class TeamDetailPage extends GetView<TeamController> {
|
||||
itemCount: controller.teamMembers.length,
|
||||
itemBuilder: (context, index) {
|
||||
final member = controller.teamMembers[index];
|
||||
final isDummyEmail = member.email?.startsWith('dummy_') ?? false;
|
||||
final displayName = isDummyEmail
|
||||
? '${member.lastname} ${member.firstname}'
|
||||
: member.isActive
|
||||
? '${member.lastname} ${member.firstname}'
|
||||
: '${member.email?.split('@')[0] ?? ''}(未承認)';
|
||||
return ListTile(
|
||||
title: Text('${member.lastname}, ${member.firstname}'),
|
||||
onTap: () => Get.toNamed(
|
||||
title: Text(displayName),
|
||||
subtitle: isDummyEmail ? Text('Email未設定') : null,
|
||||
onTap: () async {
|
||||
final result = await Get.toNamed(
|
||||
AppPages.MEMBER_DETAIL,
|
||||
arguments: {'mode': 'edit', 'member': member, 'teamId': controller.selectedTeam.value?.id},
|
||||
),
|
||||
);
|
||||
if (result == true) {
|
||||
await controller.fetchTeamMembers(controller.selectedTeam.value!.id);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
child: Text('新しいメンバーを追加'),
|
||||
onPressed: () => Get.toNamed(
|
||||
onPressed: () async {
|
||||
await Get.toNamed(
|
||||
AppPages.MEMBER_DETAIL,
|
||||
arguments: {'mode': 'new', 'teamId': controller.selectedTeam.value?.id},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
if (controller.selectedTeam.value != null) {
|
||||
controller.fetchTeamMembers(controller.selectedTeam.value!.id);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@ import 'package:rogapp/model/category.dart';
|
||||
import 'package:rogapp/model/user.dart';
|
||||
import 'package:rogapp/pages/index/index_controller.dart';
|
||||
import '../utils/const.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
|
||||
|
||||
class ApiService extends GetxService{
|
||||
@ -19,16 +21,18 @@ class ApiService extends GetxService{
|
||||
String baseUrl = '';
|
||||
String token = 'your-auth-token';
|
||||
|
||||
Future<void> init() async {
|
||||
Future<ApiService> init() async {
|
||||
try {
|
||||
// ここで必要な初期化処理を行う
|
||||
serverUrl = ConstValues.currentServer();
|
||||
baseUrl = '$serverUrl/api';
|
||||
//await Future.delayed(Duration(seconds: 2)); // 仮の遅延(実際の初期化処理に置き換えてください)
|
||||
print('ApiService initialized successfully');
|
||||
return this;
|
||||
} catch(e) {
|
||||
print('Error in ApiService initialization: $e');
|
||||
rethrow; // エラーを再スローして、呼び出し元で処理できるようにする
|
||||
//return this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,7 +236,8 @@ class ApiService extends GetxService{
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
return Team.fromJson(json.decode(response.body));
|
||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||
return Team.fromJson(json.decode(decodedResponse));
|
||||
} else {
|
||||
throw Exception('Failed to create team');
|
||||
}
|
||||
@ -255,7 +260,9 @@ class ApiService extends GetxService{
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return Team.fromJson(json.decode(response.body));
|
||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||
|
||||
return Team.fromJson(json.decode(decodedResponse));
|
||||
} else {
|
||||
throw Exception('Failed to update team');
|
||||
}
|
||||
@ -295,10 +302,15 @@ class ApiService extends GetxService{
|
||||
}
|
||||
}
|
||||
|
||||
Future<User> createTeamMember(int teamId, String email, String firstname, String lastname, DateTime? dateOfBirth) async {
|
||||
Future<User> createTeamMember(int teamId, String? email, String firstname, String lastname, DateTime? dateOfBirth,bool? female) async {
|
||||
init();
|
||||
getToken();
|
||||
|
||||
String? formattedDateOfBirth;
|
||||
if (dateOfBirth != null) {
|
||||
formattedDateOfBirth = DateFormat('yyyy-MM-dd').format(dateOfBirth);
|
||||
}
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse('$baseUrl/teams/$teamId/members/'),
|
||||
headers: {
|
||||
@ -309,21 +321,28 @@ class ApiService extends GetxService{
|
||||
'email': email,
|
||||
'firstname': firstname,
|
||||
'lastname': lastname,
|
||||
'date_of_birth': dateOfBirth?.toIso8601String(),
|
||||
'date_of_birth': formattedDateOfBirth,
|
||||
'female': female,
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
return User.fromJson(json.decode(response.body));
|
||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||
return User.fromJson(json.decode(decodedResponse));
|
||||
} else {
|
||||
throw Exception('Failed to create team member');
|
||||
}
|
||||
}
|
||||
|
||||
Future<User> updateTeamMember(int teamId,int memberId, String firstname, String lastname, DateTime? dateOfBirth) async {
|
||||
Future<User> updateTeamMember(int teamId,int? memberId, String firstname, String lastname, DateTime? dateOfBirth,bool? female) async {
|
||||
init();
|
||||
getToken();
|
||||
|
||||
String? formattedDateOfBirth;
|
||||
if (dateOfBirth != null) {
|
||||
formattedDateOfBirth = DateFormat('yyyy-MM-dd').format(dateOfBirth);
|
||||
}
|
||||
|
||||
final response = await http.put(
|
||||
Uri.parse('$baseUrl/teams/$teamId/members/$memberId/'),
|
||||
headers: {
|
||||
@ -333,12 +352,14 @@ class ApiService extends GetxService{
|
||||
body: json.encode({
|
||||
'firstname': firstname,
|
||||
'lastname': lastname,
|
||||
'date_of_birth': dateOfBirth?.toIso8601String(),
|
||||
'date_of_birth': formattedDateOfBirth,
|
||||
'female': female,
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return User.fromJson(json.decode(response.body));
|
||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||
return User.fromJson(json.decode(decodedResponse));
|
||||
} else {
|
||||
throw Exception('Failed to update team member');
|
||||
}
|
||||
@ -378,13 +399,14 @@ class ApiService extends GetxService{
|
||||
getToken();
|
||||
|
||||
final response = await http.get(
|
||||
Uri.parse('$baseUrl/entries/'),
|
||||
Uri.parse('$baseUrl/entry/'),
|
||||
headers: {'Authorization': 'Token $token', 'Content-Type': 'application/json; charset=UTF-8',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
List<dynamic> entriesJson = json.decode(response.body);
|
||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||
List<dynamic> entriesJson = json.decode(decodedResponse);
|
||||
return entriesJson.map((json) => Entry.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Failed to load entries');
|
||||
@ -416,6 +438,11 @@ class ApiService extends GetxService{
|
||||
init();
|
||||
getToken();
|
||||
|
||||
String? formattedDate;
|
||||
if (date != null) {
|
||||
formattedDate = DateFormat('yyyy-MM-dd').format(date);
|
||||
}
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse('$baseUrl/entry/'),
|
||||
headers: {
|
||||
@ -426,12 +453,14 @@ class ApiService extends GetxService{
|
||||
'team': teamId,
|
||||
'event': eventId,
|
||||
'category': categoryId,
|
||||
'date': date.toIso8601String(),
|
||||
'date': formattedDate,
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
return Entry.fromJson(json.decode(response.body));
|
||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||
|
||||
return Entry.fromJson(json.decode(decodedResponse));
|
||||
} else {
|
||||
throw Exception('Failed to create entry');
|
||||
}
|
||||
@ -441,6 +470,11 @@ class ApiService extends GetxService{
|
||||
init();
|
||||
getToken();
|
||||
|
||||
String? formattedDate;
|
||||
if (date != null) {
|
||||
formattedDate = DateFormat('yyyy-MM-dd').format(date);
|
||||
}
|
||||
|
||||
final response = await http.put(
|
||||
Uri.parse('$baseUrl/entry/$entryId/'),
|
||||
headers: {
|
||||
@ -450,12 +484,14 @@ class ApiService extends GetxService{
|
||||
body: json.encode({
|
||||
'event': eventId,
|
||||
'category': categoryId,
|
||||
'date': date.toIso8601String(),
|
||||
'date': formattedDate,
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return Entry.fromJson(json.decode(response.body));
|
||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||
|
||||
return Entry.fromJson(json.decode(decodedResponse));
|
||||
} else {
|
||||
throw Exception('Failed to update entry');
|
||||
}
|
||||
|
||||
@ -374,6 +374,11 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_map:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -660,10 +665,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.1"
|
||||
version: "0.19.0"
|
||||
isar:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@ -29,6 +29,8 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
@ -81,7 +83,7 @@ dependencies:
|
||||
circular_menu: ^2.0.1
|
||||
camera: ^0.10.0+3
|
||||
camera_camera: ^3.0.0
|
||||
intl: ^0.18.1
|
||||
intl: ^0.19.0 #^0.18.1
|
||||
modal_bottom_sheet: ^3.0.0-pre
|
||||
connectivity_plus: ^5.0.2
|
||||
flutter_map_tile_caching: ^9.0.0-dev.5
|
||||
|
||||
Reference in New Issue
Block a user