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/member_service.dart'; import '../../services/event_service.dart'; import '../../models/club_model.dart'; import 'club_settings_screen.dart'; import '../../theme/badges.dart'; class DashboardScreen extends StatefulWidget { const DashboardScreen({super.key}); @override State createState() => _DashboardScreenState(); } class _DashboardScreenState extends State { bool _isLoading = false; bool _isInit = false; @override void didChangeDependencies() { super.didChangeDependencies(); if (!_isInit) { _loadDashboardData(); _isInit = true; } } Future _loadDashboardData() async { setState(() { _isLoading = true; }); // await 이전에 messenger 캡처 (catch에서 사용) final messenger = ScaffoldMessenger.of(context); // await 이전에 서비스 인스턴스 캡처 final authService = Provider.of(context, listen: false); final clubService = Provider.of(context, listen: false); final memberService = Provider.of(context, listen: false); final eventService = Provider.of(context, listen: false); try { // 클럽 정보 로드 if (clubService.currentClub == null) { await clubService.initialize(authService.token!); } // 회원 및 이벤트 서비스 초기화 if (clubService.currentClub != null) { memberService.initialize(authService.token!); eventService.initialize(authService.token!); // 회원 및 이벤트 데이터 로드 await Future.wait([ memberService.fetchClubMembers(), eventService.fetchClubEvents(), ]); } } catch (e) { // 오류 처리 messenger.showSnackBar( SnackBar(content: Text('데이터 로드 중 오류가 발생했습니다: $e')), ); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } @override Widget build(BuildContext context) { return Scaffold( body: _isLoading ? const Center(child: CircularProgressIndicator()) : RefreshIndicator( onRefresh: _loadDashboardData, child: Consumer3( builder: (context, clubService, memberService, eventService, _) { final club = clubService.currentClub; if (club == null) { return const Center( child: Text('클럽 정보를 불러올 수 없습니다'), ); } return SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 클럽 정보 카드 _buildClubInfoCard(club), const SizedBox(height: 16), // 통계 카드들 _buildStatisticsCards( memberCount: memberService.members.length, eventCount: eventService.events.length, ), const SizedBox(height: 16), // 최근 이벤트 목록 _buildRecentEventsList(eventService), ], ), ); }, ), ), ); } Widget _buildClubInfoCard(Club club) { return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Row( children: [ if (club.logo != null) ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.network( club.logo!, width: 60, height: 60, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Container( width: 60, height: 60, color: Colors.grey[300], child: const Icon(Icons.sports_golf), ); }, ), ) else Container( width: 60, height: 60, decoration: BoxDecoration( color: Colors.blue[100], borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.sports_golf, size: 30, color: Colors.blue, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( club.name, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), if (club.description != null) Text( club.description!, style: TextStyle( fontSize: 14, color: Colors.grey[600], ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), ], ), ), IconButton( icon: const Icon(Icons.settings), tooltip: '클럽 설정', onPressed: () { Navigator.of(context).pushNamed(ClubSettingsScreen.routeName); }, ), ], ), const SizedBox(height: 16), if (club.address != null) _buildInfoRow(Icons.location_on, club.address!), if (club.phone != null) _buildInfoRow(Icons.phone, club.phone!), if (club.email != null) _buildInfoRow(Icons.email, club.email!), if (club.website != null) _buildInfoRow(Icons.language, club.website!), ], ), ), ); } Widget _buildInfoRow(IconData icon, String text) { return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Row( children: [ Icon(icon, size: 16, color: Colors.grey[600]), const SizedBox(width: 8), Expanded( child: Text( text, style: TextStyle(fontSize: 14, color: Colors.grey[800]), ), ), ], ), ); } Widget _buildStatisticsCards({required int memberCount, required int eventCount}) { return Row( children: [ Expanded( child: _buildStatCard( title: '회원', value: memberCount.toString(), icon: Icons.people, color: Colors.blue, ), ), const SizedBox(width: 16), Expanded( child: _buildStatCard( title: '이벤트', value: eventCount.toString(), icon: Icons.event, color: Colors.orange, ), ), ], ); } Widget _buildStatCard({ required String title, required String value, required IconData icon, required Color color, }) { return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Icon(icon, size: 32, color: color), const SizedBox(height: 8), Text( value, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: color, ), ), Text( title, style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), ], ), ), ); } Widget _buildRecentEventsList(EventService eventService) { final events = eventService.events; if (events.isEmpty) { return const Card( child: Padding( padding: EdgeInsets.all(16.0), child: Center( child: Text('예정된 이벤트가 없습니다'), ), ), ); } // 날짜 기준으로 정렬 (가까운 날짜순) final sortedEvents = [...events]; sortedEvents.sort((a, b) => a.startDate.compareTo(b.startDate)); // 앞으로 진행될 이벤트만 필터링 (최대 3개) final upcomingEvents = sortedEvents .where((event) => event.startDate.isAfter(DateTime.now())) .take(3) .toList(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '다가오는 이벤트', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), TextButton( onPressed: () { // 이벤트 목록 화면으로 이동 (추후 구현) }, child: const Text('모두 보기'), ), ], ), const SizedBox(height: 8), ...upcomingEvents.map((event) => _buildEventCard(event)), ], ); } Widget _buildEventCard(event) { final dateFormat = DateFormat('yyyy년 MM월 dd일 HH:mm'); return Card( margin: const EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), title: Text( event.title, style: const TextStyle( fontWeight: FontWeight.bold, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), if (event.status != null) ...[ Wrap( spacing: 4, runSpacing: 4, children: [ BadgeStyles.eventStatus(event.status!), ], ), const SizedBox(height: 4), ], Text( dateFormat.format(event.startDate), style: TextStyle( color: Colors.grey[700], ), ), if (event.location != null) Text( event.location!, style: TextStyle( color: Colors.grey[600], ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), trailing: const Icon(Icons.arrow_forward_ios, size: 16), onTap: () { // 이벤트 상세 화면으로 이동 (추후 구현) }, ), ); } }