1752 lines
86 KiB
Dart
1752 lines
86 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/foundation.dart' as K;
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:tasktracker/Analytics.dart';
|
|
import 'package:tasktracker/Categories.dart';
|
|
import 'package:tasktracker/Journal.dart';
|
|
import 'package:tasktracker/Projects.dart';
|
|
import 'package:tasktracker/Todos.dart';
|
|
import 'package:tasktracker/Welcome.dart';
|
|
import 'package:tasktracker/splash.dart';
|
|
import 'package:tasktracker/theme_provider.dart';
|
|
import 'Settings/Settings.dart';
|
|
import 'package:wakelock/wakelock.dart';
|
|
import 'Data.dart';
|
|
import 'NewTask.dart';
|
|
import 'newActivity.dart';
|
|
import 'Tasks.dart';
|
|
import 'Activities.dart';
|
|
import 'User.dart' as User;
|
|
import 'package:syncfusion_flutter_charts/charts.dart';
|
|
import 'Dialogs.dart';
|
|
import 'CustomWidgets.dart';
|
|
import 'package:firebase_core/firebase_core.dart';
|
|
|
|
final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
|
|
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;
|
|
},
|
|
);
|
|
}
|
|
|
|
extension HexColor on Color {
|
|
/// String is in the format "aabbcc" or "ffaabbcc" with an optional leading "#".
|
|
static Color fromHex(String hexString) {
|
|
final buffer = StringBuffer();
|
|
if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
|
|
buffer.write(hexString.replaceFirst('#', ''));
|
|
return Color(int.parse(buffer.toString(), radix: 16));
|
|
}
|
|
|
|
/// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`)./home/warlock/Desktop/Task Tracker/tasktracker
|
|
String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}'
|
|
'${alpha.toRadixString(16).padLeft(2, '0')}'
|
|
'${red.toRadixString(16).padLeft(2, '0')}'
|
|
'${green.toRadixString(16).padLeft(2, '0')}'
|
|
'${blue.toRadixString(16).padLeft(2, '0')}';
|
|
}
|
|
|
|
void main() async {
|
|
//Wakelock.enable(); // or Wakelock.toggle(on: true);
|
|
|
|
if (!K.kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
await Firebase.initializeApp();
|
|
}
|
|
//
|
|
// ByteData data = await PlatformAssetBundle().load('assets/ca/certificate.crt');
|
|
// SecurityContext.defaultContext.setTrustedCertificatesBytes(data.buffer.asUint8List());
|
|
|
|
runApp(const MyApp());
|
|
}
|
|
|
|
class MyApp extends StatelessWidget {
|
|
const MyApp({Key? key}) : super(key: key);
|
|
// This widget is the root of your application.
|
|
@override
|
|
Widget build(BuildContext context) => ChangeNotifierProvider(
|
|
create: (context) => ThemeProvider(),
|
|
builder: (context, _) {
|
|
final themeProvider = Provider.of<ThemeProvider>(context);
|
|
|
|
return MaterialApp(
|
|
title: 'Task Tracker',
|
|
themeMode: themeProvider.themeMode,
|
|
theme: ThemeData(
|
|
accentColor: Colors.redAccent,
|
|
brightness: Brightness.light,
|
|
primaryColor: Colors.amber,
|
|
fontFamily: 'Noto-Sans'),
|
|
darkTheme: ThemeData(
|
|
backgroundColor: Colors.black,
|
|
accentColor: Colors.redAccent,
|
|
brightness: Brightness.dark,
|
|
primaryColor: Colors.amber,
|
|
fontFamily: 'Noto-Sans'),
|
|
navigatorKey: navigatorKey,
|
|
//home: const SplashScreen(),
|
|
initialRoute: '/',
|
|
routes: {
|
|
'/': (context) => const SplashScreen(),
|
|
'/welcome': (context) => const WelcomePage(),
|
|
'/home': (context) => const MyHomePage(),
|
|
'/Tasks': (context) => const Tasks(),
|
|
'/Categories': (context) => const Categories(),
|
|
'/Activities': (context) => const Activities(),
|
|
'/Settings': (context) => const SettingsPage(),
|
|
'/Projects': (context) => const Projects(),
|
|
'/Journal': (context) => const JournalPage(),
|
|
'/Todos': (context) => const TodosPage(),
|
|
'/Analytics': (context) => const AnalyticsPage()
|
|
});
|
|
});
|
|
}
|
|
|
|
List<String> days = [];
|
|
String curDay = "";
|
|
|
|
DateTime? firstDay = null;
|
|
DateTime? lastDay = null;
|
|
DateTimeRange? taskTypeRange = null;
|
|
DateTimeRange? catsRange = null;
|
|
DateTimeRange? prodRange = null;
|
|
|
|
List<ProductivityMapData> pastProductivityData = <ProductivityMapData>[
|
|
ProductivityMapData('02/24', 32),
|
|
ProductivityMapData('02/25', 5),
|
|
ProductivityMapData('02/26', 56),
|
|
ProductivityMapData('02/27', 33),
|
|
ProductivityMapData('02/28', 50)
|
|
];
|
|
List<ProductivityMapData> productivityData = <ProductivityMapData>[
|
|
ProductivityMapData('02/24', 35),
|
|
ProductivityMapData('02/25', 28),
|
|
ProductivityMapData('02/26', 34),
|
|
ProductivityMapData('02/27', 32),
|
|
ProductivityMapData('02/28', 40)
|
|
];
|
|
|
|
List<TaskTypeMapData> taskTypesData = <TaskTypeMapData>[
|
|
TaskTypeMapData('Eat', 3600, Colors.green),
|
|
TaskTypeMapData('Play', 300, Colors.blue)
|
|
];
|
|
List<CatMapData> catsData = <CatMapData>[
|
|
CatMapData('Jan', 35, Colors.green),
|
|
CatMapData('Feb', 28, Colors.blueAccent),
|
|
CatMapData('Mar', 34, Colors.yellow),
|
|
CatMapData('Apr', 32, Colors.grey),
|
|
];
|
|
|
|
List<CatMapData> dailyData = <CatMapData>[
|
|
CatMapData('Jan', 35, Colors.green),
|
|
CatMapData('Feb', 28, Colors.blueAccent),
|
|
CatMapData('Mar', 34, Colors.yellow),
|
|
CatMapData('Apr', 32, Colors.grey),
|
|
];
|
|
|
|
List<CatMapData> hourglassCatData = <CatMapData>[];
|
|
|
|
List<Todo> relativeTodos = [];
|
|
|
|
class MyHomePage extends StatefulWidget {
|
|
const MyHomePage({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
State<MyHomePage> createState() => _MyHomePageState();
|
|
}
|
|
|
|
class _MyHomePageState extends State<MyHomePage> {
|
|
var connectivitySub;
|
|
var refreshSub;
|
|
|
|
@override
|
|
void initState() {
|
|
// TODO: implement initState
|
|
print("Im home!");
|
|
init(context);
|
|
super.initState();
|
|
print("Initializing refresh stream on main dart");
|
|
|
|
// connectivitySub=Connectivity().onConnectivityChanged.listen((result) {
|
|
// if (this.mounted) {
|
|
// setState(() {});
|
|
// }
|
|
// });
|
|
refreshSub = User.refreshStream.stream.listen((event) {
|
|
if (event == false) {
|
|
//Update done, Lets go update data
|
|
LoadStats();
|
|
}
|
|
});
|
|
LoadStats();
|
|
|
|
startLoadStatRefresher();
|
|
// User.progressDialog=progressDialog;
|
|
}
|
|
|
|
double hourglassTime = 0;
|
|
List<Color> hourglassColors = [Colors.black];
|
|
List<double> hourglassStops = [1];
|
|
int hourglassTotalTime = 0;
|
|
void startLoadStatRefresher() async {
|
|
int lastMinute = 0;
|
|
while (true) {
|
|
if (DateTime.now().minute != lastMinute) {
|
|
lastMinute = DateTime.now().minute;
|
|
|
|
LoadStats();
|
|
}
|
|
await Refresh();
|
|
// hourglassTime+=0.05;
|
|
// if(hourglassTime > 1){
|
|
// hourglassTime=0;
|
|
// }
|
|
hourglassTime =
|
|
((DateTime.now().hour * 60) + DateTime.now().minute) / 1440;
|
|
// hourglassTime = 1;
|
|
// print('hourglass time : $hourglassTime');
|
|
hourglassColors = [];
|
|
hourglassStops = [];
|
|
hourglassTotalTime = 0;
|
|
hourglassCatData.forEach((element) {
|
|
// if(element.time > hourglassTotalTime){
|
|
hourglassTotalTime += element.time;
|
|
// }
|
|
});
|
|
// print('hourglass cat data');
|
|
double stopsTotal = 0;
|
|
for (int i = 0; i < hourglassCatData.length; i++) {
|
|
CatMapData element = hourglassCatData[i];
|
|
// print('${element.name} : ${element.time} / $hourglassTotalTime = ${element.time / hourglassTotalTime}');
|
|
double thisStop = (element.time / hourglassTotalTime);
|
|
hourglassColors.add(element.color);
|
|
hourglassStops.add(stopsTotal + thisStop);
|
|
stopsTotal += thisStop;
|
|
if (i < hourglassCatData.length - 1) {
|
|
hourglassColors.add(hourglassCatData[i + 1].color);
|
|
hourglassStops.add(stopsTotal + thisStop + 0.001);
|
|
}
|
|
}
|
|
// print('total Stops ${stopsTotal}');
|
|
// print('maxT: $hourglassTotalTime');
|
|
if (hourglassColors.isEmpty) {
|
|
hourglassColors.add(Colors.black);
|
|
hourglassStops.add(1);
|
|
}
|
|
|
|
// print('hourglass \n$hourglassColors \n$hourglassStops');
|
|
setState(() {});
|
|
await Future.delayed(Duration(seconds: 1));
|
|
}
|
|
}
|
|
|
|
void init(BuildContext context) async {
|
|
await Future.delayed(Duration(seconds: 1));
|
|
refreshSub = User.refreshStream.stream.listen((value) {
|
|
print("Streaming refresh : $value");
|
|
if (value) {
|
|
// Dialogs.waiting("Syncing...");
|
|
print("Opening progress dialog");
|
|
} else {
|
|
// Dialogs.hide();
|
|
// Dialogs.hide();
|
|
print("Closing progress dialog");
|
|
}
|
|
});
|
|
//await User.refreshUserData();
|
|
}
|
|
|
|
DateTime? lastProductive = null;
|
|
@override
|
|
void dispose() {
|
|
// TODO: implement dispose
|
|
super.dispose();
|
|
connectivitySub?.cancel();
|
|
refreshSub?.closeSteam();
|
|
}
|
|
|
|
String ongoingActName = "";
|
|
DateTime ongoingActStime = DateTime.now();
|
|
|
|
Future<void> Refresh() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
if (prefs.containsKey('current_activity')) {
|
|
List<String> data = [];
|
|
try {
|
|
data = prefs.getString('current_activity')!.split('<td>');
|
|
ongoingActName = data[0];
|
|
ongoingActStime = DateTime.parse(data[2]);
|
|
} catch (e) {
|
|
ongoingActName = "";
|
|
}
|
|
} else {
|
|
ongoingActName = "";
|
|
}
|
|
|
|
if (this.mounted) {
|
|
setState(() {});
|
|
}
|
|
}
|
|
|
|
bool loadingStats = false;
|
|
|
|
DateFormat dFormat = DateFormat("yyyy-MM-dd");
|
|
void LoadStats() async {
|
|
// return;
|
|
// await User.refreshUserData();
|
|
|
|
if (loadingStats) {
|
|
print('loading stats already');
|
|
return;
|
|
} else {
|
|
loadingStats = true;
|
|
}
|
|
await Refresh();
|
|
|
|
Map<Category, int> catTimeMap = <Category, int>{};
|
|
Map<Category, int> catBriefMap = <Category, int>{};
|
|
|
|
Map<String, int> productivtyActs = <String, int>{};
|
|
Map<String, int> unproductivtyActs = <String, int>{};
|
|
Map<TaskType, int> taskTypesDuration = <TaskType, int>{};
|
|
hourglassCatData = [];
|
|
hourglassColors = [];
|
|
firstDay = null;
|
|
lastDay = null;
|
|
String lastDate = "";
|
|
lastProductive = null;
|
|
days = [];
|
|
for (var element in User.activities) {
|
|
if (lastDay == null) {
|
|
lastDay = element.endTime;
|
|
}
|
|
if (taskTypeRange == null) {
|
|
print("$lastDay - $firstDay");
|
|
taskTypeRange = DateTimeRange(
|
|
start: lastDay!.subtract(const Duration(days: 0)), end: lastDay!);
|
|
}
|
|
if (catsRange == null) {
|
|
print("$lastDay - $firstDay");
|
|
catsRange = DateTimeRange(
|
|
start: lastDay!.subtract(const Duration(days: 0)), end: lastDay!);
|
|
}
|
|
if (prodRange == null) {
|
|
print("$lastDay - $firstDay");
|
|
prodRange = DateTimeRange(
|
|
start: lastDay!.subtract(const Duration(days: 7)), end: lastDay!);
|
|
}
|
|
firstDay = element.startTime;
|
|
String thisDate = dFormat.format(element.startTime);
|
|
|
|
int thisMinutes = element.endTime.difference(element.startTime).inMinutes;
|
|
|
|
if (!days.contains(thisDate)) {
|
|
days.add(dFormat.format(element.startTime));
|
|
}
|
|
if (curDay == "") {
|
|
curDay = dFormat.format(DateTime.now());
|
|
}
|
|
|
|
if ((element.startTime.isAfter(taskTypeRange!.start) &&
|
|
element.startTime.isBefore(taskTypeRange!.end)) ||
|
|
(dFormat.format(element.startTime) ==
|
|
dFormat.format(taskTypeRange!.start) ||
|
|
dFormat.format(element.startTime) ==
|
|
dFormat.format(taskTypeRange!.end))) {
|
|
if (taskTypesDuration.containsKey(element.taskType)) {
|
|
taskTypesDuration[element.taskType] =
|
|
taskTypesDuration[element.taskType]! + thisMinutes;
|
|
} else {
|
|
taskTypesDuration.putIfAbsent(element.taskType, () => thisMinutes);
|
|
}
|
|
}
|
|
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 {
|
|
productivtyActs.putIfAbsent(thisDate, () => thisMinutes);
|
|
}
|
|
} else {
|
|
if (unproductivtyActs.containsKey(thisDate)) {
|
|
unproductivtyActs[thisDate] =
|
|
(unproductivtyActs[thisDate]! + thisMinutes);
|
|
} else {
|
|
unproductivtyActs.putIfAbsent(thisDate, () => thisMinutes);
|
|
}
|
|
}
|
|
}
|
|
if (thisDate == curDay) {
|
|
if (element.taskType.cat == null) {
|
|
continue;
|
|
}
|
|
print("Null : ${thisMinutes}");
|
|
if (catTimeMap.containsKey(element.taskType.cat)) {
|
|
catTimeMap[element.taskType.cat!] =
|
|
(catTimeMap[element.taskType.cat]! + thisMinutes);
|
|
} else {
|
|
catTimeMap.putIfAbsent(element.taskType.cat!, () => thisMinutes);
|
|
}
|
|
}
|
|
|
|
if ((element.startTime.isAfter(catsRange!.start) &&
|
|
element.startTime.isBefore(catsRange!.end)) ||
|
|
(dFormat.format(element.startTime) ==
|
|
dFormat.format(catsRange!.start) ||
|
|
dFormat.format(element.startTime) ==
|
|
dFormat.format(catsRange!.end))) {
|
|
if (element.taskType.cat == null) {
|
|
continue;
|
|
}
|
|
print("Null : ${thisMinutes}");
|
|
if (catBriefMap.containsKey(element.taskType.cat)) {
|
|
catBriefMap[element.taskType.cat!] =
|
|
(catBriefMap[element.taskType.cat]! + thisMinutes);
|
|
} else {
|
|
catBriefMap.putIfAbsent(element.taskType.cat!, () => thisMinutes);
|
|
}
|
|
}
|
|
|
|
if (dFormat.format(element.startTime) == dFormat.format(DateTime.now())) {
|
|
if (element.taskType.cat == null) {
|
|
continue;
|
|
}
|
|
print("Null : ${thisMinutes}");
|
|
|
|
int? existingEntryIndex;
|
|
|
|
for (int i = 0; i < hourglassCatData.length; i++) {
|
|
if (hourglassCatData[i].name == element.taskType.category) {
|
|
existingEntryIndex = i;
|
|
}
|
|
}
|
|
|
|
if (existingEntryIndex == null) {
|
|
hourglassCatData.add(CatMapData(element.taskType.category,
|
|
thisMinutes, HexColor.fromHex(element.taskType.cat!.color)));
|
|
} else {
|
|
hourglassCatData[existingEntryIndex!].time += thisMinutes;
|
|
}
|
|
}
|
|
|
|
hourglassCatData.sort((a, b) => a.time.compareTo(b.time));
|
|
// hourglassCatData = hourglassCatData.reversed.toList();
|
|
}
|
|
dailyData = <CatMapData>[];
|
|
productivityData = <ProductivityMapData>[];
|
|
taskTypesData = <TaskTypeMapData>[];
|
|
catsData = <CatMapData>[];
|
|
int trackedTime = 0;
|
|
catTimeMap.forEach((key, value) {
|
|
//print(key.name + " : $value");
|
|
Color barCol = HexColor.fromHex(key.color);
|
|
dailyData.add(CatMapData(key.name, value, barCol));
|
|
trackedTime += value;
|
|
});
|
|
int untrackedTime = 1440 - trackedTime;
|
|
if (untrackedTime < 0) {
|
|
User.refreshUserData().then((val) => LoadStats());
|
|
print("Shit went wrong!");
|
|
}
|
|
print("Tracked time : $trackedTime");
|
|
|
|
dailyData.sort((a, b) {
|
|
return a.name.toLowerCase().compareTo(b.name.toLowerCase());
|
|
});
|
|
if (untrackedTime > 0) {
|
|
dailyData.add(CatMapData("Untracked", 1440 - trackedTime, Colors.black));
|
|
} else {}
|
|
bool untrackedUnprod = await Settings.getUntrackedUnproductive();
|
|
for (var element in days) {
|
|
// if(productivtyActs.containsKey(element) && unproductivtyActs.containsKey(element)){
|
|
int prodActs = (productivtyActs[element] ?? 0);
|
|
int unprodActs = (unproductivtyActs[element] ?? 0);
|
|
double prod = (untrackedUnprod)
|
|
? ((prodActs / 1440) * 100)
|
|
: ((prodActs / unprodActs) * 100);
|
|
var newProdData = ProductivityMapData(element, prod);
|
|
if (prod > 0 && !productivityData.contains(newProdData)) {
|
|
productivityData.add(newProdData);
|
|
}
|
|
// }
|
|
}
|
|
|
|
//Past Prod
|
|
Map<DateTime, double> pastProdData = AnalyticTools.getProductivities(
|
|
DateTimeRange(
|
|
start: prodRange!.start
|
|
.subtract(Duration(days: prodRange!.duration.inDays + 1)),
|
|
end: prodRange!.start.subtract(Duration(days: 1))));
|
|
pastProductivityData = [];
|
|
pastProdData.forEach((key, value) {
|
|
pastProductivityData.add(ProductivityMapData(
|
|
dFormat
|
|
.format(key.add(Duration(days: prodRange!.duration.inDays + 1))),
|
|
value));
|
|
});
|
|
|
|
taskTypesDuration.forEach((key, value) {
|
|
print("$key : $value");
|
|
taskTypesData.add(
|
|
TaskTypeMapData(key.name, value, HexColor.fromHex(key.cat!.color)));
|
|
});
|
|
|
|
taskTypesData.sort((a, b) {
|
|
return a.time.compareTo(b.time);
|
|
});
|
|
|
|
catBriefMap.forEach((key, value) {
|
|
print(key.name + " : $value");
|
|
Color barCol = HexColor.fromHex(key.color);
|
|
catsData.add(CatMapData(key.name, value, barCol));
|
|
});
|
|
catsData.sort((a, b) => a.time.compareTo(b.time));
|
|
|
|
//relative TOdos
|
|
List<String> relativeTodoDays = [];
|
|
relativeTodos = [];
|
|
for (int i = 0; i < 2; i++) {
|
|
relativeTodoDays
|
|
.add(dFormat.format(DateTime.now().add(Duration(days: i))));
|
|
}
|
|
|
|
User.todos.forEach((element) {
|
|
if (relativeTodoDays.contains(dFormat.format(element.dueDate))) {
|
|
//Suitaable
|
|
relativeTodos.add(element);
|
|
}
|
|
});
|
|
|
|
//curDay = days[0];
|
|
if (this.mounted) {
|
|
setState(() {});
|
|
}
|
|
|
|
// print('productivity data');
|
|
// productivityData.forEach((element) {
|
|
// print(element.day);
|
|
// });
|
|
loadingStats = false;
|
|
// loadingStats=false;
|
|
}
|
|
|
|
void showOfflineSnack() async {
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
if (User.offline) {
|
|
const SnackBar offlineSnack = SnackBar(
|
|
content: Text('Offline'),
|
|
duration: Duration(seconds: 100),
|
|
);
|
|
// ScaffoldMessenger.of(context).showSnackBar(offlineSnack);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
double avgProd = 0;
|
|
double pastAvgProd = 0;
|
|
int _avgProdCount = 0;
|
|
int _pastAvgProdCount = 0;
|
|
productivityData.forEach((element) {
|
|
avgProd += element.productivity;
|
|
_avgProdCount++;
|
|
});
|
|
|
|
pastProductivityData.forEach((element) {
|
|
pastAvgProd += element.productivity;
|
|
_pastAvgProdCount++;
|
|
});
|
|
|
|
avgProd = avgProd / _avgProdCount;
|
|
pastAvgProd = pastAvgProd / _pastAvgProdCount;
|
|
bool landscape = ((MediaQuery.of(context).size.width /
|
|
MediaQuery.of(context).size.height) >
|
|
1);
|
|
return Scaffold(
|
|
floatingActionButton: FloatingActionButton.extended(
|
|
onPressed: () {
|
|
Navigator.of(context)
|
|
.push(MaterialPageRoute(builder: (context) => NewActivity()));
|
|
},
|
|
label: Text("New Activity"),
|
|
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: [
|
|
FaIcon(FontAwesomeIcons.chartBar),
|
|
SizedBox(width: 10),
|
|
Text('Summary')
|
|
]),
|
|
Row(
|
|
children: [
|
|
(User.offline)
|
|
? Icon(Icons
|
|
.signal_cellular_connected_no_internet_4_bar_outlined)
|
|
: InkWell(
|
|
onTap: () {
|
|
setState(() async {
|
|
await User.refreshUserData();
|
|
LoadStats();
|
|
});
|
|
},
|
|
child: Icon(Icons.refresh, size: 30),
|
|
)
|
|
],
|
|
)
|
|
],
|
|
),
|
|
//Container(color: Colors.red,child: Text("Offline",style:TextStyle(fontSize: 5))),
|
|
],
|
|
)),
|
|
drawer: landscape ? null : navDrawer(context, 0),
|
|
body: SafeArea(
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
landscape ? navDrawer(context, 0) : Container(),
|
|
(User.activities.isEmpty)
|
|
? Container(
|
|
width: 1000,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
Expanded(
|
|
flex: 1,
|
|
child: Container(),
|
|
),
|
|
Expanded(
|
|
flex: 2,
|
|
child:
|
|
Image(image: AssetImage('images/empty.png'))),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Text(
|
|
"Add your first activity to access Summary",
|
|
style: TextStyle(
|
|
color: Colors.grey,
|
|
fontStyle: FontStyle.italic),
|
|
))
|
|
]),
|
|
)
|
|
: Expanded(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.max,
|
|
children: [
|
|
if (User.offline)
|
|
Container(
|
|
child: Container(
|
|
width: 1000,
|
|
color: Colors.red,
|
|
child: Align(
|
|
alignment: Alignment.center,
|
|
child: Text("Offline")))),
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.vertical,
|
|
child: Column(
|
|
children: [
|
|
(false)
|
|
? Container(
|
|
padding: EdgeInsets.all(20),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceBetween,
|
|
mainAxisSize: MainAxisSize.max,
|
|
children: [
|
|
Text("Good\nMorning!",
|
|
style: TextStyle(
|
|
fontSize: 23,
|
|
fontStyle:
|
|
FontStyle.italic)),
|
|
Text(
|
|
"12%",
|
|
style: TextStyle(fontSize: 30),
|
|
),
|
|
Column(
|
|
children: [
|
|
Text(DateFormat("yy - MM-dd")
|
|
.format(DateTime.now())),
|
|
Text(
|
|
DateFormat("HH:mm")
|
|
.format(DateTime.now()),
|
|
style: TextStyle(
|
|
fontSize: 40)),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
)
|
|
: Container(),
|
|
//Ongoing activity card
|
|
(ongoingActName == "")
|
|
? Container()
|
|
: Container(
|
|
padding: EdgeInsets.all(10),
|
|
child: Card(
|
|
color: Colors.white12,
|
|
elevation: 20,
|
|
shadowColor: Colors.green,
|
|
child: Padding(
|
|
padding:
|
|
const EdgeInsets.symmetric(
|
|
horizontal: 25,
|
|
vertical: 20),
|
|
child: Column(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.spaceBetween,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
SpinKitPouringHourGlass(
|
|
color: Colors.amber),
|
|
SizedBox(
|
|
width: 20,
|
|
),
|
|
Flexible(
|
|
child: Text(
|
|
"You are engaged in '$ongoingActName' since ${DateFormat("hh:mm").format(ongoingActStime)}.",
|
|
style: TextStyle(
|
|
fontSize: 19),
|
|
textAlign:
|
|
TextAlign.center,
|
|
)),
|
|
],
|
|
),
|
|
SizedBox(
|
|
height: 10,
|
|
),
|
|
Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.spaceBetween,
|
|
children: [
|
|
Container(
|
|
child: Text(MinutesToTimeString(
|
|
DateTime.now()
|
|
.difference(
|
|
ongoingActStime)
|
|
.inMinutes))),
|
|
Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.end,
|
|
children: [
|
|
InkWell(
|
|
onTap: () {
|
|
Dialogs
|
|
.ongoing();
|
|
},
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors
|
|
.green,
|
|
borderRadius:
|
|
BorderRadius.circular(
|
|
10)),
|
|
child: Padding(
|
|
padding: EdgeInsets.symmetric(
|
|
vertical:
|
|
8,
|
|
horizontal:
|
|
15),
|
|
child: Text(
|
|
'Take Action')))),
|
|
],
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
)),
|
|
),
|
|
if (relativeTodos.isNotEmpty)
|
|
Container(
|
|
padding: EdgeInsets.all(10),
|
|
child: Card(
|
|
color: Colors.white10,
|
|
elevation: 20,
|
|
shadowColor: Colors.black,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 20, vertical: 10),
|
|
child: Column(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.spaceBetween,
|
|
children: [
|
|
Text('Close To-Do',
|
|
style: TextStyle(
|
|
fontSize: 16)),
|
|
MaterialButton(
|
|
height: 30,
|
|
color: Colors.green,
|
|
onPressed: () {
|
|
Navigator.of(context)
|
|
.pushNamed('/Todos');
|
|
},
|
|
child: Row(
|
|
children: [
|
|
Text('More'),
|
|
Icon(Icons
|
|
.keyboard_arrow_right)
|
|
],
|
|
),
|
|
)
|
|
],
|
|
),
|
|
LimitedBox(
|
|
maxHeight: 250,
|
|
child: ListView.builder(
|
|
shrinkWrap: true,
|
|
itemCount:
|
|
relativeTodos.length,
|
|
itemBuilder:
|
|
(context, index) {
|
|
return Container(
|
|
padding:
|
|
EdgeInsets.all(10),
|
|
margin:
|
|
EdgeInsets.symmetric(
|
|
vertical: 2),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black26,
|
|
borderRadius:
|
|
BorderRadius
|
|
.circular(
|
|
10)),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.spaceBetween,
|
|
children: [
|
|
SizedBox(
|
|
width: MediaQuery.of(
|
|
context)
|
|
.size
|
|
.width *
|
|
0.7,
|
|
child: Text(
|
|
'${relativeTodos[index].task!.name} -> ${relativeTodos[index].metadata}')),
|
|
Container(
|
|
padding: EdgeInsets
|
|
.symmetric(
|
|
horizontal:
|
|
5),
|
|
decoration: BoxDecoration(
|
|
borderRadius:
|
|
BorderRadius
|
|
.circular(
|
|
20),
|
|
color: Colors
|
|
.blue),
|
|
child: Text(DateFormat(
|
|
'MM/dd')
|
|
.format(relativeTodos[
|
|
index]
|
|
.dueDate)))
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
)
|
|
],
|
|
),
|
|
)),
|
|
),
|
|
Container(
|
|
padding: EdgeInsets.all(10),
|
|
child: Card(
|
|
color: Colors.white10,
|
|
elevation: 20,
|
|
shadowColor: Colors.black,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 25, vertical: 20),
|
|
child: Column(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceAround,
|
|
children: [
|
|
Container(
|
|
width: 100,
|
|
height: 150,
|
|
child: CustomPaint(
|
|
painter: HourglassPainter(
|
|
hourglassTime,
|
|
hourglassColors,
|
|
hourglassStops),
|
|
)),
|
|
Column(
|
|
children: [
|
|
Text(
|
|
DateFormat("MMMM-dd")
|
|
.format(
|
|
DateTime.now()),
|
|
style: TextStyle(
|
|
fontSize: 18)),
|
|
Text(
|
|
DateFormat("hh:mm a")
|
|
.format(
|
|
DateTime.now()),
|
|
style: TextStyle(
|
|
fontSize: 40,
|
|
fontWeight:
|
|
FontWeight
|
|
.bold)),
|
|
Container(
|
|
height: 20,
|
|
)
|
|
],
|
|
)
|
|
],
|
|
)
|
|
],
|
|
),
|
|
)),
|
|
),
|
|
Container(
|
|
height: 400,
|
|
padding: EdgeInsets.all(10),
|
|
child: Card(
|
|
color: Colors.white10,
|
|
elevation: 20,
|
|
shadowColor: Colors.black,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10, vertical: 20),
|
|
child: Column(
|
|
children: [
|
|
Padding(
|
|
padding:
|
|
const EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 0),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.spaceBetween,
|
|
children: [
|
|
Text("Productivity",
|
|
style: TextStyle(
|
|
color: Colors.green,
|
|
fontWeight:
|
|
FontWeight.bold)),
|
|
InkWell(
|
|
onTap: () async {
|
|
DateTimeRange? value =
|
|
await showDateRangePicker(
|
|
context: context,
|
|
firstDate:
|
|
firstDay ??
|
|
DateTime
|
|
.now(),
|
|
lastDate: lastDay ??
|
|
DateTime
|
|
.now());
|
|
if (value != null) {
|
|
prodRange =
|
|
DateTimeRange(
|
|
start: DateTime(
|
|
value.start
|
|
.year,
|
|
value.start
|
|
.month,
|
|
value.start
|
|
.day),
|
|
end: DateTime(
|
|
value.end
|
|
.year,
|
|
value.end
|
|
.month,
|
|
value.end
|
|
.day,
|
|
23,
|
|
59,
|
|
59));
|
|
}
|
|
|
|
LoadStats();
|
|
},
|
|
child: Text((prodRange !=
|
|
null)
|
|
? (DateFormat("MM/dd")
|
|
.format(
|
|
prodRange!
|
|
.start) +
|
|
" - " +
|
|
DateFormat("MM/dd")
|
|
.format(
|
|
prodRange!
|
|
.end))
|
|
: 'n/a'),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
Divider(),
|
|
Row(
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text("Today : "),
|
|
Text(
|
|
"${(productivityData.length > 0) ? productivityData[0].productivity.toStringAsFixed(1) : 'n/a'}%",
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
color: (productivityData
|
|
.length >
|
|
1)
|
|
? ((productivityData[
|
|
0]
|
|
.productivity >
|
|
productivityData[
|
|
1]
|
|
.productivity)
|
|
? Colors
|
|
.lightGreenAccent
|
|
: Colors
|
|
.red)
|
|
: Colors.white))
|
|
],
|
|
),
|
|
Row(
|
|
children: [
|
|
Text("Yesterday : "),
|
|
Text(
|
|
"${(productivityData.length > 1) ? productivityData[1].productivity.toStringAsFixed(1) : 'n/a'}%",
|
|
style: TextStyle(
|
|
fontSize: 18))
|
|
],
|
|
),
|
|
],
|
|
),
|
|
Expanded(
|
|
child: SfCartesianChart(
|
|
// Initialize category axis
|
|
primaryXAxis: CategoryAxis(),
|
|
series: <
|
|
LineSeries<
|
|
ProductivityMapData,
|
|
String>>[
|
|
LineSeries<
|
|
ProductivityMapData,
|
|
String>(
|
|
// Bind data source
|
|
markerSettings: MarkerSettings(
|
|
isVisible: true,
|
|
shape: DataMarkerType
|
|
.circle),
|
|
dataSource: productivityData.reversed
|
|
.toList(),
|
|
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) {
|
|
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");
|
|
},
|
|
pointColorMapper: (ProductivityMapData sales, _) => (User.journalExists(dFormat.parse(sales.day)) ? Colors.lightGreenAccent : Colors.green)),
|
|
LineSeries<
|
|
ProductivityMapData,
|
|
String>(
|
|
// Bind data source
|
|
// dashArray: [0,0.5,1],
|
|
dashArray: [2, 5],
|
|
markerSettings:
|
|
MarkerSettings(
|
|
isVisible: true,
|
|
shape:
|
|
DataMarkerType
|
|
.circle),
|
|
dataSource:
|
|
pastProductivityData
|
|
.reversed
|
|
.toList(),
|
|
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),
|
|
|
|
color: Colors.grey
|
|
// 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,
|
|
]),
|
|
),
|
|
Row(
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text("Average : "),
|
|
Text(
|
|
"${(productivityData.length > 0) ? avgProd.toStringAsFixed(1) : 'n/a'}%",
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
color: (avgProd >
|
|
pastAvgProd)
|
|
? Colors
|
|
.lightGreenAccent
|
|
: Colors.red))
|
|
],
|
|
),
|
|
Row(
|
|
children: [
|
|
Text("Past Average : "),
|
|
Text(
|
|
"${(productivityData.length > 1) ? pastAvgProd.toStringAsFixed(1) : 'n/a'}%",
|
|
style: TextStyle(
|
|
fontSize: 18))
|
|
],
|
|
),
|
|
],
|
|
),
|
|
SizedBox(
|
|
height: 20,
|
|
),
|
|
if (lastProductive != null &&
|
|
DateTime.now()
|
|
.difference(
|
|
lastProductive!)
|
|
.inMinutes >
|
|
60)
|
|
RichText(
|
|
text: TextSpan(children: <
|
|
TextSpan>[
|
|
TextSpan(
|
|
text:
|
|
"You haven't been productive in last",
|
|
style: TextStyle(
|
|
color: Colors.orange)),
|
|
TextSpan(
|
|
text:
|
|
" ${MinutesToTimeString(DateTime.now().difference(lastProductive!).inMinutes)}",
|
|
style: TextStyle(
|
|
color: Colors.redAccent,
|
|
fontWeight:
|
|
FontWeight.bold))
|
|
]))
|
|
],
|
|
),
|
|
)),
|
|
),
|
|
Container(
|
|
height: 400,
|
|
padding: EdgeInsets.all(10),
|
|
child: Card(
|
|
color: Colors.white10,
|
|
elevation: 20,
|
|
shadowColor: Colors.black,
|
|
child: Padding(
|
|
padding: EdgeInsets.all(8),
|
|
child: (!days.isEmpty)
|
|
? Column(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.spaceBetween,
|
|
children: [
|
|
Padding(
|
|
padding:
|
|
EdgeInsets
|
|
.all(
|
|
20),
|
|
child: Text(
|
|
'Daily Briefing',
|
|
style: TextStyle(
|
|
fontWeight:
|
|
FontWeight.bold))),
|
|
dayPickerWidget(
|
|
days,
|
|
value: curDay,
|
|
onChange:
|
|
(_value) {
|
|
print(
|
|
'new val : $_value');
|
|
curDay = _value;
|
|
setState(() {
|
|
LoadStats();
|
|
});
|
|
}),
|
|
]),
|
|
Expanded(
|
|
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,
|
|
yValueMapper: (CatMapData data, _) =>
|
|
data.time,
|
|
dataLabelMapper:
|
|
(CatMapData sales,
|
|
_) =>
|
|
MinutesToTimeString(sales
|
|
.time),
|
|
dataLabelSettings: DataLabelSettings(
|
|
isVisible:
|
|
true,
|
|
useSeriesColor:
|
|
true,
|
|
overflowMode:
|
|
OverflowMode
|
|
.shift,
|
|
showZeroValue:
|
|
false))
|
|
]))
|
|
],
|
|
)
|
|
: Row(
|
|
mainAxisSize:
|
|
MainAxisSize.max,
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.center,
|
|
children: [
|
|
CircularProgressIndicator()
|
|
])))),
|
|
Container(
|
|
height: (taskTypesData.length * 45)
|
|
.clamp(350, 1000)
|
|
.toDouble(),
|
|
padding: EdgeInsets.all(10),
|
|
child: Card(
|
|
color: Colors.white10,
|
|
elevation: 20,
|
|
shadowColor: Colors.black,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 25, vertical: 25),
|
|
child: Column(children: [
|
|
Row(
|
|
mainAxisSize:
|
|
MainAxisSize.max,
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.spaceBetween,
|
|
children: [
|
|
Text("Task Types",
|
|
style: TextStyle(
|
|
fontWeight:
|
|
FontWeight
|
|
.bold)),
|
|
InkWell(
|
|
onTap: () async {
|
|
DateTimeRange? value =
|
|
await showDateRangePicker(
|
|
context:
|
|
context,
|
|
firstDate:
|
|
firstDay ??
|
|
DateTime
|
|
.now(),
|
|
lastDate: lastDay ??
|
|
DateTime
|
|
.now());
|
|
if (value != null) {
|
|
taskTypeRange = value;
|
|
}
|
|
|
|
LoadStats();
|
|
},
|
|
child: Text((taskTypeRange !=
|
|
null)
|
|
? (DateFormat("MM/dd")
|
|
.format(
|
|
taskTypeRange!
|
|
.start) +
|
|
" - " +
|
|
DateFormat(
|
|
"MM/dd")
|
|
.format(
|
|
taskTypeRange!
|
|
.end))
|
|
: 'n/a'),
|
|
)
|
|
]),
|
|
Expanded(
|
|
// maxHeight: 300,
|
|
// maxWidth: 100,
|
|
child: SfCartesianChart(
|
|
primaryXAxis:
|
|
CategoryAxis(),
|
|
//primaryYAxis: NumericAxis(minimum: 0, maximum: 40, interval: 10),
|
|
series: <
|
|
ChartSeries<
|
|
TaskTypeMapData,
|
|
String>>[
|
|
BarSeries<TaskTypeMapData, String>(
|
|
dataSource:
|
|
taskTypesData,
|
|
xValueMapper:
|
|
(TaskTypeMapData data, _) =>
|
|
data.task,
|
|
yValueMapper: (TaskTypeMapData data, _) =>
|
|
data.time / 60,
|
|
pointColorMapper:
|
|
(TaskTypeMapData data, _) =>
|
|
data.color,
|
|
dataLabelMapper: (TaskTypeMapData data, _) =>
|
|
MinutesToTimeString(
|
|
data.time),
|
|
dataLabelSettings:
|
|
DataLabelSettings(
|
|
isVisible:
|
|
true),
|
|
color: Color.fromRGBO(
|
|
8, 142, 255, 1))
|
|
]),
|
|
)
|
|
])))),
|
|
Container(
|
|
height: (catsData.length * 45)
|
|
.clamp(350, 1000)
|
|
.toDouble(),
|
|
padding: EdgeInsets.all(10),
|
|
child: Card(
|
|
color: Colors.white10,
|
|
elevation: 20,
|
|
shadowColor: Colors.black,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 25, vertical: 25),
|
|
child: Column(children: [
|
|
Row(
|
|
mainAxisSize:
|
|
MainAxisSize.max,
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.spaceBetween,
|
|
children: [
|
|
Text("Categories",
|
|
style: TextStyle(
|
|
fontWeight:
|
|
FontWeight
|
|
.bold)),
|
|
InkWell(
|
|
onTap: () async {
|
|
DateTimeRange? value =
|
|
await showDateRangePicker(
|
|
context:
|
|
context,
|
|
firstDate:
|
|
firstDay ??
|
|
DateTime
|
|
.now(),
|
|
lastDate: lastDay ??
|
|
DateTime
|
|
.now());
|
|
if (value != null) {
|
|
catsRange = value;
|
|
}
|
|
|
|
LoadStats();
|
|
},
|
|
child: Text((catsRange !=
|
|
null)
|
|
? (DateFormat("MM/dd")
|
|
.format(
|
|
catsRange!
|
|
.start) +
|
|
" - " +
|
|
DateFormat(
|
|
"MM/dd")
|
|
.format(
|
|
catsRange!
|
|
.end))
|
|
: 'n/a'),
|
|
)
|
|
]),
|
|
Expanded(
|
|
// maxHeight: 300,
|
|
// maxWidth: 100,
|
|
child: SfCartesianChart(
|
|
primaryXAxis:
|
|
CategoryAxis(),
|
|
//primaryYAxis: NumericAxis(minimum: 0, maximum: 40, interval: 10),
|
|
series: <
|
|
ChartSeries<CatMapData,
|
|
String>>[
|
|
BarSeries<CatMapData, String>(
|
|
dataSource: catsData,
|
|
xValueMapper:
|
|
(CatMapData data, _) =>
|
|
data.name,
|
|
yValueMapper: (CatMapData data, _) =>
|
|
data.time / 60,
|
|
pointColorMapper:
|
|
(CatMapData data, _) =>
|
|
data.color,
|
|
dataLabelMapper: (CatMapData data, _) =>
|
|
MinutesToTimeString(
|
|
data.time),
|
|
dataLabelSettings:
|
|
DataLabelSettings(
|
|
isVisible:
|
|
true),
|
|
color: Color.fromRGBO(
|
|
8, 142, 255, 1))
|
|
]),
|
|
)
|
|
])))),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget dayPickerWidget(List<String> list,
|
|
{required String value, required Function(String value) onChange}) {
|
|
if (!list.contains(value)) {
|
|
print("resetting");
|
|
onChange(list[0]);
|
|
}
|
|
bool nextAvailable = (list.indexOf(value) < (list.length - 1));
|
|
bool prevAvailable = (list.indexOf(value) > 0);
|
|
return Row(
|
|
children: [
|
|
InkWell(
|
|
onTap: () {
|
|
if (nextAvailable) {
|
|
onChange(list[list.indexOf(value) + 1]);
|
|
}
|
|
},
|
|
child: Container(
|
|
height: 40,
|
|
width: 40,
|
|
child: Icon(Icons.arrow_back_ios,
|
|
size: 18, color: (nextAvailable) ? Colors.white : Colors.grey),
|
|
),
|
|
),
|
|
Text(
|
|
value,
|
|
),
|
|
InkWell(
|
|
onTap: () {
|
|
if (prevAvailable) {
|
|
onChange(list[list.indexOf(value) - 1]);
|
|
}
|
|
},
|
|
child: Container(
|
|
height: 40,
|
|
width: 40,
|
|
child: Icon(Icons.arrow_forward_ios,
|
|
size: 18,
|
|
color: (prevAvailable) ? Colors.white : Colors.grey),
|
|
))
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
Widget moreButton() {
|
|
return MaterialButton(
|
|
onPressed: () {},
|
|
color: Colors.green,
|
|
child: Row(
|
|
children: [Text('More'), Icon(Icons.keyboard_arrow_right)],
|
|
));
|
|
}
|
|
|
|
Drawer navDrawer(BuildContext context, int pageIndex) {
|
|
return Drawer(
|
|
child: ListView(
|
|
children: [
|
|
Padding(
|
|
padding: EdgeInsets.all(16),
|
|
child:
|
|
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
|
Text("Time Tracker",
|
|
style: TextStyle(
|
|
fontSize: 25,
|
|
color: Theme.of(context).accentColor,
|
|
fontWeight: FontWeight.bold)),
|
|
Icon(
|
|
Icons.more_time,
|
|
size: 30,
|
|
),
|
|
])),
|
|
Divider(),
|
|
ListTile(
|
|
selected: (pageIndex == 0),
|
|
title: Text('Summary'),
|
|
leading:
|
|
Icon(Icons.article_outlined, color: Theme.of(context).primaryColor),
|
|
onTap: () {
|
|
if (pageIndex == 0) {
|
|
return;
|
|
}
|
|
Navigator.of(context).pushReplacementNamed('/home');
|
|
},
|
|
),
|
|
ListTile(
|
|
selected: (pageIndex == 1),
|
|
title: Text('Analytics'),
|
|
leading: Icon(Icons.analytics_outlined,
|
|
color: Theme.of(context).primaryColor),
|
|
onTap: () {
|
|
if (pageIndex == 1) {
|
|
return;
|
|
}
|
|
Navigator.of(context).pushReplacementNamed('/Analytics');
|
|
},
|
|
),
|
|
Divider(),
|
|
ListTile(
|
|
selected: (pageIndex == 2),
|
|
title: Text('Activities'),
|
|
leading: Icon(Icons.task, color: Theme.of(context).primaryColor),
|
|
onTap: () {
|
|
if (pageIndex == 2) {
|
|
return;
|
|
}
|
|
Navigator.of(context).pushReplacementNamed('/Activities');
|
|
},
|
|
),
|
|
ListTile(
|
|
selected: (pageIndex == 3),
|
|
title: Text('Task Types'),
|
|
leading: Icon(Icons.task, color: Theme.of(context).primaryColor),
|
|
onTap: () {
|
|
if (pageIndex == 3) {
|
|
return;
|
|
}
|
|
Navigator.of(context).pushReplacementNamed('/Tasks');
|
|
},
|
|
),
|
|
ListTile(
|
|
selected: (pageIndex == 4),
|
|
title: Text('Categories'),
|
|
leading: Icon(Icons.account_tree_outlined,
|
|
color: Theme.of(context).primaryColor),
|
|
onTap: () {
|
|
if (pageIndex == 4) {
|
|
return;
|
|
}
|
|
Navigator.of(context).pushReplacementNamed('/Categories');
|
|
},
|
|
),
|
|
Divider(),
|
|
ListTile(
|
|
selected: (pageIndex == 7),
|
|
title: Text('Projects'),
|
|
leading: FaIcon(FontAwesomeIcons.rocket,
|
|
color: Theme.of(context).primaryColor),
|
|
onTap: () {
|
|
if (pageIndex == 7) {
|
|
return;
|
|
}
|
|
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'),
|
|
leading: FaIcon(FontAwesomeIcons.bookJournalWhills,
|
|
color: Theme.of(context).primaryColor),
|
|
onTap: () {
|
|
if (pageIndex == 8) {
|
|
return;
|
|
}
|
|
Navigator.of(context).pushReplacementNamed('/Journal');
|
|
},
|
|
),
|
|
// ListTile(
|
|
// selected: (pageIndex == 7),
|
|
// title: Text('TODO'),
|
|
// leading: Icon(Icons.check, color: Theme.of(context).primaryColor),
|
|
// onTap: () {
|
|
// if (pageIndex == 7) {
|
|
// return;
|
|
// }
|
|
// // Navigator.of(context).pushReplacementNamed('/Todo');
|
|
// },
|
|
// ),
|
|
Divider(),
|
|
ListTile(
|
|
selected: (pageIndex == 5),
|
|
title: Text('Settings'),
|
|
leading: Icon(Icons.settings, color: Colors.blueGrey),
|
|
onTap: () {
|
|
if (pageIndex == 5) {
|
|
return;
|
|
}
|
|
Navigator.of(context).pushNamed('/Settings');
|
|
},
|
|
),
|
|
ListTile(
|
|
selected: (pageIndex == 6),
|
|
title: Text('About'),
|
|
leading: Icon(Icons.help_outline_outlined),
|
|
onTap: () {
|
|
showAboutDialog(context: context);
|
|
},
|
|
),
|
|
],
|
|
));
|
|
}
|
|
|
|
class CatMapData {
|
|
CatMapData(this.name, this.time, this.color);
|
|
final String name;
|
|
int time;
|
|
final Color color;
|
|
}
|
|
|
|
class ProductivityMapData {
|
|
ProductivityMapData(this.day, this.productivity);
|
|
|
|
final String day;
|
|
final double productivity;
|
|
}
|
|
|
|
class TaskTypeMapData {
|
|
TaskTypeMapData(this.task, this.time, this.color);
|
|
final Color color;
|
|
final String task;
|
|
final int time;
|
|
}
|
|
|
|
String MinutesToTimeString(minutes) {
|
|
int hours = (minutes / 60).floor();
|
|
int mins = minutes % 60;
|
|
|
|
String str = "";
|
|
if (hours > 0) {
|
|
str += hours.toString() + "h";
|
|
}
|
|
if (mins > 0) {
|
|
str += ((hours > 0) ? " " : "") + mins.toString() + "m";
|
|
}
|
|
|
|
return str;
|
|
}
|