feat: create base for recurring entries
This commit is contained in:
parent
b16b61b242
commit
ab3bc9869c
12 changed files with 206 additions and 12 deletions
32
lib/api/recurring_entry.dart
Normal file
32
lib/api/recurring_entry.dart
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:prasule/api/walletentry.dart';
|
||||||
|
import 'package:prasule/api/category.dart';
|
||||||
|
import 'package:prasule/api/entry_data.dart';
|
||||||
|
part 'recurring_entry.g.dart';
|
||||||
|
|
||||||
|
enum RecurringType { byDay, byMonth, byYear }
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
/// Used to store data about recurring entries
|
||||||
|
class RecurringEntry extends WalletSingleEntry {
|
||||||
|
/// Sets recurring by day, month or year
|
||||||
|
RecurringType recurringType;
|
||||||
|
|
||||||
|
/// Indicates by how many days/months/years should we recur
|
||||||
|
int recurEvery;
|
||||||
|
RecurringEntry(
|
||||||
|
{required super.data,
|
||||||
|
required super.type,
|
||||||
|
required super.date,
|
||||||
|
required super.category,
|
||||||
|
required super.id,
|
||||||
|
required this.recurringType,
|
||||||
|
required this.recurEvery});
|
||||||
|
|
||||||
|
factory RecurringEntry.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$RecurringEntryFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() => _$RecurringEntryToJson(this);
|
||||||
|
}
|
41
lib/api/recurring_entry.g.dart
Normal file
41
lib/api/recurring_entry.g.dart
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'recurring_entry.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
RecurringEntry _$RecurringEntryFromJson(Map<String, dynamic> json) =>
|
||||||
|
RecurringEntry(
|
||||||
|
data: EntryData.fromJson(json['data'] as Map<String, dynamic>),
|
||||||
|
type: $enumDecode(_$EntryTypeEnumMap, json['type']),
|
||||||
|
date: DateTime.parse(json['date'] as String),
|
||||||
|
category:
|
||||||
|
WalletCategory.fromJson(json['category'] as Map<String, dynamic>),
|
||||||
|
id: json['id'] as int,
|
||||||
|
recurringType: $enumDecode(_$RecurringTypeEnumMap, json['recurringType']),
|
||||||
|
recurEvery: json['recurEvery'] as int,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$RecurringEntryToJson(RecurringEntry instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'type': _$EntryTypeEnumMap[instance.type]!,
|
||||||
|
'data': instance.data,
|
||||||
|
'date': instance.date.toIso8601String(),
|
||||||
|
'category': instance.category,
|
||||||
|
'id': instance.id,
|
||||||
|
'recurringType': _$RecurringTypeEnumMap[instance.recurringType]!,
|
||||||
|
'recurEvery': instance.recurEvery,
|
||||||
|
};
|
||||||
|
|
||||||
|
const _$EntryTypeEnumMap = {
|
||||||
|
EntryType.expense: 'expense',
|
||||||
|
EntryType.income: 'income',
|
||||||
|
};
|
||||||
|
|
||||||
|
const _$RecurringTypeEnumMap = {
|
||||||
|
RecurringType.byDay: 'byDay',
|
||||||
|
RecurringType.byMonth: 'byMonth',
|
||||||
|
RecurringType.byYear: 'byYear',
|
||||||
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:currency_picker/currency_picker.dart';
|
import 'package:currency_picker/currency_picker.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:prasule/api/category.dart';
|
import 'package:prasule/api/category.dart';
|
||||||
|
import 'package:prasule/api/recurring_entry.dart';
|
||||||
import 'package:prasule/api/walletentry.dart';
|
import 'package:prasule/api/walletentry.dart';
|
||||||
part 'wallet.g.dart';
|
part 'wallet.g.dart';
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ class Wallet {
|
||||||
final String name;
|
final String name;
|
||||||
final List<WalletCategory> categories;
|
final List<WalletCategory> categories;
|
||||||
final List<WalletSingleEntry> entries;
|
final List<WalletSingleEntry> entries;
|
||||||
|
final List<RecurringEntry> recurringEntries;
|
||||||
double starterBalance;
|
double starterBalance;
|
||||||
@JsonKey(fromJson: _currencyFromJson)
|
@JsonKey(fromJson: _currencyFromJson)
|
||||||
final Currency currency;
|
final Currency currency;
|
||||||
|
@ -21,7 +23,8 @@ class Wallet {
|
||||||
required this.currency,
|
required this.currency,
|
||||||
this.categories = const [],
|
this.categories = const [],
|
||||||
this.entries = const [],
|
this.entries = const [],
|
||||||
this.starterBalance = 0});
|
this.starterBalance = 0,
|
||||||
|
this.recurringEntries = const []});
|
||||||
|
|
||||||
/// Connect the generated [_$WalletEntry] function to the `fromJson`
|
/// Connect the generated [_$WalletEntry] function to the `fromJson`
|
||||||
/// factory.
|
/// factory.
|
||||||
|
|
|
@ -19,12 +19,17 @@ Wallet _$WalletFromJson(Map<String, dynamic> json) => Wallet(
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const [],
|
const [],
|
||||||
starterBalance: (json['starterBalance'] as num?)?.toDouble() ?? 0,
|
starterBalance: (json['starterBalance'] as num?)?.toDouble() ?? 0,
|
||||||
|
recurringEntries: (json['recurringEntries'] as List<dynamic>?)
|
||||||
|
?.map((e) => RecurringEntry.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList() ??
|
||||||
|
const [],
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$WalletToJson(Wallet instance) => <String, dynamic>{
|
Map<String, dynamic> _$WalletToJson(Wallet instance) => <String, dynamic>{
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'categories': instance.categories,
|
'categories': instance.categories,
|
||||||
'entries': instance.entries,
|
'entries': instance.entries,
|
||||||
|
'recurringEntries': instance.recurringEntries,
|
||||||
'starterBalance': instance.starterBalance,
|
'starterBalance': instance.starterBalance,
|
||||||
'currency': instance.currency,
|
'currency': instance.currency,
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,6 +61,8 @@
|
||||||
"createTestData":"Vytvořit vzorková data",
|
"createTestData":"Vytvořit vzorková data",
|
||||||
"spendingStats":"Statistiky utrácení",
|
"spendingStats":"Statistiky utrácení",
|
||||||
"yearly":"Roční",
|
"yearly":"Roční",
|
||||||
"monthly":"Měsíční"
|
"monthly":"Měsíční",
|
||||||
|
"sourceCode":"Zdrojový kód",
|
||||||
|
"recurringEntries":"Opakující se záznamy"
|
||||||
|
|
||||||
}
|
}
|
|
@ -97,5 +97,7 @@
|
||||||
"createTestData":"Create test data",
|
"createTestData":"Create test data",
|
||||||
"spendingStats":"Spending statistics",
|
"spendingStats":"Spending statistics",
|
||||||
"yearly":"Yearly",
|
"yearly":"Yearly",
|
||||||
"monthly":"Monthly"
|
"monthly":"Monthly",
|
||||||
|
"sourceCode":"Source code",
|
||||||
|
"recurringEntries":"Recurring entries"
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:prasule/pw/platformroute.dart';
|
import 'package:prasule/pw/platformroute.dart';
|
||||||
import 'package:prasule/views/graph_view.dart';
|
import 'package:prasule/views/graph_view.dart';
|
||||||
import 'package:prasule/views/home.dart';
|
import 'package:prasule/views/home.dart';
|
||||||
|
import 'package:prasule/views/recurring_view.dart';
|
||||||
|
|
||||||
/// Makes the drawer because I won't enter the same code in every view
|
/// Makes the drawer because I won't enter the same code in every view
|
||||||
Drawer makeDrawer(BuildContext context, int page) => Drawer(
|
Drawer makeDrawer(BuildContext context, int page) => Drawer(
|
||||||
|
@ -39,6 +40,21 @@ Drawer makeDrawer(BuildContext context, int page) => Drawer(
|
||||||
.pushReplacement(platformRoute((p0) => const GraphView()));
|
.pushReplacement(platformRoute((p0) => const GraphView()));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.loop),
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context)!.recurringEntries,
|
||||||
|
),
|
||||||
|
selected: page == 3,
|
||||||
|
onTap: () {
|
||||||
|
if (page == 3) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Navigator.of(context).pushReplacement(
|
||||||
|
platformRoute((p0) => const RecurringEntriesView()));
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:prasule/api/wallet.dart';
|
import 'package:prasule/api/wallet.dart';
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:currency_picker/currency_picker.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
@ -26,6 +25,7 @@ import 'package:prasule/views/settings/settings.dart';
|
||||||
import 'package:prasule/views/settings/tessdata_list.dart';
|
import 'package:prasule/views/settings/tessdata_list.dart';
|
||||||
import 'package:prasule/views/setup.dart';
|
import 'package:prasule/views/setup.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
class HomeView extends StatefulWidget {
|
class HomeView extends StatefulWidget {
|
||||||
const HomeView({super.key});
|
const HomeView({super.key});
|
||||||
|
@ -197,6 +197,14 @@ class _HomeViewState extends State<HomeView> {
|
||||||
);
|
);
|
||||||
} else if (value == AppLocalizations.of(context)!.about) {
|
} else if (value == AppLocalizations.of(context)!.about) {
|
||||||
showAboutDialog(
|
showAboutDialog(
|
||||||
|
children: [
|
||||||
|
PlatformButton(
|
||||||
|
text: AppLocalizations.of(context)!.sourceCode,
|
||||||
|
onPressed: () => launchUrlString(
|
||||||
|
"https://git.mnau.xyz/hernik/prasule",
|
||||||
|
mode: LaunchMode.externalApplication),
|
||||||
|
),
|
||||||
|
],
|
||||||
context: context,
|
context: context,
|
||||||
applicationLegalese: AppLocalizations.of(context)!.license,
|
applicationLegalese: AppLocalizations.of(context)!.license,
|
||||||
applicationName: "Prašule");
|
applicationName: "Prašule");
|
||||||
|
|
27
lib/views/recurring_view.dart
Normal file
27
lib/views/recurring_view.dart
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:prasule/api/wallet.dart';
|
||||||
|
import 'package:prasule/util/drawer.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
class RecurringEntriesView extends StatefulWidget {
|
||||||
|
const RecurringEntriesView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RecurringEntriesView> createState() => _RecurringEntriesViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RecurringEntriesViewState extends State<RecurringEntriesView> {
|
||||||
|
Wallet? selectedWallet;
|
||||||
|
List<Wallet> wallets = [];
|
||||||
|
String? locale;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(AppLocalizations.of(context)!.recurringEntries),
|
||||||
|
),
|
||||||
|
drawer: makeDrawer(context, 3),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
66
pubspec.lock
66
pubspec.lock
|
@ -1041,6 +1041,70 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.2"
|
||||||
|
url_launcher:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: url_launcher
|
||||||
|
sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.1"
|
||||||
|
url_launcher_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_android
|
||||||
|
sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.0"
|
||||||
|
url_launcher_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_ios
|
||||||
|
sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.2.1"
|
||||||
|
url_launcher_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_linux
|
||||||
|
sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
url_launcher_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_macos
|
||||||
|
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
url_launcher_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_platform_interface
|
||||||
|
sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
url_launcher_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_web
|
||||||
|
sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
url_launcher_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_windows
|
||||||
|
sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1131,4 +1195,4 @@ packages:
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.2.0 <4.0.0"
|
dart: ">=3.2.0 <4.0.0"
|
||||||
flutter: ">=3.7.0"
|
flutter: ">=3.16.0"
|
||||||
|
|
|
@ -16,7 +16,6 @@ dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
|
@ -39,6 +38,7 @@ dependencies:
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
fl_chart: ^0.65.0
|
fl_chart: ^0.65.0
|
||||||
|
url_launcher: ^6.2.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -76,7 +76,6 @@ flutter_launcher_icons:
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
# The following section is specific to Flutter packages.
|
||||||
flutter:
|
flutter:
|
||||||
generate: true
|
generate: true
|
||||||
|
@ -87,18 +86,14 @@ flutter:
|
||||||
assets:
|
assets:
|
||||||
- assets/
|
- assets/
|
||||||
- assets/tessdata/
|
- assets/tessdata/
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
# assets:
|
||||||
# - images/a_dot_burr.jpeg
|
# - images/a_dot_burr.jpeg
|
||||||
# - images/a_dot_ham.jpeg
|
# - images/a_dot_ham.jpeg
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
|
||||||
# For details regarding adding assets from package dependencies, see
|
# For details regarding adding assets from package dependencies, see
|
||||||
# https://flutter.dev/assets-and-images/#from-packages
|
# https://flutter.dev/assets-and-images/#from-packages
|
||||||
|
|
||||||
# To add custom fonts to your application, add a fonts section here,
|
# To add custom fonts to your application, add a fonts section here,
|
||||||
# in this "flutter" section. Each entry in this list should have a
|
# in this "flutter" section. Each entry in this list should have a
|
||||||
# "family" key with the font family name, and a "fonts" key with a
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
|
|
Loading…
Reference in a new issue