// calendar_form.dart import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import './calendar_model.dart'; import './calendar_api.dart'; class CalendarForm extends StatefulWidget { final String userId; final DateTime? initialDate; // 接收從行事曆首頁傳來的預設日期 const CalendarForm({required this.userId, this.initialDate, super.key}); @override State createState() => _CalendarFormState(); } class _CalendarFormState extends State { final _formKey = GlobalKey(); final CalendarApiService _apiService = CalendarApiService(); final TextEditingController _titleController = TextEditingController(); final TextEditingController _descController = TextEditingController(); final TextEditingController _projectController = TextEditingController(); String _selectedType = '會議'; String _selectedLevel = 'NORMAL'; late DateTime _startDate; late TimeOfDay _startTime; late DateTime _endDate; late TimeOfDay _endTime; bool _isSubmitting = false; @override void initState() { super.initState(); // 如果有傳入初始日期,就用它;否則用今天 DateTime baseDate = widget.initialDate ?? DateTime.now(); _startDate = baseDate; _endDate = baseDate; // 預設時間:目前時間的下一個整點,例如現在 10:25,預設 11:00 ~ 12:00 int nextHour = TimeOfDay.now().hour + 1; _startTime = TimeOfDay(hour: nextHour > 23 ? 23 : nextHour, minute: 0); _endTime = TimeOfDay(hour: nextHour + 1 > 23 ? 23 : nextHour + 1, minute: 0); } // 結合日期與時間的選擇器 Future _pickDateTime(bool isStart) async { final date = await showDatePicker( context: context, initialDate: isStart ? _startDate : _endDate, firstDate: DateTime(2020), lastDate: DateTime(2030), builder: (context, child) => Theme( data: ThemeData.light().copyWith(colorScheme: const ColorScheme.light(primary: Colors.blueAccent)), child: child!, ), ); if (date == null) return; final time = await showTimePicker( context: context, initialTime: isStart ? _startTime : _endTime, builder: (context, child) => Theme( data: ThemeData.light().copyWith(colorScheme: const ColorScheme.light(primary: Colors.blueAccent)), child: child!, ), ); if (time == null) return; setState(() { if (isStart) { _startDate = date; _startTime = time; // 防呆:如果結束日期早於開始日期,自動順延 if (_endDate.isBefore(_startDate)) _endDate = _startDate; } else { _endDate = date; _endTime = time; } }); } void _handleSubmit() async { if (_formKey.currentState!.validate()) { setState(() => _isSubmitting = true); // 合併 Date 與 Time 成為完整的 DateTime final start = DateTime(_startDate.year, _startDate.month, _startDate.day, _startTime.hour, _startTime.minute); final end = DateTime(_endDate.year, _endDate.month, _endDate.day, _endTime.hour, _endTime.minute); final newEvent = CalendarEvent( uuid: '', // 由後端生成 title: _titleController.text, description: _descController.text, startTime: start, endTime: end, type: _selectedType, level: _selectedLevel, personId: widget.userId, projectId: _projectController.text.isNotEmpty ? _projectController.text : null, ); bool success = await _apiService.createEvent(newEvent); if (mounted) { setState(() => _isSubmitting = false); if (success) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('行程新增成功!'))); Navigator.pop(context, true); // 回傳 true 通知列表刷新 } else { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('新增失敗,請檢查網路連線。', style: TextStyle(color: Colors.white)), backgroundColor: Colors.red)); } } } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF8F9FA), appBar: AppBar( title: const Text('新增行程', style: TextStyle(fontWeight: FontWeight.bold)), backgroundColor: Colors.white, foregroundColor: Colors.black87, elevation: 0.5, ), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildTextField('行程標題 *', _titleController, '例如:部門月會', Icons.title, isRequired: true), const SizedBox(height: 16), Row( children: [ Expanded(child: _buildDropdownField('類型', _selectedType, ['會議', '拜訪客戶', '專案開發', '私人行程', '其他'], (v) => setState(() => _selectedType = v!))), const SizedBox(width: 16), Expanded( child: _buildDropdownField('重要性', _selectedLevel, ['URGENT', 'HIGH', 'NORMAL'], (v) => setState(() => _selectedLevel = v!), displayNames: {'URGENT': '緊急', 'HIGH': '高', 'NORMAL': '一般'} ) ), ], ), const SizedBox(height: 24), const Text('時間設定', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.blueGrey)), const SizedBox(height: 12), _buildTimePickerTile('開始時間', _startDate, _startTime, true), const SizedBox(height: 12), _buildTimePickerTile('結束時間', _endDate, _endTime, false), const SizedBox(height: 24), _buildTextField('專案關聯 (選填)', _projectController, '輸入專案代號或名稱', Icons.folder_open), const SizedBox(height: 16), _buildTextField('行程詳細說明', _descController, '記錄會議大綱或行程備註...', Icons.notes, isMultiline: true), const SizedBox(height: 40), SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: _isSubmitting ? null : _handleSubmit, style: ElevatedButton.styleFrom( backgroundColor: Colors.blueAccent, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), child: _isSubmitting ? const CircularProgressIndicator(color: Colors.white) : const Text('儲存行程', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)), ), ), const SizedBox(height: 20), ], ), ), ), ); } // 共用輸入框元件 Widget _buildTextField(String label, TextEditingController controller, String hint, IconData icon, {bool isMultiline = false, bool isRequired = false}) { return Container( decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.grey.shade200)), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: TextFormField( controller: controller, maxLines: isMultiline ? 4 : 1, decoration: InputDecoration( icon: Icon(icon, color: Colors.grey.shade400, size: 20), labelText: label, labelStyle: TextStyle(color: Colors.grey.shade600, fontSize: 14), border: InputBorder.none, hintText: hint, hintStyle: TextStyle(color: Colors.grey.shade300), ), validator: isRequired ? (v) => v == null || v.isEmpty ? '此欄位為必填' : null : null, ), ); } // 共用下拉選單元件 (支援實際值與顯示名稱不同) Widget _buildDropdownField(String label, String value, List items, ValueChanged onChanged, {Map? displayNames}) { return Container( decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.grey.shade200)), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: DropdownButtonFormField( initialValue: value, decoration: InputDecoration(labelText: label, border: InputBorder.none, labelStyle: TextStyle(color: Colors.grey.shade600, fontSize: 14)), items: items.map((s) => DropdownMenuItem(value: s, child: Text(displayNames != null ? displayNames[s]! : s, style: const TextStyle(fontSize: 15)))).toList(), onChanged: onChanged, icon: Icon(Icons.keyboard_arrow_down, color: Colors.grey.shade400), ), ); } // 時間選擇按鈕元件 Widget _buildTimePickerTile(String label, DateTime date, TimeOfDay time, bool isStart) { return InkWell( onTap: () => _pickDateTime(isStart), borderRadius: BorderRadius.circular(10), child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.grey.shade200)), child: Row( children: [ Icon(Icons.access_time, color: isStart ? Colors.blueAccent : Colors.orangeAccent, size: 22), const SizedBox(width: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: TextStyle(fontSize: 12, color: Colors.grey.shade500)), const SizedBox(height: 4), Text("${DateFormat('yyyy/MM/dd').format(date)} ${time.format(context)}", style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), ], ), const Spacer(), Icon(Icons.edit_calendar, color: Colors.grey.shade300, size: 20), ], ), ), ); } }