479 lines
23 KiB
Dart
479 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}) : super(key: key);
|
|
late DateTime? sTime;
|
|
late DateTime? eTime;
|
|
late String? metadata;
|
|
late String? selectedTask;
|
|
@override
|
|
_NewActivity createState() => _NewActivity(eTime: eTime, sTime: sTime, selectedCat: selectedTask, metadata: metadata);
|
|
}
|
|
|
|
class _NewActivity extends State<NewActivity> {
|
|
late DateTime init_sTime;
|
|
late DateTime init_eTime;
|
|
late String init_selectedTask;
|
|
_NewActivity({DateTime? eTime, DateTime? sTime, String? metadata, String? selectedCat}) {
|
|
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"}");
|
|
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;
|
|
},
|
|
);
|
|
}
|