Projects Details (error)

This commit is contained in:
Sewmina
2022-03-27 21:16:29 +05:30
parent f64ac7efbd
commit 2f280f1795
11 changed files with 429 additions and 55 deletions

BIN
images/project.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -78,30 +78,37 @@ class _ActivitiesState extends State<Activities> {
label: Text("New Activity"),
icon: Icon(Icons.add)),
appBar: AppBar(
toolbarHeight: (searching) ? 90 : null,
title: (searching)
? Row(
? Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: TextField(onChanged: (text){setState(() {
children: [
Expanded(
child: TextField(onChanged: (text){setState(() {
});},controller: searchController, focusNode: _focus, decoration: InputDecoration(
filled: true,
),),
});},controller: searchController, focusNode: _focus, decoration: InputDecoration(
filled: true,
),),
),
InkWell(
onTap: (){searching=false;
searchController.clear();
setState(() {
});},
child: Container(
margin: EdgeInsets.all( 10),
child: Icon(Icons.cancel),
),
)
],
),
InkWell(
onTap: (){searching=false;
searchController.clear();
setState(() {
});},
child: Container(
margin: EdgeInsets.all( 10),
child: Icon(Icons.cancel),
),
)
],
)
Text('searched time : ${Main.MinutesToTimeString(searchTime)}',style: TextStyle(fontSize: 15),)
],
)
: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -175,7 +182,7 @@ class _ActivitiesState extends State<Activities> {
));
}
int searchTime = 0;
List<Widget> PrintTasks() {
List<Widget> _tasks = [];
print('Priting cats : ' + User.taskTypes.length.toString());
@@ -211,6 +218,7 @@ class _ActivitiesState extends State<Activities> {
}
print(productivtyActs);
//for (var element in User.activities) {
searchTime=0;
for(int i =0; i < User.activities.length; i++){
Activity element =User.activities[i];
if(searching){
@@ -219,6 +227,7 @@ class _ActivitiesState extends State<Activities> {
bool matchCategory = element.taskType.cat!.name.toLowerCase().contains(searchController.text.toLowerCase());
if(matchMetadata || matchTaskType || matchCategory){
//Good to go
searchTime += element.endTime.difference(element.startTime).inMinutes;
}else{
continue;
}
@@ -464,10 +473,19 @@ class _ActivitiesState extends State<Activities> {
SizedBox(
width: 20,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: (productive) ? Colors.green : Colors.red),
child: Text(activity.taskType.cat?.name ?? 'n/a'))
Row(
children: [
(activity.taskType.relatedProject!= null) ?Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: Colors.black26),
child: Text(activity.taskType.relatedProject!.name ?? 'n/a')) : Container(),
SizedBox(width: 10,),
Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: (productive) ? Colors.green : Colors.red),
child: Text(activity.taskType.cat?.name ?? 'n/a')),
],
)
// Icon(Icons.circle,
// color: (productive)
// ? Colors.green

View File

@@ -103,7 +103,7 @@ class HourglassPainter extends CustomPainter{
..strokeCap=StrokeCap.round
..strokeWidth=1;
print('bottom: $bottomStartHeight, top: $bottomEndHeight');
// print('bottom: $bottomStartHeight, top: $bottomEndHeight');
canvas.drawPath(fallingSand, contentPainter);
canvas.drawPath(topContent, contentPainter);

View File

@@ -225,6 +225,7 @@ class _NewProject2State extends State<NewProject2> {
Widget build(BuildContext context) {
List<Widget> stepsWidgets = printSteps();
etaHours =0;
steps.forEach((element) {
etaHours+=element.eta;
});

View File

@@ -3,16 +3,19 @@ import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:intl/intl.dart';
import 'package:tasktracker/NewCategory.dart';
import 'package:tasktracker/NewProject.dart';
import 'Data.dart';
import 'User.dart' as User;
DateFormat dateFormat = DateFormat("yyyy-MM-dd HH:mm:ss");
DateFormat durationFormat = DateFormat("HH:mm:ss");
class NewTask extends StatefulWidget {
const NewTask({Key? key}) : super(key: key);
NewTask({Key? key, this.t_cat, this.t_name, this.t_relProj}) : super(key: key);
late String? t_name;
late String? t_cat;
late String? t_relProj;
@override
_NewTaskState createState() => _NewTaskState();
_NewTaskState createState() => _NewTaskState(t_cat: t_cat, t_name: t_name, t_relProj: t_relProj);
}
List<String> getCategoryNames(){
@@ -39,6 +42,19 @@ List<String> getProjectNames(){
String selectedCat = User.categories[0].name;
String selectedProj = "None";
class _NewTaskState extends State<NewTask> {
bool editing = false;
String? oldName;
_NewTaskState({String? t_name, String? t_cat, String? t_relProj}){
if(t_name !=null && t_cat != null){
editing = true;
oldName = t_name;
}
nameController.text = t_name ?? '';
selectedCat = t_cat ?? User.categories[0].name;
selectedProj = t_relProj ?? 'None';
}
TextEditingController nameController = TextEditingController();
bool productive = true;
@@ -46,7 +62,7 @@ class _NewTaskState extends State<NewTask> {
Widget build(BuildContext context) {
List<String> cats = getCategoryNames();
return Scaffold(
appBar: AppBar(title: Text('New Task Type')),
appBar: AppBar(title: Text('${(editing) ? 'Edit ' : 'New '} Task Type')),
body: Container(
height: MediaQuery.of(context).size.height,
child: Column(
@@ -99,7 +115,7 @@ class _NewTaskState extends State<NewTask> {
child: Text(value),
);
}).toList(),
onChanged: (String? _value) {
onChanged:(selectedProj == 'None') ? (String? _value) {
if(_value != null) {
if (_value.contains("+Add New Category")) {
Navigator.of(context).push(MaterialPageRoute(builder: (context)=>NewCategory()));
@@ -110,7 +126,7 @@ class _NewTaskState extends State<NewTask> {
setState(() {
});
})),
} : null)),
Padding(
padding: const EdgeInsets.fromLTRB(0, 20, 0, 10),
child: Text('Related Project'),
@@ -138,7 +154,7 @@ class _NewTaskState extends State<NewTask> {
child: Text(value),
);
}).toList(),
onChanged: (String? _value) {
onChanged: (String? _value) async{
if(_value != null) {
if (_value.contains("+Add New Project")) {
Navigator.of(context).push(MaterialPageRoute(builder: (context)=>NewProject()));
@@ -146,6 +162,13 @@ class _NewTaskState extends State<NewTask> {
selectedProj = _value!;
if(_value.contains("None")){
}else{
Project? relProj = await User.getProjectFromId(selectedProj);
if(relProj==null){
}else{
selectedCat = relProj.cat!.name;
}
}
}
}
@@ -195,10 +218,14 @@ class _NewTaskState extends State<NewTask> {
),
onPressed: () {
setState(() {
add_action();
if(editing){
edit_action();
}else {
add_action();
}
});
},
child: Text('Add Task Type',
child: Text((editing) ? 'Apply' : 'Add Task Type',
style: TextStyle(fontSize: 20))))),
],
))
@@ -217,6 +244,19 @@ class _NewTaskState extends State<NewTask> {
return route.isFirst;
});
}
void edit_action() async{
String catName = nameController.value.text;
print('editing Task Type $oldName => : $catName, $selectedCat');
if(catName.length< 2){
showAlertDialog(context, 'Category needs a name', 'Please enter a name for this category');
return;
}
await User.UserOperations.editTaskType(oldName! ,catName,selectedCat,relatedProject: (selectedProj == 'None') ? '' : selectedProj);
Navigator.of(context).popUntil((route){
return route.isFirst;
});
}
}
String _printDuration(Duration duration) {

142
lib/ProjectDetails.dart Normal file
View File

@@ -0,0 +1,142 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'Data.dart';
class ProjectDetails extends StatefulWidget {
const ProjectDetails({Key? key}) : super(key: key);
@override
State<ProjectDetails> createState() => _ProjectDetailsState();
}
class _ProjectDetailsState extends State<ProjectDetails> {
@override
Widget build(BuildContext context) {
List<Widget> stepsWidgets = printSteps();
etaHours =0;
steps.forEach((element) {
etaHours+=element.eta;
});
return Scaffold(
appBar: AppBar(title: Row(
children: [
FaIcon(FontAwesomeIcons.projectDiagram),
SizedBox(width: 15,),
Text('This app'),
],
)),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Card(child:LimitedBox(
maxHeight: 300,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.white10,
),
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')
)),
),
),),
Card(child:Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text('Actions'),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(onPressed: (){}, child: Row(
children: [
FaIcon(FontAwesomeIcons.edit),
SizedBox(width: 10,),
Text('Edit'),
],
)),
ElevatedButton(style:ElevatedButton.styleFrom(
primary: Colors.red,
),onPressed: (){}, child: Row(
children: [
FaIcon(FontAwesomeIcons.deleteLeft),
SizedBox(width: 10,),
Text('Delete'),
],
))
],
),
],
),
))
],
),
) ,
);
}
List<Widget> printSteps() {
List<Widget> _steps = [];
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(Divider());
for (int i = 0; i < steps.length; i++) {
ProjectStep value = steps[i];
_steps.add(InkWell(
child: Container(
height: 30,
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;
}
}

View File

@@ -1,8 +1,11 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
import 'package:tasktracker/Data.dart';
import 'package:tasktracker/NewProject.dart';
import 'package:tasktracker/ProjectDetails.dart';
import 'Dialogs.dart';
import 'User.dart' as User;
import 'main.dart';
@@ -53,6 +56,7 @@ class _ProjectsState extends State<Projects> {
BottomNavigationBarItem(icon: FaIcon(FontAwesomeIcons.newspaper),label:'Summary'),
BottomNavigationBarItem(icon: FaIcon(FontAwesomeIcons.info),label:'Details'),
],
currentIndex:selectedPage,
onTap: (val){
selectedPage= val;
setState(() {
@@ -94,13 +98,97 @@ class _ProjectsState extends State<Projects> {
],
)),
drawer: navDrawer(context, 7),
body: (selectedPage == 0) ?
Container(child: Column(
body:
(User.projects.isEmpty)? Container(child: Image.asset(('images/project.png'),color: Colors.white.withOpacity(0.6), colorBlendMode: BlendMode.modulate,)) :
(selectedPage == 0) ?
Container(
padding: EdgeInsets.all(15),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Card(child:
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
FaIcon(FontAwesomeIcons.cogs, color: Colors.yellow),
SizedBox(width: 15,),
Text('Active Projects (${User.projects.length})',style:TextStyle(fontSize: 18)),
],
),
Divider(),
Container(
height: 100,
child: ListView.builder(
itemCount: User.projects.length,
itemBuilder: (context,index){
return InkWell(
onTap: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context)=> ProjectDetails()));
},
child: Container(
decoration: BoxDecoration(color: Colors.black26, borderRadius: BorderRadius.circular(10)),
padding:EdgeInsets.all(8),
margin: EdgeInsets.all(1),
child:
Row(mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(flex:3,child: Text(User.projects[index].name)),
Expanded(flex:2,child: Text('20% [200h]')),
Expanded(
flex:2,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: User.projects[index].cat!.productive ? Colors.green : Colors.redAccent,
),
padding:EdgeInsets.symmetric(horizontal: 8),
child:Text(User.projects[index].cat!.name)
)
],),
)
],
)
),
);
}
),
)
],
),
)),
SizedBox(height: 30,),
Card(child:
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
FaIcon(FontAwesomeIcons.check,color: Colors.green,),
SizedBox(width: 15,),
Text('Finished Projects (0)',style:TextStyle(fontSize: 18)),
],
),
Divider(),
Container(
height: 50,
)
],
),
)),
],
))
:Container(
:
Container(
padding: EdgeInsets.all(10),
child: Column(
children: [

View File

@@ -167,6 +167,7 @@ class _TasksState extends State<Tasks> {
children: [
(relatedProjects.isNotEmpty)
? Container(
margin: EdgeInsets.symmetric(horizontal: 5),
padding: EdgeInsets.symmetric(horizontal: 10),
decoration:
BoxDecoration(borderRadius: BorderRadius.circular(10), color: Colors.black26),
@@ -185,6 +186,18 @@ class _TasksState extends State<Tasks> {
Container(margin: EdgeInsets.fromLTRB(15, 0, 15, 10), height: 2, color: color)
]),
),
(selecting)
? InkWell(
child:Container(margin:EdgeInsets.all(8),child: Icon(Icons.edit)),
onTap: () {
print('edit $name : $catName : $relatedProjects');
Navigator.of(context).push(MaterialPageRoute(builder: (context)=> NewTask(t_name: name, t_cat: catName, t_relProj: (relatedProjects.isEmpty)? null : relatedProjects,))).then((value){setState(() {
});});
})
: Container(),
]);
}

View File

@@ -898,6 +898,43 @@ class UserOperations {
}
}
static Future<void> editTaskType(String oldName, String name, String category, {bool bulk = false, String? relatedProject = null}) async {
Map<String, String> queryBody = <String, String>{
'id': username + oldName,
'username': username,
'name': name,
'category': username + category,
'related_project' :(relatedProject==null) ? '' : (username + relatedProject)
};
if (cacheEnabled) {
//Add Query
Map<String, Object> query = {Queries.colLink: 'edit_taskType', Queries.colData: jsonEncode(queryBody)};
print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}");
await cacheDb.insert('Queries', query);
//update Cache
await cacheDb.rawUpdate("UPDATE TaskTypes SET ${TaskType.colId}='${username+name}', ${TaskType.colName}='$name', ${TaskType.colCategory}='${username+category}', ${TaskType.colRelatedProject}='${(relatedProject == 'None') ? '' : relatedProject}' WHERE id='${username+oldName}'");
await refreshUserData(forceOffline: true);
} else {
try {
http.Response queryResponse = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/add_taskType.php'), body: queryBody));
print("Query executed : Results{${queryResponse.body}");
if (queryResponse.body.toLowerCase().contains("success")) {
//Success
}
} catch (e) {
print('NC: Error editing task $e}');
}
}
if (!bulk) {
//Add to server and refresh Cache
await executeQueries();
}
}
static Future<void> addActivity(String type, DateTime sTime, DateTime eTime,
{String metadata = 'null', bool bulk = false, Function(int)? onOverlap}) async {
Map<String, String> queryBody = <String, String>{
@@ -1098,7 +1135,7 @@ class UserOperations {
print('NC: Error adding prjct $e}');
}
}
UserOperations.addTaskType(name, category, relatedProject: name);
await executeQueries();
}

View File

@@ -208,14 +208,23 @@ class _MyHomePageState extends State<MyHomePage> {
hourglassStops = [];
hourglassTotalTime=0;
hourglassCatData.forEach((element) {
if(element.time > hourglassTotalTime){
hourglassTotalTime=element.time;
}
// if(element.time > hourglassTotalTime){
hourglassTotalTime+=element.time;
// }
});
hourglassCatData.forEach((element) {
print('hourglass cat data');
double stopsTotal = 0;
for(int i =0 ; i < hourglassCatData.length; i++) {
CatMapData element = hourglassCatData[i];
print('${element.name} : ${element.time} / $hourglassTotalTime = ${element.time / hourglassTotalTime}');
double thisStop = ( element.time/hourglassTotalTime);
hourglassColors.add(element.color);
hourglassStops.add(element.time/hourglassTotalTime);
});
hourglassStops.add(stopsTotal+thisStop);
stopsTotal += thisStop;
if(i < hourglassCatData.length-1){hourglassColors.add(hourglassCatData[i+1].color);
hourglassStops.add(stopsTotal+thisStop + 0.001);}
}
print('total Stops ${stopsTotal}');
print('maxT: $hourglassTotalTime');
if(hourglassColors.isEmpty){
hourglassColors.add(Colors.black);
@@ -272,15 +281,23 @@ class _MyHomePageState extends State<MyHomePage> {
ongoingActName = "";
}
setState(() {
if(mounted) {
setState(() {
});
}
}
bool loadingStats = false;
void LoadStats() async {
// return;
// await User.refreshUserData();
if(loadingStats){
print('loading stats already');
return;
}else {
loadingStats=true;
}
await Refresh();
DateFormat dFormat = DateFormat("MM/dd");
@@ -434,8 +451,9 @@ class _MyHomePageState extends State<MyHomePage> {
int prodActs = (productivtyActs[element] ?? 0);
int unprodActs = (unproductivtyActs[element] ?? 0);
double prod = (untrackedUnprod) ? ((prodActs / 1440) * 100) : ((prodActs / unprodActs)*100);
if(prod>0){
productivityData.add(ProductivityMapData(element, prod));
var newProdData = ProductivityMapData(element, prod);
if(prod>0 && !productivityData.contains(newProdData)){
productivityData.add(newProdData);
}
// }
}
@@ -462,7 +480,11 @@ class _MyHomePageState extends State<MyHomePage> {
});
}
print('prodData : $productivityData');
// print('productivity data');
// productivityData.forEach((element) {
// print(element.day);
// });
loadingStats=false;
// loadingStats=false;
}

View File

@@ -58,7 +58,8 @@ class _NewActivity extends State<NewActivity> {
if(_cats.contains(element.name)){
}else{
_cats.add(name);}
_cats.add(name + ((element.relatedProject !=null) ? ' [${element.relatedProject!.name}]' :''));
}
});
return _cats;
}
@@ -112,7 +113,9 @@ class _NewActivity extends State<NewActivity> {
});});
}else{
selectedCat = _value ?? 'n/a';
}
setState(() {
});
@@ -398,9 +401,14 @@ class _NewActivity extends State<NewActivity> {
if(startTime.isAfter(endTime)){
showAlertDialog(context, 'Really?', 'Start time and end time doesnt make any sense');
}
print('adding Task Type : $selectedCat at $startTime - $endTime');
String selectedTasks = selectedCat;
if(selectedTasks.contains('[') && selectedTasks.contains(']')){
selectedTasks = selectedTasks.substring(0, selectedTasks.indexOf('[')-1);
print('Project task : $selectedTasks');
}
print('adding Task Type : $selectedTasks at $startTime - $endTime');
bool failed=false;
await User.UserOperations.addActivity(selectedCat,startTime, endTime,metadata:metadataController.text, onOverlap: (overlapCount){
await User.UserOperations.addActivity(selectedTasks,startTime, endTime,metadata:metadataController.text, onOverlap: (overlapCount){
showAlertDialog(context, 'Error adding activity', 'Cannot add activity between ${dateFormat.format(startTime)} - ${dateFormat.format(endTime)}, $overlapCount activities are already added within this time range');
failed=true;
});
@@ -416,9 +424,14 @@ class _NewActivity extends State<NewActivity> {
}
void edit_action() async{
print('adding Task Type : $selectedCat at $startTime - $endTime');
String selectedTasks = selectedCat;
if(selectedTasks.contains('[') && selectedTasks.contains(']')){
selectedTasks = selectedTasks.substring(0, selectedTasks.indexOf('[')-1);
print('Project task : $selectedTasks');
}
print('adding Task Type : $selectedTasks at $startTime - $endTime');
bool failed=false;
await User.UserOperations.editActivity(init_sTime,init_eTime,selectedCat,startTime, endTime,metadata:metadataController.text, onOverlap: (overlapCount){
await User.UserOperations.editActivity(init_sTime,init_eTime,selectedTasks,startTime, endTime,metadata:metadataController.text, onOverlap: (overlapCount){
showAlertDialog(context, 'Error editing activity', 'Cannot add activity between ${dateFormat.format(startTime)} - ${dateFormat.format(endTime)}, $overlapCount activities are already added within this time range');
failed=true;
});