LCOV - code coverage report
Current view: top level - lib/screens/club - dashboard_screen.dart Coverage Total Hit
Test: lcov.info Lines: 0.6 % 154 1
Test Date: 2025-08-12 03:21:51 Functions: - 0 0

            Line data    Source code
       1              : import 'package:flutter/material.dart';
       2              : import 'package:provider/provider.dart';
       3              : import 'package:intl/intl.dart';
       4              : 
       5              : import '../../services/auth_service.dart';
       6              : import '../../services/club_service.dart';
       7              : import '../../services/member_service.dart';
       8              : import '../../services/event_service.dart';
       9              : import '../../models/club_model.dart';
      10              : import 'club_settings_screen.dart';
      11              : 
      12              : class DashboardScreen extends StatefulWidget {
      13           24 :   const DashboardScreen({super.key});
      14              : 
      15            0 :   @override
      16            0 :   State<DashboardScreen> createState() => _DashboardScreenState();
      17              : }
      18              : 
      19              : class _DashboardScreenState extends State<DashboardScreen> {
      20              :   bool _isLoading = false;
      21              :   bool _isInit = false;
      22              : 
      23            0 :   @override
      24              :   void didChangeDependencies() {
      25            0 :     super.didChangeDependencies();
      26            0 :     if (!_isInit) {
      27            0 :       _loadDashboardData();
      28            0 :       _isInit = true;
      29              :     }
      30              :   }
      31              : 
      32            0 :   Future<void> _loadDashboardData() async {
      33            0 :     setState(() {
      34            0 :       _isLoading = true;
      35              :     });
      36              : 
      37              :     try {
      38            0 :       final authService = Provider.of<AuthService>(context, listen: false);
      39            0 :       final clubService = Provider.of<ClubService>(context, listen: false);
      40              :       
      41              :       // 클럽 정보 로드
      42            0 :       if (clubService.currentClub == null) {
      43            0 :         await clubService.initialize(authService.token!);
      44              :       }
      45              :       
      46              :       // 회원 및 이벤트 서비스 초기화
      47            0 :       if (clubService.currentClub != null) {
      48            0 :         final memberService = Provider.of<MemberService>(context, listen: false);
      49            0 :         final eventService = Provider.of<EventService>(context, listen: false);
      50              :         
      51            0 :         memberService.initialize(authService.token!);
      52            0 :         eventService.initialize(authService.token!);
      53              :         
      54              :         // 회원 및 이벤트 데이터 로드
      55            0 :         await Future.wait([
      56            0 :           memberService.fetchClubMembers(),
      57            0 :           eventService.fetchClubEvents(),
      58              :         ]);
      59              :       }
      60              :     } catch (e) {
      61              :       // 오류 처리
      62            0 :       ScaffoldMessenger.of(context).showSnackBar(
      63            0 :         SnackBar(content: Text('데이터 로드 중 오류가 발생했습니다: $e')),
      64              :       );
      65              :     } finally {
      66            0 :       setState(() {
      67            0 :         _isLoading = false;
      68              :       });
      69              :     }
      70              :   }
      71              : 
      72            0 :   @override
      73              :   Widget build(BuildContext context) {
      74            0 :     return Scaffold(
      75            0 :       body: _isLoading
      76              :           ? const Center(child: CircularProgressIndicator())
      77            0 :           : RefreshIndicator(
      78            0 :               onRefresh: _loadDashboardData,
      79            0 :               child: Consumer3<ClubService, MemberService, EventService>(
      80            0 :                 builder: (context, clubService, memberService, eventService, _) {
      81            0 :                   final club = clubService.currentClub;
      82              :                   
      83              :                   if (club == null) {
      84              :                     return const Center(
      85              :                       child: Text('클럽 정보를 불러올 수 없습니다'),
      86              :                     );
      87              :                   }
      88              :                   
      89            0 :                   return SingleChildScrollView(
      90              :                     physics: const AlwaysScrollableScrollPhysics(),
      91              :                     padding: const EdgeInsets.all(16.0),
      92            0 :                     child: Column(
      93              :                       crossAxisAlignment: CrossAxisAlignment.start,
      94            0 :                       children: [
      95              :                         // 클럽 정보 카드
      96            0 :                         _buildClubInfoCard(club),
      97              :                         const SizedBox(height: 16),
      98              :                         
      99              :                         // 통계 카드들
     100            0 :                         _buildStatisticsCards(
     101            0 :                           memberCount: memberService.members.length,
     102            0 :                           eventCount: eventService.events.length,
     103              :                         ),
     104              :                         const SizedBox(height: 16),
     105              :                         
     106              :                         // 최근 이벤트 목록
     107            0 :                         _buildRecentEventsList(eventService),
     108              :                       ],
     109              :                     ),
     110              :                   );
     111              :                 },
     112              :               ),
     113              :             ),
     114              :     );
     115              :   }
     116              : 
     117            0 :   Widget _buildClubInfoCard(Club club) {
     118            0 :     return Card(
     119              :       elevation: 2,
     120            0 :       shape: RoundedRectangleBorder(
     121            0 :         borderRadius: BorderRadius.circular(12),
     122              :       ),
     123            0 :       child: Padding(
     124              :         padding: const EdgeInsets.all(16.0),
     125            0 :         child: Column(
     126              :           crossAxisAlignment: CrossAxisAlignment.start,
     127            0 :           children: [
     128            0 :             Row(
     129              :               mainAxisAlignment: MainAxisAlignment.spaceBetween,
     130            0 :               children: [
     131            0 :                 Expanded(
     132            0 :                   child: Row(
     133            0 :                     children: [
     134            0 :                       if (club.logo != null)
     135            0 :                         ClipRRect(
     136            0 :                           borderRadius: BorderRadius.circular(8),
     137            0 :                           child: Image.network(
     138            0 :                             club.logo!,
     139              :                             width: 60,
     140              :                             height: 60,
     141              :                             fit: BoxFit.cover,
     142            0 :                             errorBuilder: (context, error, stackTrace) {
     143            0 :                               return Container(
     144              :                                 width: 60,
     145              :                                 height: 60,
     146            0 :                                 color: Colors.grey[300],
     147              :                                 child: const Icon(Icons.sports_golf),
     148              :                               );
     149              :                             },
     150              :                           ),
     151              :                         )
     152              :                       else
     153            0 :                         Container(
     154              :                           width: 60,
     155              :                           height: 60,
     156            0 :                           decoration: BoxDecoration(
     157            0 :                             color: Colors.blue[100],
     158            0 :                             borderRadius: BorderRadius.circular(8),
     159              :                           ),
     160              :                           child: const Icon(
     161              :                             Icons.sports_golf,
     162              :                             size: 30,
     163              :                             color: Colors.blue,
     164              :                           ),
     165              :                         ),
     166            0 :                       const SizedBox(width: 16),
     167            0 :                       Expanded(
     168            0 :                         child: Column(
     169              :                           crossAxisAlignment: CrossAxisAlignment.start,
     170            0 :                           children: [
     171            0 :                             Text(
     172            0 :                               club.name,
     173              :                               style: const TextStyle(
     174              :                                 fontSize: 20,
     175              :                                 fontWeight: FontWeight.bold,
     176              :                               ),
     177              :                             ),
     178            0 :                             if (club.description != null)
     179            0 :                               Text(
     180            0 :                                 club.description!,
     181            0 :                                 style: TextStyle(
     182              :                                   fontSize: 14,
     183            0 :                                   color: Colors.grey[600],
     184              :                                 ),
     185              :                                 maxLines: 2,
     186              :                                 overflow: TextOverflow.ellipsis,
     187              :                               ),
     188              :                           ],
     189              :                         ),
     190              :                       ),
     191              :                     ],
     192              :                   ),
     193              :                 ),
     194            0 :                 IconButton(
     195              :                   icon: const Icon(Icons.settings),
     196              :                   tooltip: '클럽 설정',
     197            0 :                   onPressed: () {
     198            0 :                     Navigator.of(context).pushNamed(ClubSettingsScreen.routeName);
     199              :                   },
     200              :                 ),
     201              :               ],
     202              :             ),
     203              :             const SizedBox(height: 16),
     204            0 :             if (club.address != null)
     205            0 :               _buildInfoRow(Icons.location_on, club.address!),
     206            0 :             if (club.phone != null)
     207            0 :               _buildInfoRow(Icons.phone, club.phone!),
     208            0 :             if (club.email != null)
     209            0 :               _buildInfoRow(Icons.email, club.email!),
     210            0 :             if (club.website != null)
     211            0 :               _buildInfoRow(Icons.language, club.website!),
     212              :           ],
     213              :         ),
     214              :       ),
     215              :     );
     216              :   }
     217              : 
     218            0 :   Widget _buildInfoRow(IconData icon, String text) {
     219            0 :     return Padding(
     220              :       padding: const EdgeInsets.only(bottom: 8.0),
     221            0 :       child: Row(
     222            0 :         children: [
     223            0 :           Icon(icon, size: 16, color: Colors.grey[600]),
     224              :           const SizedBox(width: 8),
     225            0 :           Expanded(
     226            0 :             child: Text(
     227              :               text,
     228            0 :               style: TextStyle(fontSize: 14, color: Colors.grey[800]),
     229              :             ),
     230              :           ),
     231              :         ],
     232              :       ),
     233              :     );
     234              :   }
     235              : 
     236            0 :   Widget _buildStatisticsCards({required int memberCount, required int eventCount}) {
     237            0 :     return Row(
     238            0 :       children: [
     239            0 :         Expanded(
     240            0 :           child: _buildStatCard(
     241              :             title: '회원',
     242            0 :             value: memberCount.toString(),
     243              :             icon: Icons.people,
     244              :             color: Colors.blue,
     245              :           ),
     246              :         ),
     247              :         const SizedBox(width: 16),
     248            0 :         Expanded(
     249            0 :           child: _buildStatCard(
     250              :             title: '이벤트',
     251            0 :             value: eventCount.toString(),
     252              :             icon: Icons.event,
     253              :             color: Colors.orange,
     254              :           ),
     255              :         ),
     256              :       ],
     257              :     );
     258              :   }
     259              : 
     260            0 :   Widget _buildStatCard({
     261              :     required String title,
     262              :     required String value,
     263              :     required IconData icon,
     264              :     required Color color,
     265              :   }) {
     266            0 :     return Card(
     267              :       elevation: 2,
     268            0 :       shape: RoundedRectangleBorder(
     269            0 :         borderRadius: BorderRadius.circular(12),
     270              :       ),
     271            0 :       child: Padding(
     272              :         padding: const EdgeInsets.all(16.0),
     273            0 :         child: Column(
     274            0 :           children: [
     275            0 :             Icon(icon, size: 32, color: color),
     276              :             const SizedBox(height: 8),
     277            0 :             Text(
     278              :               value,
     279            0 :               style: TextStyle(
     280              :                 fontSize: 24,
     281              :                 fontWeight: FontWeight.bold,
     282              :                 color: color,
     283              :               ),
     284              :             ),
     285            0 :             Text(
     286              :               title,
     287            0 :               style: TextStyle(
     288              :                 fontSize: 14,
     289            0 :                 color: Colors.grey[600],
     290              :               ),
     291              :             ),
     292              :           ],
     293              :         ),
     294              :       ),
     295              :     );
     296              :   }
     297              : 
     298            0 :   Widget _buildRecentEventsList(EventService eventService) {
     299            0 :     final events = eventService.events;
     300              :     
     301            0 :     if (events.isEmpty) {
     302              :       return const Card(
     303              :         child: Padding(
     304              :           padding: EdgeInsets.all(16.0),
     305              :           child: Center(
     306              :             child: Text('예정된 이벤트가 없습니다'),
     307              :           ),
     308              :         ),
     309              :       );
     310              :     }
     311              :     
     312              :     // 날짜 기준으로 정렬 (가까운 날짜순)
     313            0 :     final sortedEvents = [...events];
     314            0 :     sortedEvents.sort((a, b) => a.startDate.compareTo(b.startDate));
     315              :     
     316              :     // 앞으로 진행될 이벤트만 필터링 (최대 3개)
     317              :     final upcomingEvents = sortedEvents
     318            0 :         .where((event) => event.startDate.isAfter(DateTime.now()))
     319            0 :         .take(3)
     320            0 :         .toList();
     321              :     
     322            0 :     return Column(
     323              :       crossAxisAlignment: CrossAxisAlignment.start,
     324            0 :       children: [
     325            0 :         Row(
     326              :           mainAxisAlignment: MainAxisAlignment.spaceBetween,
     327            0 :           children: [
     328              :             const Text(
     329              :               '다가오는 이벤트',
     330              :               style: TextStyle(
     331              :                 fontSize: 18,
     332              :                 fontWeight: FontWeight.bold,
     333              :               ),
     334              :             ),
     335            0 :             TextButton(
     336            0 :               onPressed: () {
     337              :                 // 이벤트 목록 화면으로 이동 (추후 구현)
     338              :               },
     339              :               child: const Text('모두 보기'),
     340              :             ),
     341              :           ],
     342              :         ),
     343              :         const SizedBox(height: 8),
     344            0 :         ...upcomingEvents.map((event) => _buildEventCard(event)),
     345              :       ],
     346              :     );
     347              :   }
     348              : 
     349            0 :   Widget _buildEventCard(event) {
     350            0 :     final dateFormat = DateFormat('yyyy년 MM월 dd일 HH:mm');
     351              :     
     352            0 :     return Card(
     353              :       margin: const EdgeInsets.only(bottom: 12),
     354            0 :       shape: RoundedRectangleBorder(
     355            0 :         borderRadius: BorderRadius.circular(12),
     356              :       ),
     357            0 :       child: ListTile(
     358              :         contentPadding: const EdgeInsets.symmetric(
     359              :           horizontal: 16,
     360              :           vertical: 8,
     361              :         ),
     362            0 :         title: Text(
     363            0 :           event.title,
     364              :           style: const TextStyle(
     365              :             fontWeight: FontWeight.bold,
     366              :           ),
     367              :         ),
     368            0 :         subtitle: Column(
     369              :           crossAxisAlignment: CrossAxisAlignment.start,
     370            0 :           children: [
     371              :             const SizedBox(height: 4),
     372            0 :             Text(
     373            0 :               dateFormat.format(event.startDate),
     374            0 :               style: TextStyle(
     375            0 :                 color: Colors.grey[700],
     376              :               ),
     377              :             ),
     378            0 :             if (event.location != null)
     379            0 :               Text(
     380            0 :                 event.location!,
     381            0 :                 style: TextStyle(
     382            0 :                   color: Colors.grey[600],
     383              :                 ),
     384              :               ),
     385              :           ],
     386              :         ),
     387              :         trailing: const Icon(Icons.arrow_forward_ios, size: 16),
     388            0 :         onTap: () {
     389              :           // 이벤트 상세 화면으로 이동 (추후 구현)
     390              :         },
     391              :       ),
     392              :     );
     393              :   }
     394              : }
        

Generated by: LCOV version 2.3.1-1