diff --git a/lib/consts.dart b/lib/consts.dart index 82d1220..9ad38ec 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -9,7 +9,7 @@ class TTConsts { static get SESSION_SERVER => "https://api.zontreck.com/timetrack/$UPDATE_CHANNEL/timetrack.php"; - static const VERSION = "1.0.0-beta.8"; + static const VERSION = "1.0.0-beta.9"; static bool UPDATE_AVAILABLE = false; static UpdateChannel UPDATE_CHANNEL = UpdateChannel.beta; diff --git a/lib/data.dart b/lib/data.dart index b8a45f5..bba5652 100644 --- a/lib/data.dart +++ b/lib/data.dart @@ -24,6 +24,7 @@ class SessionData { static Callbacks Calls = Callbacks(); static String LastSessionID = ""; static String DisplayError = ""; + static double? TotalPay; /// Is true if the try-catch is tripped or if not running on Android static bool isWeb = false; @@ -204,27 +205,32 @@ class SessionData { saveData["positions"] = posx; saveData["start"] = StartTime.toIso8601String(); saveData["end"] = EndTime.toIso8601String(); + if (TotalPay != null) saveData["totalPay"] = TotalPay; return saveData; } - static Future DownloadData() async { + static Future DownloadData() async { Dio dio = Dio(); Map payload = {"cmd": "get", "id": LastSessionID}; // Send the data, and get the response - var reply = await dio.post( - TTConsts.SESSION_SERVER, - data: json.encode(payload), - ); + try { + var reply = await dio.post( + TTConsts.SESSION_SERVER, + data: json.encode(payload), + ); - LoadData(reply.data as Map); + return LoadData(reply.data as Map); + } catch (E) { + return false; + } } - static void LoadData(Map jsMap) { + static bool LoadData(Map jsMap) { if (jsMap.containsKey("error")) { LastSessionID = ""; - return; + return false; } List trips = jsMap['trips'] as List; List pos = jsMap['positions'] as List; @@ -244,7 +250,14 @@ class SessionData { if (jsMap.containsKey("end")) EndTime = DateTime.parse(jsMap["end"] as String); + if (jsMap.containsKey("totalPay")) + TotalPay = jsMap["totalPay"] as double; + else + TotalPay = null; + IsReadOnly = true; + + return true; } static Future GetNewLocation() async { diff --git a/lib/pages/HomePage.dart b/lib/pages/HomePage.dart index 03f01c2..2fae2b2 100644 --- a/lib/pages/HomePage.dart +++ b/lib/pages/HomePage.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:libacflutter/Constants.dart'; +import 'package:libacflutter/Prompt.dart'; import 'package:timetrack/consts.dart'; import 'package:timetrack/data.dart'; @@ -164,11 +165,38 @@ class _HomePageState extends State { ), ElevatedButton( onPressed: () async { - SessionData.currentDelivery!.MarkEndLocation(); + await showDialog( + context: context, + builder: (builder) { + return AlertDialog( + icon: Icon(Icons.warning), + title: Text("Are you sure you want to end the trip?"), + content: Text( + "Once ended, a marker for your current delivery will be dropped at your current location", + ), + actions: [ + ElevatedButton( + onPressed: () { + Navigator.pop(context); - SessionData.EndTrip(); + SessionData.currentDelivery!.MarkEndLocation(); - setState(() {}); + SessionData.EndTrip(); + + setState(() {}); + }, + child: Text("Yes"), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text("Cancel"), + ), + ], + ); + }, + ); }, child: Text("END TRIP"), ), @@ -181,10 +209,37 @@ class _HomePageState extends State { children: [ ElevatedButton( onPressed: () async { - SessionData.currentDelivery!.MarkEndLocation(); - SessionData.GetNewDelivery(); + await showDialog( + context: context, + builder: (builder) { + return AlertDialog( + icon: Icon(Icons.warning), + title: Text("Are you sure you want to start a new delivery?"), + content: Text( + "Once ended, a marker for your current delivery will be dropped at your current location", + ), + actions: [ + ElevatedButton( + onPressed: () { + Navigator.pop(context); - setState(() {}); + SessionData.currentDelivery!.MarkEndLocation(); + SessionData.GetNewDelivery(); + + setState(() {}); + }, + child: Text("Yes"), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text("Cancel"), + ), + ], + ); + }, + ); }, child: Text("Start new delivery"), ), @@ -197,10 +252,35 @@ class _HomePageState extends State { children: [ ElevatedButton( onPressed: () async { - SessionData.GetNewTrip(); - SessionData.GetNewDelivery(); + await showDialog( + context: context, + builder: (builder) { + return AlertDialog( + icon: Icon(Icons.warning), + title: Text("Are you sure you want to start a new trip?"), + content: Text("Starting a trip will also start a delivery."), + actions: [ + ElevatedButton( + onPressed: () { + Navigator.pop(context); - setState(() {}); + SessionData.GetNewTrip(); + SessionData.GetNewDelivery(); + + setState(() {}); + }, + child: Text("Yes"), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text("Cancel"), + ), + ], + ); + }, + ); }, child: Text("Start New Trip"), ), @@ -220,8 +300,55 @@ class _HomePageState extends State { }, ); } else { - SessionData.Logout(); - setState(() {}); + await showDialog( + context: context, + builder: (builder) { + return AlertDialog( + icon: Icon(Icons.warning), + title: Text("Are you sure you want to end the work day?"), + content: Text( + "Ending the work day will finalize your session. A code will pop up. To copy a URL to this session to your clipboard, tap and hold on the session code. You will be asked to enter the total pay if you tap yes.\n\nEnding the work day cannot be undone.", + ), + actions: [ + ElevatedButton( + onPressed: () async { + Navigator.pop(context); + + // Prompt for the total pay + var reply = await showDialog( + context: context, + builder: (bldx) { + return InputPrompt( + title: "What was the total pay?", + prompt: + "Enter the total pay here. If you do not want to, simply leave the field blank and a 0 will be used instead.", + type: InputPromptType.Number, + ); + }, + ); + if (reply == null || reply as String == "") { + reply = 0.0; + } + + if (reply is String) reply = double.parse(reply); + + SessionData.TotalPay = reply as double; + + await SessionData.Logout(); + setState(() {}); + }, + child: Text("Yes"), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text("Cancel"), + ), + ], + ); + }, + ); } }, child: Text("End work day"), @@ -234,7 +361,7 @@ class _HomePageState extends State { return Column( children: [ Text( - "You are now on the clock\nYour location is being tracked for record keeping purposes.\n\nYou started at ${SessionData.StartTime.toLocal()}\n\n", + "You are now on the clock\nYour location is being tracked for record keeping purposes.\n\nYou started ${SessionData.GetTotalTimeWorked(SessionData.StartTime, DateTime.now())}\n\n", style: TextStyle(fontSize: 18), ), if (SessionData.currentTrip != null) GetTripWidgets(), diff --git a/lib/pages/WebMainPage.dart b/lib/pages/WebMainPage.dart index d5301fb..3f71db7 100644 --- a/lib/pages/WebMainPage.dart +++ b/lib/pages/WebMainPage.dart @@ -96,10 +96,43 @@ class _WebMain extends State { ), ElevatedButton( onPressed: () async { - SessionData.IsReadOnly = false; - SessionData.Trips = []; - SessionData.positions = []; - SessionData.DisplayError = ""; + await showDialog( + context: context, + builder: (builder) { + return AlertDialog( + icon: Icon(Icons.warning_amber), + title: Text("Are you sure?"), + content: Text( + "If you close the session, you will need to re-enter the session code if you wish to load it again.", + ), + actions: [ + ElevatedButton( + onPressed: () async { + SessionData.IsReadOnly = false; + SessionData.Trips = []; + SessionData.positions = []; + SessionData.DisplayError = ""; + SessionData.StartTime = DateTime(0); + SessionData.EndTime = DateTime(0); + SessionData.LastSessionID = ""; + sessionIDController.text = ""; + + setState(() {}); + + Navigator.pop(context); + }, + child: Text("Yes"), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text("Cancel"), + ), + ], + ); + }, + ); }, child: Text("Close Session"), ), @@ -119,7 +152,22 @@ class _WebMain extends State { ElevatedButton( onPressed: () async { SessionData.LastSessionID = sessionIDController.text; - await SessionData.DownloadData(); + bool success = await SessionData.DownloadData(); + + if (!success) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "ERROR: The provided session code was not found on the server", + style: TextStyle(fontSize: 18, color: Colors.white), + ), + backgroundColor: LibACFlutterConstants.TITLEBAR_COLOR, + ), + snackBarAnimationStyle: AnimationStyle( + duration: Duration(seconds: 5), + ), + ); + } setState(() {}); }, child: Text("Load Session", style: TextStyle(fontSize: 18)), diff --git a/lib/pages/WorkData.dart b/lib/pages/WorkData.dart index 6bf2097..1277fb7 100644 --- a/lib/pages/WorkData.dart +++ b/lib/pages/WorkData.dart @@ -45,20 +45,21 @@ class _WorkData extends State { "Total saved GPS Positions: ${SessionData.positions.length}", style: TextStyle(fontSize: 18), ), - Text( - "Start Date & Time: ${SessionData.StartTime.toString()}", - style: TextStyle(fontSize: 18), - ), - if (SessionData.IsReadOnly) + if (SessionData.StartTime.year != 0) + Text( + "Start Date & Time: ${SessionData.StartTime.toString()}", + style: TextStyle(fontSize: 18), + ), + if (SessionData.IsReadOnly && SessionData.StartTime.year != 0) Text( "End Date & Time: ${SessionData.EndTime.toString()}", style: TextStyle(fontSize: 18), ), - - Text( - "Total time worked: ${SessionData.GetTotalTimeWorked(SessionData.StartTime, SessionData.EndTime)}", - style: TextStyle(fontSize: 18), - ), + if (SessionData.StartTime.year != 0) + Text( + "Total time worked: ${SessionData.GetTotalTimeWorked(SessionData.StartTime, SessionData.EndTime)}", + style: TextStyle(fontSize: 18), + ), if (SessionData.StartTime.year == 0) ListTile( title: Text("ERROR"), @@ -67,6 +68,20 @@ class _WorkData extends State { ), tileColor: LibACFlutterConstants.TITLEBAR_COLOR, ), + SizedBox(height: 10), + if (SessionData.TotalPay == null) + ListTile( + title: Text("ERROR"), + subtitle: Text( + "This TTX session file appears to have been saved in a early beta build. It does not contain the total pay.", + ), + tileColor: LibACFlutterConstants.TITLEBAR_COLOR, + ), + if (SessionData.TotalPay != null) + Text( + "Total Pay: ${SessionData.TotalPay!}", + style: TextStyle(fontSize: 18), + ), SizedBox(height: 20), diff --git a/pubspec.yaml b/pubspec.yaml index ce42f31..02a786a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: timetrack -description: "A new Flutter project." +description: "Time Track - A simple, yet effective GPS, time, and mile tracker" # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: "none" # Remove this line if you wish to publish to pub.dev @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.0-beta.8 +version: 1.0.0-beta.9 environment: sdk: ^3.7.2