fix(calendar): 월별 방문 카테고리 집계 반영
This commit is contained in:
@@ -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,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user