feat: add payment card grouping and analysis
This commit is contained in:
41
lib/utils/payment_card_utils.dart
Normal file
41
lib/utils/payment_card_utils.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 결제수단 관련 공통 유틸리티
|
||||
class PaymentCardUtils {
|
||||
static const List<String> colorPalette = [
|
||||
'#FF6B6B',
|
||||
'#F97316',
|
||||
'#F59E0B',
|
||||
'#10B981',
|
||||
'#06B6D4',
|
||||
'#3B82F6',
|
||||
'#6366F1',
|
||||
'#8B5CF6',
|
||||
'#EC4899',
|
||||
'#14B8A6',
|
||||
'#0EA5E9',
|
||||
'#94A3B8',
|
||||
];
|
||||
|
||||
static const Map<String, IconData> iconMap = {
|
||||
'credit_card': Icons.credit_card_rounded,
|
||||
'payments': Icons.payments_rounded,
|
||||
'wallet': Icons.account_balance_wallet_rounded,
|
||||
'bank': Icons.account_balance_rounded,
|
||||
'shopping': Icons.shopping_bag_rounded,
|
||||
'subscriptions': Icons.subscriptions_rounded,
|
||||
'bolt': Icons.bolt_rounded,
|
||||
};
|
||||
|
||||
static IconData iconForName(String name) {
|
||||
return iconMap[name] ?? Icons.credit_card_rounded;
|
||||
}
|
||||
|
||||
static Color colorFromHex(String hex) {
|
||||
var value = hex.replaceAll('#', '');
|
||||
if (value.length == 6) {
|
||||
value = 'ff$value';
|
||||
}
|
||||
return Color(int.parse(value, radix: 16));
|
||||
}
|
||||
}
|
||||
155
lib/utils/subscription_grouping_helper.dart
Normal file
155
lib/utils/subscription_grouping_helper.dart
Normal file
@@ -0,0 +1,155 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../l10n/app_localizations.dart';
|
||||
import '../models/payment_card_model.dart';
|
||||
import '../models/subscription_model.dart';
|
||||
import '../providers/category_provider.dart';
|
||||
import '../providers/payment_card_provider.dart';
|
||||
import 'subscription_category_helper.dart';
|
||||
|
||||
enum SubscriptionGroupingMode { category, paymentCard }
|
||||
|
||||
class SubscriptionGroupData {
|
||||
final String id;
|
||||
final String title;
|
||||
final List<SubscriptionModel> subscriptions;
|
||||
final SubscriptionGroupingMode mode;
|
||||
final PaymentCardModel? paymentCard;
|
||||
final bool isUnassignedCard;
|
||||
final String? categoryKey;
|
||||
final String? subtitle;
|
||||
|
||||
const SubscriptionGroupData({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.subscriptions,
|
||||
required this.mode,
|
||||
this.paymentCard,
|
||||
this.isUnassignedCard = false,
|
||||
this.categoryKey,
|
||||
this.subtitle,
|
||||
});
|
||||
}
|
||||
|
||||
class SubscriptionGroupingHelper {
|
||||
static const _unassignedCardKey = '__unassigned__';
|
||||
|
||||
static List<SubscriptionGroupData> buildGroups({
|
||||
required BuildContext context,
|
||||
required List<SubscriptionModel> subscriptions,
|
||||
required SubscriptionGroupingMode mode,
|
||||
required CategoryProvider categoryProvider,
|
||||
required PaymentCardProvider paymentCardProvider,
|
||||
}) {
|
||||
if (mode == SubscriptionGroupingMode.paymentCard) {
|
||||
return _groupByPaymentCard(
|
||||
context: context,
|
||||
subscriptions: subscriptions,
|
||||
paymentCardProvider: paymentCardProvider,
|
||||
);
|
||||
}
|
||||
|
||||
return _groupByCategory(
|
||||
context: context,
|
||||
subscriptions: subscriptions,
|
||||
categoryProvider: categoryProvider,
|
||||
);
|
||||
}
|
||||
|
||||
static List<SubscriptionGroupData> _groupByCategory({
|
||||
required BuildContext context,
|
||||
required List<SubscriptionModel> subscriptions,
|
||||
required CategoryProvider categoryProvider,
|
||||
}) {
|
||||
final localizedMap = SubscriptionCategoryHelper.categorizeSubscriptions(
|
||||
subscriptions, categoryProvider, context);
|
||||
|
||||
final orderMap = <String, int>{};
|
||||
for (var i = 0; i < categoryProvider.categories.length; i++) {
|
||||
orderMap[categoryProvider.categories[i].name] = i;
|
||||
}
|
||||
|
||||
final groups = localizedMap.entries.map((entry) {
|
||||
final title =
|
||||
categoryProvider.getLocalizedCategoryName(context, entry.key);
|
||||
return SubscriptionGroupData(
|
||||
id: entry.key,
|
||||
title: title,
|
||||
subscriptions: entry.value,
|
||||
mode: SubscriptionGroupingMode.category,
|
||||
categoryKey: entry.key,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
groups.sort((a, b) {
|
||||
final ai = orderMap[a.categoryKey] ?? 999;
|
||||
final bi = orderMap[b.categoryKey] ?? 999;
|
||||
if (ai != bi) {
|
||||
return ai.compareTo(bi);
|
||||
}
|
||||
return a.title.compareTo(b.title);
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
static List<SubscriptionGroupData> _groupByPaymentCard({
|
||||
required BuildContext context,
|
||||
required List<SubscriptionModel> subscriptions,
|
||||
required PaymentCardProvider paymentCardProvider,
|
||||
}) {
|
||||
final map = <String, List<SubscriptionModel>>{};
|
||||
|
||||
for (final sub in subscriptions) {
|
||||
final key = sub.paymentCardId ?? _unassignedCardKey;
|
||||
map.putIfAbsent(key, () => []).add(sub);
|
||||
}
|
||||
|
||||
final loc = AppLocalizations.of(context);
|
||||
final groups = <SubscriptionGroupData>[];
|
||||
|
||||
map.forEach((key, subs) {
|
||||
if (key == _unassignedCardKey) {
|
||||
groups.add(
|
||||
SubscriptionGroupData(
|
||||
id: key,
|
||||
title: loc.paymentCardUnassigned,
|
||||
subtitle: loc.paymentCardUnassigned,
|
||||
subscriptions: subs,
|
||||
mode: SubscriptionGroupingMode.paymentCard,
|
||||
isUnassignedCard: true,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
final card = paymentCardProvider.getCardById(key);
|
||||
final title = card?.issuerName ?? loc.paymentCardUnassigned;
|
||||
final subtitle =
|
||||
card != null ? '****${card.last4}' : loc.paymentCardUnassigned;
|
||||
groups.add(
|
||||
SubscriptionGroupData(
|
||||
id: key,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
subscriptions: subs,
|
||||
mode: SubscriptionGroupingMode.paymentCard,
|
||||
paymentCard: card,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
groups.sort((a, b) {
|
||||
if (a.isUnassignedCard != b.isUnassignedCard) {
|
||||
return a.isUnassignedCard ? 1 : -1;
|
||||
}
|
||||
final aDefault = a.paymentCard?.isDefault ?? false;
|
||||
final bDefault = b.paymentCard?.isDefault ?? false;
|
||||
if (aDefault != bDefault) {
|
||||
return aDefault ? -1 : 1;
|
||||
}
|
||||
return a.title.toLowerCase().compareTo(b.title.toLowerCase());
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user