大幅変更&環境バージョンアップ
This commit is contained in:
11
lib/pages/entry/entry_binding.dart
Normal file
11
lib/pages/entry/entry_binding.dart
Normal file
@ -0,0 +1,11 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:gifunavi/pages/entry/entry_controller.dart';
|
||||
import 'package:gifunavi/services/api_service.dart';
|
||||
|
||||
class EntryBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<ApiService>(() => ApiService());
|
||||
Get.lazyPut<EntryController>(() => EntryController());
|
||||
}
|
||||
}
|
||||
310
lib/pages/entry/entry_controller.dart
Normal file
310
lib/pages/entry/entry_controller.dart
Normal file
@ -0,0 +1,310 @@
|
||||
// lib/entry/entry_controller.dart
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:gifunavi/model/entry.dart';
|
||||
import 'package:gifunavi/model/event.dart';
|
||||
import 'package:gifunavi/model/team.dart';
|
||||
import 'package:gifunavi/model/category.dart';
|
||||
import 'package:gifunavi/services/api_service.dart';
|
||||
|
||||
import '../index/index_controller.dart';
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
class EntryController extends GetxController {
|
||||
late ApiService _apiService;
|
||||
|
||||
final entries = <Entry>[].obs;
|
||||
final events = <Event>[].obs;
|
||||
final teams = <Team>[].obs;
|
||||
final categories = <NewCategory>[].obs;
|
||||
|
||||
final selectedEvent = Rx<Event?>(null);
|
||||
final selectedTeam = Rx<Team?>(null);
|
||||
final selectedCategory = Rx<NewCategory?>(null);
|
||||
final selectedDate = Rx<DateTime?>(null);
|
||||
|
||||
final currentEntry = Rx<Entry?>(null);
|
||||
final isLoading = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() async {
|
||||
super.onInit();
|
||||
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(),
|
||||
fetchTeams(),
|
||||
fetchCategories(),
|
||||
]);
|
||||
if (Get.arguments != null && Get.arguments['entry'] != null) {
|
||||
currentEntry.value = Get.arguments['entry'];
|
||||
initializeEditMode(currentEntry.value!);
|
||||
} else {
|
||||
// 新規作成モードの場合、最初のイベントを選択
|
||||
if (events.isNotEmpty) {
|
||||
selectedEvent.value = events.first;
|
||||
selectedDate.value = events.first.startDatetime;
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
print('Error initializing data: $e');
|
||||
Get.snackbar('Error', 'Failed to load initial data');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void initializeEditMode(Entry entry) {
|
||||
currentEntry.value = entry;
|
||||
selectedEvent.value = entry.event;
|
||||
selectedTeam.value = entry.team;
|
||||
selectedCategory.value = entry.category;
|
||||
selectedDate.value = entry.date;
|
||||
}
|
||||
|
||||
void updateEvent(Event? value) {
|
||||
selectedEvent.value = value;
|
||||
if (value != null) {
|
||||
// イベント変更時に日付を調整
|
||||
if (selectedDate.value == null ||
|
||||
selectedDate.value!.isBefore(value.startDatetime) ||
|
||||
selectedDate.value!.isAfter(value.endDatetime)) {
|
||||
selectedDate.value = value.startDatetime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateTeam(Team? value) {
|
||||
selectedTeam.value = value;
|
||||
if (value != null) {
|
||||
selectedCategory.value = value.category;
|
||||
}
|
||||
}
|
||||
//void updateTeam(Team? value) => selectedTeam.value = value;
|
||||
void updateCategory(NewCategory? value) => selectedCategory.value = value;
|
||||
//void updateDate(DateTime value) => selectedDate.value = value;
|
||||
void updateDate(DateTime value) {
|
||||
selectedDate.value = tz.TZDateTime.from(value, tz.getLocation('Asia/Tokyo'));
|
||||
}
|
||||
/*
|
||||
void updateDate(DateTime value){
|
||||
selectedDate.value = DateFormat('yyyy-MM-dd').format(value!) as DateTime?;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
void _initializeEntryData() {
|
||||
if (currentEntry.value != null) {
|
||||
selectedEvent.value = currentEntry.value!.event;
|
||||
selectedTeam.value = currentEntry.value!.team;
|
||||
selectedCategory.value = currentEntry.value!.category;
|
||||
selectedDate.value = currentEntry.value!.date;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchEntries() async {
|
||||
try {
|
||||
final fetchedEntries = await _apiService.getEntries();
|
||||
entries.assignAll(fetchedEntries);
|
||||
} catch (e) {
|
||||
print('Error fetching entries: $e');
|
||||
Get.snackbar('Error', 'Failed to fetch entries');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchEvents() async {
|
||||
try {
|
||||
final fetchedEvents = await _apiService.getEvents();
|
||||
events.assignAll(fetchedEvents.map((event) {
|
||||
// end_dateの7日前を締め切りとして設定
|
||||
final deadlineDateTime = event.endDatetime.subtract(const Duration(days: 7));
|
||||
return Event(
|
||||
id: event.id,
|
||||
eventName: event.eventName,
|
||||
startDatetime: event.startDatetime,
|
||||
endDatetime: event.endDatetime,
|
||||
deadlineDateTime: deadlineDateTime,
|
||||
);
|
||||
}).toList());
|
||||
} catch (e) {
|
||||
print('Error fetching events: $e');
|
||||
Get.snackbar('Error', 'Failed to fetch events');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchEvents_old() async {
|
||||
try {
|
||||
final fetchedEvents = await _apiService.getEvents();
|
||||
events.assignAll(fetchedEvents);
|
||||
} catch (e) {
|
||||
print('Error fetching events: $e');
|
||||
Get.snackbar('Error', 'Failed to fetch events');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchTeams() async {
|
||||
try {
|
||||
final fetchedTeams = await _apiService.getTeams();
|
||||
teams.assignAll(fetchedTeams);
|
||||
} catch (e) {
|
||||
print('Error fetching teams: $e');
|
||||
Get.snackbar('Error', 'Failed to fetch team');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchCategories() async {
|
||||
try {
|
||||
final fetchedCategories = await _apiService.getCategories();
|
||||
categories.assignAll(fetchedCategories);
|
||||
} catch (e) {
|
||||
print('Error fetching categories: $e');
|
||||
Get.snackbar('Error', 'Failed to fetch categories');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createEntry() async {
|
||||
if (selectedEvent.value == null || selectedTeam.value == null ||
|
||||
selectedCategory.value == null || selectedDate.value == null) {
|
||||
Get.snackbar('Error', 'Please fill all fields');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isLoading.value = true;
|
||||
// Get zekken number
|
||||
final updatedCategory = await _apiService.getZekkenNumber(selectedCategory.value!.id);
|
||||
final zekkenNumber = updatedCategory.categoryNumber.toString();
|
||||
|
||||
final newEntry = await _apiService.createEntry(
|
||||
selectedTeam.value!.id,
|
||||
selectedEvent.value!.id,
|
||||
selectedCategory.value!.id,
|
||||
selectedDate.value!,
|
||||
zekkenNumber,
|
||||
);
|
||||
entries.add(newEntry);
|
||||
Get.back();
|
||||
} catch (e) {
|
||||
print('Error creating entry: $e');
|
||||
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);
|
||||
}
|
||||
|
||||
bool isEntryEditable(Event event) {
|
||||
return DateTime.now().isBefore(event.deadlineDateTime);
|
||||
}
|
||||
|
||||
Future<void> updateEntry() async {
|
||||
if (currentEntry.value == null) {
|
||||
Get.snackbar('Error', 'No entry selected for update');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isEntryEditable(currentEntry.value!.event)) {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Text('エントリー変更不可'),
|
||||
content: Text('締め切りを過ぎているため、エントリーの変更はできません。変更が必要な場合は事務局にお問い合わせください。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('OK'),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
isLoading.value = true;
|
||||
final updatedEntry = await _apiService.updateEntry(
|
||||
currentEntry.value!.id,
|
||||
currentEntry.value!.team.id,
|
||||
selectedEvent.value!.id,
|
||||
selectedCategory.value!.id,
|
||||
selectedDate.value!,
|
||||
currentEntry.value!.zekkenNumber,
|
||||
);
|
||||
final index = entries.indexWhere((entry) => entry.id == updatedEntry.id);
|
||||
if (index != -1) {
|
||||
entries[index] = updatedEntry;
|
||||
}
|
||||
Get.back();
|
||||
} catch (e) {
|
||||
print('Error updating entry: $e');
|
||||
Get.snackbar('Error', 'Failed to update entry');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateEntryCategory(int entryId, int newCategoryId) async {
|
||||
try {
|
||||
//await _apiService.updateEntryCategory(entryId, newCategoryId);
|
||||
final updatedEntry = await _apiService.updateEntry(
|
||||
currentEntry.value!.id,
|
||||
currentEntry.value!.team.id,
|
||||
selectedEvent.value!.id,
|
||||
newCategoryId,
|
||||
currentEntry.value!.date!,
|
||||
currentEntry.value!.zekkenNumber,
|
||||
);
|
||||
await fetchEntries();
|
||||
} catch (e) {
|
||||
print('Error updating entry category: $e');
|
||||
Get.snackbar('エラー', 'エントリーのカテゴリ更新に失敗しました');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteEntry() async {
|
||||
if (currentEntry.value == null) {
|
||||
Get.snackbar('Error', 'No entry selected for deletion');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isLoading.value = true;
|
||||
await _apiService.deleteEntry(currentEntry.value!.id);
|
||||
entries.removeWhere((entry) => entry.id == currentEntry.value!.id);
|
||||
Get.back();
|
||||
} catch (e) {
|
||||
print('Error deleting entry: $e');
|
||||
Get.snackbar('Error', 'Failed to delete entry');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool isOwner() {
|
||||
// Implement logic to check if the current user is the owner of the entry
|
||||
return true; // Placeholder
|
||||
}
|
||||
}
|
||||
193
lib/pages/entry/entry_detail_page.dart
Normal file
193
lib/pages/entry/entry_detail_page.dart
Normal file
@ -0,0 +1,193 @@
|
||||
// lib/pages/entry/entry_detail_page.dart
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:gifunavi/pages/entry/entry_controller.dart';
|
||||
import 'package:gifunavi/model/event.dart';
|
||||
import 'package:gifunavi/model/category.dart';
|
||||
import 'package:gifunavi/model/team.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
class EntryDetailPage extends GetView<EntryController> {
|
||||
const EntryDetailPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Map<String, dynamic> arguments = Get.arguments ?? {};
|
||||
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: Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildDropdown<Event>(
|
||||
label: 'イベント',
|
||||
items: controller.events,
|
||||
selectedId: controller.selectedEvent.value?.id,
|
||||
onChanged: (eventId) => controller.updateEvent(
|
||||
controller.events.firstWhere((e) => e.id == eventId)
|
||||
),
|
||||
getDisplayName: (event) => event.eventName,
|
||||
getId: (event) => event.id,
|
||||
),
|
||||
const 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,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildCategoryDropdown(),
|
||||
/*
|
||||
_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,
|
||||
),
|
||||
|
||||
*/
|
||||
const SizedBox(height: 16),
|
||||
ListTile(
|
||||
title: const Text('日付'),
|
||||
subtitle: Text(
|
||||
controller.selectedDate.value != null
|
||||
? DateFormat('yyyy-MM-dd').format(tz.TZDateTime.from(controller.selectedDate.value!, tz.getLocation('Asia/Tokyo')))
|
||||
: '日付を選択してください',
|
||||
),
|
||||
onTap: () async {
|
||||
if (controller.selectedEvent.value == null) {
|
||||
Get.snackbar('Error', 'Please select an event first');
|
||||
return;
|
||||
}
|
||||
final tz.TZDateTime now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo'));
|
||||
final tz.TZDateTime eventStart = tz.TZDateTime.from(controller.selectedEvent.value!.startDatetime, tz.getLocation('Asia/Tokyo'));
|
||||
final tz.TZDateTime eventEnd = tz.TZDateTime.from(controller.selectedEvent.value!.endDatetime, tz.getLocation('Asia/Tokyo'));
|
||||
|
||||
final tz.TZDateTime initialDate = controller.selectedDate.value != null
|
||||
? tz.TZDateTime.from(controller.selectedDate.value!, tz.getLocation('Asia/Tokyo'))
|
||||
: (now.isAfter(eventStart) ? now : eventStart);
|
||||
|
||||
// 選択可能な最初の日付を設定(今日かイベント開始日のうち、より後の日付)
|
||||
final tz.TZDateTime firstDate = now.isAfter(eventStart) ? now : eventStart;
|
||||
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: initialDate.isAfter(firstDate) ? initialDate : firstDate,
|
||||
firstDate: firstDate,
|
||||
lastDate: eventEnd,
|
||||
);
|
||||
if (picked != null) {
|
||||
controller.updateDate(tz.TZDateTime.from(picked, tz.getLocation('Asia/Tokyo')));
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
if (mode == 'new')
|
||||
ElevatedButton(
|
||||
onPressed: () => controller.createEntry(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size(double.infinity, 50),
|
||||
),
|
||||
child: const Text('エントリーを作成'),
|
||||
)
|
||||
else
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => controller.deleteEntry(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size(0, 50),
|
||||
),
|
||||
child: const Text('エントリーを削除'),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => controller.updateEntry(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.lightBlue,
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size(0, 50),
|
||||
),
|
||||
child: const Text('エントリーを更新'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCategoryDropdown() {
|
||||
final eligibleCategories = controller.categories.where((c) =>
|
||||
c.baseCategory == controller.selectedCategory.value?.baseCategory
|
||||
).toList();
|
||||
|
||||
return DropdownButtonFormField<NewCategory>(
|
||||
decoration: InputDecoration(labelText: 'カテゴリ'),
|
||||
value: controller.selectedCategory.value,
|
||||
items: eligibleCategories.map((category) => DropdownMenuItem<NewCategory>(
|
||||
value: category,
|
||||
child: Text(category.categoryName),
|
||||
)).toList(),
|
||||
onChanged: (value) => controller.updateCategory(value),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
116
lib/pages/entry/entry_list_page.dart
Normal file
116
lib/pages/entry/entry_list_page.dart
Normal file
@ -0,0 +1,116 @@
|
||||
// lib/pages/entry/entry_list_page.dart
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:gifunavi/pages/entry/entry_controller.dart';
|
||||
import 'package:gifunavi/routes/app_pages.dart';
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
class EntryListPage extends GetView<EntryController> {
|
||||
const EntryListPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('エントリー管理'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () => Get.toNamed(AppPages.ENTRY_DETAIL, arguments: {'mode': 'new'}),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Obx(() {
|
||||
if (controller.entries.isEmpty) {
|
||||
return const Center(
|
||||
child: Text('表示するエントリーがありません。'),
|
||||
);
|
||||
}
|
||||
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: const 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> {
|
||||
const EntryListPage_old({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('エントリー管理'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () => Get.toNamed(AppPages.ENTRY_DETAIL, arguments: {'mode': 'new'}),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Obx((){
|
||||
if (controller.isLoading.value) {
|
||||
return const 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:gifunavi/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:gifunavi/model/entry.dart';
|
||||
import 'package:gifunavi/pages/index/index_controller.dart';
|
||||
import 'package:gifunavi/pages/destination/destination_controller.dart';
|
||||
import 'package:gifunavi/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);
|
||||
}
|
||||
}
|
||||
}
|
||||
108
lib/pages/entry/event_entries_page.dart
Normal file
108
lib/pages/entry/event_entries_page.dart
Normal file
@ -0,0 +1,108 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:gifunavi/pages/entry/event_entries_controller.dart';
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
class EventEntriesPage_old extends GetView<EventEntriesController> {
|
||||
const EventEntriesPage_old({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const 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> {
|
||||
const EventEntriesPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Obx(() => Text(controller.showTodayEntries.value ? 'イベント参加' : 'イベント参照')),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Obx(() => Text(
|
||||
controller.showTodayEntries.value ? '本日のエントリー' : 'すべてのエントリー',
|
||||
style: const 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 const 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: const 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user