new favourites feature

This commit is contained in:
davmar
2026-09-10 16:25:24 +02:00
parent aec89ac41d
commit 83295439d5
22 changed files with 3168 additions and 282 deletions
+109
View File
@@ -0,0 +1,109 @@
/// Named Overview chart filter snapshots (local favourites).
library;
import '../models/transaction_meta.dart';
import '../widgets/label_filter_dialog.dart';
/// Persisted Overview lens: period, metric, and Labels-chart options.
class OverviewFavourite {
const OverviewFavourite({
required this.id,
required this.name,
this.display = true,
this.metric = OverviewMetricMode.flow,
this.period = OverviewPeriodMode.monthly,
this.labelIds = const [],
this.flow = TxFlowFilter.expense,
this.barStyle = LabelChartBarStyle.parallel,
this.invertExpensePerspective = false,
});
final String id;
final String name;
/// When true, this favourite appears on the Favourites overview view.
final bool display;
final OverviewMetricMode metric;
final OverviewPeriodMode period;
/// Empty = all labels (Labels metric only).
final List<String> labelIds;
final TxFlowFilter flow;
final LabelChartBarStyle barStyle;
final bool invertExpensePerspective;
OverviewFavourite copyWith({
String? id,
String? name,
bool? display,
OverviewMetricMode? metric,
OverviewPeriodMode? period,
List<String>? labelIds,
TxFlowFilter? flow,
LabelChartBarStyle? barStyle,
bool? invertExpensePerspective,
}) {
return OverviewFavourite(
id: id ?? this.id,
name: name ?? this.name,
display: display ?? this.display,
metric: metric ?? this.metric,
period: period ?? this.period,
labelIds: labelIds ?? this.labelIds,
flow: flow ?? this.flow,
barStyle: barStyle ?? this.barStyle,
invertExpensePerspective:
invertExpensePerspective ?? this.invertExpensePerspective,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'display': display,
'metric': metric.name,
'period': period.name,
'labelIds': labelIds,
'flow': flow.name,
'barStyle': barStyle.name,
'invertExpensePerspective': invertExpensePerspective,
};
factory OverviewFavourite.fromJson(Map<String, dynamic> json) {
return OverviewFavourite(
id: json['id'] as String? ?? 'fav_${DateTime.now().millisecondsSinceEpoch}',
name: (json['name'] as String?)?.trim().isNotEmpty == true
? (json['name'] as String).trim()
: 'Favourite',
display: json['display'] as bool? ?? true,
metric: OverviewMetricMode.values.firstWhere(
(e) => e.name == json['metric'],
orElse: () => OverviewMetricMode.flow,
),
period: OverviewPeriodMode.values.firstWhere(
(e) => e.name == json['period'],
orElse: () => OverviewPeriodMode.monthly,
),
labelIds: [
for (final id in (json['labelIds'] as List? ?? const []))
if (id is String && id.isNotEmpty) id,
],
flow: TxFlowFilter.values.firstWhere(
(e) => e.name == json['flow'],
orElse: () => TxFlowFilter.expense,
),
barStyle: LabelChartBarStyle.values.firstWhere(
(e) => e.name == json['barStyle'],
orElse: () => LabelChartBarStyle.parallel,
),
invertExpensePerspective:
json['invertExpensePerspective'] as bool? ?? false,
);
}
}
enum OverviewMetricMode { flow, labels }
enum OverviewPeriodMode { monthly, yearly }