Files
TaskTracker/lib/newActivity.dart
2022-04-08 06:32:40 +05:30

484 lines
23 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:intl/intl.dart';
import 'package:tasktracker/NewTask.dart';
import 'package:tasktracker/NotificationsManager.dart';
import 'package:tasktracker/main.dart';
import 'Data.dart';
import 'User.dart' as User;
DateFormat dateTimeFormat = DateFormat("yyyy-MM-dd HH:mm:ss");
DateFormat durationFormat = DateFormat("HH:mm:ss");
class NewActivity extends StatefulWidget {
NewActivity({Key? key, this.selectedTask, this.sTime, this.eTime, this.metadata, this.todo}) : super(key: key);
late Todo? todo;
late DateTime? sTime;
late DateTime? eTime;
late String? metadata;
late String? selectedTask;
@override
_NewActivity createState() => _NewActivity(eTime: eTime, sTime: sTime, selectedCat: selectedTask, metadata: metadata,todo:todo);
}
class _NewActivity extends State<NewActivity> {
late DateTime init_sTime;
late DateTime init_eTime;
late String init_selectedTask;
Todo? todo;
_NewActivity({DateTime? eTime, DateTime? sTime, String? metadata, String? selectedCat, this.todo}) {
editing = sTime != null && eTime != null && selectedCat != null;
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;
late DateTime endTime;
TextEditingController metadataController = TextEditingController();
late String selectedCat;
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 Activity' : 'New Activity')),
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 (optional)',
filled: true,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(20))),
),
],
)),
Container(
child: Divider(
height: 30,
)),
Container(padding: EdgeInsets.all(10), child: Text('Start Time')),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
QuickTimeButton('Last', Function: () {
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)),
child: MaterialButton(
onPressed: () {
setState(() {
DatePicker.showDateTimePicker(context, maxTime: endTime, showTitleActions: true, onChanged: (date) {
// print('change $date');
}, onConfirm: (date) {
setState(() {
if (endTime.compareTo(date) < 0) {
const snackBar = SnackBar(
content: Text('You cannot start something after you ended it!'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} else {
startTime = date;
}
});
}, currentTime: startTime, locale: LocaleType.en);
});
},
child: Text(dateTimeFormat.format(startTime), style: TextStyle(color: Colors.blue)))),
QuickTimeButton('Now', Function: () {
startTime = DateTime.now();
})
],
),
SizedBox(
height: 10,
),
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)),
child: MaterialButton(
onPressed: () {
setState(() {
DatePicker.showDateTimePicker(context, showTitleActions: true, minTime: startTime, onChanged: (date) {
// print('change $date');
}, onConfirm: (date) {
setState(() {
if (startTime.compareTo(date) > 0) {
const snackBar = SnackBar(
content: Text('You cannot end something before you start it!'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} else {
endTime = date;
}
});
}, currentTime: endTime, locale: LocaleType.en);
});
},
child: Text(dateTimeFormat.format(endTime), style: TextStyle(color: Colors.blue)))),
QuickTimeButton('Now', Function: () {
endTime = DateTime.now();
})
],
),
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: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.lightbulb, color: Colors.yellowAccent),
Text('Not finished yet?', style: TextStyle(fontSize: 18)),
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,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: MaterialButton(
color: Colors.green,
onPressed: () {
User.StartActivityTimer(selectedCat, metadataController.text, startTime);
//Go home and show card
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.access_time),
SizedBox(
width: 10,
),
Text('Start Timer'),
],
),
),
),
)
])),
)
: Container(),
SizedBox(
height: 30,
),
Text('Duration : ' + _printDuration(endTime.difference(startTime)),
style: TextStyle(
fontSize: 20,
)),
Divider(
height: 30,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [],
)
]),
],
)),
),
),
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 Activity', 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 (startTime.isAfter(endTime)) {
showAlertDialog(context, 'Really?', 'Start time and end time doesnt make any sense');
}
String selectedTasks = selectedCat;
if (selectedTasks.contains('[') && selectedTasks.contains(']')) {
selectedTasks = selectedTasks.substring(0, selectedTasks.indexOf('[') - 1);
print('Project task : $selectedTasks');
}
print('adding Task Type : $selectedTasks at $startTime - $endTime');
bool failed = false;
await User.UserOperations.addActivity(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;
});
if (!failed) {
print("popping : ${navigatorKey.currentWidget?.toStringShort() ?? "n/a"}");
if(todo!=null){
await User.UserOperations.deleteTodo(todo!.id);
}
Navigator.of(navigatorKey.currentContext!).popUntil((route) {
return route.isFirst;
});
} else {
print("Failed adding new activity");
}
}
void edit_action() async {
String selectedTasks = selectedCat;
if (selectedTasks.contains('[') && selectedTasks.contains(']')) {
selectedTasks = selectedTasks.substring(0, selectedTasks.indexOf('[') - 1);
print('Project task : $selectedTasks');
}
print('adding Task Type : $selectedTasks at $startTime - $endTime');
bool failed = false;
await User.UserOperations.editActivity(init_sTime, init_eTime, 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;
});
if (!failed) {
print("popping : ${navigatorKey.currentWidget?.toStringShort() ?? "n/a"}");
Navigator.of(navigatorKey.currentContext!).popUntil((route) {
return route.isFirst;
});
} else {
print("Failed editing new activity");
}
}
}
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;
},
);
}