Files
TaskTracker/lib/ProjectDetails.dart
2022-04-22 16:49:15 +05:30

494 lines
22 KiB
Dart

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<ProjectDetails> createState() => _ProjectDetailsState(project);
}
DateTime justDate(DateTime dateTime) {
return DateTime(dateTime.year, dateTime.month, dateTime.day);
}
class _ProjectDetailsState extends State<ProjectDetails> {
_ProjectDetailsState(this.project);
Project project;
int etaHours = 0;
int totalTimeSpent = 0;
List<ProjectChartData> timeSpentData = [];
List<ProjectChartData> timeProgressionData = [];
List<ProjectChartData> stepsProgressionData = [];
String chartOption = 'Time Spent';
DateTimeRange? chartRange;
@override
Widget build(BuildContext context) {
List<Widget> 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<ProjectStep> 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<String>(
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: <String>['Time Spent', 'Time Progression', 'Steps Progression', 'Time & Steps Progression']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
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: <LineSeries<ProjectChartData, String>>[
if (chartOption == 'Time Spent')
LineSeries<ProjectChartData, String>(
// 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<ProjectChartData, String>(
// 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<ProjectChartData, String>(
// 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<Widget> printSteps() {
List<Widget> _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;
}