Project Steps completion

This commit is contained in:
Sewmina
2022-03-28 07:02:55 +05:30
parent bfe3c7fa5d
commit 0763820b97
4 changed files with 468 additions and 169 deletions

View File

@@ -127,17 +127,19 @@ class Project{
class ProjectStep{ class ProjectStep{
ProjectStep(this.stepName,this.eta,this.progress); ProjectStep(this.stepName,this.eta,this.progress);
ProjectStep.fromJson(Map<String,dynamic> json): stepName=json['name'], eta=json['eta'], progress=json['progress']; ProjectStep.fromJson(Map<String,dynamic> json): stepName=json['name'], eta=json['eta'], progress=json['progress'], finishedDate=(json['finishedDate']!=null) ? DateTime.parse(json['finishedDate']) : null;
Map<String, dynamic> toJson()=>{ Map<String, dynamic> toJson()=>{
'name': stepName, 'name': stepName,
'eta':eta, 'eta':eta,
'progress':progress 'progress':progress,
if(finishedDate!=null) 'finishedDate':finishedDate.toString()
}; };
String stepName; String stepName;
int eta; int eta;
int progress; int progress;
DateTime? finishedDate;
} }

View File

@@ -1,5 +1,6 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@@ -186,6 +187,81 @@ class Dialogs{
} }
} }
static completeStepDialog(Project project,Function(DateTime) onComplete, ProjectStep step) async{
context=navigatorKey.currentContext;
DateTime finishedDate = DateTime.now();
DateFormat dateFormat = DateFormat('yyyy-MM-dd');
if(context!=null) {
// return StatefulBuilder(builder: (context, setState) {
return showDialog(
context: context!,
builder: (BuildContext context) {
return StatefulBuilder(builder: (context, setState)
{
return AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
backgroundColor: Color(0xFF1C1C1C),
title: Text('Completed ${step.stepName}?'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('When did you complete?'),
SizedBox(height: 20,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
DatePicker.showDatePicker(context,
showTitleActions: true,
theme: DatePickerTheme(), onChanged: (date) {
// print('change $date');
}, onConfirm: (date) {
setState(() {
finishedDate = date;
});
}, currentTime: finishedDate, locale: LocaleType.en);
setState(() {});
},
child: Container(
decoration: BoxDecoration(
color: Colors.black26,
borderRadius: BorderRadius.circular(15)
),
padding: EdgeInsets.symmetric(horizontal: 25, vertical: 10),
child: Text((dateFormat.format(finishedDate) ==dateFormat.format(DateTime.now())) ? 'Today' : dateFormat.format(finishedDate), style: TextStyle(color: Colors.blue),),
),
),
],
)
],
),
actions: [
MaterialButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('No', style: TextStyle(color: Colors.red)),
),
MaterialButton(
color: Colors.green,
onPressed: () {
User.UserOperations.CompleteProjectStep(project, step, finishedDate);
Navigator.of(context).pop();
},
child: Text('Complete', style: TextStyle(color: Colors.white)),
),
],
);
});
}
);
}else{
print('context is null');
}
}
static hide(){ static hide(){
showing=false; showing=false;
Navigator.of(navigatorKey.currentContext!).popUntil((route){ Navigator.of(navigatorKey.currentContext!).popUntil((route){

View File

@@ -1,10 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:tasktracker/NewProject.dart';
import 'package:tasktracker/Projects.dart'; import 'package:tasktracker/Projects.dart';
import 'User.dart' as User; import 'User.dart' as User;
import 'Dialogs.dart';
import 'Data.dart'; import 'Data.dart';
import 'main.dart';
class ProjectDetails extends StatefulWidget { class ProjectDetails extends StatefulWidget {
ProjectDetails({Key? key, required this.project}) : super(key: key); ProjectDetails({Key? key, required this.project}) : super(key: key);
@@ -18,61 +22,193 @@ DateTime justDate(DateTime dateTime){
} }
class _ProjectDetailsState extends State<ProjectDetails> { class _ProjectDetailsState extends State<ProjectDetails> {
_ProjectDetailsState(this.project); _ProjectDetailsState(this.project);
Project project; Project project;
int etaHours = 0; int etaHours = 0;
List<ProjectChartData> chartData = []; int totalTimeSpent = 0;
List<ProjectChartData> timeSpentData = [];
List<ProjectChartData> timeProgressionData = [];
String chartOption = 'Time Spent';
DateTimeRange? chartRange;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
List<Widget> stepsWidgets = printSteps(); List<Widget> stepsWidgets = printSteps();
etaHours =0; etaHours = (project.steps.length > 0) ? 0 : project.eta ?? 0;
totalTimeSpent = 0;
project.steps.forEach((element) { project.steps.forEach((element) {
etaHours += element.eta; etaHours += element.eta;
}); });
int lastIndex = -1; int lastIndex = -1;
int lastDay = -1; int lastDay = -1;
User.activities.forEach((element) { timeSpentData = [];
if(element.taskType.relatedProject == project){ 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 (withinRange) {
if (firstDateTime == null) {
firstDateTime = element.startTime;
}
lastDateTime = element.startTime;
if (lastDay != element.startTime.day) { if (lastDay != element.startTime.day) {
chartData.add(ProjectChartData(justDate(element.startTime), element.endTime.difference(element.startTime).inMinutes)); if (totalTimeSpent > 0) {
timeProgressionData.add(ProjectChartData(justDate(element.startTime), totalTimeSpent));
}
timeSpentData.add(ProjectChartData(justDate(element.startTime), element.endTime.difference(element.startTime).inMinutes));
lastIndex++; lastIndex++;
lastDay = element.startTime.day; lastDay = element.startTime.day;
print('new day : $lastDay');
} else { } else {
chartData[lastIndex].timeSpent += element.endTime.difference(element.startTime).inMinutes; 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;
return Scaffold( return Scaffold(
appBar: AppBar(title: Row( appBar: AppBar(
title: Row(
children: [ children: [
FaIcon(FontAwesomeIcons.projectDiagram), FaIcon(FontAwesomeIcons.projectDiagram),
SizedBox(width: 15,), SizedBox(
width: 15,
),
Text(project.name), Text(project.name),
], ],
)), )),
body: Padding( body: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
Card( 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,
), ),
Card(child: Padding( 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')
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)
]),
],
))),
Card(
child: Padding(
padding: EdgeInsets.all(8), padding: EdgeInsets.all(8),
child: Column( child: Column(
children: [ children: [
Text("Time Management"), Text("Time Management"),
SizedBox(height: 10,), SizedBox(
height: 10,
),
Padding( Padding(
padding: const EdgeInsets.all(5.0), padding: const EdgeInsets.all(5.0),
child: Row( child: Row(
children: [ children: [
Expanded(flex:1,child: Text('ETA :',textAlign: TextAlign.end,)), Expanded(
Expanded(flex: 3,child: Text( '${etaHours} Hours',textAlign: TextAlign.center,),) flex: 1,
child: Text(
'ETA :',
textAlign: TextAlign.end,
)),
Expanded(
flex: 3,
child: Text(
(etaHours > 0) ? '${etaHours} Hours' : 'You have no idea',
textAlign: TextAlign.center,
),
)
], ],
), ),
), ),
@@ -80,8 +216,19 @@ class _ProjectDetailsState extends State<ProjectDetails> {
padding: const EdgeInsets.all(5.0), padding: const EdgeInsets.all(5.0),
child: Row( child: Row(
children: [ children: [
Expanded(flex:1,child: Text('Deadline :',textAlign: TextAlign.end,)), Expanded(
Expanded(flex: 3,child: Text( '${DateFormat('yyyy-MM-dd').format(project.deadline)}',textAlign: TextAlign.center,),) 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,
),
)
], ],
), ),
), ),
@@ -89,36 +236,67 @@ class _ProjectDetailsState extends State<ProjectDetails> {
padding: const EdgeInsets.all(5.0), padding: const EdgeInsets.all(5.0),
child: Row( child: Row(
children: [ children: [
Expanded(flex:1,child: Text('Remaining :',textAlign: TextAlign.end,)), Expanded(
Expanded(flex: 3,child: Text( '${durationToDays(project.deadline.difference(DateTime.now()))}',textAlign: TextAlign.center,),) 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,
),
)
], ],
), ),
), ),
], ],
), ),
)), )),
Card(child:Padding( if (project.steps.length > 0)
Card(
child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
children: [ children: [
Text('Steps',), Text(
'Steps',
),
LimitedBox( LimitedBox(
maxHeight: 300, maxHeight: 300,
child: Container( child: Container(
padding: EdgeInsets.all(10), padding: EdgeInsets.all(10),
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
child: (stepsWidgets.isNotEmpty) ? Container( child: (stepsWidgets.isNotEmpty)
? Container(
child: Column( child: Column(
children: stepsWidgets, children: stepsWidgets,
), ),
) : Container( )
: Container(height: 20, child: Text('Click on + to add steps'))),
height: 20,
child: Text('Click on + to add steps')
)),
), ),
), ),
Container( Container(
@@ -135,9 +313,10 @@ class _ProjectDetailsState extends State<ProjectDetails> {
), ),
], ],
), ),
),), ),
),
Card(child:Padding( Card(
child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
children: [ children: [
@@ -145,19 +324,28 @@ class _ProjectDetailsState extends State<ProjectDetails> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
ElevatedButton(onPressed: (){}, child: Row( ElevatedButton(
onPressed: () {},
child: Row(
children: [ children: [
FaIcon(FontAwesomeIcons.edit), FaIcon(FontAwesomeIcons.edit),
SizedBox(width: 10,), SizedBox(
width: 10,
),
Text('Edit'), Text('Edit'),
], ],
)), )),
ElevatedButton(style:ElevatedButton.styleFrom( ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.red, primary: Colors.red,
),onPressed: (){}, child: Row( ),
onPressed: () {},
child: Row(
children: [ children: [
FaIcon(FontAwesomeIcons.deleteLeft), FaIcon(FontAwesomeIcons.deleteLeft),
SizedBox(width: 10,), SizedBox(
width: 10,
),
Text('Delete'), Text('Delete'),
], ],
)) ))
@@ -169,36 +357,27 @@ class _ProjectDetailsState extends State<ProjectDetails> {
], ],
), ),
), ),
),
); );
} }
List<Widget> printSteps() { List<Widget> printSteps() {
List<Widget> _steps = []; List<Widget> _steps = [];
//Checkbox(value: false, onChanged: null),
Widget title=Container(
height: 30,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: Colors.white10),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(flex: 4, child: Text('Name')),
// Expanded(flex:1,child: Icon(Icons.timelapse)),
Expanded(flex: 2, child: Text("Progress")),
Expanded(
flex: 3,
child: Text('ETA',textAlign: TextAlign.end,))
],
));
// _steps.add(title); // _steps.add(title);
// _steps.add(Divider()); // _steps.add(Divider());
for (int i = 0; i < project.steps.length; i++) { for (int i = 0; i < project.steps.length; i++) {
ProjectStep value = project.steps[i]; ProjectStep value = project.steps[i];
_steps.add(InkWell( _steps.add(Row(
children: [
InkWell(onTap: (){
Dialogs.completeStepDialog(project,(date) {
User.UserOperations.CompleteProjectStep(project, project.steps[i], date);
}, project.steps[i]);
},child: Container(margin:EdgeInsets.all(2),child: Icon((project.steps[i].finishedDate!= null) ?Icons.check_box : Icons.check_box_outline_blank))),
Expanded(
child: Container( child: Container(
height: 30, height: 34,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), color: Colors.black26), decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), color: Colors.black26),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2), padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
child: Row( child: Row(
@@ -215,16 +394,17 @@ class _ProjectDetailsState extends State<ProjectDetails> {
textAlign: TextAlign.end, textAlign: TextAlign.end,
)) ))
], ],
)))); )),
),
],
));
} }
return _steps; return _steps;
} }
} }
class ProjectChartData { class ProjectChartData {
ProjectChartData(this.day, this.timeSpent); ProjectChartData(this.day, this.timeSpent);
DateTime day; DateTime day;

View File

@@ -649,14 +649,14 @@ Future<List<Project>> GetProjects(bool forceOffline) async {
} }
List<Map> cats = await cacheDb.rawQuery('SELECT * FROM Projects'); List<Map> cats = await cacheDb.rawQuery('SELECT * FROM Projects');
print(cats.length); print('projects : ${cats.length}');
for (Map element in cats) { for (Map element in cats) {
String? name = element[Project.colName]; String? name = element[Project.colName];
String? category = element[Project.colCat]; String? category = element[Project.colCat];
String? stepsJson = element[Project.colSteps]; String? stepsJson = element[Project.colSteps];
String? deadline = element[Project.colDeadline]; String? deadline = element[Project.colDeadline];
int? eta= element[Project.colEta]; int? eta= element[Project.colEta];
print(name);
if (name == null || category == null || stepsJson == null || deadline == null || eta==null) { if (name == null || category == null || stepsJson == null || deadline == null || eta==null) {
print("Something is null!\nname:${name == null}, cat:${category == null}, steps:${stepsJson == null}, deadline${deadline == null}"); print("Something is null!\nname:${name == null}, cat:${category == null}, steps:${stepsJson == null}, deadline${deadline == null}");
@@ -1139,6 +1139,47 @@ class UserOperations {
await executeQueries(); await executeQueries();
} }
static Future<void> CompleteProjectStep(Project project, ProjectStep step, DateTime finishedDate) async {
project.steps.forEach((element) {
if(element.stepName == step.stepName){
element.finishedDate = finishedDate;
}
});
Map<String, String> queryBody = <String, String>{
'name': username+project.name,
'username': username,
'steps': jsonEncode(project.steps),
};
if (cacheEnabled) {
//Add Query
Map<String, Object> query = {Queries.colLink: 'update_projectSteps', Queries.colData: jsonEncode(queryBody)};
print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}");
await cacheDb.insert('Queries', query);
await cacheDb.rawUpdate("UPDATE Projects SET steps='${jsonEncode(project.steps)}' WHERE ${Project.colName}='${project.name}'");
//update Cache
// Map<String, Object> data = {Project.colName: username+name, Project.colCat: category, Project.colSteps: jsonEncode(steps),Project.colEta: eta, Project.colDeadline: deadline.toString()};
// await cacheDb.insert('Projects', data);
await refreshUserData(forceOffline: true);
} else {
try {
http.Response queryResponse = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/update_projectSteps.php'), body: queryBody));
print("Query executed : Results{${queryResponse.body}");
if (queryResponse.body.toLowerCase().contains("success")) {
//Success
}
} catch (e) {
print('NC: Error completing prjctStep $e}');
}
}
await executeQueries();
}
static Future<void> deleteTask(String name, {bulk = false}) async { static Future<void> deleteTask(String name, {bulk = false}) async {
Map<String, String> queryBody = <String, String>{ Map<String, String> queryBody = <String, String>{
'id': username + name, 'id': username + name,