diff options
| author | Aldrik Ramaekers <aldrik@amftech.nl> | 2022-11-13 23:07:11 +0100 |
|---|---|---|
| committer | Aldrik Ramaekers <aldrik@amftech.nl> | 2022-11-13 23:07:11 +0100 |
| commit | 03bd15045beee64dab5795053b662d02f7049e31 (patch) | |
| tree | c51f242454e5d1b30adda5e87b09484ab66f7267 /lib | |
| parent | 8c4abd2b2705589fe668be52614979a12ff04869 (diff) | |
v0.8, make it comfy, add salary log
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/RoutingExample.dart | 41 | ||||
| -rw-r--r-- | lib/config/defaults.dart | 4 | ||||
| -rw-r--r-- | lib/main.dart | 2 | ||||
| -rw-r--r-- | lib/pages/developer_page.dart | 2 | ||||
| -rw-r--r-- | lib/pages/logbook_page.dart | 201 | ||||
| -rw-r--r-- | lib/services/local_salary_provider_service.dart | 72 | ||||
| -rw-r--r-- | lib/services/storegear_api_service.dart | 2 | ||||
| -rw-r--r-- | lib/style/style.dart | 7 | ||||
| -rw-r--r-- | lib/widgets/agenda_week.dart | 61 | ||||
| -rw-r--r-- | lib/widgets/agenda_week_title.dart | 24 |
10 files changed, 350 insertions, 66 deletions
diff --git a/lib/RoutingExample.dart b/lib/RoutingExample.dart index 10332ee..19033e2 100644 --- a/lib/RoutingExample.dart +++ b/lib/RoutingExample.dart @@ -72,6 +72,21 @@ class RoutingExample { late RoutingEngine _routingEngine; late GeoCoordinates lastPosition = GeoCoordinates(0, 0); late SearchOptions _searchOptions; + late MapMarker mapMarker; + + Future<Uint8List> _loadFileAsUint8List(String assetPathToFile) async { + // The path refers to the assets directory as specified in pubspec.yaml. + ByteData fileData = await rootBundle.load(assetPathToFile); + return Uint8List.view(fileData.buffer); + } + + Future<void> _addCircle(GeoCoordinates geoCoordinates) async { + Uint8List imagePixelData = await _loadFileAsUint8List('assets/package.png'); + MapImage circleMapImage = + MapImage.withPixelDataAndImageFormat(imagePixelData, ImageFormat.png); + mapMarker = MapMarker(geoCoordinates, circleMapImage); + hereMapController.mapScene.addMapMarker(mapMarker); + } RoutingExample(HereMapController _hereMapController) : hereMapController = _hereMapController { @@ -94,6 +109,8 @@ class RoutingExample { .lookAtPoint(GeoCoordinates(50.8434572, 5.7381166)); _hereMapController.camera.zoomTo(currentZoom); + _addCircle(GeoCoordinates(50.8434572, 5.7381166)); + timer = Timer.periodic(Duration(seconds: 1), (Timer t) => _setLocationOnMap()); @@ -121,6 +138,7 @@ class RoutingExample { void _updateLocation(Position value) { lastPosition = GeoCoordinates(value.latitude, value.longitude); + mapMarker.coordinates = lastPosition; flyTo(GeoCoordinates(value.latitude, value.longitude)); } @@ -168,13 +186,13 @@ class RoutingExample { padding: EdgeInsets.all(2), decoration: BoxDecoration( color: backgroundColor, - shape: BoxShape.circle, + shape: BoxShape.rectangle, border: Border.all(color: Color.fromARGB(0, 0, 0, 120)), ), child: GestureDetector( child: Text( label, - style: TextStyle(fontSize: 10.0), + style: TextStyle(fontSize: 20.0), ), ), ); @@ -235,7 +253,12 @@ class RoutingExample { waypoints.add(Waypoint.withDefaults(destinationGeoCoordinates)); _parcelNumberPins.add(DestinationPin( - text: item.deliverySequenceNumber.toString(), + text: item.deliverySequenceNumber.toString() + + ' = ' + + item.houseNumber! + + (item.houseNumberAddition != null + ? item.houseNumberAddition! + : ''), coords: destinationGeoCoordinates)); _destinationCoords.add(destinationGeoCoordinates); @@ -314,11 +337,11 @@ class RoutingExample { void updateHighlightedRouteSections() { // Show the next 20 parcel pins, to let the delivery driver decide on possible detours. - int maxPins = 20; + int maxPins = 300; for (int i = 0; i < _parcelNumberPins.length; i++) { DestinationPin pin = _parcelNumberPins.elementAt(i); - if (i > routeSectionCursor && i < routeSectionCursor + maxPins) { + if (i > routeSectionCursor + 1 && i < routeSectionCursor + maxPins) { if (pin.pin != null) continue; var widgetPin = hereMapController.pinWidget( _createWidget(pin.text, Color.fromARGB(200, 0, 144, 138)), @@ -338,6 +361,14 @@ class RoutingExample { widgetPin?.anchor = Anchor2D.withHorizontalAndVertical(0.5, 0.5); pin.pin = widgetPin; } + + if (i == routeSectionCursor + 1) { + var widgetPin = hereMapController.pinWidget( + _createWidget(pin.text, ui.Color.fromARGB(197, 13, 36, 241)), + _destinationCoords[i]); + widgetPin?.anchor = Anchor2D.withHorizontalAndVertical(0.5, 0.5); + pin.pin = widgetPin; + } } // Show the next 5 sections as to not clutter the screen. diff --git a/lib/config/defaults.dart b/lib/config/defaults.dart index 58af533..e059169 100644 --- a/lib/config/defaults.dart +++ b/lib/config/defaults.dart @@ -1,5 +1,5 @@ -String program_version = '0.5'; -bool debug_output = false; +String program_version = '0.8'; +bool debug_output = true; class ShiftType { String name; diff --git a/lib/main.dart b/lib/main.dart index 66ad081..513be69 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:training_planner/services/iroute_provider_service.dart'; import 'package:training_planner/services/ishift_provider_service.dart'; import 'package:training_planner/services/istoregear_api_service.dart'; +import 'package:training_planner/services/local_salary_provider_service.dart'; import 'package:training_planner/services/log_service.dart'; import 'package:training_planner/services/messaging_service.dart'; import 'package:training_planner/services/mock_route_provider_service.dart'; @@ -58,6 +59,7 @@ final LocalAuthentication localAuthService = LocalAuthentication(); final MessagingService messageService = MessagingService(); final SettingsService settingsService = SettingsService(); final IStoregearApiService apiService = StoregearApiService(); +final LocalSalaryProviderService incomeProvider = LocalSalaryProviderService(); final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); diff --git a/lib/pages/developer_page.dart b/lib/pages/developer_page.dart index b724f8c..a90d9f5 100644 --- a/lib/pages/developer_page.dart +++ b/lib/pages/developer_page.dart @@ -57,6 +57,7 @@ class _DeveloperPageState extends State<DeveloperPage> { child: Column( children: [ Text('Versie ' + program_version), + /* TextButton( onPressed: () { if (canUseLocalAuth) { @@ -73,6 +74,7 @@ class _DeveloperPageState extends State<DeveloperPage> { } }, child: Text('Bestanden verwijderen')), + */ ], ), ), diff --git a/lib/pages/logbook_page.dart b/lib/pages/logbook_page.dart index 5449d8f..44d2644 100644 --- a/lib/pages/logbook_page.dart +++ b/lib/pages/logbook_page.dart @@ -1,8 +1,12 @@ +import 'dart:convert'; + import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; import 'package:in_date_utils/in_date_utils.dart' as DateUtilities; import 'package:loading_animation_widget/loading_animation_widget.dart'; import 'package:training_planner/main.dart'; +import 'package:training_planner/services/local_salary_provider_service.dart'; +import 'package:training_planner/services/log_service.dart'; import 'package:training_planner/shift.dart'; import 'package:training_planner/style/style.dart'; import 'package:training_planner/utils/date.dart'; @@ -21,6 +25,7 @@ class MonthData { List<Shift> shifts; Duration totalWorkedTime = Duration(); double expectedSalary = 0; + double actualSalary = 0; void calculateData() { totalWorkedTime = Duration(); @@ -34,10 +39,16 @@ class MonthData { MonthData({required this.firstDayOfMonth, required this.shifts}) { calculateData(); } + + double calculateHourlyRate() { + if (totalWorkedTime.inMinutes == 0) return 0; + return actualSalary / (totalWorkedTime.inMinutes / 60.0); + } } class _LogbookPageState extends State<LogbookPage> { List<MonthData>? months; + List<IncomeData>? income; void updateMonthData(MonthData month, Shift shift) { month.shifts.add(shift); @@ -71,55 +82,100 @@ class _LogbookPageState extends State<LogbookPage> { initState() { super.initState(); - shiftProvider.getPastShifts().then( - (value) => { - if (mounted) - setState( - () { - List<Shift> allShifts = value; - sortShifts(allShifts); - }, - ) + shiftProvider.getPastShifts().then((value) async { + income = await incomeProvider.getSavedIncome(); + if (mounted) { + setState( + () { + List<Shift> allShifts = value; + sortShifts(allShifts); }, ); + } + }); } List<Widget> createMonthDataWidgets() { List<Widget> result = []; for (var month in months!) { - result.add(Padding( - padding: const EdgeInsets.only(bottom: 8, left: 10, right: 10), - child: Container( - decoration: BoxDecoration( - border: Border.all(color: Style.logbookEntryBorder), - color: Style.logbookEntryBackground, - borderRadius: BorderRadius.all(Radius.circular(8))), - child: Padding( - padding: const EdgeInsets.all(8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - DateHelper.getMonthName(month.firstDayOfMonth.month) + - ' ' + - month.firstDayOfMonth.year.toString(), - style: TextStyle( - color: Colors.white, fontWeight: FontWeight.bold), - ), - Padding(padding: EdgeInsets.only(left: 5, bottom: 5, right: 5)), - Text('Gewerkt: ' + - month.totalWorkedTime.inHours.toString() + - ' uur'), - Text('Verdiend: €' + - month.expectedSalary.toStringAsFixed(2) + - ' (schatting)'), - Padding(padding: EdgeInsets.only(left: 5, bottom: 5, right: 5)), - ], + for (var inc in income!) { + if (inc.firstDayOfMonth == month.firstDayOfMonth) { + month.actualSalary = inc.income; + break; + } + } + + result.add( + Padding( + padding: const EdgeInsets.only(bottom: 8, left: 10, right: 10), + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Style.logbookEntryBorder), + color: Style.logbookEntryBackground, + borderRadius: BorderRadius.all(Radius.circular(4))), + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + DateHelper.getMonthName(month.firstDayOfMonth.month) + + ' ' + + month.firstDayOfMonth.year.toString(), + style: TextStyle( + color: Colors.black, fontWeight: FontWeight.bold), + ), + Padding( + padding: + EdgeInsets.only(left: 5, bottom: 5, right: 5)), + Text('Gewerkt: ' + + month.totalWorkedTime.inHours.toString() + + ' uur'), + Text('Verwacht: €' + + month.expectedSalary.toStringAsFixed(2)), + Padding( + padding: + EdgeInsets.only(left: 5, bottom: 5, right: 5)), + ], + ), + Padding(padding: EdgeInsets.all(10)), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '', + style: TextStyle( + color: Colors.black, fontWeight: FontWeight.bold), + ), + Padding( + padding: + EdgeInsets.only(left: 5, bottom: 5, right: 5)), + Text('Per uur: ' + + month.calculateHourlyRate().toStringAsFixed(2) + + ' uur'), + Text('Verdiend: €' + + month.actualSalary.toStringAsFixed(2)), + Padding( + padding: + EdgeInsets.only(left: 5, bottom: 5, right: 5)), + ], + ), + Expanded( + child: Text(''), + ), + OutlinedButton( + child: Icon(Icons.edit), + onPressed: () => {requestMonthInfo(month)}, + ), + ], + ), ), ), ), - )); + ); } return result; @@ -159,6 +215,21 @@ class _LogbookPageState extends State<LogbookPage> { ); } + Widget getData() { + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/logbookbg.jpg'), + fit: BoxFit.cover, + opacity: 0.3, + ), + ), + child: getLoadingScreenOrDataList(), + ); + } + Widget getLoadingScreenOrDataList() { if (months != null) { return getDataList(); @@ -189,7 +260,59 @@ class _LogbookPageState extends State<LogbookPage> { ).createShader(rect); }, blendMode: BlendMode.dstOut, - child: getLoadingScreenOrDataList(), + child: getData(), + ); + } + + Future requestMonthInfo(MonthData month) async { + TextEditingController controller = TextEditingController(); + controller.text = month.actualSalary.toString(); + + // set up the buttons + Widget cancelButton = TextButton( + child: Text("Terug"), + onPressed: () { + Navigator.pop(context); + }, + ); + Widget continueButton = TextButton( + child: Text("Opslaan"), + onPressed: () async { + month.actualSalary = double.parse(controller.text); + for (var m in months!) { + if (m.firstDayOfMonth == month.firstDayOfMonth) { + m.actualSalary = double.parse(controller.text); + } + } + await incomeProvider.writeSavedIncome(months!); + + setState(() {}); + Navigator.pop(context); + }, + ); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + title: Text("Inkomen invullen"), + actions: [ + TextField( + controller: controller, + decoration: const InputDecoration( + labelText: 'Inkomen', + ), + keyboardType: TextInputType.number, + ), + cancelButton, + continueButton, + ], + ); + + // show the dialog + await showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, ); } } diff --git a/lib/services/local_salary_provider_service.dart b/lib/services/local_salary_provider_service.dart new file mode 100644 index 0000000..5688ad3 --- /dev/null +++ b/lib/services/local_salary_provider_service.dart @@ -0,0 +1,72 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:path_provider/path_provider.dart'; +import 'package:training_planner/pages/logbook_page.dart'; +import 'package:training_planner/services/log_service.dart'; + +class IncomeData { + final DateTime firstDayOfMonth; + final double income; + + IncomeData(this.firstDayOfMonth, this.income); + + IncomeData.fromJson(Map<String, dynamic> json) + : firstDayOfMonth = DateTime.parse(json['firstDayOfMonth']), + income = double.parse(json['income']); + + Map<String, dynamic> toJson() { + return { + 'firstDayOfMonth': firstDayOfMonth.toIso8601String(), + 'income': income.toStringAsFixed(2), + }; + } +} + +class LocalSalaryProviderService { + Future<Directory> get _localDir async { + final directory = await getApplicationDocumentsDirectory(); + return directory; + } + + Future<String> get _localPath async { + final directory = await getApplicationDocumentsDirectory(); + return directory.path; + } + + Future<File> _localFile() async { + final path = await _localPath; + String fullPath = '$path/income.json'; + File file = File(fullPath); + + bool exists = await file.exists(); + if (!exists) { + LogService.log('creating ' + fullPath); + await file.create(); + await file.writeAsString(jsonEncode([])); + } + + return File(fullPath); + } + + Future<List<IncomeData>> getSavedIncome() async { + var file = await _localFile(); + var data = await file.readAsString(); + final Iterable iterable = await jsonDecode(data); + List<IncomeData> parsedData = List<IncomeData>.from( + iterable.map((model) => IncomeData.fromJson(model))); + LogService.log('read ' + data); + return parsedData; + } + + Future<void> writeSavedIncome(List<MonthData> data) async { + var file = await _localFile(); + + List<IncomeData> dataToStore = []; + for (var item in data) { + dataToStore.add(IncomeData(item.firstDayOfMonth, item.actualSalary)); + } + LogService.log('writing ' + jsonEncode(dataToStore)); + file.writeAsString(jsonEncode(dataToStore)); + } +} diff --git a/lib/services/storegear_api_service.dart b/lib/services/storegear_api_service.dart index b26f77b..b40626d 100644 --- a/lib/services/storegear_api_service.dart +++ b/lib/services/storegear_api_service.dart @@ -14,6 +14,8 @@ class StoregearApiService extends IStoregearApiService { @override Future<LoginResponse> login(LoginRequest req) async { + //return LoginResponse(); + final response = await http.post( Uri.parse('http://dhlapis.com/delivery/v1/users/login?env_type=PROD'), body: jsonEncode(req)); diff --git a/lib/style/style.dart b/lib/style/style.dart index 8d82565..4a932e1 100644 --- a/lib/style/style.dart +++ b/lib/style/style.dart @@ -6,11 +6,10 @@ class Style { static const TextStyle listItemTitletextBold = TextStyle(color: titleColor, fontSize: 16, fontWeight: FontWeight.bold); - static const Color listEntryBackground = background; + static const Color listEntryBackground = Color.fromARGB(180, 255, 204, 0); static const Color listEntryStandardColor = Colors.black; static const Color listEntryTransparentColor = Color.fromARGB(80, 0, 0, 0); - static const Color logbookEntryBorder = Color.fromARGB(255, 140, 140, 180); - static const Color logbookEntryBackground = - Color.fromARGB(255, 180, 180, 200); + static const Color logbookEntryBorder = Color.fromARGB(255, 255, 255, 255); + static const Color logbookEntryBackground = Color.fromARGB(94, 255, 204, 0); } diff --git a/lib/widgets/agenda_week.dart b/lib/widgets/agenda_week.dart index fa3d100..9f3df63 100644 --- a/lib/widgets/agenda_week.dart +++ b/lib/widgets/agenda_week.dart @@ -1,6 +1,8 @@ import 'dart:async'; +import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:loading_animation_widget/loading_animation_widget.dart'; import 'package:training_planner/events/RefreshWeekEvent.dart'; import 'package:training_planner/main.dart'; @@ -35,14 +37,21 @@ class _AgendaWeekState extends State<AgendaWeek> { weekItems = null; }); - var data = await shiftProvider.getShiftsForWeek(this.widget.mondayOfWeek); + var data = await shiftProvider.getShiftsForWeek(widget.mondayOfWeek); if (!mounted) return; setState(() { + Duration hoursWorked = Duration(); + for (var item in data) { + hoursWorked += item.getElapsedSessionTime(); + } + weekItems = [ AgendaWeekTitle( - weekNr: this.widget.weekNr, - mondayOfWeek: this.widget.mondayOfWeek, - isCurrentWeek: this.widget.isCurrentWeek), + weekNr: widget.weekNr, + mondayOfWeek: widget.mondayOfWeek, + isCurrentWeek: widget.isCurrentWeek, + hoursWorked: hoursWorked, + ), Padding( padding: const EdgeInsets.all(10), ) @@ -66,13 +75,22 @@ class _AgendaWeekState extends State<AgendaWeek> { } @override + void didChangeDependencies() { + super.didChangeDependencies(); + + for (var item in getBackgrounds()) { + precacheImage(AssetImage('assets/goals/' + item), context); + } + } + + @override void initState() { - super.initState(); updateItems(); eventbusSubscription = eventBus.on<RefreshWeekEvent>().listen((event) { updateItems(); }); + super.initState(); } @override @@ -107,9 +125,11 @@ class _AgendaWeekState extends State<AgendaWeek> { padding: const EdgeInsets.all(10), ), AgendaWeekTitle( - weekNr: this.widget.weekNr, - mondayOfWeek: this.widget.mondayOfWeek, - isCurrentWeek: this.widget.isCurrentWeek), + weekNr: widget.weekNr, + mondayOfWeek: widget.mondayOfWeek, + isCurrentWeek: widget.isCurrentWeek, + hoursWorked: Duration(), + ), Padding( padding: const EdgeInsets.all(10), ), @@ -122,6 +142,29 @@ class _AgendaWeekState extends State<AgendaWeek> { ); } + List<String> getBackgrounds() { + return ['1.png', '2.jpg', '3.jpeg', '4.jpg', '5.jpg']; + } + + String getBackgroundImage() { + var options = getBackgrounds(); + int nrToChoose = widget.weekNr % options.length; + return options[nrToChoose]; + } + + Widget getData() { + return Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/goals/' + getBackgroundImage()), + fit: BoxFit.cover, + opacity: 0.3, + ), + ), + child: getLoadingScreenOrDataList(), + ); + } + Widget getLoadingScreenOrDataList() { if (weekItems != null) { return getDataList(); @@ -152,7 +195,7 @@ class _AgendaWeekState extends State<AgendaWeek> { ).createShader(rect); }, blendMode: BlendMode.dstOut, - child: getLoadingScreenOrDataList(), + child: getData(), ); } } diff --git a/lib/widgets/agenda_week_title.dart b/lib/widgets/agenda_week_title.dart index 2c18986..b04c4d3 100644 --- a/lib/widgets/agenda_week_title.dart +++ b/lib/widgets/agenda_week_title.dart @@ -6,19 +6,27 @@ class AgendaWeekTitle extends StatefulWidget { final int weekNr; final DateTime mondayOfWeek; final bool isCurrentWeek; + final Duration hoursWorked; - const AgendaWeekTitle({ - Key? key, - required this.weekNr, - required this.mondayOfWeek, - required this.isCurrentWeek, - }) : super(key: key); + const AgendaWeekTitle( + {Key? key, + required this.weekNr, + required this.mondayOfWeek, + required this.isCurrentWeek, + required this.hoursWorked}) + : super(key: key); @override _AgendaWeekTitleState createState() => _AgendaWeekTitleState(); } class _AgendaWeekTitleState extends State<AgendaWeekTitle> { + String _printDuration(Duration duration) { + String twoDigits(int n) => n.toString().padLeft(2, "0"); + String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); + return "${twoDigits(duration.inHours)}h ${twoDigitMinutes}m"; + } + @override Widget build(BuildContext context) { return Column(children: [ @@ -38,7 +46,9 @@ class _AgendaWeekTitleState extends State<AgendaWeekTitle> { " " + DateHelper.getMonthName(this.widget.mondayOfWeek.month) + " " + - this.widget.mondayOfWeek.year.toString(), + this.widget.mondayOfWeek.year.toString() + + " | " + + _printDuration(widget.hoursWorked), style: TextStyle( fontWeight: FontWeight.bold, )), |
