325 lines
12 KiB
Dart
325 lines
12 KiB
Dart
// lib/pages/team/member_detail_page.dart
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:gifunavi/pages/team/member_controller.dart';
|
|
import 'package:gifunavi/pages/team/team_controller.dart';
|
|
import 'package:intl/intl.dart'; // この行を追加
|
|
import 'package:gifunavi/widgets/custom_date_picker.dart';
|
|
// 追加
|
|
|
|
import 'package:gifunavi/routes/app_pages.dart';
|
|
import 'package:gifunavi/model/user.dart';
|
|
|
|
class MemberDetailPage extends StatefulWidget {
|
|
const MemberDetailPage({super.key});
|
|
|
|
@override
|
|
_MemberDetailPageState createState() => _MemberDetailPageState();
|
|
}
|
|
|
|
class _MemberDetailPageState extends State<MemberDetailPage> {
|
|
final MemberController controller = Get.find<MemberController>();
|
|
final TeamController teamController = Get.find<TeamController>();
|
|
late TextEditingController _firstNameController;
|
|
late TextEditingController _lastNameController;
|
|
late TextEditingController _emailController;
|
|
|
|
|
|
@override
|
|
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)),
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<void> _handleSaveAndNavigateBack_old() async {
|
|
bool success = await controller.saveMember();
|
|
if (success) {
|
|
Get.until((route) => Get.currentRoute == AppPages.TEAM_DETAIL);
|
|
// スナックバーが表示されるのを待つ
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
|
|
// 現在のスナックバーを安全に閉じる
|
|
if (Get.isSnackbarOpen) {
|
|
await Get.closeCurrentSnackbar();
|
|
}
|
|
|
|
// リストページに戻る
|
|
//Get.until((route) => Get.currentRoute == '/team');
|
|
// または、リストページの具体的なルート名がある場合は以下を使用
|
|
// Get.offNamed('/team');
|
|
}
|
|
}
|
|
|
|
Future<void> _handleSaveAndNavigateBack() async {
|
|
|
|
if (!controller.validateInputs()) return;
|
|
|
|
try {
|
|
await controller.saveMember();
|
|
await teamController.updateTeamComposition();
|
|
|
|
Get.until((route) => Get.currentRoute == AppPages.TEAM_DETAIL);
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
if (Get.isSnackbarOpen) {
|
|
await Get.closeCurrentSnackbar();
|
|
}
|
|
} catch (e) {
|
|
print('Error saving member: $e');
|
|
Get.snackbar('エラー', 'メンバーの保存に失敗しました: ${e.toString()}', snackPosition: SnackPosition.BOTTOM);
|
|
}
|
|
}
|
|
|
|
|
|
Future<void> _handleDeleteAndNavigateBack() async {
|
|
final confirmed = await Get.dialog<bool>(
|
|
AlertDialog(
|
|
title: const Text('確認'),
|
|
content: const Text('このメンバーを削除してもよろしいですか?'),
|
|
actions: [
|
|
TextButton(
|
|
child: const Text('キャンセル'),
|
|
onPressed: () => Get.back(result: false),
|
|
),
|
|
TextButton(
|
|
child: const Text('削除'),
|
|
onPressed: () => Get.back(result: true),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
if (confirmed == true) {
|
|
try {
|
|
if (controller.member.value != null && controller.member.value!.id != null) {
|
|
await teamController.removeMember(controller.member.value!.id!);
|
|
|
|
// スナックバーの処理を安全に行う
|
|
await _safelyCloseSnackbar();
|
|
|
|
// 画面遷移
|
|
Get.until((route) => Get.currentRoute == AppPages.TEAM_DETAIL);
|
|
|
|
} else {
|
|
Get.snackbar('エラー', 'メンバー情報が不正です', snackPosition: SnackPosition.BOTTOM);
|
|
}
|
|
} catch (e) {
|
|
print('Error deleting member: $e');
|
|
Get.snackbar('エラー', 'メンバーの削除に失敗しました: ${e.toString()}', snackPosition: SnackPosition.BOTTOM);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _safelyCloseSnackbar() async {
|
|
if (Get.isSnackbarOpen) {
|
|
try {
|
|
await Get.closeCurrentSnackbar();
|
|
} catch (e) {
|
|
print('Error closing snackbar: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
@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' ? 'メンバー追加' : 'メンバー詳細'),
|
|
),
|
|
body: Obx(() {
|
|
if (controller.isLoading.value) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
// TextEditingControllerをObxの中で作成
|
|
final emailController = TextEditingController(text: controller.email.value);
|
|
// カーソル位置を保持
|
|
emailController.selection = TextSelection.fromPosition(
|
|
TextPosition(offset: controller.email.value.length),
|
|
);
|
|
|
|
return Column(
|
|
children: [
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (mode == 'new')
|
|
TextFormField(
|
|
controller: _emailController,
|
|
onChanged: (value) => controller.updateEmail(value),
|
|
decoration: const InputDecoration(labelText: 'メールアドレス'),
|
|
keyboardType: TextInputType.emailAddress, // メールアドレス用のキーボードを表示
|
|
autocorrect: false, // 自動修正を無効化
|
|
enableSuggestions: false,
|
|
)
|
|
else if (controller.isDummyEmail)
|
|
const Text('メールアドレス: (メアド無し)')
|
|
else
|
|
Text('メールアドレス: ${controller.email.value}'),
|
|
|
|
if (controller.email.value.isEmpty || controller.isDummyEmail || mode == 'edit') ...[
|
|
TextFormField(
|
|
decoration: const InputDecoration(labelText: '姓'),
|
|
controller: _lastNameController,
|
|
onChanged: (value) => controller.updateLastName(value),
|
|
|
|
//controller: TextEditingController(text: controller.lastname.value),
|
|
),
|
|
TextFormField(
|
|
decoration: const 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: const Text('生年月日'),
|
|
subtitle: Text(controller.dateOfBirth.value != null
|
|
? '${DateFormat('yyyy年MM月dd日').format(controller.dateOfBirth.value!)} (${controller.getAgeAndGrade()})'
|
|
: '未設定'),
|
|
onTap: () async {
|
|
final date = await showDialog<DateTime>(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return CustomDatePicker(
|
|
initialDate: controller.dateOfBirth.value ?? DateTime.now(),
|
|
firstDate: DateTime(1920),
|
|
lastDate: DateTime.now(),
|
|
currentDateText: controller.dateOfBirth.value != null
|
|
? DateFormat('yyyy/MM/dd').format(controller.dateOfBirth.value!)
|
|
: '',
|
|
);
|
|
},
|
|
);
|
|
if (date != null) {
|
|
controller.dateOfBirth.value = date;
|
|
}
|
|
},
|
|
)
|
|
else
|
|
const Text('18歳以上'),
|
|
|
|
SwitchListTile(
|
|
title: const Text('性別'),
|
|
subtitle: Text(controller.female.value ? '女性' : '男性'),
|
|
value: controller.female.value,
|
|
onChanged: (value) => controller.female.value = value,
|
|
),
|
|
],
|
|
]),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
ElevatedButton(
|
|
onPressed: _handleDeleteAndNavigateBack,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.red,
|
|
foregroundColor: Colors.white,
|
|
),
|
|
child: const Text('削除'),
|
|
),
|
|
if (!controller.isDummyEmail && !controller.isApproved && mode == 'edit')
|
|
ElevatedButton(
|
|
onPressed: () => controller.resendInvitation(),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.white,
|
|
foregroundColor: Colors.black,
|
|
),
|
|
child: const Text('招待再送信'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: _handleSaveAndNavigateBack,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.green,
|
|
foregroundColor: Colors.white,
|
|
),
|
|
child: Text(controller.isDummyEmail ? '保存' :
|
|
(mode == 'new' && !controller.isDummyEmail) ? '保存・招待' :
|
|
'保存'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_firstNameController.dispose();
|
|
_lastNameController.dispose();
|
|
_emailController.dispose();
|
|
super.dispose();
|
|
}
|
|
} |