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'; import 'package:training_planner/widgets/agenda_week.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; class LogbookPage extends StatefulWidget { @override _LogbookPageState createState() => _LogbookPageState(); const LogbookPage({Key? key}) : super(key: key); } class MonthData { DateTime firstDayOfMonth; List shifts; Duration totalWorkedTime = Duration(); double expectedSalary = 0; double actualSalary = 0; void calculateData() { totalWorkedTime = Duration(); expectedSalary = 0; for (var shift in shifts) { totalWorkedTime += shift.getElapsedSessionTime(); expectedSalary += shift.getEarnedMoney(); } } 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 { List? months; List? income; void updateMonthData(MonthData month, Shift shift) { month.shifts.add(shift); month.calculateData(); } void sortShifts(List shifts) { months = []; for (var shift in shifts) { DateTime firstDayOfMonth = DateUtilities.DateUtils.firstDayOfMonth(shift.start); bool found = false; for (var month in months!) { if (month.firstDayOfMonth == firstDayOfMonth) { updateMonthData(month, shift); found = true; } } if (!found) { months! .add(MonthData(firstDayOfMonth: firstDayOfMonth, shifts: [shift])); } } months!.sort((a, b) => b.firstDayOfMonth.compareTo(a.firstDayOfMonth)); } @override initState() { super.initState(); shiftProvider.getPastShifts().then((value) async { income = await incomeProvider.getSavedIncome(); if (mounted) { setState( () { List allShifts = value; sortShifts(allShifts); }, ); } }); } List createMonthDataWidgets() { List result = []; for (var month in months!) { 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; } Widget getDataList() { var monthDataWidgets = createMonthDataWidgets(); if (monthDataWidgets.isEmpty) { return Center( child: Text('Geen data beschikbaar'), ); } return SafeArea( child: CustomScrollView( physics: null, slivers: [ SliverPadding(padding: EdgeInsets.only(top: 20)), SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return monthDataWidgets[index]; }, childCount: monthDataWidgets.length, )), SliverPadding(padding: EdgeInsets.only(top: 20)), ], ), ); } Widget getLoadingScreen() { return LoadingAnimationWidget.flickr( leftDotColor: Style.titleColor, rightDotColor: Style.background, size: MediaQuery.of(context).size.width / 4, ); } 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(); } else { return getLoadingScreen(); } } @override Widget build(BuildContext context) { return ShaderMask( shaderCallback: (Rect rect) { return LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Style.background, Colors.transparent, Colors.transparent, Style.background ], stops: [ 0.0, 0.05, 0.95, 1.0 ], // 10% purple, 80% transparent, 10% purple ).createShader(rect); }, blendMode: BlendMode.dstOut, 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; }, ); } }