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