diff --git a/lib/main.dart b/lib/main.dart index 1375d72..81912a3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:ambito/src/pages/actions/actions_page.dart'; import 'package:ambito/src/pages/actions/actions_pre_page.dart'; import 'package:ambito/src/pages/calendar/calendar_page.dart'; import 'package:ambito/src/pages/calendar/calendar_page_year.dart'; +import 'package:ambito/src/pages/dashboard/dashboard_page.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; @@ -48,6 +49,8 @@ void main() async { await MeasureRepository().buildMeasureFilters(); + await MeasureRepository().downloadImages(); + runApp(const Ambito()); } @@ -103,6 +106,41 @@ class Ambito extends StatelessWidget { name: '/massnahmendatenbank', page: () => const ActionsPage(), ), + GetPage( + name: '/dashboard', + page: () => DashboardPage( + businessId: prefs.getInt('currentUser') ?? 22, + userId: 0, + ), + ), + GetPage( + name: '/dashboard/meine-massnahmen', + page: () => DashboardPage( + businessId: prefs.getInt('currentUser') ?? 22, + userId: 0, + ), + ), + GetPage( + name: '/dashboard/urkunde', + page: () => DashboardPage( + businessId: prefs.getInt('currentUser') ?? 22, + userId: 0, + ), + ), + GetPage( + name: '/dashboard/flaechen', + page: () => DashboardPage( + businessId: prefs.getInt('currentUser') ?? 22, + userId: 0, + ), + ), + GetPage( + name: '/dashboard/stammdaten', + page: () => DashboardPage( + businessId: prefs.getInt('currentUser') ?? 22, + userId: 0, + ), + ), GetPage(name: '/massnahme/:id', page: () => const ActionDetailPage()) ], ); diff --git a/lib/src/consts/consts.dart b/lib/src/consts/consts.dart new file mode 100644 index 0000000..c2861cb --- /dev/null +++ b/lib/src/consts/consts.dart @@ -0,0 +1,14 @@ +const List months = [ + 'Januar', + 'Februar', + 'März', + 'April', + 'Mai', + 'Juni', + 'Juli', + 'August', + 'September', + 'Oktober', + 'November', + 'Dezember', +]; diff --git a/lib/src/entity/_general/filter/item_filter_repository.dart b/lib/src/entity/_general/filter/item_filter_repository.dart index fd3dd2f..bfd7010 100644 --- a/lib/src/entity/_general/filter/item_filter_repository.dart +++ b/lib/src/entity/_general/filter/item_filter_repository.dart @@ -25,7 +25,22 @@ class ItemFilterRepository extends BaseDB { .toList(); } + List? getIdsByTypeAndName(String type, String name) { + return isar.itemFilters + .where() + .typeEqualTo(type) + .and() + .nameEqualTo(name) + .idsProperty() + .findAll() + .expand((list) => list ?? []) + .toSet() + .toList(); + } + FieldDropdown getDropDown(String type, String label) { + if (type == 'month') {} + List filterItems = getByType(type)!; return FieldDropdown( diff --git a/lib/src/entity/measure/measure_repository.dart b/lib/src/entity/measure/measure_repository.dart index a92d3c5..978b502 100644 --- a/lib/src/entity/measure/measure_repository.dart +++ b/lib/src/entity/measure/measure_repository.dart @@ -51,7 +51,7 @@ class MeasureRepository extends BaseDB { ..name = entry.key ..type = 'areaType' ..description = - 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.' + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy.' ..image = 'images/actions/areatype/${entry.key.toLowerCase().replaceUmlauts()}.jpg' ..color = colors[entry.key] @@ -63,8 +63,7 @@ class MeasureRepository extends BaseDB { ..id = autoIncrement() ..name = entry.key ..type = 'group' - ..description = - 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.' + ..description = 'Lorem ipsum dolor sit amet, consetetur sadipscing.' ..image = 'images/actions/areatype/${entry.key.toLowerCase().replaceUmlauts()}.jpg' ..color = colors[entry.key] @@ -101,4 +100,18 @@ class MeasureRepository extends BaseDB { List getAllOrdered() { return isar.measures.where().sortByName().findAll(); } + + Future downloadImages() async { + List measures = isar.measures.where().findAll(); + List files = []; + for (Measure measure in measures) { + if (measure.files != null) { + for (FilePart file in measure.files!) { + files.add(file.url!); + } + } + } + logger.d(files); + return true; + } } diff --git a/lib/src/packages/ambito_api/base_api.dart b/lib/src/packages/ambito_api/base_api.dart index 174301b..d665755 100644 --- a/lib/src/packages/ambito_api/base_api.dart +++ b/lib/src/packages/ambito_api/base_api.dart @@ -27,7 +27,7 @@ class BaseApi { var json = _jsonDecoded(response.body); //if (table == 'measure') { - //logger.d(json); + // logger.d(json); //} var results = json['results']; diff --git a/lib/src/packages/ambito_downloader/ambito_downloader.dart b/lib/src/packages/ambito_downloader/ambito_downloader.dart new file mode 100644 index 0000000..d4cf2c3 --- /dev/null +++ b/lib/src/packages/ambito_downloader/ambito_downloader.dart @@ -0,0 +1,22 @@ +import '../../../main.dart'; + +class AmbitoDownloader { + static Future downloadImage( + String type, + String id, + String downloadUrl, + ) async { + logger.d(downloadUrl); + + //await WebImageDownloader.downloadImageFromWeb(downloadUrl); + + /*var documentDirectory = await getApplicationDocumentsDirectory(); + var yourPath = "${documentDirectory.path}/$type"; + var filePathAndName = '$yourPath/%id.jpg'; + + await Directory(yourPath).create(recursive: true); + File yourFile = File(filePathAndName); + yourFile.writeAsBytesSync(response.bodyBytes);*/ + return true; + } +} diff --git a/lib/src/packages/ambito_notifier/notifier/filter_notifier.dart b/lib/src/packages/ambito_notifier/notifier/filter_notifier.dart index 3b3b752..830c382 100644 --- a/lib/src/packages/ambito_notifier/notifier/filter_notifier.dart +++ b/lib/src/packages/ambito_notifier/notifier/filter_notifier.dart @@ -17,7 +17,7 @@ class AmbitoFilterNotifier extends ChangeNotifier { void setFilter(String type, String value) { prefs.setString('filter_$type', value).then((_) { activeFilters[type] = value; - activeIds[type] = ItemFilterRepository().getIdsByType(type); + activeIds[type] = ItemFilterRepository().getIdsByTypeAndName(type, value); notifyListeners(); }); } diff --git a/lib/src/packages/ambito_theme/ambito_theme.dart b/lib/src/packages/ambito_theme/ambito_theme.dart index 5112f30..ec4865f 100644 --- a/lib/src/packages/ambito_theme/ambito_theme.dart +++ b/lib/src/packages/ambito_theme/ambito_theme.dart @@ -1,6 +1,27 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; +final orangeColors = { + 'primary': const Color(0xffeebb4b), + 'secondary': const Color(0xfff8e0aa), + 'tertiary': const Color(0xfff3b25c), + 'quarternary': const Color(0xffec863a), +}; + +final redColors = { + 'primary': const Color(0xff96172f), + 'secondary': const Color(0xffca414c), + 'tertiary': const Color(0xffa83745), + 'quarternary': const Color(0xff883443), +}; + +final greenColors = { + 'primary': const Color(0xff88a44e), + 'secondary': const Color(0xffd5d808), + 'tertiary': const Color(0xffacc11e), + 'quarternary': const Color(0xff3d693f), +}; + final actionGroupColors = { 'Bauelemente': const Color(0xffFFD269).withOpacity(.4), 'Begrünung': const Color(0xff40DD74).withOpacity(.4), @@ -11,9 +32,9 @@ final actionGroupColors = { }; final actionAreaColors = { - 'Landschaft': const Color(0xfff8e0aa).withOpacity(.6), - 'Weinberg': const Color(0xffacc11e).withOpacity(.6), - 'Betriebsfläche': const Color(0xffca414c).withOpacity(.6), + 'Landschaft': const Color(0xffeebb4b).withOpacity(.3), + 'Weinberg': const Color(0xff88a44e).withOpacity(.3), + 'Betriebsfläche': const Color(0xff96172f).withOpacity(.3), 'Betriebsstätte': const Color(0xffCCCDCC).withOpacity(.6), }; @@ -46,7 +67,7 @@ class AmbitoTheme { onError: Color(0xFFFFFFFF), onErrorContainer: Color(0xFF410002), surface: Color(0xFFffffff), - onSurface: Color(0xFF656565), + onSurface: Color(0xFF666666), surfaceContainerHighest: Color(0xFFDDE3EA), onSurfaceVariant: Color(0xFF41484D), outline: Color(0xFF71787E), @@ -200,22 +221,25 @@ class AmbitoTheme { ), ); - EdgeInsets get padding => const EdgeInsets.all(16); - EdgeInsets get paddingSmall => const EdgeInsets.all(8); + EdgeInsets get padding => const EdgeInsets.all(20); + EdgeInsets get paddingSmall => const EdgeInsets.all(10); Widget get horizontalSpacer => const SizedBox( - width: 16, + width: 20, + ); + Widget get horizontalSpacerMax => const SizedBox( + width: 40, ); Widget get horizontalSpacerSmall => const SizedBox( - width: 8, + width: 10, ); Widget get verticalSpacer => const SizedBox( - height: 16, + height: 20, ); Widget get verticalSpacerSmall => const SizedBox( - height: 8, + height: 10, ); Widget get verticalSpacerMax => const SizedBox( - height: 48, + height: 40, ); ThemeData get darkThemeData => ThemeData( diff --git a/lib/src/pages/actions/action_detail_page.dart b/lib/src/pages/actions/action_detail_page.dart index 000cd47..b4f9eeb 100644 --- a/lib/src/pages/actions/action_detail_page.dart +++ b/lib/src/pages/actions/action_detail_page.dart @@ -37,6 +37,9 @@ class ActionDetailPageState extends State { Widget content = const SizedBox(); Measure? massnahme; + List contentItems = []; + List sidebarItems = []; + @override void initState() { id = Get.parameters['id'] ?? ''; @@ -44,11 +47,60 @@ class ActionDetailPageState extends State { massnahme = MeasureRepository().get(int.parse(id)) as Measure; setState(() { if (massnahme != null) { + contentItems = [ + DescriptionCard( + massnahme: massnahme!, + ), + baseTheme.verticalSpacer, + BackgroundCard( + massnahme: massnahme!, + ), + baseTheme.verticalSpacer, + PresetsCard( + massnahme: massnahme!, + ), + baseTheme.verticalSpacer, + BiodiverisityCard( + massnahme: massnahme!, + ), + baseTheme.verticalSpacer, + CreationCard( + massnahme: massnahme!, + ), + baseTheme.verticalSpacer, + MaintenanceCard( + massnahme: massnahme!, + ), + baseTheme.verticalSpacer, + FundingCard( + massnahme: massnahme!, + ), + baseTheme.verticalSpacer, + ]; + + sidebarItems = [ + AdvisorCard( + massnahme: massnahme!, + ), + baseTheme.verticalSpacer, + MaterialCard( + massnahme: massnahme!, + ), + baseTheme.verticalSpacer, + FactsheetCard( + massnahme: massnahme!, + ), + baseTheme.verticalSpacer, + ReviewCard( + massnahme: massnahme!, + ), + ]; + content = _buildInfoPage(massnahme!); } }); } - logger.d(id); + super.initState(); } @@ -58,9 +110,7 @@ class ActionDetailPageState extends State { appBar: AmbitoAppbar( links: const ['dashboard', 'massnahmen'], ), - body: SingleChildScrollView( - child: content, - ), + body: content, ); } @@ -102,9 +152,8 @@ class ActionDetailPageState extends State { color: baseTheme.currentColorScheme.primary, child: massnahme.getImage(), ), - SizedBox( - width: 1140, - child: Expanded( + Expanded( + child: SingleChildScrollView( child: Column( children: [ baseTheme.verticalSpacer, @@ -119,37 +168,13 @@ class ActionDetailPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: Column( - children: [ - DescriptionCard( - massnahme: massnahme, - ), - baseTheme.verticalSpacer, - BackgroundCard( - massnahme: massnahme, - ), - baseTheme.verticalSpacer, - PresetsCard( - massnahme: massnahme, - ), - baseTheme.verticalSpacer, - BiodiverisityCard( - massnahme: massnahme, - ), - baseTheme.verticalSpacer, - CreationCard( - massnahme: massnahme, - ), - baseTheme.verticalSpacer, - MaintenanceCard( - massnahme: massnahme, - ), - baseTheme.verticalSpacer, - FundingCard( - massnahme: massnahme, - ), - baseTheme.verticalSpacer, - ], + child: SingleChildScrollView( + child: ListView.builder( + shrinkWrap: true, + itemCount: contentItems.length, + itemBuilder: (BuildContext context, int index) { + return contentItems[index]; + }), ), ), baseTheme.horizontalSpacer, diff --git a/lib/src/pages/actions/actions_page.dart b/lib/src/pages/actions/actions_page.dart index 8b5234d..473ded3 100644 --- a/lib/src/pages/actions/actions_page.dart +++ b/lib/src/pages/actions/actions_page.dart @@ -64,8 +64,9 @@ class ActionsPageState extends State { setState(() { filterAreaType = ambitoFilterNotifier.getFilter('areaType'); filterType = ambitoFilterNotifier.getFilter('group'); + filterSupport = ambitoFilterNotifier.getFilter('fundingProgram'); + filterMonths = ambitoFilterNotifier.getFilter('month'); }); - logger.d(ambitoFilterNotifier.activeIds); } @override @@ -75,7 +76,7 @@ class ActionsPageState extends State { } void _initializeData() { - massnahmen = MeasureRepository().getAll(); + massnahmen = MeasureRepository().getAllOrdered(); _updateFilterSets(); } diff --git a/lib/src/pages/actions/actions_pre_page.dart b/lib/src/pages/actions/actions_pre_page.dart index 7130504..493dc96 100644 --- a/lib/src/pages/actions/actions_pre_page.dart +++ b/lib/src/pages/actions/actions_pre_page.dart @@ -45,10 +45,11 @@ class ActionsPrePageState extends State { }, onHover: (value) {}, child: SizedBox( - width: 300, - height: 400, + width: 262, + height: 350, child: Card( - color: actionAreaColors[filter.name!], + elevation: 4, + surfaceTintColor: actionAreaColors[filter.name!], child: Column( children: [ ClipRRect( @@ -112,41 +113,44 @@ class ActionsPrePageState extends State { links: const ['dashboard', 'massnahmen'], ), body: Center( - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - baseTheme.verticalSpacerMax, - Text( - 'Maßnahmenkategorien', - textAlign: TextAlign.start, - style: - baseTheme.currentThemeData.textTheme.headlineLarge?.copyWith( - color: baseTheme.currentColorScheme.onSurface, + child: SizedBox( + width: 1152, + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + baseTheme.verticalSpacerMax, + Text( + 'Maßnahmenkategorien', + textAlign: TextAlign.start, + style: baseTheme.currentThemeData.textTheme.headlineLarge + ?.copyWith( + color: baseTheme.currentColorScheme.onSurface, + ), ), - ), - baseTheme.verticalSpacerMax, - Wrap( - alignment: WrapAlignment.center, - spacing: 16, - children: cards, - ), - baseTheme.verticalSpacerMax, - TextButton( - onPressed: () async { - await prefs.setString('selected_areaType', ''); - await Get.toNamed('/massnahmendatenbank'); - }, - child: Text( - 'Zeig mir alle $counterComplete Maßnahmen...', - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.blueAccent, - ), - )) - ], + baseTheme.verticalSpacerMax, + Wrap( + alignment: WrapAlignment.center, + spacing: 32, + children: cards, + ), + baseTheme.verticalSpacerMax, + TextButton( + onPressed: () async { + await prefs.setString('selected_areaType', ''); + await Get.toNamed('/massnahmendatenbank'); + }, + child: Text( + 'Zeig mir alle $counterComplete Maßnahmen...', + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.blueAccent, + ), + )) + ], + ), ), ), ); diff --git a/lib/src/pages/calendar/calendar_page.dart b/lib/src/pages/calendar/calendar_page.dart index 77a5b88..c431a40 100644 --- a/lib/src/pages/calendar/calendar_page.dart +++ b/lib/src/pages/calendar/calendar_page.dart @@ -8,6 +8,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; +import '../../consts/consts.dart'; import '../../widgets/appbar/ambito_appbar.dart'; class CalendarPage extends StatefulWidget { @@ -20,20 +21,6 @@ class CalendarPage extends StatefulWidget { class CalendarPageState extends State { List appointments = []; - List months = [ - 'Januar', - 'Februar', - 'März', - 'April', - 'Mai', - 'Juni', - 'Juli', - 'August', - 'September', - 'Oktober', - 'November', - 'Dezember', - ]; List? monthFilter = []; @override diff --git a/lib/src/pages/calendar/calendar_page_year.dart b/lib/src/pages/calendar/calendar_page_year.dart index 086b4d3..e03e959 100644 --- a/lib/src/pages/calendar/calendar_page_year.dart +++ b/lib/src/pages/calendar/calendar_page_year.dart @@ -3,6 +3,7 @@ import 'package:ambito/src/entity/_general/filter/item_filter.dart'; import 'package:ambito/src/entity/measure/measure_repository.dart'; import 'package:flutter/material.dart'; +import '../../consts/consts.dart'; import '../../widgets/appbar/ambito_appbar.dart'; class CalendarPageYear extends StatefulWidget { @@ -13,22 +14,12 @@ class CalendarPageYear extends StatefulWidget { } class CalendarPageYearState extends State { - List rows = []; + List rowsBody = []; + List rowsHeader = []; - List months = [ - 'Januar', - 'Februar', - 'März', - 'April', - 'Mai', - 'Juni', - 'Juli', - 'August', - 'September', - 'Oktober', - 'November', - 'Dezember', - ]; + Set displayFilterSet = {'creation', 'maintenance'}; + + List displayFilters = []; List? monthFilter = []; @override @@ -44,9 +35,8 @@ class CalendarPageYearState extends State { padding: const EdgeInsets.all(8), child: Text( 'Maßnahme', - style: baseTheme.currentThemeData.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + style: baseTheme.currentThemeData.textTheme.titleMedium + ?.copyWith(fontWeight: FontWeight.bold, color: Colors.white), ), ), ), @@ -58,14 +48,15 @@ class CalendarPageYearState extends State { child: Text( textAlign: TextAlign.center, month, - style: baseTheme.currentThemeData.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + style: baseTheme.currentThemeData.textTheme.titleMedium + ?.copyWith(fontWeight: FontWeight.bold, color: Colors.white), ), ), ); } - rows.add(TableRow(children: cells)); + rowsHeader.add(TableRow( + decoration: const BoxDecoration(color: Color(0xFF60845E)), + children: cells)); var massnahmen = MeasureRepository().getAllOrdered(); for (var massnahme in massnahmen) { if (massnahme.name != null && massnahme.name!.isNotEmpty) { @@ -104,7 +95,7 @@ class CalendarPageYearState extends State { ); } } - rows.add( + rowsBody.add( TableRow( children: cells, ), @@ -120,52 +111,145 @@ class CalendarPageYearState extends State { appBar: AmbitoAppbar( links: const ['dashboard', 'massnahmen'], ), - body: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(32), - child: Table( - border: TableBorder( - top: const BorderSide( - width: 2, - color: Colors.black, - ), - left: const BorderSide( - width: 2, - color: Colors.black, - ), - right: const BorderSide( - width: 2, - color: Colors.black, - ), - bottom: const BorderSide( - width: 2, - color: Colors.black, - ), - horizontalInside: BorderSide( - width: 1, - color: Colors.black.withOpacity(.6), + body: Card( + child: Column( + children: [ + /*Padding( + padding: const EdgeInsets.only(top: 32, left: 32, right: 32), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + FilterChip( + label: const Text('Zeitraum der Anlage'), + selected: displayFilterSet.contains('creation'), + selectedColor: baseTheme.currentColorScheme.primary, + onSelected: (bool selected) { + setState(() { + if (selected) { + displayFilterSet.add('creation'); + } else { + displayFilterSet.remove('creation'); + } + }); + }, + ), + baseTheme.horizontalSpacerSmall, + FilterChip( + label: const Text('Zeitraum der Pflege'), + selected: displayFilterSet.contains('maintenance'), + selectedColor: baseTheme.currentColorScheme.inversePrimary, + onSelected: (bool selected) { + setState(() { + if (selected) { + displayFilterSet.add('maintenance'); + } else { + displayFilterSet.remove('maintenance'); + } + }); + }, + ), + ], + ), + ),*/ + Padding( + padding: const EdgeInsets.only(top: 32, left: 32, right: 32), + child: _buildTableHeader(fixedWidth), + ), + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: + const EdgeInsets.only(bottom: 32, left: 32, right: 32), + child: _buildTableBody(fixedWidth), + ), ), ), - columnWidths: { - 0: FlexColumnWidth(300), - 1: FlexColumnWidth(fixedWidth), - 2: FlexColumnWidth(fixedWidth), - 3: FlexColumnWidth(fixedWidth), - 4: FlexColumnWidth(fixedWidth), - 5: FlexColumnWidth(fixedWidth), - 6: FlexColumnWidth(fixedWidth), - 7: FlexColumnWidth(fixedWidth), - 8: FlexColumnWidth(fixedWidth), - 9: FlexColumnWidth(fixedWidth), - 10: FlexColumnWidth(fixedWidth), - 11: FlexColumnWidth(fixedWidth), - 12: FlexColumnWidth(fixedWidth), - 13: FlexColumnWidth(fixedWidth), - }, - children: rows, - ), + ], ), ), ); } + + Table _buildTableHeader(double fixedWidth) { + TableBorder tableBorder = TableBorder( + top: const BorderSide( + width: 2, + color: Colors.black, + ), + left: const BorderSide( + width: 2, + color: Colors.black, + ), + right: const BorderSide( + width: 2, + color: Colors.black, + ), + bottom: const BorderSide( + width: 2, + color: Colors.black, + ), + horizontalInside: BorderSide( + width: 1, + color: Colors.black.withOpacity(.6), + ), + verticalInside: const BorderSide( + width: 0, + color: Colors.transparent, + ), + ); + return _getTable(fixedWidth, tableBorder, rowsHeader); + } + + Table _buildTableBody(double fixedWidth) { + TableBorder tableBorder = TableBorder( + top: const BorderSide( + width: 0, + color: Colors.black, + ), + left: const BorderSide( + width: 2, + color: Colors.black, + ), + right: const BorderSide( + width: 2, + color: Colors.black, + ), + bottom: const BorderSide( + width: 2, + color: Colors.black, + ), + horizontalInside: BorderSide( + width: 1, + color: Colors.black.withOpacity(.6), + ), + verticalInside: const BorderSide( + width: 0, + ), + ); + return _getTable(fixedWidth, tableBorder, rowsBody); + } + + Table _getTable( + double fixedWidth, TableBorder tableBorder, List rows) { + return Table( + border: tableBorder, + columnWidths: { + 0: const FlexColumnWidth(300), + 1: FlexColumnWidth(fixedWidth), + 2: FlexColumnWidth(fixedWidth), + 3: FlexColumnWidth(fixedWidth), + 4: FlexColumnWidth(fixedWidth), + 5: FlexColumnWidth(fixedWidth), + 6: FlexColumnWidth(fixedWidth), + 7: FlexColumnWidth(fixedWidth), + 8: FlexColumnWidth(fixedWidth), + 9: FlexColumnWidth(fixedWidth), + 10: FlexColumnWidth(fixedWidth), + 11: FlexColumnWidth(fixedWidth), + 12: FlexColumnWidth(fixedWidth), + 13: FlexColumnWidth(fixedWidth), + }, + children: rows, + ); + } } diff --git a/lib/src/pages/cart/cart_page.dart b/lib/src/pages/cart/cart_page.dart new file mode 100644 index 0000000..c431a40 --- /dev/null +++ b/lib/src/pages/cart/cart_page.dart @@ -0,0 +1,162 @@ +import 'package:ambito/src/entity/_general/filter/item_filter.dart'; +import 'package:ambito/src/entity/_general/filter/item_filter_repository.dart'; +import 'package:ambito/src/entity/measure/measure.dart'; +import 'package:ambito/src/entity/measure/measure_repository.dart'; +import 'package:ambito/src/extensions/extensions.dart'; +import 'package:ambito/src/packages/ambito_theme/ambito_theme.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:syncfusion_flutter_calendar/calendar.dart'; + +import '../../consts/consts.dart'; +import '../../widgets/appbar/ambito_appbar.dart'; + +class CalendarPage extends StatefulWidget { + const CalendarPage({super.key}); + + @override + State createState() => CalendarPageState(); +} + +class CalendarPageState extends State { + List appointments = []; + + List? monthFilter = []; + + @override + void initState() { + initDataSource(); + super.initState(); + } + + initDataSource() { + monthFilter = ItemFilterRepository().getByType('month'); + for (String month in months) { + DateTime now = DateTime.now(); + int monthInt = months.indexOf(month) + 1; + + DateTime startDate = DateTime(now.year, monthInt, 1, 0, 0, 0); + DateTime endDate = DateTime( + now.year, monthInt, startDate.lastDayOfMonth.day, 23, 59, 59); + + ItemFilter? itemFilter = + monthFilter?.firstWhereOrNull((item) => item.name == month); + if (itemFilter != null) { + List currentMeasures = + MeasureRepository().getByIds(itemFilter.ids!); + for (var measure in currentMeasures) { + appointments.add( + Appointment( + startTime: startDate, + endTime: endDate, + isAllDay: true, + subject: measure!.name ?? '', + color: actionGroupColors[measure.actionGroup!.value!]!, + ), + ); + } + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AmbitoAppbar( + links: const ['dashboard', 'massnahmen'], + ), + body: Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: SfCalendar( + firstDayOfWeek: 1, + showNavigationArrow: true, + view: CalendarView.month, + dataSource: _AppointmentDataSource(appointments), + monthViewSettings: const MonthViewSettings( + appointmentDisplayMode: MonthAppointmentDisplayMode.appointment, + appointmentDisplayCount: 64, + ), + ), + ), + ), + ); + } + + _AppointmentDataSource _getCalendarDataSource() { + List appointments = []; + appointments.add(Appointment( + startTime: DateTime.now(), + endTime: DateTime.now().add(Duration(minutes: 10)), + subject: 'Meeting', + isAllDay: true, + color: Colors.blue, + startTimeZone: '', + endTimeZone: '', + )); + + return _AppointmentDataSource(appointments); + } +} + +class _AppointmentDataSource extends CalendarDataSource { + _AppointmentDataSource(List source) { + appointments = source; + } +} + +/*class ActionDataSource extends CalendarDataSource { + ActionDataSource(List source) { + appointments = source; + } + + @override + DateTime getStartTime(int index) { + return appointments![index].from; + } + + @override + DateTime getEndTime(int index) { + return appointments![index].to; + } + + @override + bool isAllDay(int index) { + return appointments![index].isAllDay; + } + + @override + String getSubject(int index) { + return appointments![index].eventName; + } + + @override + String getStartTimeZone(int index) { + return appointments![index].startTimeZone; + } + + @override + String getEndTimeZone(int index) { + return appointments![index].endTimeZone; + } + + @override + Color getColor(int index) { + return appointments![index].background; + } +} + +class Action { + Action( + {this.eventName = '', + required this.from, + required this.to, + required this.background, + this.isAllDay = false}); + + String eventName; + DateTime from; + DateTime to; + Color background; + bool isAllDay; +}*/ diff --git a/lib/src/pages/dashboard/dashboard_page.dart b/lib/src/pages/dashboard/dashboard_page.dart new file mode 100644 index 0000000..d97acd5 --- /dev/null +++ b/lib/src/pages/dashboard/dashboard_page.dart @@ -0,0 +1,444 @@ +import 'package:ambito/src/packages/ambito_theme/ambito_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:percent_indicator/linear_percent_indicator.dart'; + +import '../../../main.dart'; +import '../../widgets/appbar/ambito_appbar.dart'; + +class DashboardPage extends StatefulWidget { + const DashboardPage( + {super.key, required this.businessId, required this.userId}); + + final int businessId; + final int userId; + + @override + State createState() => DashboardPageState(); +} + +class DashboardPageState extends State { + List cards = []; + + @override + void initState() { + cards.add( + _buildCard( + Colors.green, + 'images/actions/nist_brut_unterschlupforte.jpg', + 'Meine\nMaßnahmen', + '/dashboard/meine-massnahmen', + ), + ); + cards.add( + _buildCard( + Colors.green, + 'images/actions/steinhaufen.jpg', + 'Urkunde', + '/dashboard/urkunde', + ), + ); + cards.add( + _buildCard( + Colors.green, + 'images/actions/staudenbeete.jpg', + 'Flächen', + '/dashboard/flaechen', + ), + ); + cards.add( + _buildCard( + Colors.green, + 'images/actions/pflanzgefaesse.jpg', + 'Stammdaten', + '/dashboard/stammdaten', + ), + ); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AmbitoAppbar( + links: const ['dashboard', 'massnahmen'], + ), + body: SingleChildScrollView( + child: Align( + alignment: Alignment.topCenter, + child: SizedBox( + width: 1152, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + baseTheme.verticalSpacerMax, + Text( + 'Willkommen, Max Mustermann!', + textAlign: TextAlign.start, + style: baseTheme.currentThemeData.textTheme.headlineLarge + ?.copyWith( + color: baseTheme.currentColorScheme.onSurface, + ), + ), + baseTheme.verticalSpacerMax, + Wrap( + alignment: WrapAlignment.center, + spacing: 32, + children: cards, + ), + baseTheme.verticalSpacerMax, + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 532, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Biodiversitätsbewertung', + textAlign: TextAlign.start, + style: baseTheme + .currentThemeData.textTheme.headlineMedium + ?.copyWith( + color: baseTheme.currentColorScheme.onSurface, + ), + ), + baseTheme.verticalSpacer, + SizedBox( + width: 532, + child: Card( + elevation: 4, + surfaceTintColor: + orangeColors['primary']?.withOpacity(0.3), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Fragebögen', + textAlign: TextAlign.left, + style: baseTheme.currentThemeData + .textTheme.headlineSmall + ?.copyWith( + color: orangeColors['primary'], + fontWeight: FontWeight.bold, + ), + ), + baseTheme.verticalSpacerSmall, + Row( + children: [ + LinearPercentIndicator( + width: 440, + lineHeight: 20.0, + animation: true, + percent: 0.8, + padding: EdgeInsets.zero, + backgroundColor: baseTheme + .currentColorScheme.surface, + progressColor: + orangeColors['primary'], + barRadius: const Radius.circular(8), + ), + const Spacer(), + IconButton( + iconSize: 24, + onPressed: () {}, + icon: const Icon( + Icons.arrow_forward_ios, + ), + color: Colors.white, + style: IconButton.styleFrom( + fixedSize: const Size(20, 20), + padding: const EdgeInsets.all(2), + backgroundColor: + orangeColors['primary'], + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(8), + ), + ), + ), + ], + ), + baseTheme.verticalSpacerSmall, + Text( + '4 / 5 abgeschlossen', + textAlign: TextAlign.start, + style: baseTheme + .currentThemeData.textTheme.bodyLarge + ?.copyWith( + color: baseTheme + .currentColorScheme.onSurface, + ), + ), + ], + ), + ), + ), + ), + baseTheme.verticalSpacer, + SizedBox( + width: 532, + child: Card( + elevation: 4, + surfaceTintColor: + greenColors['primary']?.withOpacity(0.3), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Ergebnisse', + textAlign: TextAlign.left, + style: baseTheme.currentThemeData + .textTheme.headlineSmall + ?.copyWith( + color: greenColors['primary'], + fontWeight: FontWeight.bold, + ), + ), + baseTheme.verticalSpacerSmall, + Row( + children: [ + LinearPercentIndicator( + width: 480, + lineHeight: 20.0, + animation: true, + percent: 0.8, + padding: EdgeInsets.zero, + backgroundColor: baseTheme + .currentColorScheme.surface, + progressColor: greenColors['primary'], + barRadius: const Radius.circular(8), + ), + ], + ), + baseTheme.verticalSpacerSmall, + Text( + 'Weinberg: 80%', + textAlign: TextAlign.start, + style: baseTheme + .currentThemeData.textTheme.bodyLarge + ?.copyWith( + color: baseTheme + .currentColorScheme.onSurface, + ), + ), + baseTheme.verticalSpacerSmall, + Row( + children: [ + LinearPercentIndicator( + width: 480, + lineHeight: 20.0, + animation: true, + percent: 0.52, + padding: EdgeInsets.zero, + backgroundColor: baseTheme + .currentColorScheme.surface, + progressColor: greenColors['primary'], + barRadius: const Radius.circular(8), + ), + ], + ), + baseTheme.verticalSpacerSmall, + Text( + 'Landschaft: 52%', + textAlign: TextAlign.start, + style: baseTheme + .currentThemeData.textTheme.bodyLarge + ?.copyWith( + color: baseTheme + .currentColorScheme.onSurface, + ), + ), + baseTheme.verticalSpacerSmall, + Row( + children: [ + LinearPercentIndicator( + width: 480, + lineHeight: 20.0, + animation: true, + percent: 0.27, + padding: EdgeInsets.zero, + backgroundColor: baseTheme + .currentColorScheme.surface, + progressColor: greenColors['primary'], + barRadius: const Radius.circular(8), + ), + ], + ), + baseTheme.verticalSpacerSmall, + Text( + 'Betriebsstätte: 27%', + textAlign: TextAlign.start, + style: baseTheme + .currentThemeData.textTheme.bodyLarge + ?.copyWith( + color: baseTheme + .currentColorScheme.onSurface, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + const Spacer(), + SizedBox( + width: 532, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Förderungen', + textAlign: TextAlign.start, + style: baseTheme + .currentThemeData.textTheme.headlineMedium + ?.copyWith( + color: baseTheme.currentColorScheme.onSurface, + ), + ), + baseTheme.verticalSpacer, + Text( + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.', + textAlign: TextAlign.start, + style: baseTheme + .currentThemeData.textTheme.bodyLarge + ?.copyWith( + color: baseTheme.currentColorScheme.onSurface, + ), + ), + baseTheme.verticalSpacer, + _buildProjectCard( + 'Projekt A', + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.', + ), + baseTheme.verticalSpacer, + _buildProjectCard( + 'Projekt B', + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.', + ), + ], + ), + ), + ], + ), + ], + ), + ), + ), + ), + ); + } + + Widget _buildCard(Color background, String image, String title, String link) { + return GestureDetector( + onTap: () { + Get.toNamed(link); + }, + child: SizedBox( + width: 262, + height: 250, + child: Card( + elevation: 4, + surfaceTintColor: Colors.white, + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: Image.asset( + image, + ), + ), + Expanded( + child: Center( + child: Text( + title, + textAlign: TextAlign.center, + style: baseTheme.currentThemeData.textTheme.headlineSmall + ?.copyWith( + color: const Color(0xFF666666), + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + ), + ); + } + + Widget _buildProjectCard(String title, String text) { + return SizedBox( + width: 532, + child: Card( + elevation: 4, + surfaceTintColor: Colors.white, + child: Padding( + padding: const EdgeInsets.all(8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + textAlign: TextAlign.center, + style: baseTheme.currentThemeData.textTheme.headlineSmall + ?.copyWith( + color: greenColors['primary'], + fontWeight: FontWeight.bold, + ), + ), + baseTheme.verticalSpacer, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + text, + softWrap: true, + overflow: TextOverflow.ellipsis, + maxLines: 3, + textAlign: TextAlign.start, + style: baseTheme.currentThemeData.textTheme.bodyLarge + ?.copyWith( + color: baseTheme.currentColorScheme.onSurface, + ), + ), + ), + IconButton( + iconSize: 24, + onPressed: () {}, + icon: const Icon( + Icons.arrow_forward_ios, + ), + color: Colors.white, + style: IconButton.styleFrom( + fixedSize: const Size(20, 20), + padding: const EdgeInsets.all(2), + backgroundColor: greenColors['primary'], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/src/widgets/appbar/ambito_appbar.dart b/lib/src/widgets/appbar/ambito_appbar.dart index 04a77f3..9f91b16 100644 --- a/lib/src/widgets/appbar/ambito_appbar.dart +++ b/lib/src/widgets/appbar/ambito_appbar.dart @@ -22,6 +22,7 @@ class AmbitoAppbar extends AppBar { ), ), leadingWidth: 80, + scrolledUnderElevation: 0, centerTitle: true, title: Builder( builder: (context) => _links(context, links), diff --git a/pubspec.lock b/pubspec.lock index 50accb4..0abbabf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -150,6 +150,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" checked_yaml: dependency: transitive description: @@ -206,6 +214,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" cupertino_icons: dependency: "direct main" description: @@ -506,6 +522,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.0" + html: + dependency: transitive + description: + name: html + sha256: "1fc58edeaec4307368c60d59b7e15b9d658b57d7f3125098b6294153c75337ec" + url: "https://pub.dev" + source: hosted + version: "0.15.5" http: dependency: "direct main" description: @@ -530,6 +554,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image_downloader_web: + dependency: "direct main" + description: + name: image_downloader_web + sha256: "11290f6ef5bd5afcb3c623c5a10c16da53a2f41a9bf238d9e20f7ff77407e39e" + url: "https://pub.dev" + source: hosted + version: "2.0.6" intl: dependency: "direct main" description: @@ -802,6 +834,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.1" + percent_indicator: + dependency: "direct main" + description: + name: percent_indicator + sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c + url: "https://pub.dev" + source: hosted + version: "4.2.3" permission_handler: dependency: "direct main" description: @@ -1207,6 +1247,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + universal_html: + dependency: transitive + description: + name: universal_html + sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971" + url: "https://pub.dev" + source: hosted + version: "2.2.4" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" uuid: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 81a396c..09eb79b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,8 @@ dependencies: flutter_rating_stars: ^1.1.0 syncfusion_flutter_calendar: ^27.1.58 timezone: ^0.9.4 + percent_indicator: ^4.2.3 + image_downloader_web: ^2.0.6 dev_dependencies: