import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:intl/intl.dart'; import 'package:syncfusion_flutter_charts/charts.dart'; import 'package:tasktracker/NewProject.dart'; import 'package:tasktracker/Projects.dart'; import 'DebugHelper.dart'; import 'User.dart' as User; import 'Dialogs.dart'; import 'Data.dart'; import 'main.dart'; class ProjectDetails extends StatefulWidget { ProjectDetails({Key? key, required this.project}) : super(key: key); Project project; @override State createState() => _ProjectDetailsState(project); } DateTime justDate(DateTime dateTime) { return DateTime(dateTime.year, dateTime.month, dateTime.day); } class _ProjectDetailsState extends State { _ProjectDetailsState(this.project); Project project; int etaHours = 0; int totalTimeSpent = 0; List timeSpentData = []; List timeProgressionData = []; List stepsProgressionData = []; String chartOption = 'Time Spent'; DateTimeRange? chartRange; @override Widget build(BuildContext context) { List stepsWidgets = printSteps(); etaHours = (project.steps.length > 0) ? 0 : project.eta ?? 0; totalTimeSpent = 0; project.steps.forEach((element) { etaHours += element.eta; }); int lastIndex = -1; int lastDay = -1; timeSpentData = []; timeProgressionData = []; DateTime? firstDateTime = null; DateTime? lastDateTime = null; User.activities.reversed.forEach((element) { bool withinRange = (chartRange == null) || (chartRange != null && chartRange!.start.isBefore(element.startTime) && chartRange!.end.isAfter(element.endTime)); if (element.taskType.relatedProject != null && element.taskType.relatedProject!.name == project.name) { // print('here'); if (firstDateTime == null) { firstDateTime = element.startTime; } lastDateTime = element.startTime; if (withinRange) { if (lastDay != element.startTime.day) { if (totalTimeSpent > 0) { timeProgressionData.add(ProjectChartData(justDate(element.startTime), totalTimeSpent)); } timeSpentData.add(ProjectChartData(justDate(element.startTime), element.endTime.difference(element.startTime).inMinutes)); lastIndex++; lastDay = element.startTime.day; print('new day : $lastDay'); } else { timeSpentData[lastIndex].timeSpent += element.endTime.difference(element.startTime).inMinutes; } } totalTimeSpent += element.endTime.difference(element.startTime).inMinutes; } }); chartRange ??= (lastDateTime != null && firstDateTime != null) ? DateTimeRange(end: lastDateTime!, start: firstDateTime!) : null; List steps = project.steps; steps.sort((a,b)=> (a.finishedDate??DateTime(0,0,0)).compareTo(b.finishedDate?? DateTime(0,0,0))); int stepsCompleted = 1; stepsProgressionData=[]; stepsProgressionData.add(ProjectChartData(timeProgressionData[0]!.day,0)); for(int i =0; i < steps.length; i++){ if(steps[i].finishedDate==null){continue;} if(steps[i].finishedDate!.isAfter(chartRange!.start) && steps[i].finishedDate!.isBefore(chartRange!.end)){ stepsCompleted+= ((steps[i].progress / 6000) * totalTimeSpent).toInt(); Debug.LogTest('${steps[i].stepName} : ${stepsCompleted} -> ${((steps[i].progress / 10000) * totalTimeSpent).toInt()}'); stepsProgressionData.add(ProjectChartData(steps[i].finishedDate!, stepsCompleted,metadata: steps[i].stepName)); } } return Scaffold( appBar: AppBar( title: Row( children: [ FaIcon(FontAwesomeIcons.projectDiagram), SizedBox( width: 15, ), Text(project.name), ], )), body: Padding( padding: const EdgeInsets.all(8.0), child: SingleChildScrollView( child: Column( children: [ Card( child: Padding( padding: EdgeInsets.all(8), child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ DropdownButton( value: chartOption, icon: const Icon(Icons.arrow_downward), elevation: 16, style: const TextStyle( color: Colors.white, ), alignment: Alignment.center, underline: Container( height: 2, color: Colors.deepPurpleAccent, ), onChanged: (String? newValue) { setState(() { chartOption = newValue!; }); }, items: ['Time Spent', 'Time Progression', 'Steps Progression', 'Time & Steps Progression'] .map>((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), ), Row( children: [ FaIcon(FontAwesomeIcons.calendar), SizedBox( width: 8, ), InkWell( onTap: () async { DateTimeRange? value = await showDateRangePicker( context: context, firstDate: firstDay ?? DateTime.now(), lastDate: lastDateTime ?? DateTime.now()); if (value != null) { chartRange = value; } setState(() {}); }, child: Text((chartRange != null) ? (DateFormat("MM/dd").format(chartRange!.start) + " - " + DateFormat("MM/dd").format(chartRange!.end)) : 'n/a'), ), ], ) ], ), ), SfCartesianChart( // Initialize category axis primaryXAxis: CategoryAxis(), series: >[ if (chartOption == 'Time Spent') LineSeries( // Bind data source dataSource: timeSpentData, xValueMapper: (ProjectChartData sales, _) => DateFormat("MM/dd").format(sales.day), yValueMapper: (ProjectChartData sales, _) => sales.timeSpent / 60, dataLabelMapper: (ProjectChartData sales, _) => MinutesToTimeString(sales.timeSpent), dataLabelSettings: DataLabelSettings(overflowMode: OverflowMode.hide, showZeroValue: false, isVisible: true), onPointTap: (ChartPointDetails point) { showAlertDialog(context, DateFormat("MM/dd").format(timeSpentData[point.pointIndex!].day), "I'll show you detailed info about this day in future, When my master creates the feature"); }, color: Colors.blue), if (chartOption == 'Time Progression' || chartOption == 'Time & Steps Progression') LineSeries( // Bind data source dataSource: timeProgressionData, xValueMapper: (ProjectChartData sales, _) => DateFormat("MM/dd").format(sales.day), yValueMapper: (ProjectChartData sales, _) => sales.timeSpent / 60, dataLabelMapper: (ProjectChartData sales, _) => MinutesToTimeString(sales.timeSpent), dataLabelSettings: DataLabelSettings(overflowMode: OverflowMode.hide, showZeroValue: false, isVisible: true), onPointTap: (ChartPointDetails point) { showAlertDialog(context, DateFormat("MM/dd").format(timeSpentData[point.pointIndex!].day), "I'll show you detailed info about this day in future, When my master creates the feature"); }, color: Colors.blue), if (chartOption == 'Steps Progression'|| chartOption == 'Time & Steps Progression') LineSeries( // Bind data source dataSource: stepsProgressionData, xValueMapper: (ProjectChartData sales, _) => DateFormat("MM/dd").format(sales.day), yValueMapper: (ProjectChartData sales, _) => sales.timeSpent, dataLabelMapper: (ProjectChartData sales, _) => sales.metadata, dataLabelSettings: DataLabelSettings(overflowMode: OverflowMode.hide, showZeroValue: false, isVisible: true), onPointTap: (ChartPointDetails point) { showAlertDialog(context, DateFormat("MM/dd").format(timeSpentData[point.pointIndex!].day), "I'll show you detailed info about this day in future, When my master creates the feature"); }, color: Colors.yellow) ]), ], ))), Card( child: Padding( padding: EdgeInsets.all(8), child: Column( children: [ Text("Time Management"), SizedBox( height: 10, ), Padding( padding: const EdgeInsets.all(5.0), child: Row( children: [ Expanded( flex: 1, child: Text( 'ETA :', textAlign: TextAlign.end, )), Expanded( flex: 3, child: Text( (etaHours > 0) ? '${etaHours} Hours' : 'You have no idea', textAlign: TextAlign.center, ), ) ], ), ), Padding( padding: const EdgeInsets.all(5.0), child: Row( children: [ Expanded( flex: 1, child: Text( 'Time Spent :', textAlign: TextAlign.end, )), Expanded( flex: 3, child: Text( (totalTimeSpent > 0) ? MinutesToTimeString(totalTimeSpent) : 'Not a single minute', textAlign: TextAlign.center, ), ) ], ), ), Padding( padding: const EdgeInsets.all(5.0), child: Row( children: [ Expanded( flex: 1, child: Text( 'Deadline :', textAlign: TextAlign.end, )), Expanded( flex: 3, child: Text( '${DateFormat('yyyy-MM-dd').format(project.deadline)}', textAlign: TextAlign.center, ), ) ], ), ), Padding( padding: const EdgeInsets.all(5.0), child: Row( children: [ Expanded( flex: 1, child: Text( 'Remaining :', textAlign: TextAlign.end, )), Expanded( flex: 3, child: Text( '${durationToDays(project.deadline.difference(DateTime.now()))}', textAlign: TextAlign.center, ), ) ], ), ), ], ), )), if (project.steps.length > 0) Card( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ Text( 'Steps', ), LimitedBox( maxHeight: 300, child: Container( padding: EdgeInsets.all(10), child: SingleChildScrollView( scrollDirection: Axis.vertical, child: (stepsWidgets.isNotEmpty) ? Container( child: Column( children: stepsWidgets, ), ) : Container(height: 20, child: Text('Click on + to add steps'))), ), ), Container( padding: EdgeInsets.symmetric(horizontal: 40, vertical: 10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: Colors.black26, ), child: Column( children: [ Text("Total time : $etaHours Hours"), ], ), ), ], ), ), ), Card( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ Text('Actions'), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ if(project.steps.length<=0)ElevatedButton( style: ElevatedButton.styleFrom( primary: Colors.green, ), onPressed: () { Dialogs.showQuestionDialog("WARNING!", 'You will lose all data about this project if you continue!\nAre you sure you want to delete?', onResponse: (value){ }); }, child: Row( children: [ FaIcon(FontAwesomeIcons.check), SizedBox( width: 10, ), Text('Complete'), ], )), ElevatedButton( onPressed: () { Navigator.of(context).push(MaterialPageRoute(builder: (context)=>NewProject(multiline: project.steps.isNotEmpty, projectName: project.name, selectedCategory: project.cat!.name,m_steps: project.steps,))); }, child: Row( children: [ FaIcon(FontAwesomeIcons.edit), SizedBox( width: 10, ), Text('Edit'), ], )), ElevatedButton( style: ElevatedButton.styleFrom( primary: Colors.red, ), onPressed: () { Dialogs.showQuestionDialog("WARNING!", 'You will lose all data about this project if you continue!\nAre you sure you want to delete?', onResponse: (value){ }); }, child: Row( children: [ FaIcon(FontAwesomeIcons.deleteLeft), SizedBox( width: 10, ), Text('Delete'), ], )) ], ), ], ), )) ], ), ), ), ); } List printSteps() { List _steps = []; //Checkbox(value: false, onChanged: null), // _steps.add(title); // _steps.add(Divider()); for (int i = 0; i < project.steps.length; i++) { ProjectStep value = project.steps[i]; _steps.add(Row( children: [ InkWell(onTap: ()async{ if(project.steps[i].finishedDate!= null){ await Dialogs.showQuestionDialog('Are you sure?', 'Do you want to undone following step?\n\n${project.steps[i].stepName}', onResponse: (value)async{ if(value==true){ await User.UserOperations.UndoProjectStep(project, project.steps[i]); } }); setState(() { }); }else { await Dialogs.completeStepDialog(project, (date)async { await User.UserOperations.CompleteProjectStep(project, project.steps[i], date); }, project.steps[i]); setState(() { }); } },child: Container(margin:EdgeInsets.all(2),child: Icon((project.steps[i].finishedDate!= null) ?Icons.check_box : Icons.check_box_outline_blank))), Expanded( child: Container( height: 34, decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), color: Colors.black26), padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded(flex: 4, child: Text(value.stepName)), // Expanded(flex:1,child: Icon(Icons.timelapse)), Expanded(flex: 1, child: Text("${value.progress}%")), Expanded( flex: 3, child: Text( value.eta.toString() + " Hours", textAlign: TextAlign.end, )) ], )), ), ], )); } return _steps; } } class ProjectChartData { ProjectChartData(this.day, this.timeSpent,{this.metadata}); DateTime day; int timeSpent; String? metadata; }