diff --git a/assets/i18n/de.json b/assets/i18n/de.json index faf5c19..badc68a 100644 --- a/assets/i18n/de.json +++ b/assets/i18n/de.json @@ -28,6 +28,24 @@ "title": "Recherchieren Sie in allen Biodiversitätsmaßnahmen", "intro": "In dieser Datenbank finden Sie die Maßnahmen, die zu Ihrem Betrieb und Ihrer Region passen." } + }, + "general": { + "lists": { + "months": { + "1": "Januar", + "2": "Februar", + "3": "März", + "4": "April", + "5": "Mai", + "6": "Juni", + "7": "Juli", + "8": "August", + "9": "September", + "10": "Oktober", + "11": "November", + "12": "Dezember" + } + } } } \ No newline at end of file diff --git a/lib/src/domain/entity/month/month.dart b/lib/src/domain/entity/month/month.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/extensions/datetime_extensions.dart b/lib/src/extensions/datetime_extensions.dart new file mode 100644 index 0000000..ae3a324 --- /dev/null +++ b/lib/src/extensions/datetime_extensions.dart @@ -0,0 +1,69 @@ +extension DateHelpers on DateTime { + String nowString() { + return DateTime.now().toUtc().toIso8601String(); + } + + bool isToday() { + final now = DateTime.now(); + return _isSameDay(this, now); + } + + bool isYesterday() { + final yesterday = DateTime.now().subtract(const Duration(days: 1)); + return _isSameDay(this, yesterday); + } + + bool isTomorrow() { + final tomorrow = DateTime.now().add(const Duration(days: 1)); + return _isSameDay(this, tomorrow); + } + + bool isMorning() => hour > 3 && hour <= 12; + bool isAfternoon() => hour > 12 && hour <= 18; + bool isEvening() => (hour >= 0 && hour <= 3) || (hour > 18 && hour <= 23); + + String getTimeOfDay() { + if (isMorning()) return 'morning'; + if (isAfternoon()) return 'afternoon'; + return 'evening'; + } + + DateTime findFirstDateOfTheWeek() { + return subtract(Duration(days: weekday - 1)); + } + + DateTime findLastDateOfTheWeek() { + return add(Duration(days: DateTime.daysPerWeek - weekday)); + } + + String formatSeconds(int value) { + final hours = (value ~/ 3600).toString().padLeft(2, '0'); + final minutes = ((value % 3600) ~/ 60).toString().padLeft(2, '0'); + final seconds = (value % 60).toString().padLeft(2, '0'); + return "$hours:$minutes:$seconds"; + } + + String formatMinutes(int value, {bool allowZero = false}) { + final hours = (value ~/ 3600).toString().padLeft(2, '0'); + var minutes = ((value % 3600) ~/ 60).toString().padLeft(2, '0'); + if (minutes == '00' && !allowZero) { + minutes = '01'; + } + return "$hours:$minutes"; + } + + // Helper method to check if two dates are on the same day + bool _isSameDay(DateTime date1, DateTime date2) { + return date1.day == date2.day && + date1.month == date2.month && + date1.year == date2.year; + } + + DateTime firstDayOfYear() { + return DateTime(year); + } + + DateTime lastDayOfYear() { + return DateTime(year + 1, 1, 0); + } +} diff --git a/lib/src/pages/actions/actions_page.dart b/lib/src/pages/actions/actions_page.dart index d75afd6..0d9944b 100644 --- a/lib/src/pages/actions/actions_page.dart +++ b/lib/src/pages/actions/actions_page.dart @@ -1,6 +1,10 @@ +import 'package:ambito/src/domain/entity/id_value_color/id_value_color.dart'; import 'package:ambito/src/domain/entity/massnahme/massnahme.dart'; import 'package:ambito/src/packages/ambito_db/base_db.dart'; -import 'package:ambito/src/widgets/form/dropdown_menu.dart'; +import 'package:ambito/src/widgets/form/fields/field_daterangepicker.dart'; +import 'package:ambito/src/widgets/form/fields/field_dropdown.dart'; +import 'package:ambito/src/widgets/form/form_widget.dart'; +import 'package:ambito/src/widgets/form/form_widget_type.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:expandable_text/expandable_text.dart'; import 'package:flutter/material.dart'; @@ -32,6 +36,8 @@ class ActionsPageState extends State { List region = []; String? filterSupport; Set support = {}; + String? filterMonths; + Set months = {}; @override void initState() { @@ -50,27 +56,34 @@ class ActionsPageState extends State { Set updatedTypes = {}; Set updatedAreaTypes = {}; Set updatedSupports = {}; + Set updatedMonths = {}; for (var massnahme in massnahmen) { if (massnahme.actionGroup != null) { updatedTypes.add(massnahme.actionGroup!.value!); } if (massnahme.factsheetAreaType != null) { - for (var aType in massnahme.factsheetAreaType!) { + for (IdValueColor aType in massnahme.factsheetAreaType!) { updatedAreaTypes.add(aType.value!); } } if (massnahme.fundingPrograms != null) { - for (var aType in massnahme.fundingPrograms!) { + for (IdValueColor aType in massnahme.fundingPrograms!) { updatedSupports.add(aType.value!); } } + if (massnahme.timeFrame != null) { + for (IdValueColor tfType in massnahme.timeFrame!) { + updatedMonths.add(tfType.value!); + } + } } setState(() { type.addAll(updatedTypes); areaType.addAll(updatedAreaTypes); support.addAll(updatedSupports); + months.addAll(updatedMonths.toList()); }); List actionCards = massnahmen.map((massnahme) { @@ -133,7 +146,100 @@ class ActionsPageState extends State { } Widget getFilter(BuildContext context) { - return Row( + List fields = []; + + fields.add( + FieldDropdown( + name: 'ort', + label: 'Ort der Maßnahme', + filterValue: filterAreaType, + onClear: () { + setState(() { + filterAreaType = null; + }); + }, + onSelected: ((String? value) { + setState(() { + filterAreaType = value; + }); + }), + entries: areaType.toList(), + ), + ); + + fields.add( + FieldDropdown( + name: 'art', + label: 'Art der Maßnahme', + filterValue: filterType, + onClear: () { + setState(() { + filterType = null; + }); + }, + onSelected: ((String? value) { + setState(() { + filterType = value; + }); + }), + entries: type.toList(), + ), + ); + + fields.add( + FieldDropdown( + name: 'support', + label: 'Förderprogramm', + filterValue: filterSupport, + onClear: () { + setState(() { + filterSupport = null; + }); + }, + onSelected: ((String? value) { + setState(() { + filterSupport = value; + }); + }), + entries: support.toList(), + ), + ); + + var monthsSorted = months.toList(); + monthsSorted.sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase())); + + /*fields.add( + FieldDropdown( + name: 'months', + label: 'Beginn der Maßnahme', + filterValue: filterMonths, + onClear: () { + setState(() { + filterMonths = null; + }); + }, + onSelected: ((String? value) { + setState(() { + filterMonths = value; + }); + }), + entries: monthsSorted, + ), + );*/ + fields.add( + FieldDaterangepicker( + name: 'months', + label: 'Beginn der Maßnahme', + filterValue: filterMonths, + onClear: () {}, + onSelected: (String? value) {}, + entries: months.toList(), + ), + ); + + return FormWidget(type: FormWidgetType.vertical, fields: fields); + + /*return Row( mainAxisAlignment: MainAxisAlignment.start, children: [ DropdownMenuWidget( @@ -191,7 +297,7 @@ class ActionsPageState extends State { entries: support.toList(), ), ], - ); + );*/ } Widget getCard(BuildContext context, Massnahme massnahme) { diff --git a/lib/src/widgets/form/fields/field_daterangepicker.dart b/lib/src/widgets/form/fields/field_daterangepicker.dart new file mode 100644 index 0000000..9c71e99 --- /dev/null +++ b/lib/src/widgets/form/fields/field_daterangepicker.dart @@ -0,0 +1,54 @@ +import 'package:ambito/src/extensions/datetime_extensions.dart'; +import 'package:ambito/src/widgets/form/form_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; + +class FieldDaterangepicker extends FormWidgetField { + FieldDaterangepicker( + {required this.name, + required this.label, + required this.filterValue, + required this.onClear, + required this.onSelected, + required this.entries}); + + final String name; + final String label; + final String? filterValue; + final VoidCallback onClear; + final void Function(String?) onSelected; + final List entries; + + FormBuilderField get() { + DateTime now = DateTime.now(); + + return FormBuilderDateRangePicker( + name: name, + firstDate: now.firstDayOfYear(), + lastDate: now.lastDayOfYear()); + + return FormBuilderDropdown( + name: name, + initialValue: filterValue, + decoration: InputDecoration( + labelText: label, + prefix: IconButton( + icon: const Icon(Icons.cancel_outlined), + onPressed: onClear, + ), + hintText: label, + ), + onReset: onClear, + onChanged: onSelected, + items: entries + .map( + (entry) => DropdownMenuItem( + value: entry, + alignment: Alignment.centerLeft, + child: Text(entry), + ), + ) + .toList(), + ); + } +} diff --git a/lib/src/widgets/form/fields/field_dropdown.dart b/lib/src/widgets/form/fields/field_dropdown.dart new file mode 100644 index 0000000..d6c8738 --- /dev/null +++ b/lib/src/widgets/form/fields/field_dropdown.dart @@ -0,0 +1,46 @@ +import 'package:ambito/src/widgets/form/form_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; + +class FieldDropdown extends FormWidgetField { + FieldDropdown( + {required this.name, + required this.label, + required this.filterValue, + required this.onClear, + required this.onSelected, + required this.entries}); + + final String name; + final String label; + final String? filterValue; + final VoidCallback onClear; + final void Function(String?) onSelected; + final List entries; + + FormBuilderField get() { + return FormBuilderDropdown( + name: name, + initialValue: filterValue, + decoration: InputDecoration( + labelText: label, + prefix: IconButton( + icon: const Icon(Icons.cancel_outlined), + onPressed: onClear, + ), + hintText: label, + ), + onReset: onClear, + onChanged: onSelected, + items: entries + .map( + (entry) => DropdownMenuItem( + value: entry, + alignment: Alignment.centerLeft, + child: Text(entry), + ), + ) + .toList(), + ); + } +} diff --git a/lib/src/widgets/form/form_widget.dart b/lib/src/widgets/form/form_widget.dart new file mode 100644 index 0000000..a7c2d9f --- /dev/null +++ b/lib/src/widgets/form/form_widget.dart @@ -0,0 +1,33 @@ +import 'package:ambito/src/widgets/form/form_widget_type.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; + +abstract class FormWidgetField { + FormBuilderField get() { + return FormBuilderTextField(name: ''); + } +} + +class FormWidget extends StatelessWidget { + FormWidget({super.key, required this.type, required this.fields}); + + final _formKey = GlobalKey(); + + final FormWidgetType type; + final List fields; + + @override + Widget build(BuildContext context) { + return SizedBox( + child: FormBuilder( + key: _formKey, + child: (type == FormWidgetType.vertical) + ? Column( + children: fields.map((element) => element.get()).toList(), + ) + : Row( + children: fields.map((element) => element.get()).toList(), + ), + )); + } +} diff --git a/lib/src/widgets/form/form_widget_type.dart b/lib/src/widgets/form/form_widget_type.dart new file mode 100644 index 0000000..0fad176 --- /dev/null +++ b/lib/src/widgets/form/form_widget_type.dart @@ -0,0 +1 @@ +enum FormWidgetType { vertical, horizontal } diff --git a/pubspec.lock b/pubspec.lock index 5b36393..ac8e6f2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,23 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "72.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.2" analyzer: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "6.7.0" args: dependency: transitive description: @@ -85,18 +90,18 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" + sha256: dd09dd4e2b078992f42aac7f1a622f01882a8492fef08486b27ddde929c19f04 url: "https://pub.dev" source: hosted - version: "2.4.11" + version: "2.4.12" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe + sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 url: "https://pub.dev" source: hosted - version: "7.3.1" + version: "7.3.2" built_collection: dependency: transitive description: @@ -221,10 +226,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "2.3.7" dio: dependency: transitive description: @@ -318,6 +323,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.0" + flutter_form_builder: + dependency: "direct main" + description: + name: flutter_form_builder + sha256: b42ad8a9b3e8a629eb0fee8e4feaf7c2862fc40808ebf9f7ddc9da798e890136 + url: "https://pub.dev" + source: hosted + version: "9.4.1" flutter_i18n: dependency: "direct main" description: @@ -561,18 +574,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -613,6 +626,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + macros: + dependency: transitive + description: + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + url: "https://pub.dev" + source: hosted + version: "0.1.2-main.4" maps_toolkit: dependency: "direct main" description: @@ -633,18 +654,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mgrs_dart: dependency: transitive description: @@ -926,10 +947,10 @@ packages: dependency: transitive description: name: sqflite_common - sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" + sha256: "4058172e418eb7e7f2058dcb7657d451a8fc264afa0dea4dbd0f304a57131611" url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.4+3" stack_trace: dependency: transitive description: @@ -966,10 +987,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + sha256: "51b08572b9f091f8c3eb4d9d4be253f196ff0075d5ec9b10a884026d5b55d7bc" url: "https://pub.dev" source: hosted - version: "3.1.0+1" + version: "3.3.0+2" term_glyph: dependency: transitive description: @@ -982,10 +1003,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" timing: dependency: transitive description: @@ -1022,10 +1043,10 @@ packages: dependency: transitive description: name: uuid - sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77 + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff url: "https://pub.dev" source: hosted - version: "4.5.0" + version: "4.5.1" vector_math: dependency: transitive description: @@ -1038,10 +1059,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" watcher: dependency: transitive description: @@ -1054,10 +1075,10 @@ packages: dependency: transitive description: name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" web_socket: dependency: transitive description: @@ -1115,5 +1136,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.4.4 <4.0.0" - flutter: ">=3.22.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index 02aab9a..3ff0505 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: path_provider: ^2.1.4 cached_network_image: ^3.4.1 http: ^1.2.2 + flutter_form_builder: ^9.4.1 dev_dependencies: