diff --git a/lib/Data.dart b/lib/Data.dart index cb06fe5..b8cc8e7 100644 --- a/lib/Data.dart +++ b/lib/Data.dart @@ -25,16 +25,18 @@ class Category{ class TaskType{ - TaskType(this.id, this.name, this.category, [this.cat = null]); + TaskType(this.id, this.name, this.category,{this.relatedProject, this.cat}); String id; String name; String category; Category? cat; + Project? relatedProject; static String colId = "id"; static String colName="name"; static String colCategory = "category_id"; + static String colRelatedProject = "related_project"; } class Activity{ @@ -108,6 +110,7 @@ class Project{ Project(this.name, this.category, this.steps,this.eta, this.deadline,{this.cat}); String name; + String getName()=> name.replaceAll(User.username, ""); String category; Category? cat; List steps; diff --git a/lib/Dialogs.dart b/lib/Dialogs.dart index 634a6f0..f63bd13 100644 --- a/lib/Dialogs.dart +++ b/lib/Dialogs.dart @@ -151,7 +151,9 @@ class Dialogs{ crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: stepWidgets, - ) + ), + SizedBox(height: 30,), + Text("Estimate Time : ${project.eta}") ], ), actions: [ diff --git a/lib/NewTask.dart b/lib/NewTask.dart index d0f8312..590fadd 100644 --- a/lib/NewTask.dart +++ b/lib/NewTask.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; 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 'User.dart' as User; DateFormat dateFormat = DateFormat("yyyy-MM-dd HH:mm:ss"); @@ -24,7 +25,19 @@ List getCategoryNames(){ return _cats; } +List getProjectNames(){ + List _projects = []; + _projects.add("None"); + _projects.add('+Add New Project'); + User.projects.forEach((element) { + String name = element.getName(); + _projects.add(name); + }); + return _projects; +} + String selectedCat = User.categories[0].name; +String selectedProj = "None"; class _NewTaskState extends State { TextEditingController nameController = TextEditingController(); bool productive = true; @@ -96,6 +109,48 @@ class _NewTaskState extends State { } setState(() { + }); + })), + Padding( + padding: const EdgeInsets.fromLTRB(0, 20, 0, 10), + child: Text('Related Project'), + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: 12, vertical: 1), + decoration: BoxDecoration( + color: Colors.blueGrey, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Colors.grey, width: 2)), + child: DropdownButton( + dropdownColor: Colors.blueGrey, + iconSize: 30, + elevation: 10, + borderRadius: BorderRadius.circular(10), + value: selectedProj, + isExpanded: true, + items: getProjectNames().map>( + (String value) { + + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + onChanged: (String? _value) { + if(_value != null) { + if (_value.contains("+Add New Project")) { + Navigator.of(context).push(MaterialPageRoute(builder: (context)=>NewProject())); + }else{ + selectedProj = _value!; + if(_value.contains("None")){ + + } + } + } + setState(() { + }); })), Container( @@ -157,7 +212,7 @@ class _NewTaskState extends State { showAlertDialog(context, 'Category needs a name', 'Please enter a name for this category'); return; } - await User.UserOperations.addTaskType(catName,selectedCat); + await User.UserOperations.addTaskType(catName,selectedCat,relatedProject: (selectedProj == 'None') ? '' : selectedProj); Navigator.of(context).popUntil((route){ return route.isFirst; }); diff --git a/lib/Projects.dart b/lib/Projects.dart index a973815..e616edd 100644 --- a/lib/Projects.dart +++ b/lib/Projects.dart @@ -31,13 +31,20 @@ class _ProjectsState extends State { super.dispose(); refreshSub?.close(); } + + void refresh(){ + setState(() { + + }); + } + int selectedPage = 0; @override Widget build(BuildContext context) { return SafeArea(child: Scaffold( floatingActionButton: FloatingActionButton.extended( onPressed: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewProject())).then((value) => {User.refreshUserData().then((va) => {})}); + Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewProject())).then((value) => {User.refreshUserData().then((va) => {refresh()})}); }, label: Text("New Project"), icon: Icon(Icons.add)), @@ -69,7 +76,9 @@ class _ProjectsState extends State { ? Icon(Icons.signal_cellular_connected_no_internet_4_bar_outlined) : InkWell( onTap: () async{ - await User.refreshUserData(); + User.refreshUserData().then((val){setState(() { + + });}); setState(() { //LoadStats(); @@ -86,11 +95,19 @@ class _ProjectsState extends State { )), drawer: navDrawer(context, 7), body: (selectedPage == 0) ? - Container(child:Text('Summary goes here')) + Container(child: Column( + children: [ + + ], + )) :Container( padding: EdgeInsets.all(10), child: Column( - children: printProjects(), + children: [ + Column( + children: printProjects(), + ), + ], ), ), )); @@ -98,8 +115,9 @@ class _ProjectsState extends State { List printProjects(){ List projectWidgets = []; - + print(User.projects.length); for (var element in User.projects) { + print(element.name); if(element.cat==null){print(element.name + " has no cat");continue;} projectWidgets.add(ProjectCard(element)); } @@ -146,7 +164,7 @@ class _ProjectsState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text("${project.steps.length} steps"), + (project.steps.length > 0) ? Text("${project.steps.length} steps") : Container(), Container( decoration: BoxDecoration( color: Colors.redAccent, diff --git a/lib/Tasks.dart b/lib/Tasks.dart index 1812647..65ddf62 100644 --- a/lib/Tasks.dart +++ b/lib/Tasks.dart @@ -10,31 +10,33 @@ class Tasks extends StatefulWidget { @override _TasksState createState() => _TasksState(); } + class _TasksState extends State { @override void initState() { // TODO: implement initState super.initState(); - // UpdateList(); - // init(context); + // UpdateList(); + // init(context); } var refreshSub; - void init(BuildContext context) async{ + void init(BuildContext context) async { await Future.delayed(Duration(seconds: 1)); refreshSub = User.refreshStream.stream.listen((value) { print("Streaming refresh : $value"); - if(value){ + if (value) { // dialogs.waiting(context, "Syncing"); print("Opening progress dialog"); - }else{ + } else { // dialogs.hide(context); print("Closing progress dialog"); } }); } - @override void dispose() { + @override + void dispose() { // TODO: implement dispose super.dispose(); refreshSub?.cancel(); @@ -43,34 +45,41 @@ class _TasksState extends State { @override Widget build(BuildContext context) { return Scaffold( - floatingActionButton: FloatingActionButton.extended( onPressed: () { - Navigator.of(context) - .push(MaterialPageRoute(builder: (context) => NewTask())) - .then((value) => UpdateList()); + Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewTask())).then((value) => UpdateList()); }, label: Text("New Task Type"), icon: Icon(Icons.add)), appBar: AppBar( title: Row( - mainAxisSize: MainAxisSize.max, + mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row(children: [ - Icon(Icons.task, color: Theme.of(context).primaryColor), - SizedBox(width: 10), - Text('Task Types') - ]), - (selecting)?Row(children: [ - InkWell(onTap: (){ - DeleteSelectedTasks(); - }, child: Icon(Icons.delete,size: 30,)), - SizedBox(width: 20,), - InkWell(onTap: (){setState(() { - selecting=false; - });}, child: Icon(Icons.close,size: 30),) - ]) : Container(), + Row(children: [Icon(Icons.task, color: Theme.of(context).primaryColor), SizedBox(width: 10), Text('Task Types')]), + (selecting) + ? Row(children: [ + InkWell( + onTap: () { + DeleteSelectedTasks(); + }, + child: Icon( + Icons.delete, + size: 30, + )), + SizedBox( + width: 20, + ), + InkWell( + onTap: () { + setState(() { + selecting = false; + }); + }, + child: Icon(Icons.close, size: 30), + ) + ]) + : Container(), ], )), drawer: navDrawer(context, 3), @@ -83,13 +92,12 @@ class _TasksState extends State { } void UpdateList() async { - - // try{progressDialog.show(max:100, msg: 'Loading Task Types...');}catch(e){} + // try{progressDialog.show(max:100, msg: 'Loading Task Types...');}catch(e){} await User.refreshUserData(); - // hideProgressDialog(); - if(mounted)setState(() {}); + // hideProgressDialog(); + if (mounted) setState(() {}); - // try{progressDialog.update(value: 100);}catch(e){} + // try{progressDialog.update(value: 100);}catch(e){} } List PrintTasks() { @@ -102,7 +110,8 @@ class _TasksState extends State { } else { Color color = HexColor.fromHex(element.cat?.color ?? '#000000'); bool productive = element.cat?.productive ?? true; - Widget task = TaskCard(context, name, productive, color, element.cat?.name ?? 'n/a'); + Widget task = TaskCard( + context, name, productive, color, element.cat?.name ?? 'n/a', (element.relatedProject != null) ? element.relatedProject!.getName() : ''); _tasks.add(task); } }); @@ -111,8 +120,7 @@ class _TasksState extends State { } bool selecting = false; - Widget TaskCard( - BuildContext context, String name, bool productive, Color color, String catName) { + Widget TaskCard(BuildContext context, String name, bool productive, Color color, String catName, String relatedProjects) { return Row(children: [ // Container(), (selecting) @@ -129,57 +137,58 @@ class _TasksState extends State { Card( // color: color, - elevation:20, + elevation: 20, shadowColor: color, child: InkWell( onTap: () { //Open Respective Category - if(selecting){ + if (selecting) { OnItemSelected(name); } - setState(() { - - }); + setState(() {}); }, onLongPress: () { print('gonna delete'); selecting = !selecting; selectedTasks = [name]; setState(() {}); - }, child: Container( padding: EdgeInsets.all(10), child: Column( children: [ - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, + Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text( + name, + ), + // Icon(Icons.analytics, color: color, size: 20,), + Row( + mainAxisSize: MainAxisSize.min, children: [ - Text(name, - ), - // Icon(Icons.analytics, color: color, size: 20,), + (relatedProjects.isNotEmpty) + ? Container( + padding: EdgeInsets.symmetric(horizontal: 10), + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(10), color: Colors.black26), + child: Text(relatedProjects)) + : Container(), Container( padding: EdgeInsets.symmetric(horizontal: 10), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: (productive) ? Colors.green : Colors.red - ), - child:Text(catName) - ) - ]), + decoration: + BoxDecoration(borderRadius: BorderRadius.circular(10), color: (productive) ? Colors.green : Colors.red), + child: Text(catName)), + ], + ) + ]), ], )))), - Container( - margin: EdgeInsets.fromLTRB(15, 0, 15, 10), - height: 2, - color: color) + Container(margin: EdgeInsets.fromLTRB(15, 0, 15, 10), height: 2, color: color) ]), ), ]); } - void OnItemSelected(String name){ + void OnItemSelected(String name) { if (!selectedTasks.contains(name)) { selectedTasks.add(name); } else { @@ -187,20 +196,17 @@ class _TasksState extends State { } } - void DeleteSelectedTasks() async{ + void DeleteSelectedTasks() async { selectedTasks.forEach((element) async { - await User.UserOperations.deleteTask(element, bulk:true); + await User.UserOperations.deleteTask(element, bulk: true); }); await Future.delayed(Duration(seconds: 2)); await User.UserOperations.executeQueries(); - selectedTasks=[]; - selecting=false; - setState(() { - }); - + selectedTasks = []; + selecting = false; + setState(() {}); } - } List selectedTasks = []; diff --git a/lib/User.dart b/lib/User.dart index 1594d30..5c610c6 100644 --- a/lib/User.dart +++ b/lib/User.dart @@ -198,7 +198,7 @@ void onCacheDatabaseCreate(Database db, int newVersion) async { await db.execute(CategoriesTableSQL); print("Initiated Categories Table"); - String TaskTableSQL = 'CREATE TABLE TaskTypes(id TEXT PRIMARY KEY, ${TaskType.colName} TEXT, ${TaskType.colCategory} TEXT, ' + String TaskTableSQL = 'CREATE TABLE TaskTypes(id TEXT PRIMARY KEY, ${TaskType.colName} TEXT, ${TaskType.colCategory} TEXT, ${TaskType.colRelatedProject} TEXT, ' 'FOREIGN KEY (${TaskType.colCategory}) REFERENCES Categories(${Category.colCatId}))'; // print(TaskTableSQL); await db.execute(TaskTableSQL); @@ -376,14 +376,21 @@ Future> GetTaskTypes(bool forceOffline) async { String? id = element[TaskType.colId].toString(); String? name = element[TaskType.colName].toString(); String? category = element[TaskType.colCategory].toString(); + String? related_project = element[TaskType.colRelatedProject].toString(); Category? cat = await getCatFromId(category); if (id == null || name == null || category == null) { print("Something is null!"); print("name:{$name}, cat:{$category}, prod:{$id}"); continue; } + Project? relatedProject; + if(related_project.isNotEmpty){ + relatedProject = await getProjectFromId(related_project); + print('got a tasktype with project'); + } + // print("name:{$name}, cat:{$category}, prod:{$id}"); - _taskTypes.add(TaskType(id, name, category, cat)); + _taskTypes.add(TaskType(id, name, category, cat:cat, relatedProject: relatedProject)); } taskTypes = _taskTypes; } else { @@ -399,7 +406,7 @@ Future> GetTaskTypes(bool forceOffline) async { for (var value in data) { Map data = jsonDecode(value); Category? cat = await getCatFromId(data['category_id']); - _taskTypes.add(TaskType(data['task_id'], data['name'], data['category_id'], cat)); + _taskTypes.add(TaskType(data['task_id'], data['name'], data['category_id'], cat:cat,relatedProject: data['related_project'])); //print(cat); } }catch(e){ @@ -424,8 +431,8 @@ Future UpdateTaskTypesFromServer() async { for (var value in data) { Map cat = jsonDecode(value); //print(cat); - await cacheDb.rawInsert("INSERT OR REPLACE INTO TaskTypes (${TaskType.colId},${TaskType.colName},${TaskType.colCategory}) " - "VALUES ('${cat['task_id']}','${cat['name']}','${cat['category_id']}') "); + await cacheDb.rawInsert("INSERT OR REPLACE INTO TaskTypes (${TaskType.colId},${TaskType.colName},${TaskType.colCategory},${TaskType.colRelatedProject}) " + "VALUES ('${cat['task_id']}','${cat['name']}','${cat['category_id']}', '${cat['related_project']}') "); print(await cacheDb.query("TaskTypes")); } @@ -648,8 +655,10 @@ Future> GetProjects(bool forceOffline) async { String? category = element[Project.colCat]; String? stepsJson = element[Project.colSteps]; String? deadline = element[Project.colDeadline]; + int? eta= element[Project.colEta]; - if (name == null || category == null || stepsJson == null || deadline == 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("TaskType:{$name}, Start Time:{$category}, endTime:{$stepsJson}, metadata:${deadline}"); continue; @@ -659,13 +668,14 @@ Future> GetProjects(bool forceOffline) async { print('steps : $stepsJson'); List _steps = jsonDecode(stepsJson); List steps = []; - int eta = 0; + int m_eta = 0; _steps.forEach((element) { ProjectStep step = ProjectStep.fromJson(element); - eta+=step.eta; + m_eta += step.eta; steps.add(step); print(element); }); + eta = (m_eta > 0) ? m_eta : eta; // print(steps); _projects.add(Project(name.replaceAll(username, ""),category,steps,eta,DateTime.parse(deadline),cat: cat)); @@ -728,8 +738,8 @@ Future UpdateProjectsFromServer() async { print('project data'); print(cat); await cacheDb.rawInsert( - "INSERT OR REPLACE INTO Projects (${Project.colName}, ${Project.colCat}, ${Project.colSteps}, ${Project.colDeadline}) " - "VALUES ('${cat['name']}', '${cat['category']}', '${cat['steps']}', '${cat['deadline']}')"); + "INSERT OR REPLACE INTO Projects (${Project.colName}, ${Project.colCat}, ${Project.colSteps}, ${Project.colDeadline}, ${Project.colEta}) " + "VALUES ('${cat['name']}', '${cat['category']}', '${cat['steps']}', '${cat['deadline']}', ${cat['eta']})"); } } else { print("No activities for now"); @@ -768,6 +778,21 @@ Future getCatFromId(String catId) async { return cat; } +Future getProjectFromId(String projectId) async { + // await GetTaskTypes(false); + Project? project = null; + for (var element in projects) { + if (element.getName() ==projectId.replaceAll(username, "")) { + project = element; + } + } + if (project == null) { + print('Got null project for ${projectId} after searching on ${projects.length}'); + } + + return project; +} + //Helpers class Helpers { Future _getId() async { @@ -833,13 +858,14 @@ class UserOperations { } } - static Future addTaskType(String name, String category, {bool bulk = false}) async { + static Future addTaskType(String name, String category, {bool bulk = false, String? relatedProject = null}) async { Map queryBody = { 'id': username + name, 'username': username, 'device_id': await Settings.UUID(), 'name': name, - 'category': username + category + 'category': username + category, + 'related_project' :(relatedProject==null) ? '' : (username + relatedProject) }; if (cacheEnabled) { @@ -851,7 +877,8 @@ class UserOperations { await cacheDb.insert('Queries', query); //update Cache - Map data = {TaskType.colId: username + name, Category.colName: name, Category.colCatId: username + category}; + Map data = {TaskType.colId: username + name, Category.colName: name, Category.colCatId: username + category,}; + if(relatedProject!=null || relatedProject =='None'){data.putIfAbsent(TaskType.colRelatedProject, () => relatedProject.toString());} await cacheDb.insert('TaskTypes', data); await refreshUserData(forceOffline: true); } else { @@ -1057,7 +1084,7 @@ class UserOperations { await cacheDb.insert('Queries', query); //update Cache - Map data = {Project.colName: username+name, Project.colCat: category, Project.colSteps: jsonEncode(steps), Project.colDeadline: deadline.toString()}; + Map 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 {