Todo basics added
This commit is contained in:
@@ -8,6 +8,7 @@ import 'newActivity.dart';
|
||||
import 'Data.dart';
|
||||
import 'User.dart' as User;
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
|
||||
class Activities extends StatefulWidget {
|
||||
const Activities({Key? key}) : super(key: key);
|
||||
|
||||
@@ -15,8 +16,6 @@ class Activities extends StatefulWidget {
|
||||
_ActivitiesState createState() => _ActivitiesState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
class _ActivitiesState extends State<Activities> {
|
||||
//late ProgressDialog progressDialog;
|
||||
TextEditingController searchController = TextEditingController();
|
||||
@@ -28,11 +27,10 @@ class _ActivitiesState extends State<Activities> {
|
||||
|
||||
if (!_focus.hasFocus) {
|
||||
searching = false;
|
||||
setState(() {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
var refreshSub;
|
||||
@override
|
||||
void initState() {
|
||||
@@ -41,9 +39,7 @@ class _ActivitiesState extends State<Activities> {
|
||||
_focus.addListener(_onFocusChange);
|
||||
refreshSub = User.refreshStream.stream.listen((event) {
|
||||
if (!event) {
|
||||
setState(() {
|
||||
|
||||
});
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
//UpdateList();
|
||||
@@ -58,6 +54,7 @@ class _ActivitiesState extends State<Activities> {
|
||||
_focus.dispose();
|
||||
refreshSub?.closeStream();
|
||||
}
|
||||
|
||||
void UpdateList() async {
|
||||
try {
|
||||
//progressDialog.show(max: 100, msg: 'Loading Activities');
|
||||
@@ -68,6 +65,7 @@ class _ActivitiesState extends State<Activities> {
|
||||
// progressDialog.update(value: 100);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
Map<String, ActivityListDateGroup> activitiesGroups = <String, ActivityListDateGroup>{};
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -91,18 +89,23 @@ class _ActivitiesState extends State<Activities> {
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(onChanged: (text){setState(() {
|
||||
|
||||
});},controller: searchController, focusNode: _focus, decoration: InputDecoration(
|
||||
child: TextField(
|
||||
onChanged: (text) {
|
||||
setState(() {});
|
||||
},
|
||||
controller: searchController,
|
||||
focusNode: _focus,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
),),
|
||||
),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: (){searching=false;
|
||||
onTap: () {
|
||||
searching = false;
|
||||
searchController.clear();
|
||||
setState(() {
|
||||
|
||||
});},
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.all(10),
|
||||
child: Icon(Icons.cancel),
|
||||
@@ -110,7 +113,10 @@ class _ActivitiesState extends State<Activities> {
|
||||
)
|
||||
],
|
||||
),
|
||||
Text('searched time : ${Main.MinutesToTimeString(searchTime)}',style: TextStyle(fontSize: 15),)
|
||||
Text(
|
||||
'searched time : ${Main.MinutesToTimeString(searchTime)}',
|
||||
style: TextStyle(fontSize: 15),
|
||||
)
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
@@ -131,7 +137,6 @@ class _ActivitiesState extends State<Activities> {
|
||||
size: 30,
|
||||
),
|
||||
)),
|
||||
|
||||
InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
@@ -147,9 +152,7 @@ class _ActivitiesState extends State<Activities> {
|
||||
onTap: () {
|
||||
searching = true;
|
||||
_focus.requestFocus();
|
||||
setState(() {
|
||||
|
||||
});
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.all(10),
|
||||
@@ -161,9 +164,7 @@ class _ActivitiesState extends State<Activities> {
|
||||
InkWell(
|
||||
onTap: () {
|
||||
UpdateList();
|
||||
setState(() {
|
||||
|
||||
});
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(margin: EdgeInsets.all(10), child: Icon(Icons.refresh, size: 30)),
|
||||
)
|
||||
@@ -180,11 +181,10 @@ class _ActivitiesState extends State<Activities> {
|
||||
itemBuilder: (context, index) {
|
||||
// return activities[index];
|
||||
return StickyHeader(
|
||||
|
||||
header: activitiesGroups.values.toList()[index].dateSeperator,
|
||||
content:Column(children: activitiesGroups.values.toList()[index].activities,)
|
||||
);
|
||||
|
||||
content: Column(
|
||||
children: activitiesGroups.values.toList()[index].activities,
|
||||
));
|
||||
})
|
||||
// SingleChildScrollView(
|
||||
// child: Column(
|
||||
@@ -192,12 +192,12 @@ class _ActivitiesState extends State<Activities> {
|
||||
// ))
|
||||
));
|
||||
}
|
||||
|
||||
DateFormat dFormat = DateFormat("yyyy-MM-dd");
|
||||
ItemScrollController scrollController = ItemScrollController();
|
||||
ScrollController controller = ScrollController();
|
||||
int searchTime = 0;
|
||||
Map<String, ActivityListDateGroup> PrintTasks() {
|
||||
|
||||
Map<String, ActivityListDateGroup> _tasks = <String, ActivityListDateGroup>{};
|
||||
|
||||
print('Priting cats : ' + User.taskTypes.length.toString());
|
||||
@@ -250,7 +250,6 @@ class _ActivitiesState extends State<Activities> {
|
||||
|
||||
String thisDate = dFormat.format(element.startTime);
|
||||
|
||||
|
||||
if (thisDate != lastDate) {
|
||||
int prodActs = productivtyActs[thisDate] ?? 0;
|
||||
int unProdActs = unproductivtyActs[thisDate] ?? 0;
|
||||
@@ -291,18 +290,20 @@ class _ActivitiesState extends State<Activities> {
|
||||
String gap = Main.MinutesToTimeString(sTime.difference(eTime).inMinutes);
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
|
||||
children: [
|
||||
Container(padding: EdgeInsets.fromLTRB(10, 0, 5, 0), child: Column(
|
||||
mainAxisSize: MainAxisSize.max,mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.fromLTRB(10, 0, 5, 0),
|
||||
child: Column(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
// Text(dateFormat.format(activity.endTime)),
|
||||
SizedBox(width: 1, height: 30, child: Container(color: Colors.white)),
|
||||
Text(dateFormat.format(eTime))])),
|
||||
Text(dateFormat.format(eTime))
|
||||
])),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewActivity(sTime: eTime,eTime: sTime))).then((value) => UpdateList());
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (context) => NewActivity(sTime: eTime, eTime: sTime)))
|
||||
.then((value) => UpdateList());
|
||||
},
|
||||
child: Card(
|
||||
child: Row(
|
||||
@@ -315,15 +316,10 @@ class _ActivitiesState extends State<Activities> {
|
||||
child: Text('Untracked period $gap'),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.add,size:30),
|
||||
Text("Add Activity")
|
||||
],
|
||||
children: [Icon(Icons.add, size: 30), Text("Add Activity")],
|
||||
),
|
||||
|
||||
],
|
||||
)
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -335,7 +331,6 @@ class _ActivitiesState extends State<Activities> {
|
||||
List<String> keys = activitiesGroups.keys.toList();
|
||||
List<ActivityListDateGroup> values = activitiesGroups.values.toList();
|
||||
for (int i = 0; i < activitiesGroups.length; i++) {
|
||||
|
||||
if (keys[i] == dFormat.format(date)) {
|
||||
break;
|
||||
}
|
||||
@@ -374,7 +369,9 @@ class _ActivitiesState extends State<Activities> {
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 15,),
|
||||
SizedBox(
|
||||
width: 15,
|
||||
),
|
||||
Icon(Icons.circle),
|
||||
SizedBox(
|
||||
width: 10,
|
||||
@@ -390,10 +387,18 @@ class _ActivitiesState extends State<Activities> {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
if(prodPercentage < 35)Text(Main.MinutesToTimeString(prodActs),),
|
||||
if (prodPercentage < 35)
|
||||
Text(
|
||||
Main.MinutesToTimeString(prodActs),
|
||||
),
|
||||
Container(
|
||||
child: Align(
|
||||
child: (prodPercentage >= 35) ?FittedBox(child: Text(Main.MinutesToTimeString(prodActs),)) : Container(),
|
||||
child: (prodPercentage >= 35)
|
||||
? FittedBox(
|
||||
child: Text(
|
||||
Main.MinutesToTimeString(prodActs),
|
||||
))
|
||||
: Container(),
|
||||
alignment: Alignment.center,
|
||||
),
|
||||
width: (prodPercentage) * 1,
|
||||
@@ -415,7 +420,8 @@ class _ActivitiesState extends State<Activities> {
|
||||
SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text(prodPercentage.toStringAsFixed(1) + "%", style: TextStyle(color: Colors.green, fontWeight: FontWeight.bold, fontSize: 20))
|
||||
Text(prodPercentage.toStringAsFixed(1) + "%",
|
||||
style: TextStyle(color: Colors.green, fontWeight: FontWeight.bold, fontSize: 20))
|
||||
],
|
||||
)
|
||||
// CustomPaint(
|
||||
@@ -436,20 +442,22 @@ class _ActivitiesState extends State<Activities> {
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
bool selecting = false;
|
||||
Widget ActivityCard(BuildContext context, String name, DateTime sTime, DateTime eTime, bool productive, Color color, Activity activity, int totalMinutes) {
|
||||
Widget ActivityCard(
|
||||
BuildContext context, String name, DateTime sTime, DateTime eTime, bool productive, Color color, Activity activity, int totalMinutes) {
|
||||
DateFormat dateFormat = DateFormat("HH:mm");
|
||||
int thisMinutes = (activity.endTime.difference(activity.startTime).inMinutes);
|
||||
|
||||
int timePercentage = ((thisMinutes / totalMinutes) * 100).toInt();
|
||||
// print("$thisMinutes / $totalMinutes");
|
||||
bool containsMetadata = ((activity.metadata ?? 'null') != 'null') && ((activity.metadata ?? '').isNotEmpty);
|
||||
bool containsStepData = (containsMetadata && activity.metadata!.contains('[') && activity.metadata!.contains(']'));
|
||||
var _timeSpan = eTime.difference(sTime);
|
||||
String timeSpan = ((_timeSpan.inHours > 0) ? _timeSpan.inHours.toString() + 'h ' : '') + ((_timeSpan.inMinutes % 60 > 0) ? (_timeSpan.inMinutes % 60).toString() + 'm' : '');
|
||||
String timeSpan = ((_timeSpan.inHours > 0) ? _timeSpan.inHours.toString() + 'h ' : '') +
|
||||
((_timeSpan.inMinutes % 60 > 0) ? (_timeSpan.inMinutes % 60).toString() + 'm' : '');
|
||||
return Row(children: [
|
||||
// Container(),
|
||||
(selecting)
|
||||
@@ -460,8 +468,11 @@ class _ActivitiesState extends State<Activities> {
|
||||
OnItemSelected(activity);
|
||||
setState(() {});
|
||||
})
|
||||
: Container(padding: EdgeInsets.fromLTRB(10, 0, 5, 0), child: Column(
|
||||
mainAxisSize: MainAxisSize.max,mainAxisAlignment: MainAxisAlignment.center,
|
||||
: Container(
|
||||
padding: EdgeInsets.fromLTRB(10, 0, 5, 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Text(dateFormat.format(activity.endTime)),
|
||||
SizedBox(width: 1, height: 100, child: Container(color: Colors.white)),
|
||||
@@ -497,8 +508,8 @@ class _ActivitiesState extends State<Activities> {
|
||||
children: [
|
||||
Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, children: [
|
||||
Text(name + " [$timeSpan]", style: TextStyle(fontSize: 17)),
|
||||
if (containsMetadata)Row(mainAxisAlignment:MainAxisAlignment.start,mainAxisSize: MainAxisSize.max,children: [
|
||||
|
||||
if (containsMetadata)
|
||||
Row(mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [
|
||||
Icon(
|
||||
Icons.arrow_forward_outlined,
|
||||
size: 20,
|
||||
@@ -506,10 +517,11 @@ class _ActivitiesState extends State<Activities> {
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 3,
|
||||
child: Text(
|
||||
activity.metadata ?? '',
|
||||
(containsStepData)
|
||||
? activity.metadata!.substring(activity.metadata!.indexOf(']') + 1)
|
||||
: (activity.metadata ?? ''),
|
||||
),
|
||||
),
|
||||
|
||||
]),
|
||||
// Icon(Icons.analytics, color: color, size: 20,),
|
||||
]),
|
||||
@@ -523,14 +535,29 @@ class _ActivitiesState extends State<Activities> {
|
||||
),
|
||||
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,),
|
||||
(activity.taskType.relatedProject != null)
|
||||
? Row(
|
||||
children: [
|
||||
if (containsStepData)
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: (productive) ? Colors.green : Colors.red),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: Colors.black26),
|
||||
child: Text(activity.metadata!.substring(1, activity.metadata!.indexOf(']')) ?? 'n/a')),
|
||||
SizedBox(width: 8 ,),
|
||||
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')),
|
||||
],
|
||||
)
|
||||
@@ -540,7 +567,8 @@ class _ActivitiesState extends State<Activities> {
|
||||
// : Colors.red)
|
||||
])
|
||||
],
|
||||
)))),
|
||||
))
|
||||
)),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.fromLTRB(15, 0, 15, 10),
|
||||
@@ -550,14 +578,25 @@ class _ActivitiesState extends State<Activities> {
|
||||
)),
|
||||
]),
|
||||
),
|
||||
if(selecting)InkWell(child: Container(margin:EdgeInsets.all(10),child: Icon(Icons.edit)),onTap: (){
|
||||
if (selecting)
|
||||
InkWell(
|
||||
child: Container(margin: EdgeInsets.all(10), child: Icon(Icons.edit)),
|
||||
onTap: () {
|
||||
selecting = false;
|
||||
selectedActivities = [];
|
||||
setState(() {
|
||||
|
||||
});
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewActivity(sTime: activity.trueStartTime,eTime: activity.trueEndTime,metadata: activity.metadata,selectedTask: activity.taskType.name,))).then((value) => UpdateList());
|
||||
},)
|
||||
setState(() {});
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(
|
||||
builder: (context) => NewActivity(
|
||||
sTime: activity.trueStartTime,
|
||||
eTime: activity.trueEndTime,
|
||||
metadata: activity.metadata,
|
||||
selectedTask: activity.taskType.name +
|
||||
((activity.taskType.relatedProject != null) ? ' [${activity.taskType.relatedProject!.name}]' : ''),
|
||||
)))
|
||||
.then((value) => UpdateList());
|
||||
},
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -592,5 +631,4 @@ class ActivityListDateGroup{
|
||||
List<Widget> activities;
|
||||
}
|
||||
|
||||
|
||||
List<Activity> selectedActivities = [];
|
||||
|
||||
@@ -155,7 +155,21 @@ class Journal{
|
||||
static String colDescription = 'Desc';
|
||||
}
|
||||
|
||||
class Todo{
|
||||
Todo(this.id, this.category,this.metadata, this.dueDate, {this.notificationTime, this.task});
|
||||
|
||||
String id;
|
||||
String category;
|
||||
TaskType? task;
|
||||
String metadata;
|
||||
DateTime dueDate;
|
||||
DateTime? notificationTime;
|
||||
|
||||
static String colCat = 'category';
|
||||
static String colMetadata = 'metadata';
|
||||
static String colDueDate = 'due_date';
|
||||
static String colNotificationTime = 'notification_time';
|
||||
}
|
||||
|
||||
|
||||
class Settings{
|
||||
|
||||
@@ -310,6 +310,60 @@ class Dialogs{
|
||||
}
|
||||
}
|
||||
|
||||
static showJournalLink(DateTime date) async{
|
||||
context=navigatorKey.currentContext;
|
||||
|
||||
if(context!=null) {
|
||||
int journalId = -1;
|
||||
for (int i =0; i < User.journal.length; i++) {
|
||||
print('${User.journal[i].day } : $date');
|
||||
if(DateFormat('yyyy-MM-dd').format(User.journal[i].day) == DateFormat('yyyy-MM-dd').format(date)){
|
||||
journalId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(journalId < 0){return;}
|
||||
String title= '${(User.journal[journalId].title!= null && User.journal[journalId].title!.isNotEmpty) ? User.journal[journalId].title : DateFormat('MMMM-dd').format(date)}';
|
||||
String description = User.journal[journalId].description ?? '';
|
||||
if(description.length > 60){
|
||||
description = description.substring(0,60) + '...(click more for more)';
|
||||
}
|
||||
return showDialog(
|
||||
context: context!,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
|
||||
backgroundColor: Color(0xFF1C1C1C),
|
||||
title: Text(title),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(description)
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
MaterialButton(
|
||||
onPressed: (){
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text('No', style:TextStyle(color: Colors.red)),
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: (){
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text('Yes', style:TextStyle(color: Colors.green)),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
);
|
||||
}else{
|
||||
print('context is null');
|
||||
}
|
||||
}
|
||||
|
||||
static hide(){
|
||||
showing=false;
|
||||
Navigator.of(navigatorKey.currentContext!).popUntil((route){
|
||||
|
||||
@@ -80,6 +80,9 @@ class _JournalPageState extends State<JournalPage>{
|
||||
|
||||
});
|
||||
},child: Container(margin:EdgeInsets.all(8),child: Icon(Icons.cancel))),
|
||||
if(!selecting)InkWell(onTap:(){setState(() {
|
||||
|
||||
});},child: Container(margin: EdgeInsets.all(8), child: Icon(Icons.refresh),))
|
||||
],
|
||||
)
|
||||
],
|
||||
|
||||
@@ -37,11 +37,13 @@ bool editing = false;
|
||||
bool multiLine = true;
|
||||
String selectedCat = User.categories[0].name;
|
||||
|
||||
String oldName = '[]';
|
||||
class _NewProjectState extends State<NewProject> {
|
||||
|
||||
_NewProjectState({bool? multiline, String? pname, String? selectedCategory, List<ProjectStep>? m_steps, int? m_eta}){
|
||||
multiLine=multiline ?? true;
|
||||
nameController.text = pname ?? '';
|
||||
if(pname!=null){oldName=pname;}
|
||||
if(selectedCategory!=null){selectedCat = selectedCategory;}
|
||||
if(m_steps!=null){steps=m_steps;}else{print('no steps?');}
|
||||
if(multiline!=null && pname != null && selectedCategory != null && m_steps!=null){
|
||||
@@ -500,7 +502,12 @@ class _NewProject2State extends State<NewProject2> {
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(primary: Colors.green, shape: StadiumBorder()),
|
||||
onPressed: () {
|
||||
if(editing){
|
||||
OnClickEdit();
|
||||
}else{
|
||||
|
||||
OnClickAdd();
|
||||
}
|
||||
},
|
||||
child: Text((editing) ? 'Apply' : 'Add', style: TextStyle(fontSize: 20))))),
|
||||
],
|
||||
@@ -688,6 +695,16 @@ class _NewProject2State extends State<NewProject2> {
|
||||
return route.isFirst;
|
||||
});
|
||||
}
|
||||
|
||||
void OnClickEdit() async{
|
||||
if(projectName==null || deadline.isBefore(DateTime.now())){showAlertDialog(context, 'Error', 'Please make sure you have entered correct information'); return;}
|
||||
|
||||
User.UserOperations.editProject(oldName,projectName!, selectedCat, steps,etaHours, deadline);
|
||||
|
||||
Navigator.of(navigatorKey.currentContext!).popUntil((route){
|
||||
return route.isFirst;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:tasktracker/NewProject.dart';
|
||||
import 'Data.dart';
|
||||
import 'User.dart' as User;
|
||||
|
||||
DateFormat dateFormat = DateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
DateFormat dateTimeFormat = DateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
DateFormat durationFormat = DateFormat("HH:mm:ss");
|
||||
|
||||
class NewTask extends StatefulWidget {
|
||||
|
||||
337
lib/NewTodo.dart
Normal file
337
lib/NewTodo.dart
Normal file
@@ -0,0 +1,337 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tasktracker/NewTask.dart';
|
||||
import 'package:tasktracker/NotificationsManager.dart';
|
||||
import 'Data.dart';
|
||||
import 'Dialogs.dart';
|
||||
import 'User.dart' as User;
|
||||
|
||||
DateFormat dateFormat = DateFormat("yyyy-MM-dd");
|
||||
DateFormat dateTimeFormat = DateFormat("yyyy-MM-dd HH:mm");
|
||||
DateFormat durationFormat = DateFormat("HH:mm:ss");
|
||||
|
||||
class NewTodo extends StatefulWidget {
|
||||
NewTodo({Key? key, this.selectedTask, this.metadata, this.dueDate,this.notificationTime}) : super(key: key);
|
||||
late String? metadata;
|
||||
late String? selectedTask;
|
||||
late DateTime? dueDate;
|
||||
late DateTime? notificationTime;
|
||||
@override
|
||||
_NewActivity createState() => _NewActivity(selectedCat: selectedTask, metadata: metadata);
|
||||
}
|
||||
|
||||
class _NewActivity extends State<NewTodo> {
|
||||
late String init_selectedTask;
|
||||
_NewActivity({String? metadata, String? selectedCat, DateTime? DueDate, this.notificationTime}) {
|
||||
editing =selectedCat != null && DueDate !=null;
|
||||
dueDate = DueDate ?? DateTime.now().add(Duration(days: 1));
|
||||
this.metadataController.text = metadata ?? "";
|
||||
if(this.metadataController.text.contains('[') && this.metadataController.text.contains(']') ){
|
||||
this.metadataController.text = this.metadataController.text.substring(this.metadataController.text.indexOf(']')+1);
|
||||
selectedStep = metadata!.substring(1,metadata!.indexOf(']'));
|
||||
}else{
|
||||
selectedStep='None';
|
||||
}
|
||||
this.init_selectedTask = this.selectedCat = selectedCat ?? User.taskTypes[0].name;
|
||||
print(" meta:$metadata, task: $selectedCat");
|
||||
}
|
||||
|
||||
TextEditingController metadataController = TextEditingController();
|
||||
late String selectedCat;
|
||||
late DateTime dueDate;
|
||||
DateTime? notificationTime;
|
||||
|
||||
bool editing = false;
|
||||
Map<String, TaskType?> taskTypes = <String, TaskType?>{};
|
||||
Map<String, TaskType?> getActivities() {
|
||||
Map<String, TaskType?> _cats = <String, TaskType?>{};
|
||||
_cats.putIfAbsent("+Add New Task Type", () => null);
|
||||
if (User.taskTypes.isEmpty) {
|
||||
} else {
|
||||
print(User.taskTypes[0].name + " : " + selectedCat);
|
||||
}
|
||||
User.taskTypes.forEach((element) {
|
||||
String name = element.name;
|
||||
if (_cats.keys.toString().contains(element.name)) {
|
||||
} else {
|
||||
String displayName = (name + ((element.relatedProject != null) ? ' [${element.relatedProject!.name}]' : ''));
|
||||
_cats.putIfAbsent(displayName, () => element);
|
||||
}
|
||||
});
|
||||
return _cats;
|
||||
}
|
||||
|
||||
String? selectedStep = null;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
taskTypes = getActivities();
|
||||
bool canSelectStep = false;
|
||||
List<String> steps = ['None'];
|
||||
if (taskTypes[selectedCat] != null) {
|
||||
if (taskTypes[selectedCat]!.relatedProject != null) {
|
||||
//Got a project. But is it multi step?
|
||||
if (taskTypes[selectedCat]!.relatedProject!.steps.isNotEmpty) {
|
||||
canSelectStep = true;
|
||||
bool matchesSelectedStep = false;
|
||||
taskTypes[selectedCat]!.relatedProject!.steps.forEach((element) {
|
||||
if (element.finishedDate == null) {
|
||||
steps.add(element.stepName);
|
||||
if (selectedStep == null) {
|
||||
selectedStep = element.stepName;
|
||||
matchesSelectedStep = true;
|
||||
}
|
||||
if (element.stepName == selectedStep) {
|
||||
matchesSelectedStep = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!matchesSelectedStep) {
|
||||
selectedStep = steps[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text((editing) ? 'Edit To-Do' : 'New To-Do')),
|
||||
body: Column(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||
Expanded(
|
||||
flex: 9,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(20, 10, 20, 10),
|
||||
child: Column(
|
||||
children: [
|
||||
Column(children: [
|
||||
Container(padding: EdgeInsets.all(10), child: Text('Task')),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black12, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey, width: 2)),
|
||||
child: DropdownButton<String>(
|
||||
dropdownColor: Color(0xFF222222),
|
||||
iconSize: 30,
|
||||
elevation: 10,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
value: selectedCat,
|
||||
isExpanded: true,
|
||||
items: getActivities().keys.map<DropdownMenuItem<String>>((String value) {
|
||||
print(value);
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (String? _value) {
|
||||
if (_value == '+Add New Task Type') {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewTask())).then((val) {
|
||||
setState(() {});
|
||||
});
|
||||
} else {
|
||||
selectedCat = _value ?? 'n/a';
|
||||
}
|
||||
setState(() {});
|
||||
})),
|
||||
if (canSelectStep) Container(padding: EdgeInsets.all(10), child: Text('Step')),
|
||||
if (canSelectStep)
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black12, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey, width: 2)),
|
||||
child: DropdownButton<String>(
|
||||
dropdownColor: Color(0xFF222222),
|
||||
iconSize: 30,
|
||||
elevation: 10,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
value: selectedStep,
|
||||
isExpanded: true,
|
||||
items: steps.map<DropdownMenuItem<String>>((String value) {
|
||||
print(value);
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (String? _value) {
|
||||
selectedStep = _value;
|
||||
setState(() {});
|
||||
})),
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Column(
|
||||
children: [
|
||||
TextField(
|
||||
controller: metadataController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Description (required)',
|
||||
filled: true,
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(20))),
|
||||
),
|
||||
],
|
||||
)),
|
||||
Container(
|
||||
child: Divider(
|
||||
)),
|
||||
ListTile(
|
||||
leading: FaIcon(FontAwesomeIcons.calendarDay),
|
||||
title: Text('Due Date'),
|
||||
subtitle: Text('When do you want to do this?'),
|
||||
onTap:(){setState(() {
|
||||
DatePicker.showDatePicker(context, showTitleActions: true, onChanged: (date) {
|
||||
// print('change $date');
|
||||
}, onConfirm: (date) {
|
||||
setState(() {
|
||||
dueDate=date;
|
||||
});
|
||||
}, currentTime: dueDate, locale: LocaleType.en);
|
||||
});} ,
|
||||
trailing: InkWell(
|
||||
child:Text(dateFormat.format(dueDate), style: TextStyle(color: Colors.blue))
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
|
||||
ListTile(
|
||||
title: Text('Notification'),
|
||||
leading: FaIcon((notificationTime==null) ?FontAwesomeIcons.bellSlash :FontAwesomeIcons.bell),
|
||||
trailing: Switch.adaptive(value: notificationTime!=null, onChanged: (val){
|
||||
if(notificationTime==null){
|
||||
notificationTime = DateTime.now().add(Duration(days: 1));
|
||||
}else{
|
||||
notificationTime = null;
|
||||
}
|
||||
setState(() {
|
||||
|
||||
});
|
||||
}),
|
||||
)
|
||||
]),
|
||||
ListTile(
|
||||
enabled: notificationTime!=null,
|
||||
leading: FaIcon(FontAwesomeIcons.clock),
|
||||
title: Text('Notify me at'),
|
||||
subtitle: Text('When do you want to do this?'),
|
||||
onTap:(){setState(() {
|
||||
DatePicker.showDateTimePicker(context, showTitleActions: true, onChanged: (date) {
|
||||
// print('change $date');
|
||||
}, onConfirm: (date) {
|
||||
setState(() {
|
||||
notificationTime=date;
|
||||
});
|
||||
}, currentTime: notificationTime, locale: LocaleType.en);
|
||||
});} ,
|
||||
trailing: InkWell(
|
||||
child:Text(dateTimeFormat.format(notificationTime ?? DateTime.now()), style: TextStyle(color: Colors.blue))
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 0),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(primary: Colors.red, shape: StadiumBorder()),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text('Back', style: TextStyle(fontSize: 20))))),
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 0),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(primary: Colors.green, shape: StadiumBorder()),
|
||||
onPressed: () {
|
||||
if (editing) {
|
||||
edit_action();
|
||||
} else {
|
||||
add_action();
|
||||
}
|
||||
},
|
||||
child: Text((editing) ? 'Apply' : 'Add Todo', style: TextStyle(fontSize: 20))))),
|
||||
],
|
||||
)),
|
||||
)
|
||||
]));
|
||||
}
|
||||
|
||||
Widget QuickTimeButton(text, {Function}) {
|
||||
return InkWell(
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
height: 30,
|
||||
decoration: BoxDecoration(color: Colors.blueAccent, borderRadius: BorderRadius.circular(50)),
|
||||
child: Align(
|
||||
child: Text(text),
|
||||
alignment: Alignment.center,
|
||||
)),
|
||||
onTap: () {
|
||||
Function();
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void add_action() async {
|
||||
if(metadataController.text.isEmpty){
|
||||
Dialogs.showAlertDialog(context, 'Invalid data', 'Please enter description to add new todo');
|
||||
return;
|
||||
}
|
||||
|
||||
await User.UserOperations.addTodo(selectedCat, metadataController.text, dueDate, notificationTime);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
void edit_action() async {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
String _printDuration(Duration duration) {
|
||||
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
||||
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
|
||||
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
|
||||
return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
|
||||
}
|
||||
|
||||
showAlertDialog(BuildContext context, String title, String message) {
|
||||
// set up the button
|
||||
Widget okButton = TextButton(
|
||||
child: Text("OK"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
|
||||
// set up the AlertDialog
|
||||
AlertDialog alert = AlertDialog(
|
||||
title: Text(title),
|
||||
content: Text(message),
|
||||
actions: [
|
||||
okButton,
|
||||
],
|
||||
);
|
||||
|
||||
// show the dialog
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return alert;
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'User.dart' as User;
|
||||
import 'main.dart';
|
||||
class TodoPage extends StatefulWidget {
|
||||
const TodoPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<TodoPage> createState() => _TodoPageState();
|
||||
}
|
||||
|
||||
class _TodoPageState extends State<TodoPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(child: Scaffold(
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
// Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewTodo())).then((value) => {User.refreshUserData().then((va) => {})});
|
||||
},
|
||||
label: Text("New Todo"),
|
||||
icon: Icon(Icons.add)),
|
||||
appBar: AppBar(
|
||||
title: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(children: [Icon(Icons.check, color: Theme.of(context).primaryColor), SizedBox(width: 10), Text('Todo')]),
|
||||
Row(
|
||||
children: [
|
||||
(User.offline)
|
||||
? Icon(Icons.signal_cellular_connected_no_internet_4_bar_outlined)
|
||||
: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
//LoadStats();
|
||||
});
|
||||
},
|
||||
child: Icon(Icons.refresh, size: 30),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
//Container(color: Colors.red,child: Text("Offline",style:TextStyle(fontSize: 5))),
|
||||
],
|
||||
)),
|
||||
drawer: navDrawer(context, 7),
|
||||
body: Column(
|
||||
children: PrintTodos(),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
List<Widget> PrintTodos(){
|
||||
List<Widget> todos = [];
|
||||
|
||||
todos.forEach((element) {
|
||||
|
||||
});
|
||||
|
||||
todos.add(prioritySeperator('- High Priority (0)'));
|
||||
todos.add(prioritySeperator('- Low Priority (0)'));
|
||||
|
||||
return todos;
|
||||
}
|
||||
|
||||
Widget todoItem(String name){
|
||||
return Row(children:[Text(name)]);
|
||||
}
|
||||
|
||||
Widget prioritySeperator(String text){
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(15,15,15,0),
|
||||
child: Row(
|
||||
children: [Text(text,style:TextStyle(fontSize: 18, color: Colors.grey))],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
278
lib/Todos.dart
Normal file
278
lib/Todos.dart
Normal file
@@ -0,0 +1,278 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
import 'package:tasktracker/NewJournal.dart';
|
||||
import 'package:tasktracker/NewTodo.dart';
|
||||
import 'package:tasktracker/main.dart';
|
||||
import 'User.dart' as User;
|
||||
import 'Dialogs.dart';
|
||||
|
||||
class TodosPage extends StatefulWidget {
|
||||
const TodosPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<TodosPage> createState() => _TodosPageState();
|
||||
}
|
||||
|
||||
class _TodosPageState extends State<TodosPage> {
|
||||
bool selecting = false;
|
||||
List<int> selectedIndexes = [];
|
||||
int expandedIndex = -1;
|
||||
var refreshStream;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
|
||||
refreshStream = User.refreshStream.stream.listen((event) {
|
||||
if (!event) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// TODO: implement dispose
|
||||
super.dispose();
|
||||
refreshStream?.close();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewTodo())).then((val) {
|
||||
setState(() {});
|
||||
});
|
||||
},
|
||||
label: Text("New To-Do"),
|
||||
icon: Icon(Icons.add)),
|
||||
appBar: AppBar(
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
FaIcon(FontAwesomeIcons.calendarCheck),
|
||||
SizedBox(
|
||||
width: 15,
|
||||
),
|
||||
Text('To-Do')
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
if (selecting && selectedIndexes.length > 0)
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
selecting = false;
|
||||
for (int element in selectedIndexes) {
|
||||
await User.UserOperations.deleteJournal(User.todos[element].id);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(margin: EdgeInsets.all(8), child: Icon(Icons.delete))),
|
||||
if (selecting)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
selecting = false;
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(margin: EdgeInsets.all(8), child: Icon(Icons.cancel))),
|
||||
if (!selecting)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.all(8),
|
||||
child: Icon(Icons.refresh),
|
||||
))
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
drawer: navDrawer(context, 9),
|
||||
body: Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: ScrollablePositionedList.builder(
|
||||
itemCount: User.todos.length,
|
||||
itemBuilder: (context, index) {
|
||||
int maxCharCount = 100;
|
||||
bool containsStepData = User.todos[index].metadata.contains('[') && User.todos[index].metadata.contains(']');
|
||||
return Container(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (selecting) {
|
||||
if (selectedIndexes.contains(index)) {
|
||||
selectedIndexes.remove(index);
|
||||
} else {
|
||||
selectedIndexes.add(index);
|
||||
}
|
||||
setState(() {});
|
||||
} else {
|
||||
if (expandedIndex == index) {
|
||||
expandedIndex = -1;
|
||||
//_controller..reverse(from: 0.5);
|
||||
} else {
|
||||
expandedIndex = index;
|
||||
// _controller..forward(from: 0);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
selecting = !selecting;
|
||||
if (!selectedIndexes.contains(index)) {
|
||||
selectedIndexes.add(index);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
if (selecting)
|
||||
Checkbox(
|
||||
value: selectedIndexes.contains(index),
|
||||
onChanged: (newVal) {
|
||||
if (selectedIndexes.contains(index)) {
|
||||
selectedIndexes.remove(index);
|
||||
} else {
|
||||
selectedIndexes.add(index);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
Card(
|
||||
|
||||
// color: color,
|
||||
elevation: 30,
|
||||
shadowColor: colorFromHex(User.todos[index].task!.cat!.color),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
//Open Respective Category
|
||||
if (selecting) {
|
||||
//OnItemSelected(activity);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
onLongPress: () {
|
||||
print('gonna delete');
|
||||
selecting = !selecting;
|
||||
// selectedActivities = [activity];
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(15),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, children: [
|
||||
Text(User.todos[index].task!.name, style: TextStyle(fontSize: 17)),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [
|
||||
Icon(
|
||||
Icons.arrow_forward_outlined,
|
||||
size: 20,
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 3,
|
||||
child: Text(
|
||||
(containsStepData)
|
||||
? User.todos[index].metadata.substring(User.todos[index].metadata!.indexOf(']') + 1)
|
||||
: (User.todos[index].metadata ?? ''),
|
||||
),
|
||||
),
|
||||
]),
|
||||
// Icon(Icons.analytics, color: color, size: 20,),
|
||||
]),
|
||||
SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: Colors.blue),
|
||||
child: Text(DateFormat('yyyy-MM-dd').format(User.todos[index].dueDate))),
|
||||
SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
(User.todos[index].task!.relatedProject != null)
|
||||
? Row(
|
||||
children: [
|
||||
if (containsStepData)
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
|
||||
decoration:
|
||||
BoxDecoration(borderRadius: BorderRadius.circular(10), color: Colors.black26),
|
||||
child: Text(User.todos[index].metadata!
|
||||
.substring(1, User.todos[index].metadata!.indexOf(']')) ??
|
||||
'n/a')),
|
||||
SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
|
||||
decoration:
|
||||
BoxDecoration(borderRadius: BorderRadius.circular(10), color: Colors.black26),
|
||||
child: Text(User.todos[index].task!.relatedProject!.name ?? 'n/a')),
|
||||
],
|
||||
)
|
||||
: Container(),
|
||||
SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: (User.todos[index].task!.cat!.productive) ? Colors.green : Colors.red),
|
||||
child: Text(User.todos[index].task!.cat?.name ?? 'n/a')),
|
||||
],
|
||||
)
|
||||
// Icon(Icons.circle,
|
||||
// color: (productive)
|
||||
// ? Colors.green
|
||||
// : Colors.red)
|
||||
])
|
||||
],
|
||||
)))),
|
||||
Container(
|
||||
margin: EdgeInsets.fromLTRB(15, 0, 15, 10),
|
||||
height: 2,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(flex: 100, child: Container(color: colorFromHex(User.todos[index].task!.cat!.color))),
|
||||
Expanded(flex: 0, child: Container())
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (selecting)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
// Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewJournal(date: User.todos[index].day, title: User.todos[index].title, text: User.todos[index].description,))).then((val) {
|
||||
// setState(() {});
|
||||
// });
|
||||
selecting = false;
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(margin: EdgeInsets.all(8), child: FaIcon(FontAwesomeIcons.edit)))
|
||||
],
|
||||
),
|
||||
));
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
317
lib/User.dart
317
lib/User.dart
@@ -29,6 +29,7 @@ List<TaskType> taskTypes = [];
|
||||
List<Activity> activities = [];
|
||||
List<Project> projects = [];
|
||||
List<Journal> journal = [];
|
||||
List<Todo> todos = [];
|
||||
|
||||
bool offline = true;
|
||||
bool registered = false;
|
||||
@@ -86,8 +87,8 @@ Future<void> BuildBridgeToServer() async{
|
||||
while (true) {
|
||||
await UserOperations.executeQueries();
|
||||
try {
|
||||
http.Response response = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/bridge.php'),
|
||||
body: <String, String>{"username": username}));
|
||||
http.Response response =
|
||||
(await http.post(Uri.parse('http://161.97.127.136/task_tracker/bridge.php'), body: <String, String>{"username": username}));
|
||||
|
||||
print("bridge retreive (try): ${response.body}");
|
||||
List<String> data = response.body.split(",");
|
||||
@@ -114,12 +115,10 @@ Future<void> BuildBridgeToServer() async{
|
||||
prefs.setString('rev', response.body);
|
||||
await refreshUserData();
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
print("Error with bridge : $e");
|
||||
}
|
||||
await Future.delayed(Duration(seconds: 5));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +126,8 @@ bool m_refreshing = false;
|
||||
Future<void> refreshUserData({bool forceOffline = false}) async {
|
||||
print('refreshing user data');
|
||||
if (m_refreshing) {
|
||||
print('Called while refreshing. Return');return;
|
||||
print('Called while refreshing. Return');
|
||||
return;
|
||||
}
|
||||
m_refreshing = true;
|
||||
|
||||
@@ -138,16 +138,16 @@ Future<void> refreshUserData({bool forceOffline = false}) async {
|
||||
taskTypes = await GetTaskTypes(true);
|
||||
activities = await GetActivities(true);
|
||||
journal = await GetJournals(true);
|
||||
todos = await GetTodos(true);
|
||||
|
||||
refreshStream.add(false);
|
||||
} else {
|
||||
|
||||
categories = await GetCategories(false);
|
||||
projects = await GetProjects(false);
|
||||
taskTypes = await GetTaskTypes(false);
|
||||
activities = await GetActivities(false);
|
||||
journal = await GetJournals(false);
|
||||
|
||||
todos= await GetTodos(false);
|
||||
}
|
||||
|
||||
m_refreshing = false;
|
||||
@@ -156,7 +156,6 @@ Future<void> refreshUserData({bool forceOffline = false}) async {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<bool> cacheDbExist() async {
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
Directory directory = await getApplicationDocumentsDirectory();
|
||||
@@ -202,7 +201,8 @@ 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, ${TaskType.colRelatedProject} 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);
|
||||
@@ -219,9 +219,13 @@ void onCacheDatabaseCreate(Database db, int newVersion) async {
|
||||
|
||||
String JournalTableSQL = 'CREATE TABLE Journal(id TEXT PRIMARY KEY, ${Journal.colTitle} TEXT, ${Journal.colDescription})';
|
||||
await db.execute(JournalTableSQL);
|
||||
|
||||
String TodoTableSQL = 'CREATE TABLE Todos(id TEXT PRIMARY KEY, ${Todo.colCat} TEXT, ${Todo.colMetadata} TEXT, ${Todo.colDueDate} DATE, ${Todo.colNotificationTime} DATETIME)';
|
||||
await db.execute(TodoTableSQL);
|
||||
|
||||
String QueriesTableSQL = 'CREATE TABLE Queries(id INTEGER PRIMARY KEY AUTOINCREMENT, ${Queries.colLink} TEXT,${Queries.colData} TEXT)';
|
||||
// print(QueriesTableSQL);
|
||||
await db.execute(QueriesTableSQL);
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
if (prefs.getBool("registered") ?? false) {
|
||||
@@ -437,7 +441,8 @@ Future<void> UpdateTaskTypesFromServer() async {
|
||||
for (var value in data) {
|
||||
Map<String, dynamic> cat = jsonDecode(value);
|
||||
//print(cat);
|
||||
await cacheDb.rawInsert("INSERT OR REPLACE INTO TaskTypes (${TaskType.colId},${TaskType.colName},${TaskType.colCategory},${TaskType.colRelatedProject}) "
|
||||
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"));
|
||||
@@ -447,7 +452,6 @@ Future<void> UpdateTaskTypesFromServer() async {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void StartActivityTimer(String taskType, String metadata, DateTime startTime) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
@@ -455,7 +459,6 @@ void StartActivityTimer(String taskType, String metadata, DateTime startTime) as
|
||||
NotificationManager.RescheduleNotifications();
|
||||
|
||||
UserOperations.startOngoing(prefs.getString('current_activity')!);
|
||||
|
||||
}
|
||||
|
||||
Future<List<String>?> getOngoingData() async {
|
||||
@@ -465,10 +468,8 @@ Future<List<String>?> getOngoingData() async{
|
||||
try {
|
||||
data = prefs.getString('current_activity')!.split('<td>');
|
||||
return data;
|
||||
}catch(e){
|
||||
}
|
||||
}else{
|
||||
}
|
||||
} catch (e) {}
|
||||
} else {}
|
||||
}
|
||||
|
||||
void StopActivityTimer() async {
|
||||
@@ -623,7 +624,7 @@ Future<void> UpdateActivitiesFromServer() async {
|
||||
//print(cat);
|
||||
await cacheDb.rawInsert(
|
||||
"INSERT OR REPLACE INTO Activities (${Activity.colType}, ${Activity.colStartTime}, ${Activity.colEndTime}, ${Activity.colMetadata}) "
|
||||
"VALUES ('${cat['task_id']}', '${cat['sTime']}','${cat['eTime']}', '${cat['metadata']}') ");
|
||||
"VALUES ('${cat['task_id']}', '${cat['sTime']}','${cat['eTime']}', '${cat['metadata'].toString().replaceAll("'", "''")}') ");
|
||||
}
|
||||
} else {
|
||||
print("No activities for now");
|
||||
@@ -670,7 +671,11 @@ Future<List<Project>> GetProjects(bool forceOffline) async {
|
||||
continue;
|
||||
}
|
||||
Category? cat = await getCatFromId(category);
|
||||
if(cat==null){print('couldnt find cat');continue;}
|
||||
print('searching for $category');
|
||||
if (cat == null) {
|
||||
print('couldnt find cat');
|
||||
continue;
|
||||
}
|
||||
print('steps : $stepsJson');
|
||||
List<dynamic> _steps = jsonDecode(stepsJson);
|
||||
List<ProjectStep> steps = [];
|
||||
@@ -710,7 +715,6 @@ Future<List<Project>> GetProjects(bool forceOffline) async {
|
||||
print('null found');
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
projects = _projects;
|
||||
@@ -810,7 +814,8 @@ Future<List<Journal>> GetJournals(bool forceOffline) async {
|
||||
for (var value in data) {
|
||||
Map<String, dynamic> cat = jsonDecode(value);
|
||||
//print(catData);
|
||||
_categories.add(Journal(cat['id'],DateTime.parse(cat['id'].toString().replaceAll(username, '')), title:cat['title'], description:cat['text']));
|
||||
_categories
|
||||
.add(Journal(cat['id'], DateTime.parse(cat['id'].toString().replaceAll(username, '')), title: cat['title'], description: cat['text']));
|
||||
}
|
||||
journal = _categories;
|
||||
} catch (e) {
|
||||
@@ -833,8 +838,7 @@ Future<void> UpdateJournalsFromServer() async {
|
||||
for (var value in data) {
|
||||
Map<String, dynamic> cat = jsonDecode(value);
|
||||
//print(catData);
|
||||
await cacheDb
|
||||
.rawInsert("INSERT OR REPLACE INTO Journal (id, ${Journal.colTitle},${Journal.colDescription}) "
|
||||
await cacheDb.rawInsert("INSERT OR REPLACE INTO Journal (id, ${Journal.colTitle},${Journal.colDescription}) "
|
||||
"VALUES ('${cat['id']}','${cat['title'].toString().replaceAll("'", "''")}','${cat['description'].toString().replaceAll("'", "''")}') ");
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -842,6 +846,100 @@ Future<void> UpdateJournalsFromServer() async {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Todo>> GetTodos(bool forceOffline) async {
|
||||
if (cacheEnabled) {
|
||||
List<Todo> _todos = [];
|
||||
if (offline || forceOffline) {
|
||||
//Retreive from cacheDB
|
||||
|
||||
} else {
|
||||
//Check if server got updated, If not go for cache
|
||||
|
||||
//Validate device_id to check updates
|
||||
|
||||
bool catsUpdated = false;
|
||||
// try {
|
||||
// http.Response update_response = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/check_update.php'), body: <String, String>{"username": username, "device_id": android_id}));
|
||||
// final data = update_response.body.split(',');
|
||||
// catsUpdated = data[0] == '1';
|
||||
// } catch (e) {
|
||||
// print(e);
|
||||
// }
|
||||
|
||||
//Update CacheDB
|
||||
if (!catsUpdated) {
|
||||
await UpdateTodosFromServer();
|
||||
}
|
||||
}
|
||||
|
||||
List<Map> cats = await cacheDb.query('Todos');
|
||||
print(cats.length);
|
||||
for (Map element in cats) {
|
||||
String? id = element['id'].toString();
|
||||
String? task_id = element[Todo.colCat];
|
||||
String? metadata = element[Todo.colMetadata];
|
||||
String? due_date = element[Todo.colDueDate];
|
||||
String? notification_time = element[Todo.colNotificationTime];
|
||||
|
||||
if (id == null || task_id == null || metadata == null || due_date == null) {
|
||||
print("Something is null!");
|
||||
print("id:{$id}, task:{$task_id}, metadata:${metadata}, due_date: ${due_date}");
|
||||
continue;
|
||||
}
|
||||
TaskType? taskType = await getTaskFromId(task_id);
|
||||
if(taskType == null){print('got null taask for this todo!');print("id:{$id}, task:{$task_id}, metadata:${metadata}, due_date: ${due_date}");}
|
||||
DateTime dueDate = DateTime.parse(due_date);
|
||||
DateTime? notificationTime = (notification_time == null) ? null : ((notification_time.isEmpty || notification_time =='null') ? null :DateTime.parse(notification_time));
|
||||
// print("name:{$catName}, color:{$catColor}, prod:{$catProductive}");
|
||||
_todos.add(Todo(id,task_id,metadata,dueDate, notificationTime: notificationTime, task:taskType));
|
||||
}
|
||||
todos = _todos;
|
||||
} else {
|
||||
print("NC: Updating todos as $username");
|
||||
try {
|
||||
http.Response response = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/get_todos.php'),
|
||||
body: <String, String>{"username": username, "device_id": await Settings.UUID()}));
|
||||
|
||||
print(response.body);
|
||||
List<String> data = response.body.split("<td>");
|
||||
List<Journal> _categories = [];
|
||||
for (var value in data) {
|
||||
Map<String, dynamic> cat = jsonDecode(value);
|
||||
//print(catData);
|
||||
_categories
|
||||
.add(Journal(cat['id'], DateTime.parse(cat['id'].toString().replaceAll(username, '')), title: cat['title'], description: cat['text']));
|
||||
}
|
||||
journal = _categories;
|
||||
} catch (e) {
|
||||
print("Error while cats NC: $e");
|
||||
}
|
||||
}
|
||||
// journal.sort((a, b) => b.day.compareTo(a.day));
|
||||
return todos;
|
||||
}
|
||||
|
||||
Future<void> UpdateTodosFromServer() async {
|
||||
print("Updating Todos as $username");
|
||||
try {
|
||||
http.Response response = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/get_todos.php'),
|
||||
body: <String, String>{"username": username, "device_id": await Settings.UUID()}));
|
||||
|
||||
print(response.body);
|
||||
List<String> data = response.body.split("<td>");
|
||||
await cacheDb.delete("Todos");
|
||||
for (var value in data) {
|
||||
Map<String, dynamic> cat = jsonDecode(value);
|
||||
//print(catData);
|
||||
await cacheDb.rawInsert("INSERT OR REPLACE INTO Todos (id, ${Todo.colCat},${Todo.colMetadata},${Todo.colDueDate},${Todo.colNotificationTime}) "
|
||||
"VALUES ('${cat['id'].toString().replaceAll("'", "''")}', '${cat['task_id']}', '${cat['metadata'].toString().replaceAll("'", "''")}', '${cat['due_date']}', '${cat['notification_time']}') ");
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error while cats $e");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<TaskType?> getTaskFromId(String taskId) async {
|
||||
// await GetTaskTypes(false);
|
||||
TaskType? cat = null;
|
||||
@@ -885,6 +983,19 @@ Future<Project?> getProjectFromId(String projectId) async {
|
||||
return project;
|
||||
}
|
||||
|
||||
bool journalExists(DateTime date){
|
||||
int journalId = -1;
|
||||
for (int i =0; i < journal.length; i++) {
|
||||
// print('${journal[i].day } : $date');
|
||||
if(DateFormat('yyyy-MM-dd').format(journal[i].day) == DateFormat('yyyy-MM-dd').format(date)){
|
||||
journalId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (journalId > 0);
|
||||
}
|
||||
|
||||
//Helpers
|
||||
class Helpers {
|
||||
Future<String?> _getId() async {
|
||||
@@ -969,8 +1080,14 @@ class UserOperations {
|
||||
await cacheDb.insert('Queries', query);
|
||||
|
||||
//update Cache
|
||||
Map<String, Object> data = {TaskType.colId: username + name, Category.colName: name, Category.colCatId: username + category,};
|
||||
if(relatedProject!=null || relatedProject =='None'){data.putIfAbsent(TaskType.colRelatedProject, () => relatedProject.toString());}
|
||||
Map<String, Object> 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 {
|
||||
@@ -1008,7 +1125,8 @@ class UserOperations {
|
||||
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 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 {
|
||||
@@ -1213,7 +1331,13 @@ class UserOperations {
|
||||
await cacheDb.insert('Queries', query);
|
||||
|
||||
//update Cache
|
||||
Map<String, Object> data = {Project.colName: username+name, Project.colCat: category, Project.colSteps: jsonEncode(steps),Project.colEta: eta, Project.colDeadline: deadline.toString()};
|
||||
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 {
|
||||
@@ -1231,8 +1355,52 @@ class UserOperations {
|
||||
await executeQueries();
|
||||
}
|
||||
|
||||
static Future<void> CompleteProjectStep(Project project, ProjectStep step, DateTime finishedDate) async {
|
||||
static Future<void> editProject(String oldName, String name, String category, List<ProjectStep> steps, int eta, DateTime deadline) async {
|
||||
Map<String, String> queryBody = <String, String>{
|
||||
'oldName': username + oldName,
|
||||
'name': username + name,
|
||||
'username': username,
|
||||
'category_id': username + category,
|
||||
'steps': jsonEncode(steps),
|
||||
'eta': eta.toString(),
|
||||
'deadline': DateFormat("yyyy-MM-dd").format(deadline)
|
||||
};
|
||||
|
||||
if (cacheEnabled) {
|
||||
//Add Query
|
||||
Map<String, Object> query = {Queries.colLink: 'edit_project', Queries.colData: jsonEncode(queryBody)};
|
||||
|
||||
print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}");
|
||||
|
||||
await cacheDb.insert('Queries', query);
|
||||
|
||||
//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.rawUpdate(
|
||||
"UPDATE Projects SET ${Project.colName}='${username + name}', ${Project.colCat}='${username+category}', ${Project.colSteps}='${jsonEncode(steps)}', ${Project.colEta}='${eta}', ${Project.colDeadline}='${deadline.toString()}' WHERE ${Project.colName}='${username+oldName}'");
|
||||
await cacheDb.rawUpdate("UPDATE TaskTypes SET ${TaskType.colRelatedProject}='${username+name}' WHERE ${TaskType.colRelatedProject}='${username+oldName}'");
|
||||
await refreshUserData(forceOffline: true);
|
||||
} else {
|
||||
try {
|
||||
http.Response queryResponse = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/edit_project.php'), body: queryBody));
|
||||
print("Query executed : Results{${queryResponse.body}");
|
||||
if (queryResponse.body.toLowerCase().contains("success")) {
|
||||
//Success
|
||||
}
|
||||
} catch (e) {
|
||||
print('NC: Error adding prjct $e}');
|
||||
}
|
||||
}
|
||||
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;
|
||||
@@ -1273,7 +1441,6 @@ class UserOperations {
|
||||
}
|
||||
|
||||
static Future<void> UndoProjectStep(Project project, ProjectStep step) async {
|
||||
|
||||
project.steps.forEach((element) {
|
||||
if (element.stepName == step.stepName) {
|
||||
element.finishedDate = null;
|
||||
@@ -1314,7 +1481,6 @@ class UserOperations {
|
||||
}
|
||||
|
||||
static Future<void> addJournal(DateTime day, String title, String text) async {
|
||||
|
||||
bool exist = false;
|
||||
for (var element in journal) {
|
||||
if (element.day == day) {
|
||||
@@ -1323,14 +1489,11 @@ class UserOperations {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(exist){return;}
|
||||
if (exist) {
|
||||
return;
|
||||
}
|
||||
String id = username + DateFormat('yyyy-MM-dd').format(day);
|
||||
Map<String, String> queryBody = <String, String>{
|
||||
'username': username,
|
||||
'id': id,
|
||||
'title': title,
|
||||
'description': text
|
||||
};
|
||||
Map<String, String> queryBody = <String, String>{'username': username, 'id': id, 'title': title, 'description': text};
|
||||
if (cacheEnabled) {
|
||||
//Add Query
|
||||
Map<String, Object> query = {Queries.colLink: 'add_journal', Queries.colData: jsonEncode(queryBody)};
|
||||
@@ -1340,11 +1503,7 @@ class UserOperations {
|
||||
await cacheDb.insert('Queries', query);
|
||||
|
||||
//update Cache
|
||||
Map<String, Object> data = {
|
||||
'id':id,
|
||||
Journal.colTitle: title,
|
||||
Journal.colDescription:text
|
||||
};
|
||||
Map<String, Object> data = {'id': id, Journal.colTitle: title, Journal.colDescription: text};
|
||||
await cacheDb.insert('Journal', data);
|
||||
await refreshUserData(forceOffline: true);
|
||||
} else {
|
||||
@@ -1361,18 +1520,12 @@ class UserOperations {
|
||||
}
|
||||
//Add to server and refresh Cache
|
||||
await executeQueries();
|
||||
|
||||
}
|
||||
|
||||
static Future<void> editJournal(DateTime oldDay, DateTime day, String title, String text) async {
|
||||
String oldId = username + DateFormat('yyyy-MM-dd').format(oldDay);
|
||||
String id = username + DateFormat('yyyy-MM-dd').format(day);
|
||||
Map<String, String> queryBody = <String, String>{
|
||||
'username': username,
|
||||
'old_id':oldId,
|
||||
'id': id,
|
||||
'title': title,
|
||||
'description': text
|
||||
};
|
||||
Map<String, String> queryBody = <String, String>{'username': username, 'old_id': oldId, 'id': id, 'title': title, 'description': text};
|
||||
if (cacheEnabled) {
|
||||
//Add Query
|
||||
Map<String, Object> query = {Queries.colLink: 'edit_journal', Queries.colData: jsonEncode(queryBody)};
|
||||
@@ -1380,13 +1533,10 @@ class UserOperations {
|
||||
print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}");
|
||||
|
||||
await cacheDb.insert('Queries', query);
|
||||
await cacheDb.rawUpdate("UPDATE Journal SET id='$id', ${Journal.colTitle}='${title.toString().replaceAll("'", "''")}', ${Journal.colDescription}='${text.toString().replaceAll("'", "''")}' WHERE id='$oldId'");
|
||||
await cacheDb.rawUpdate(
|
||||
"UPDATE Journal SET id='$id', ${Journal.colTitle}='${title.toString().replaceAll("'", "''")}', ${Journal.colDescription}='${text.toString().replaceAll("'", "''")}' WHERE id='$oldId'");
|
||||
//update Cache
|
||||
Map<String, Object> data = {
|
||||
'id':id,
|
||||
Journal.colTitle: title,
|
||||
Journal.colDescription:text
|
||||
};
|
||||
Map<String, Object> data = {'id': id, Journal.colTitle: title, Journal.colDescription: text};
|
||||
// await cacheDb.insert('Journal', data);
|
||||
await refreshUserData(forceOffline: true);
|
||||
} else {
|
||||
@@ -1403,9 +1553,54 @@ class UserOperations {
|
||||
}
|
||||
//Add to server and refresh Cache
|
||||
await executeQueries();
|
||||
|
||||
}
|
||||
|
||||
static Future<void> addTodo(String taskType, String metadata, DateTime dueDate, DateTime? notificationTime) async {
|
||||
|
||||
String taskId = username + taskType;
|
||||
if(taskId.contains('[') && taskId.contains(']')){
|
||||
//has a project related
|
||||
taskId = taskId.substring(0, taskId.indexOf('[') - 1);
|
||||
}
|
||||
|
||||
String id = username + taskId + metadata;
|
||||
|
||||
Map<String, String> queryBody = <String, String>{
|
||||
'username': username,
|
||||
'task': taskId,
|
||||
'metadata': metadata,
|
||||
'due_date': DateFormat('yyyy-MM-dd').format(dueDate),
|
||||
if(notificationTime!=null)'notification_time':DateFormat('yyyy-MM-dd HH:mm').format(notificationTime)
|
||||
};
|
||||
if (cacheEnabled) {
|
||||
//Add Query
|
||||
Map<String, Object> query = {Queries.colLink: 'add_todo', Queries.colData: jsonEncode(queryBody)};
|
||||
|
||||
print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}");
|
||||
|
||||
await cacheDb.insert('Queries', query);
|
||||
|
||||
//update Cache
|
||||
Map<String, Object> data = {'id': id, Todo.colCat: username+taskType, Todo.colMetadata: metadata, Todo.colDueDate: DateFormat('yyyy-MM-dd').format(dueDate), if(notificationTime!=null)Todo.colNotificationTime:DateFormat('yyyy-MM-dd HH:mm').format(notificationTime)};
|
||||
await cacheDb.insert('Todos', data);
|
||||
await refreshUserData(forceOffline: true);
|
||||
} else {
|
||||
try {
|
||||
http.Response queryResponse = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/add_todo.php'), body: queryBody));
|
||||
print("Query executed : Results{${queryResponse.body}");
|
||||
if (queryResponse.body.toLowerCase().contains("success")) {
|
||||
//Success
|
||||
}
|
||||
} catch (e) {
|
||||
print('NC: Error adding journal entry $e}');
|
||||
}
|
||||
//executeQueries();
|
||||
}
|
||||
//Add to server and refresh Cache
|
||||
await executeQueries();
|
||||
}
|
||||
|
||||
|
||||
static Future<void> deleteTask(String name, {bulk = false}) async {
|
||||
Map<String, String> queryBody = <String, String>{
|
||||
'id': username + name,
|
||||
@@ -1534,8 +1729,7 @@ class UserOperations {
|
||||
await cacheDb.insert('Queries', query);
|
||||
|
||||
//update Cache
|
||||
String deleteQuery =
|
||||
"DELETE FROM Projects WHERE ${Project.colName}='${username+project}'";
|
||||
String deleteQuery = "DELETE FROM Projects WHERE ${Project.colName}='${username + project}'";
|
||||
print("delteQuery : $deleteQuery");
|
||||
|
||||
await cacheDb.rawDelete(deleteQuery);
|
||||
@@ -1571,8 +1765,7 @@ class UserOperations {
|
||||
await cacheDb.insert('Queries', query);
|
||||
|
||||
//update Cache
|
||||
String deleteQuery =
|
||||
"DELETE FROM Journal WHERE id='$id'";
|
||||
String deleteQuery = "DELETE FROM Journal WHERE id='$id'";
|
||||
print("delteQuery : $deleteQuery");
|
||||
|
||||
await cacheDb.rawDelete(deleteQuery);
|
||||
@@ -1617,7 +1810,7 @@ class UserOperations {
|
||||
try {
|
||||
http.Response queryResponse = (await http.post(Uri.parse('http://161.97.127.136/task_tracker/$file.php'), body: body));
|
||||
print("Query executed : Results{${queryResponse.body}");
|
||||
if (queryResponse.body.toLowerCase().contains("success")) {
|
||||
if (queryResponse.body.toLowerCase().contains("+")) {
|
||||
await cacheDb.rawDelete('DELETE FROM Queries WHERE id=$id');
|
||||
}
|
||||
offline = false;
|
||||
|
||||
@@ -12,7 +12,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tasktracker/Categories.dart';
|
||||
import 'package:tasktracker/Journal.dart';
|
||||
import 'package:tasktracker/Projects.dart';
|
||||
import 'package:tasktracker/Todo.dart';
|
||||
import 'package:tasktracker/Todos.dart';
|
||||
import 'package:tasktracker/Welcome.dart';
|
||||
import 'package:tasktracker/splash.dart';
|
||||
import 'package:tasktracker/theme_provider.dart';
|
||||
@@ -113,7 +113,8 @@ class MyApp extends StatelessWidget {
|
||||
'/Activities': (context) => const Activities(),
|
||||
'/Settings': (context) => const SettingsPage(),
|
||||
'/Projects':(context)=> const Projects(),
|
||||
'/Journal': (context)=> const JournalPage()
|
||||
'/Journal': (context)=> const JournalPage(),
|
||||
'/Todos':(context)=> const TodosPage()
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -205,7 +206,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
// }
|
||||
hourglassTime = ((DateTime.now().hour * 60) + DateTime.now().minute) / 1440;
|
||||
// hourglassTime = 1;
|
||||
print('hourglass time : $hourglassTime');
|
||||
// print('hourglass time : $hourglassTime');
|
||||
hourglassColors =[];
|
||||
hourglassStops = [];
|
||||
hourglassTotalTime=0;
|
||||
@@ -214,11 +215,11 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
hourglassTotalTime+=element.time;
|
||||
// }
|
||||
});
|
||||
print('hourglass cat data');
|
||||
// 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}');
|
||||
// print('${element.name} : ${element.time} / $hourglassTotalTime = ${element.time / hourglassTotalTime}');
|
||||
double thisStop = ( element.time/hourglassTotalTime);
|
||||
hourglassColors.add(element.color);
|
||||
hourglassStops.add(stopsTotal+thisStop);
|
||||
@@ -228,14 +229,14 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
hourglassStops.add(stopsTotal+thisStop + 0.001);
|
||||
}
|
||||
}
|
||||
print('total Stops ${stopsTotal}');
|
||||
print('maxT: $hourglassTotalTime');
|
||||
// print('total Stops ${stopsTotal}');
|
||||
// print('maxT: $hourglassTotalTime');
|
||||
if(hourglassColors.isEmpty){
|
||||
hourglassColors.add(Colors.black);
|
||||
hourglassStops.add(1);
|
||||
}
|
||||
|
||||
print('hourglass \n$hourglassColors \n$hourglassStops');
|
||||
// print('hourglass \n$hourglassColors \n$hourglassStops');
|
||||
setState(() {
|
||||
|
||||
});
|
||||
@@ -292,6 +293,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
}
|
||||
}
|
||||
bool loadingStats = false;
|
||||
DateFormat dFormat = DateFormat("yyyy-MM-dd");
|
||||
void LoadStats() async {
|
||||
// return;
|
||||
// await User.refreshUserData();
|
||||
@@ -304,7 +306,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
}
|
||||
await Refresh();
|
||||
|
||||
DateFormat dFormat = DateFormat("MM/dd");
|
||||
|
||||
Map<Category, int> catTimeMap = <Category, int>{};
|
||||
Map<Category, int> catBriefMap = <Category, int>{};
|
||||
|
||||
@@ -354,13 +356,16 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
taskTypesDuration.putIfAbsent(element.taskType, () => thisMinutes);
|
||||
}
|
||||
}
|
||||
|
||||
if ((element.startTime.isAfter(prodRange!.start) && element.startTime.isBefore(prodRange!.end)) ||
|
||||
(dFormat.format(element.startTime) == dFormat.format(prodRange!.start) || dFormat.format(element.startTime) == dFormat.format(prodRange!.end))) {
|
||||
if (element.taskType.cat?.productive ?? false) {
|
||||
if (lastProductive == null) {
|
||||
lastProductive = element.trueEndTime;
|
||||
}
|
||||
}}
|
||||
if ((element.startTime.isAfter(prodRange!.start) && element.startTime.isBefore(prodRange!.end)) ||
|
||||
(dFormat.format(element.startTime) == dFormat.format(prodRange!.start) || dFormat.format(element.startTime) == dFormat.format(prodRange!.end))) {
|
||||
if (element.taskType.cat?.productive ?? false) {
|
||||
// if (lastProductive == null) {
|
||||
// lastProductive = element.trueEndTime;
|
||||
// }
|
||||
if (productivtyActs.containsKey(thisDate)) {
|
||||
productivtyActs[thisDate] = (productivtyActs[thisDate]! + thisMinutes);
|
||||
} else {
|
||||
@@ -728,15 +733,19 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
series: <LineSeries<ProductivityMapData, String>>[
|
||||
LineSeries<ProductivityMapData, String>(
|
||||
// Bind data source
|
||||
markerSettings: MarkerSettings(isVisible: true, shape: DataMarkerType.circle),
|
||||
dataSource: productivityData.reversed.toList(),
|
||||
xValueMapper: (ProductivityMapData sales, _) => sales.day,
|
||||
xValueMapper: (ProductivityMapData sales, _) => DateFormat('MM-dd').format(dFormat.parse(sales.day)),
|
||||
yValueMapper: (ProductivityMapData sales, _) => sales.productivity,
|
||||
dataLabelMapper: (ProductivityMapData sales, _) => sales.productivity.toStringAsFixed(1) + "%",
|
||||
dataLabelSettings: DataLabelSettings(overflowMode: OverflowMode.hide, showZeroValue: false, isVisible: true),
|
||||
onPointTap: (ChartPointDetails point){
|
||||
showAlertDialog(context, productivityData[point.pointIndex!].day, "I'll show you detailed info about this day in future, When my master creates the feature");
|
||||
Dialogs.showJournalLink(dFormat.parse(productivityData[productivityData.length-point.pointIndex!-1].day));
|
||||
//showAlertDialog(context, productivityData[point.pointIndex!].day, "I'll show you detailed info about this day in future, When my master creates the feature");
|
||||
},
|
||||
color: Colors.green)
|
||||
pointColorMapper: (ProductivityMapData sales, _)=> (User.journalExists(dFormat.parse(sales.day)) ? Colors.lightGreenAccent : Colors.green)
|
||||
)
|
||||
//color: User.journalExists(dFormat.parse(productivityData[(productivityData.length-point.pointIndex!-1) as int].day)) ?Colors.green : Colors.red,
|
||||
]),
|
||||
),
|
||||
SizedBox(height: 20,),
|
||||
@@ -778,7 +787,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
child: SfCircularChart(legend: Legend(isVisible: true,position: LegendPosition.bottom,overflowMode: LegendItemOverflowMode.wrap), series: <CircularSeries>[
|
||||
// Render pie chart
|
||||
PieSeries<CatMapData, String>(
|
||||
|
||||
dataSource: dailyData,
|
||||
pointColorMapper: (CatMapData data, _) => data.color,
|
||||
xValueMapper: (CatMapData data, _) => data.name,
|
||||
@@ -1013,6 +1021,17 @@ Drawer navDrawer(BuildContext context, int pageIndex) {
|
||||
Navigator.of(context).pushReplacementNamed('/Projects');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
selected: (pageIndex == 9),
|
||||
title: Text('To-Do'),
|
||||
leading: FaIcon(FontAwesomeIcons.calendarCheck, color: Theme.of(context).primaryColor),
|
||||
onTap: () {
|
||||
if (pageIndex == 9) {
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pushReplacementNamed('/Todos');
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
selected: (pageIndex == 8),
|
||||
title: Text('Journal'),
|
||||
|
||||
@@ -7,7 +7,8 @@ import 'package:tasktracker/NotificationsManager.dart';
|
||||
import 'package:tasktracker/main.dart';
|
||||
import 'Data.dart';
|
||||
import 'User.dart' as User;
|
||||
DateFormat dateFormat = DateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
DateFormat dateTimeFormat = DateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
DateFormat durationFormat = DateFormat("HH:mm:ss");
|
||||
|
||||
class NewActivity extends StatefulWidget {
|
||||
@@ -20,8 +21,6 @@ class NewActivity extends StatefulWidget {
|
||||
_NewActivity createState() => _NewActivity(eTime: eTime, sTime: sTime, selectedCat: selectedTask, metadata: metadata);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class _NewActivity extends State<NewActivity> {
|
||||
late DateTime init_sTime;
|
||||
late DateTime init_eTime;
|
||||
@@ -32,9 +31,14 @@ class _NewActivity extends State<NewActivity> {
|
||||
this.init_sTime = this.startTime = sTime ?? DateTime.now();
|
||||
this.init_eTime = this.endTime = eTime ?? DateTime.now().add(Duration(minutes: 30));
|
||||
this.metadataController.text = metadata ?? "";
|
||||
if(this.metadataController.text.contains('[') && this.metadataController.text.contains(']') ){
|
||||
this.metadataController.text = this.metadataController.text.substring(this.metadataController.text.indexOf(']')+1);
|
||||
selectedStep = metadata!.substring(1,metadata!.indexOf(']'));
|
||||
}else{
|
||||
selectedStep='None';
|
||||
}
|
||||
this.init_selectedTask = this.selectedCat = selectedCat ?? User.taskTypes[0].name;
|
||||
print("etime:$eTime, sTime:$sTime, meta:$metadata, task: $selectedCat");
|
||||
|
||||
}
|
||||
|
||||
late DateTime startTime;
|
||||
@@ -42,56 +46,74 @@ class _NewActivity extends State<NewActivity> {
|
||||
TextEditingController metadataController = TextEditingController();
|
||||
late String selectedCat;
|
||||
|
||||
|
||||
bool editing = false;
|
||||
|
||||
List<String> getActivities(){
|
||||
List<String> _cats = [];
|
||||
_cats.add("+Add New Task Type");
|
||||
Map<String, TaskType?> taskTypes = <String, TaskType?>{};
|
||||
Map<String, TaskType?> getActivities() {
|
||||
Map<String, TaskType?> _cats = <String, TaskType?>{};
|
||||
_cats.putIfAbsent("+Add New Task Type", () => null);
|
||||
if (User.taskTypes.isEmpty) {
|
||||
|
||||
} else {
|
||||
print(User.taskTypes[0].name + " : " + selectedCat);
|
||||
}
|
||||
User.taskTypes.forEach((element) {
|
||||
String name = element.name;
|
||||
if(_cats.contains(element.name)){
|
||||
|
||||
if (_cats.keys.toString().contains(element.name)) {
|
||||
} else {
|
||||
_cats.add(name + ((element.relatedProject !=null) ? ' [${element.relatedProject!.name}]' :''));
|
||||
String displayName = (name + ((element.relatedProject != null) ? ' [${element.relatedProject!.name}]' : ''));
|
||||
_cats.putIfAbsent(displayName, () => element);
|
||||
}
|
||||
});
|
||||
return _cats;
|
||||
}
|
||||
|
||||
String? selectedStep = null;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
taskTypes = getActivities();
|
||||
bool canSelectStep = false;
|
||||
List<String> steps = ['None'];
|
||||
if (taskTypes[selectedCat] != null) {
|
||||
if (taskTypes[selectedCat]!.relatedProject != null) {
|
||||
//Got a project. But is it multi step?
|
||||
if (taskTypes[selectedCat]!.relatedProject!.steps.isNotEmpty) {
|
||||
canSelectStep = true;
|
||||
bool matchesSelectedStep = false;
|
||||
taskTypes[selectedCat]!.relatedProject!.steps.forEach((element) {
|
||||
if (element.finishedDate == null) {
|
||||
steps.add(element.stepName);
|
||||
if (selectedStep == null) {
|
||||
selectedStep = element.stepName;
|
||||
matchesSelectedStep = true;
|
||||
}
|
||||
if (element.stepName == selectedStep) {
|
||||
matchesSelectedStep = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!matchesSelectedStep) {
|
||||
selectedStep = steps[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text((editing) ? 'Edit Activity' : 'New Activity')),
|
||||
body:Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
body: Column(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||
Expanded(
|
||||
flex: 9,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(20, 50, 20, 50),
|
||||
padding: EdgeInsets.fromLTRB(20, 10, 20, 10),
|
||||
child: Column(
|
||||
children: [
|
||||
Column(children: [
|
||||
Container(padding: EdgeInsets.all(10), child: Text('Task')),
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text('Task')),
|
||||
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 1),
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black12,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Colors.grey, width: 2)),
|
||||
color: Colors.black12, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey, width: 2)),
|
||||
child: DropdownButton<String>(
|
||||
dropdownColor: Color(0xFF222222),
|
||||
iconSize: 30,
|
||||
@@ -99,8 +121,7 @@ class _NewActivity extends State<NewActivity> {
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
value: selectedCat,
|
||||
isExpanded: true,
|
||||
items: getActivities().map<DropdownMenuItem<String>>(
|
||||
(String value) {
|
||||
items: getActivities().keys.map<DropdownMenuItem<String>>((String value) {
|
||||
print(value);
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
@@ -109,43 +130,56 @@ class _NewActivity extends State<NewActivity> {
|
||||
}).toList(),
|
||||
onChanged: (String? _value) {
|
||||
if (_value == '+Add New Task Type') {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (context)=> NewTask())).then((val){setState(() {
|
||||
|
||||
});});
|
||||
}else{
|
||||
|
||||
selectedCat = _value ?? 'n/a';
|
||||
|
||||
}
|
||||
setState(() {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewTask())).then((val) {
|
||||
setState(() {});
|
||||
});
|
||||
} else {
|
||||
selectedCat = _value ?? 'n/a';
|
||||
}
|
||||
setState(() {});
|
||||
})),
|
||||
if (canSelectStep) Container(padding: EdgeInsets.all(10), child: Text('Step')),
|
||||
if (canSelectStep)
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black12, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey, width: 2)),
|
||||
child: DropdownButton<String>(
|
||||
dropdownColor: Color(0xFF222222),
|
||||
iconSize: 30,
|
||||
elevation: 10,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
value: selectedStep,
|
||||
isExpanded: true,
|
||||
items: steps.map<DropdownMenuItem<String>>((String value) {
|
||||
print(value);
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (String? _value) {
|
||||
selectedStep = _value;
|
||||
setState(() {});
|
||||
})),
|
||||
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
TextField(
|
||||
controller: metadataController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Description (optional)',
|
||||
filled: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(20)
|
||||
)
|
||||
),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(20))),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
)),
|
||||
Container(
|
||||
child: Divider(
|
||||
height: 30,
|
||||
)),
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text('Start Time')),
|
||||
Container(padding: EdgeInsets.all(10), child: Text('Start Time')),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
@@ -154,23 +188,14 @@ class _NewActivity extends State<NewActivity> {
|
||||
if (User.activities.length > 0) {
|
||||
startTime = User.activities[0].endTime;
|
||||
}
|
||||
})
|
||||
,
|
||||
}),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Colors.grey, width: 2)),
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 1),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey, width: 2)),
|
||||
child: MaterialButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
DatePicker.showDateTimePicker(
|
||||
context,
|
||||
maxTime: endTime,
|
||||
showTitleActions: true,
|
||||
onChanged: (date) {
|
||||
DatePicker.showDateTimePicker(context, maxTime: endTime, showTitleActions: true, onChanged: (date) {
|
||||
// print('change $date');
|
||||
}, onConfirm: (date) {
|
||||
setState(() {
|
||||
@@ -183,15 +208,10 @@ class _NewActivity extends State<NewActivity> {
|
||||
startTime = date;
|
||||
}
|
||||
});
|
||||
},
|
||||
currentTime: startTime,
|
||||
locale: LocaleType.en);
|
||||
}, currentTime: startTime, locale: LocaleType.en);
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
dateFormat.format(startTime),
|
||||
style: TextStyle(
|
||||
color: Colors.blue)))),
|
||||
child: Text(dateTimeFormat.format(startTime), style: TextStyle(color: Colors.blue)))),
|
||||
QuickTimeButton('Now', Function: () {
|
||||
startTime = DateTime.now();
|
||||
})
|
||||
@@ -200,31 +220,21 @@ class _NewActivity extends State<NewActivity> {
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text('Ended Time')),
|
||||
|
||||
Container(padding: EdgeInsets.all(10), child: Text('Ended Time')),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Container(width: 60,),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Colors.grey, width: 2)),
|
||||
width: 60,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 1),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey, width: 2)),
|
||||
child: MaterialButton(
|
||||
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
DatePicker.showDateTimePicker(
|
||||
context,
|
||||
showTitleActions: true,
|
||||
minTime: startTime,
|
||||
onChanged: (date) {
|
||||
DatePicker.showDateTimePicker(context, showTitleActions: true, minTime: startTime, onChanged: (date) {
|
||||
// print('change $date');
|
||||
}, onConfirm: (date) {
|
||||
setState(() {
|
||||
@@ -237,30 +247,27 @@ class _NewActivity extends State<NewActivity> {
|
||||
endTime = date;
|
||||
}
|
||||
});
|
||||
},
|
||||
currentTime: endTime,
|
||||
locale: LocaleType.en);
|
||||
}, currentTime: endTime, locale: LocaleType.en);
|
||||
});
|
||||
},
|
||||
child: Text(dateFormat.format(endTime),
|
||||
style: TextStyle(
|
||||
color: Colors.blue)))),
|
||||
child: Text(dateTimeFormat.format(endTime), style: TextStyle(color: Colors.blue)))),
|
||||
QuickTimeButton('Now', Function: () {
|
||||
endTime = DateTime.now();
|
||||
})
|
||||
],
|
||||
),
|
||||
SizedBox(height: 15,),
|
||||
(!editing)?Container(
|
||||
SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
(!editing)
|
||||
? Container(
|
||||
child: Card(
|
||||
shadowColor: Colors.blue,
|
||||
elevation: 20,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
// color: Color(0xAA51AAFF),
|
||||
color: Colors.deepPurpleAccent,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children:[
|
||||
child: Column(mainAxisSize: MainAxisSize.max, children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
@@ -269,14 +276,18 @@ class _NewActivity extends State<NewActivity> {
|
||||
children: [
|
||||
Icon(Icons.lightbulb, color: Colors.yellowAccent),
|
||||
Text('Not finished yet?', style: TextStyle(fontSize: 18)),
|
||||
Container(width: 15,),
|
||||
|
||||
Container(
|
||||
width: 15,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text('Click following button to start tracking an unfinished Activity. Activity will be added when you finish it.', textAlign: TextAlign.center,),
|
||||
child: Text(
|
||||
'Click following button to start tracking an unfinished Activity. Activity will be added when you finish it.',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
@@ -293,23 +304,22 @@ class _NewActivity extends State<NewActivity> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.access_time),
|
||||
SizedBox(width: 10,),
|
||||
SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text('Start Timer'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
]
|
||||
])),
|
||||
)
|
||||
),
|
||||
) : Container(),
|
||||
: Container(),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Text('Duration : ' +
|
||||
_printDuration(
|
||||
endTime.difference(startTime)),
|
||||
Text('Duration : ' + _printDuration(endTime.difference(startTime)),
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
)),
|
||||
@@ -318,22 +328,17 @@ class _NewActivity extends State<NewActivity> {
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
],
|
||||
children: [],
|
||||
)
|
||||
]),
|
||||
|
||||
],
|
||||
|
||||
)),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(
|
||||
padding:
|
||||
EdgeInsets.symmetric(vertical: 10, horizontal: 20),
|
||||
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
@@ -343,25 +348,17 @@ class _NewActivity extends State<NewActivity> {
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 0),
|
||||
child: ElevatedButton(
|
||||
|
||||
style:ElevatedButton.styleFrom(
|
||||
primary: Colors.red,
|
||||
shape: StadiumBorder()
|
||||
),
|
||||
style: ElevatedButton.styleFrom(primary: Colors.red, shape: StadiumBorder()),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text('Back',
|
||||
style: TextStyle(fontSize: 20))))),
|
||||
child: Text('Back', style: TextStyle(fontSize: 20))))),
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 0),
|
||||
child: ElevatedButton(
|
||||
style:ElevatedButton.styleFrom(
|
||||
primary: Colors.green,
|
||||
shape: StadiumBorder()
|
||||
),
|
||||
style: ElevatedButton.styleFrom(primary: Colors.green, shape: StadiumBorder()),
|
||||
onPressed: () {
|
||||
if (editing) {
|
||||
edit_action();
|
||||
@@ -369,8 +366,7 @@ class _NewActivity extends State<NewActivity> {
|
||||
add_action();
|
||||
}
|
||||
},
|
||||
child: Text((editing) ? 'Apply':'Add Activity',
|
||||
style: TextStyle(fontSize: 20))))),
|
||||
child: Text((editing) ? 'Apply' : 'Add Activity', style: TextStyle(fontSize: 20))))),
|
||||
],
|
||||
)),
|
||||
)
|
||||
@@ -382,19 +378,16 @@ class _NewActivity extends State<NewActivity> {
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
height: 30,
|
||||
decoration:BoxDecoration(
|
||||
color: Colors.blueAccent,
|
||||
borderRadius: BorderRadius.circular(50)
|
||||
),
|
||||
child:Align(child: Text(text),alignment: Alignment.center,)
|
||||
),
|
||||
|
||||
decoration: BoxDecoration(color: Colors.blueAccent, borderRadius: BorderRadius.circular(50)),
|
||||
child: Align(
|
||||
child: Text(text),
|
||||
alignment: Alignment.center,
|
||||
)),
|
||||
onTap: () {
|
||||
Function();
|
||||
setState(() {
|
||||
|
||||
});
|
||||
},);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void add_action() async {
|
||||
@@ -408,8 +401,9 @@ class _NewActivity extends State<NewActivity> {
|
||||
}
|
||||
print('adding Task Type : $selectedTasks at $startTime - $endTime');
|
||||
bool failed = false;
|
||||
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');
|
||||
await User.UserOperations.addActivity(selectedTasks, startTime, endTime, metadata:((selectedStep!=null && selectedStep != 'None') ? '[$selectedStep]' : '')+ metadataController.text, onOverlap: (overlapCount) {
|
||||
showAlertDialog(context, 'Error adding activity',
|
||||
'Cannot add activity between ${dateTimeFormat.format(startTime)} - ${dateTimeFormat.format(endTime)}, $overlapCount activities are already added within this time range');
|
||||
failed = true;
|
||||
});
|
||||
|
||||
@@ -431,8 +425,10 @@ class _NewActivity extends State<NewActivity> {
|
||||
}
|
||||
print('adding Task Type : $selectedTasks at $startTime - $endTime');
|
||||
bool failed = false;
|
||||
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');
|
||||
await User.UserOperations.editActivity(init_sTime, init_eTime, selectedTasks, startTime, endTime,metadata: ((selectedStep!=null && selectedStep != 'None') ? '[$selectedStep]' : '')+ metadataController.text,
|
||||
onOverlap: (overlapCount) {
|
||||
showAlertDialog(context, 'Error editing activity',
|
||||
'Cannot add activity between ${dateTimeFormat.format(startTime)} - ${dateTimeFormat.format(endTime)}, $overlapCount activities are already added within this time range');
|
||||
failed = true;
|
||||
});
|
||||
|
||||
@@ -455,11 +451,12 @@ String _printDuration(Duration duration) {
|
||||
}
|
||||
|
||||
showAlertDialog(BuildContext context, String title, String message) {
|
||||
|
||||
// set up the button
|
||||
Widget okButton = TextButton(
|
||||
child: Text("OK"),
|
||||
onPressed: () { Navigator.of(context).pop(); },
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
|
||||
// set up the AlertDialog
|
||||
|
||||
Reference in New Issue
Block a user