feat: add payment card grouping and analysis
This commit is contained in:
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