import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:intl/intl.dart'; import '../../services/auth_service.dart'; import '../../services/club_service.dart'; import '../../services/event_service.dart'; import '../../models/event_model.dart'; class EventsScreen extends StatefulWidget { const EventsScreen({super.key}); @override State createState() => _EventsScreenState(); } class _EventsScreenState extends State { bool _isInit = false; String _searchQuery = ''; final TextEditingController _searchController = TextEditingController(); String _filterType = '전체'; // '전체', '예정', '지난' @override void dispose() { _searchController.dispose(); super.dispose(); } @override void didChangeDependencies() { super.didChangeDependencies(); if (!_isInit) { _loadEvents(); _isInit = true; } } Future _loadEvents() async { try { final authService = Provider.of(context, listen: false); final clubService = Provider.of(context, listen: false); final eventService = Provider.of(context, listen: false); if (clubService.currentClub != null) { eventService.initialize(authService.token!); await eventService.fetchClubEvents(); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('이벤트 목록을 불러오는데 실패했습니다: $e')), ); } } } List _getFilteredEvents(List events) { // 검색어 필터링 List filteredEvents = events; if (_searchQuery.isNotEmpty) { final query = _searchQuery.toLowerCase(); filteredEvents = filteredEvents.where((event) { return event.title.toLowerCase().contains(query) || (event.description?.toLowerCase().contains(query) ?? false) || (event.location?.toLowerCase().contains(query) ?? false); }).toList(); } // 날짜 필터링 final now = DateTime.now(); if (_filterType == '예정') { filteredEvents = filteredEvents.where((event) => event.startDate.isAfter(now)).toList(); } else if (_filterType == '지난') { filteredEvents = filteredEvents.where((event) => event.startDate.isBefore(now)).toList(); } // 날짜순 정렬 filteredEvents.sort((a, b) => a.startDate.compareTo(b.startDate)); return filteredEvents; } @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ // 검색 바 Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _searchController, decoration: InputDecoration( hintText: '이벤트 검색', prefixIcon: const Icon(Icons.search), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), contentPadding: const EdgeInsets.symmetric(vertical: 0), suffixIcon: _searchQuery.isNotEmpty ? IconButton( icon: const Icon(Icons.clear), onPressed: () { _searchController.clear(); setState(() { _searchQuery = ''; }); }, ) : null, ), onChanged: (value) { setState(() { _searchQuery = value; }); }, ), const SizedBox(height: 8), // 필터 버튼들 SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: [ _buildFilterChip('전체'), const SizedBox(width: 8), _buildFilterChip('예정'), const SizedBox(width: 8), _buildFilterChip('지난'), ], ), ), ], ), ), // 이벤트 목록 Expanded( child: Consumer( builder: (context, eventService, _) { if (eventService.isLoading) { return const Center(child: CircularProgressIndicator()); } final filteredEvents = _getFilteredEvents(eventService.events); if (filteredEvents.isEmpty) { return Center( child: Text( _searchQuery.isEmpty ? '등록된 이벤트가 없습니다' : '검색 결과가 없습니다', ), ); } return RefreshIndicator( onRefresh: _loadEvents, child: ListView.builder( padding: const EdgeInsets.only(bottom: 16), itemCount: filteredEvents.length, itemBuilder: (context, index) { final event = filteredEvents[index]; return _buildEventCard(event); }, ), ); }, ), ), ], ), floatingActionButton: FloatingActionButton( onPressed: () { // 이벤트 추가 화면으로 이동 (추후 구현) _showAddEventDialog(); }, backgroundColor: Colors.blue, child: const Icon(Icons.add, color: Colors.white), ), ); } Widget _buildFilterChip(String label) { final isSelected = _filterType == label; return FilterChip( label: Text(label), selected: isSelected, onSelected: (selected) { setState(() { _filterType = selected ? label : '전체'; }); }, backgroundColor: Colors.grey[200], selectedColor: Colors.blue[100], checkmarkColor: Colors.blue, labelStyle: TextStyle( color: isSelected ? Colors.blue[800] : Colors.black87, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ); } Widget _buildEventCard(Event event) { final dateFormat = DateFormat('yyyy년 MM월 dd일 HH:mm'); final now = DateTime.now(); final isPast = event.startDate.isBefore(now); return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), leading: Container( width: 48, height: 48, decoration: BoxDecoration( color: isPast ? Colors.grey[200] : Colors.blue[100], borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.event, color: isPast ? Colors.grey[600] : Colors.blue, ), ), title: Text( event.title, style: TextStyle( fontWeight: FontWeight.bold, color: isPast ? Colors.grey[600] : Colors.black, ), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), Text( dateFormat.format(event.startDate), style: TextStyle( color: isPast ? Colors.grey[500] : Colors.grey[700], ), ), if (event.location != null) Text( event.location!, style: TextStyle( color: isPast ? Colors.grey[500] : Colors.grey[600], ), ), ], ), trailing: PopupMenuButton( icon: const Icon(Icons.more_vert), onSelected: (value) { if (value == 'edit') { _showEditEventDialog(event); } else if (value == 'delete') { _showDeleteConfirmationDialog(event); } }, itemBuilder: (context) => [ const PopupMenuItem( value: 'edit', child: Row( children: [ Icon(Icons.edit, size: 18), SizedBox(width: 8), Text('수정'), ], ), ), const PopupMenuItem( value: 'delete', child: Row( children: [ Icon(Icons.delete, size: 18, color: Colors.red), SizedBox(width: 8), Text('삭제', style: TextStyle(color: Colors.red)), ], ), ), ], ), onTap: () { // 이벤트 상세 화면으로 이동 (추후 구현) _showEventDetailsDialog(event); }, ), ); } // 이벤트 추가 다이얼로그 void _showAddEventDialog() { final titleController = TextEditingController(); final descriptionController = TextEditingController(); final locationController = TextEditingController(); DateTime selectedDate = DateTime.now().add(const Duration(days: 1)); TimeOfDay selectedTime = TimeOfDay.now(); showDialog( context: context, builder: (context) => AlertDialog( title: const Text('이벤트 추가'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: titleController, decoration: const InputDecoration( labelText: '제목', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), TextField( controller: descriptionController, maxLines: 3, decoration: const InputDecoration( labelText: '설명', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), TextField( controller: locationController, decoration: const InputDecoration( labelText: '장소', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), ListTile( title: const Text('날짜'), subtitle: Text( DateFormat('yyyy년 MM월 dd일').format(selectedDate), ), trailing: const Icon(Icons.calendar_today), onTap: () async { final pickedDate = await showDatePicker( context: context, initialDate: selectedDate, firstDate: DateTime.now(), lastDate: DateTime.now().add(const Duration(days: 365)), ); if (pickedDate != null && context.mounted) { setState(() { selectedDate = pickedDate; }); } }, ), ListTile( title: const Text('시간'), subtitle: Text(selectedTime.format(context)), trailing: const Icon(Icons.access_time), onTap: () async { final pickedTime = await showTimePicker( context: context, initialTime: selectedTime, ); if (pickedTime != null && context.mounted) { setState(() { selectedTime = pickedTime; }); } }, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('취소'), ), ElevatedButton( onPressed: () async { if (titleController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('제목은 필수 입력 항목입니다')), ); return; } try { final eventService = Provider.of(context, listen: false); final clubService = Provider.of(context, listen: false); if (clubService.currentClub != null) { // 날짜와 시간 결합 final eventDateTime = DateTime( selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute, ); await eventService.createEvent({ 'title': titleController.text.trim(), 'description': descriptionController.text.trim(), 'location': locationController.text.trim(), 'startDate': eventDateTime.toIso8601String(), 'clubId': clubService.currentClub!.id, 'isActive': true, }); if (mounted) { Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('이벤트가 추가되었습니다')), ); } } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('이벤트 추가에 실패했습니다: $e')), ); } }, child: const Text('추가'), ), ], ), ); } // 이벤트 수정 다이얼로그 void _showEditEventDialog(Event event) { final titleController = TextEditingController(text: event.title); final descriptionController = TextEditingController(text: event.description ?? ''); final locationController = TextEditingController(text: event.location ?? ''); DateTime selectedDate = event.startDate; TimeOfDay selectedTime = TimeOfDay( hour: event.startDate.hour, minute: event.startDate.minute, ); showDialog( context: context, builder: (context) => AlertDialog( title: const Text('이벤트 수정'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: titleController, decoration: const InputDecoration( labelText: '제목', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), TextField( controller: descriptionController, maxLines: 3, decoration: const InputDecoration( labelText: '설명', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), TextField( controller: locationController, decoration: const InputDecoration( labelText: '장소', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), ListTile( title: const Text('날짜'), subtitle: Text( DateFormat('yyyy년 MM월 dd일').format(selectedDate), ), trailing: const Icon(Icons.calendar_today), onTap: () async { final pickedDate = await showDatePicker( context: context, initialDate: selectedDate, firstDate: DateTime.now().subtract(const Duration(days: 365)), lastDate: DateTime.now().add(const Duration(days: 365)), ); if (pickedDate != null && context.mounted) { setState(() { selectedDate = pickedDate; }); } }, ), ListTile( title: const Text('시간'), subtitle: Text(selectedTime.format(context)), trailing: const Icon(Icons.access_time), onTap: () async { final pickedTime = await showTimePicker( context: context, initialTime: selectedTime, ); if (pickedTime != null && context.mounted) { setState(() { selectedTime = pickedTime; }); } }, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('취소'), ), ElevatedButton( onPressed: () async { if (titleController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('제목은 필수 입력 항목입니다')), ); return; } try { final eventService = Provider.of(context, listen: false); // 날짜와 시간 결합 final eventDateTime = DateTime( selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute, ); await eventService.updateEvent(event.id, { 'title': titleController.text.trim(), 'description': descriptionController.text.trim(), 'location': locationController.text.trim(), 'startDate': eventDateTime.toIso8601String(), }); if (mounted) { Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('이벤트가 업데이트되었습니다')), ); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('이벤트 업데이트에 실패했습니다: $e')), ); } }, child: const Text('저장'), ), ], ), ); } // 이벤트 상세 정보 다이얼로그 void _showEventDetailsDialog(Event event) { final dateFormat = DateFormat('yyyy년 MM월 dd일 HH:mm'); showDialog( context: context, builder: (context) => AlertDialog( title: Text(event.title), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.calendar_today, size: 16, color: Colors.blue), const SizedBox(width: 8), Text(dateFormat.format(event.startDate)), ], ), const SizedBox(height: 8), if (event.location != null) ...[ Row( children: [ const Icon(Icons.location_on, size: 16, color: Colors.blue), const SizedBox(width: 8), Text(event.location!), ], ), const SizedBox(height: 8), ], if (event.description != null) ...[ const Divider(), const SizedBox(height: 8), Text( event.description!, style: const TextStyle(fontSize: 16), ), ], ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('닫기'), ), TextButton( onPressed: () { Navigator.of(context).pop(); _showEditEventDialog(event); }, child: const Text('수정'), ), ], ), ); } // 이벤트 삭제 확인 다이얼로그 void _showDeleteConfirmationDialog(Event event) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('이벤트 삭제'), content: Text('${event.title} 이벤트를 정말 삭제하시겠습니까?'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('취소'), ), TextButton( onPressed: () async { try { final eventService = Provider.of(context, listen: false); await eventService.deleteEvent(event.id); if (mounted) { Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('이벤트가 삭제되었습니다')), ); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('이벤트 삭제에 실패했습니다: $e')), ); } }, style: TextButton.styleFrom(foregroundColor: Colors.red), child: const Text('삭제'), ), ], ), ); } }