import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lunchpick/core/constants/app_colors.dart'; import 'package:lunchpick/core/constants/app_typography.dart'; import 'package:lunchpick/presentation/providers/visit_provider.dart'; import 'package:lunchpick/presentation/providers/restaurant_provider.dart'; class VisitStatistics extends ConsumerWidget { final DateTime selectedMonth; const VisitStatistics({ super.key, required this.selectedMonth, }); @override Widget build(BuildContext context, WidgetRef ref) { final isDark = Theme.of(context).brightness == Brightness.dark; // 월별 통계 final monthlyStatsAsync = ref.watch(monthlyVisitStatsProvider(( year: selectedMonth.year, month: selectedMonth.month, ))); // 자주 방문한 맛집 final frequentRestaurantsAsync = ref.watch(frequentRestaurantsProvider); // 주간 통계 final weeklyStatsAsync = ref.watch(weeklyVisitStatsProvider); return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: [ // 이번 달 통계 _buildMonthlyStats(monthlyStatsAsync, isDark), const SizedBox(height: 16), // 주간 통계 차트 _buildWeeklyChart(weeklyStatsAsync, isDark), const SizedBox(height: 16), // 자주 방문한 맛집 TOP 3 _buildFrequentRestaurants(frequentRestaurantsAsync, ref, isDark), ], ), ); } Widget _buildMonthlyStats(AsyncValue> statsAsync, bool isDark) { return Card( color: isDark ? AppColors.darkSurface : AppColors.lightSurface, elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${selectedMonth.month}월 방문 통계', style: AppTypography.heading2(isDark), ), const SizedBox(height: 16), statsAsync.when( data: (stats) { final totalVisits = stats.values.fold(0, (sum, count) => sum + count); final categoryCounts = stats.entries .where((e) => !e.key.contains('/')) .toList() ..sort((a, b) => b.value.compareTo(a.value)); return Column( children: [ _buildStatItem( icon: Icons.restaurant, label: '총 방문 횟수', value: '$totalVisits회', color: AppColors.lightPrimary, isDark: isDark, ), const SizedBox(height: 12), if (categoryCounts.isNotEmpty) ...[ _buildStatItem( icon: Icons.favorite, label: '가장 많이 간 카테고리', value: '${categoryCounts.first.key} (${categoryCounts.first.value}회)', color: AppColors.lightSecondary, isDark: isDark, ), ], ], ); }, loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) => Text( '통계를 불러올 수 없습니다', style: AppTypography.body2(isDark), ), ), ], ), ), ); } Widget _buildWeeklyChart(AsyncValue> statsAsync, bool isDark) { return Card( color: isDark ? AppColors.darkSurface : AppColors.lightSurface, elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '최근 7일 방문 현황', style: AppTypography.heading2(isDark), ), const SizedBox(height: 16), statsAsync.when( data: (stats) { final maxCount = stats.values.isEmpty ? 1 : stats.values.reduce((a, b) => a > b ? a : b); return SizedBox( height: 120, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.end, children: stats.entries.map((entry) { final height = maxCount == 0 ? 0.0 : (entry.value / maxCount) * 80; return Column( mainAxisAlignment: MainAxisAlignment.end, children: [ Text( entry.value.toString(), style: AppTypography.caption(isDark), ), const SizedBox(height: 4), Container( width: 30, height: height, decoration: BoxDecoration( color: AppColors.lightPrimary, borderRadius: BorderRadius.circular(4), ), ), const SizedBox(height: 4), Text( entry.key, style: AppTypography.caption(isDark), ), ], ); }).toList(), ), ); }, loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) => Text( '차트를 불러올 수 없습니다', style: AppTypography.body2(isDark), ), ), ], ), ), ); } Widget _buildFrequentRestaurants( AsyncValue> frequentAsync, WidgetRef ref, bool isDark, ) { return Card( color: isDark ? AppColors.darkSurface : AppColors.lightSurface, elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '자주 방문한 맛집 TOP 3', style: AppTypography.heading2(isDark), ), const SizedBox(height: 16), frequentAsync.when( data: (frequentList) { if (frequentList.isEmpty) { return Center( child: Text( '아직 방문 기록이 없습니다', style: AppTypography.body2(isDark), ), ); } return Column( children: frequentList.take(3).map((item) { final restaurantAsync = ref.watch(restaurantProvider(item.restaurantId)); return restaurantAsync.when( data: (restaurant) { if (restaurant == null) return const SizedBox.shrink(); return Padding( padding: const EdgeInsets.only(bottom: 12), child: Row( children: [ Container( width: 32, height: 32, decoration: BoxDecoration( color: AppColors.lightPrimary.withOpacity(0.1), shape: BoxShape.circle, ), child: Center( child: Text( '${frequentList.indexOf(item) + 1}', style: AppTypography.body1(isDark).copyWith( color: AppColors.lightPrimary, fontWeight: FontWeight.bold, ), ), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( restaurant.name, style: AppTypography.body1(isDark).copyWith( fontWeight: FontWeight.w500, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), Text( restaurant.category, style: AppTypography.caption(isDark), ), ], ), ), Text( '${item.visitCount}회', style: AppTypography.body2(isDark).copyWith( color: AppColors.lightPrimary, fontWeight: FontWeight.bold, ), ), ], ), ); }, loading: () => const SizedBox(height: 44), error: (error, stack) => const SizedBox.shrink(), ); }).toList() as List, ); }, loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) => Text( '데이터를 불러올 수 없습니다', style: AppTypography.body2(isDark), ), ), ], ), ), ); } Widget _buildStatItem({ required IconData icon, required String label, required String value, required Color color, required bool isDark, }) { return Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: color.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( icon, color: color, size: 20, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: AppTypography.caption(isDark), ), Text( value, style: AppTypography.body1(isDark).copyWith( fontWeight: FontWeight.bold, ), ), ], ), ), ], ); } }