fix(calendar): 월별 방문 카테고리 집계 반영

This commit is contained in:
JiWoong Sul
2025-12-03 18:31:37 +09:00
parent 3f659432e9
commit 637507f02a
2 changed files with 62 additions and 6 deletions

View File

@@ -22,6 +22,12 @@ class VisitStatistics extends ConsumerWidget {
month: selectedMonth.month, month: selectedMonth.month,
)), )),
); );
final monthlyCategoryStatsAsync = ref.watch(
monthlyCategoryVisitStatsProvider((
year: selectedMonth.year,
month: selectedMonth.month,
)),
);
// 자주 방문한 맛집 // 자주 방문한 맛집
final frequentRestaurantsAsync = ref.watch(frequentRestaurantsProvider); final frequentRestaurantsAsync = ref.watch(frequentRestaurantsProvider);
@@ -34,7 +40,11 @@ class VisitStatistics extends ConsumerWidget {
child: Column( child: Column(
children: [ children: [
// 이번 달 통계 // 이번 달 통계
_buildMonthlyStats(monthlyStatsAsync, isDark), _buildMonthlyStats(
monthlyStatsAsync,
monthlyCategoryStatsAsync,
isDark,
),
const SizedBox(height: 16), const SizedBox(height: 16),
const NativeAdPlaceholder(height: 360), const NativeAdPlaceholder(height: 360),
@@ -53,6 +63,7 @@ class VisitStatistics extends ConsumerWidget {
Widget _buildMonthlyStats( Widget _buildMonthlyStats(
AsyncValue<Map<String, int>> statsAsync, AsyncValue<Map<String, int>> statsAsync,
AsyncValue<Map<String, int>> categoryStatsAsync,
bool isDark, bool isDark,
) { ) {
return Card( return Card(
@@ -75,9 +86,17 @@ class VisitStatistics extends ConsumerWidget {
0, 0,
(sum, count) => sum + count, (sum, count) => sum + count,
); );
final categoryCounts = final categoryCounts = categoryStatsAsync.maybeWhen(
stats.entries.where((e) => !e.key.contains('/')).toList() data: (data) {
final entries = data.entries.toList()
..sort((a, b) => b.value.compareTo(a.value)); ..sort((a, b) => b.value.compareTo(a.value));
return entries;
},
orElse: () => <MapEntry<String, int>>[],
);
final topCategory = categoryCounts.isNotEmpty
? categoryCounts.first
: null;
return Column( return Column(
children: [ children: [
@@ -89,12 +108,21 @@ class VisitStatistics extends ConsumerWidget {
isDark: isDark, isDark: isDark,
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
if (categoryCounts.isNotEmpty) ...[ if (topCategory != null) ...[
_buildStatItem( _buildStatItem(
icon: Icons.favorite, icon: Icons.favorite,
label: '가장 많이 간 카테고리', label: '가장 많이 간 카테고리',
value: value: '${topCategory.key} (${topCategory.value}회)',
'${categoryCounts.first.key} (${categoryCounts.first.value}회)', color: AppColors.lightSecondary,
isDark: isDark,
),
] else ...[
_buildStatItem(
icon: Icons.favorite_border,
label: '가장 많이 간 카테고리',
value: categoryStatsAsync.isLoading
? '집계 중...'
: '데이터 없음',
color: AppColors.lightSecondary, color: AppColors.lightSecondary,
isDark: isDark, isDark: isDark,
), ),

View File

@@ -35,6 +35,34 @@ final monthlyVisitStatsProvider =
return repository.getMonthlyVisitStats(params.year, params.month); return repository.getMonthlyVisitStats(params.year, params.month);
}); });
/// 월별 카테고리별 방문 통계 Provider
final monthlyCategoryVisitStatsProvider =
FutureProvider.family<Map<String, int>, ({int year, int month})>((
ref,
params,
) async {
final repository = ref.watch(visitRepositoryProvider);
final restaurants = await ref.watch(restaurantListProvider.future);
final records = await repository.getVisitRecordsByDateRange(
startDate: DateTime(params.year, params.month, 1),
endDate: DateTime(params.year, params.month + 1, 0),
);
final categoryCount = <String, int>{};
for (final record in records) {
final restaurant = restaurants
.where((r) => r.id == record.restaurantId)
.firstOrNull;
if (restaurant == null) continue;
categoryCount[restaurant.category] =
(categoryCount[restaurant.category] ?? 0) + 1;
}
return categoryCount;
});
/// 방문 기록 관리 StateNotifier /// 방문 기록 관리 StateNotifier
class VisitNotifier extends StateNotifier<AsyncValue<void>> { class VisitNotifier extends StateNotifier<AsyncValue<void>> {
final VisitRepository _repository; final VisitRepository _repository;