diff --git a/www/assets/js/activitylog.js b/www/assets/js/activitylog.js
new file mode 100644
index 0000000..c91a608
--- /dev/null
+++ b/www/assets/js/activitylog.js
@@ -0,0 +1,108 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+
+function appendActivityLog(title, subtitle, content, icon, timestamp) {
+ if (typeof timestamp == "undefined") {
+ timestamp = time();
+ }
+ let entry = {
+ title: title,
+ subtitle: subtitle,
+ content: content,
+ icon: icon,
+ timestamp: timestamp
+ };
+
+ let log = getStorage("activitylog");
+ if (log == null) {
+ log = [];
+ } else {
+ try {
+ log = JSON.parse(log);
+ } catch (ex) {
+ log = [];
+ }
+ }
+
+ let pushed = false;
+ let datestr = formatTimestamp("Y-m-d", timestamp);
+ for (var i = 0; i < log.length; i++) {
+ if (formatTimestamp("Y-m-d", log[i].date) == datestr) {
+ log[i].entries.push(entry);
+ pushed = true;
+ break;
+ }
+ }
+ if (!pushed) {
+ log.push({
+ date: timestamp,
+ entries: [
+ entry
+ ]
+ });
+ }
+
+ console.log("Added activity log entry", entry);
+ console.log(log);
+
+ setStorage("activitylog", JSON.stringify(log));
+
+ // Trim the log soon but don't block for it
+ setTimeout(trimActivityLog, 100);
+}
+
+function clearActivityLog() {
+ setStorage("activitylog", "[]");
+}
+
+function trimActivityLog() {
+ let log = JSON.parse(getStorage("activitylog"));
+
+ log.sort(function (x, y) {
+ if (x.date < y.date) {
+ return 1;
+ } else if (x.date > y.date) {
+ return -1;
+ }
+ return 0;
+ });
+
+ let entries = 0;
+ let allowed = SETTINGS.activitylog_maxlength;
+
+ let newlog = [];
+
+ for (var i = 0; i < log.length; i++) {
+ let logdate = {
+ date: log[i].date,
+ entries: []
+ };
+ for (var j = 0; j < log[i].entries.length; j++) {
+ if (entries < allowed) {
+ logdate.entries.push(log[i].entries[j]);
+ }
+ entries++;
+ }
+ if (logdate.entries.length > 0) {
+ newlog.push(logdate);
+ }
+ }
+
+ if (entries - allowed > 0) {
+ newlog[newlog.length - 1].entries.push({
+ title: "Log Trimmed",
+ subtitle: "",
+ content: (entries - allowed) + " older " + ((entries - allowed) == 1 ? "entry was" : "entries were") + " removed from the log.",
+ icon: "fas fa-cut",
+ timestamp: newlog[newlog.length - 1].date
+ });
+ }
+
+ console.log(log);
+ console.log(newlog);
+ setStorage("activitylog", JSON.stringify(newlog));
+}
\ No newline at end of file
diff --git a/www/assets/js/list.js b/www/assets/js/list.js
index 311053f..2d0938d 100644
--- a/www/assets/js/list.js
+++ b/www/assets/js/list.js
@@ -183,8 +183,11 @@ function confirmDeleteAllPackages() {
"Clear Packages",
function () {
// clear
+ let count = countPackages();
+ let remaining = countRemainingPackages();
packages = [];
setStorage("packages", JSON.stringify(packages));
+ appendActivityLog("Cleared List", count + " " + (count != 1 ? "items" : "item") + " removed.", (remaining > 0 ? remaining + " " + (remaining != 1 ? "were" : "was") + " not delivered." : ""), "fas fa-trash");
loadPackageList();
if (map != null) {
map.updatePackageLayer(packages);
diff --git a/www/assets/js/notes.js b/www/assets/js/notes.js
index 746f1f7..fccbcc7 100644
--- a/www/assets/js/notes.js
+++ b/www/assets/js/notes.js
@@ -1,4 +1,4 @@
-/*
+/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
@@ -49,7 +49,7 @@ function isDeliverable(number, street) {
};
}
-function saveNote(id) {
+function saveNote(id) {
var exists = false;
var index = -1;
for (var i = 0; i < notes.length; i++) {
@@ -94,7 +94,7 @@ function saveNote(id) {
app.dialog.alert("Fill in a route (examples: C123, R001, H050).", "Error");
return false;
}
-
+
setStorage("lastrouteid", note.route);
setStorage("zipcode", note.zipcode);
@@ -111,6 +111,12 @@ function saveNote(id) {
setStorage("notes", JSON.stringify(notes));
+ if (exists) {
+ appendActivityLog("Edited note", note.number + " " + note.street, "", "fas fa-sticky-note");
+ } else {
+ appendActivityLog("Added note", note.number + " " + note.street, "", "fas fa-sticky-note");
+ }
+
app.toast.show({
text: " Note saved!",
position: "bottom",
@@ -120,12 +126,15 @@ function saveNote(id) {
}
function deleteNote(id) {
+ var note = {};
for (var i = 0; i < notes.length; i++) {
if (notes[i].id == id) {
+ note = notes[i];
notes.splice(i, 1);
}
}
setStorage("notes", JSON.stringify(notes));
+ appendActivityLog("Deleted note", note.number + " " + note.street, "", "fas fa-sticky-note");
}
$(".view-main").on("click", "#savenotebtn", function () {
diff --git a/www/assets/js/packages.js b/www/assets/js/packages.js
index dc2f012..5711312 100644
--- a/www/assets/js/packages.js
+++ b/www/assets/js/packages.js
@@ -144,6 +144,8 @@ function addPackage(address, latitude, longitude, type, callback, deadline) {
}
setStorage("packages", JSON.stringify(packages));
+ appendActivityLog("Added", SETTINGS.itemtypes[type].name, address, "fas fa-truck-loading");
+
playSound("ok");
app.toast.show({
@@ -174,6 +176,7 @@ function addPackage(address, latitude, longitude, type, callback, deadline) {
*/
function importPackageList(newlist) {
skipped = 0;
+ let count = 0;
for (latlng in newlist) {
var latitude = newlist[latlng].coords[0];
var longitude = newlist[latlng].coords[1];
@@ -195,6 +198,7 @@ function importPackageList(newlist) {
}
if (!added) {
packages[i].items.push(package);
+ count++;
added = true;
}
break;
@@ -206,6 +210,8 @@ function importPackageList(newlist) {
}
}
setStorage("packages", JSON.stringify(packages));
+
+ appendActivityLog("Imported List", count + " items added", "", "fas fa-file-download");
if (map != null) {
reloadMap();
}
@@ -237,12 +243,9 @@ function mapCalibrate(item, packagesentry) {
locationtype: locationtype
},
success: function () {
- app.toast.show({
- text: "Calibration recorded. Thank you for improving the map!",
- position: "bottom",
- destroyOnClose: true,
- closeTimeout: 1000 * 3
- });
+ appendActivityLog("Map Calibrated", item.extended.number + " " + item.extended.street,
+ "Thanks for improving the map accuracy!
Old: " + packagesentry.coords[0] + ", " + packagesentry.coords[1] + "
"
+ + "New: " + latitude + ", " + longitude + "", "fas fa-map-marked-alt");
},
error: function () {
// try again in five minutes
@@ -312,14 +315,15 @@ function markDelivered(id, delivered) {
packages[i].items[j].delivered = delivered;
if (delivered) {
- packages[i].items[j].deliverytimestamp = Date.now();
-
- setStorage("packages", JSON.stringify(packages));
-
+ packages[i].items[j].deliverytimestamp = time();
+ appendActivityLog("Delivered", SETTINGS.itemtypes[packages[i].items[j].type].name, packages[i].items[j].address, "far fa-check-circle");
mapCalibrate(packages[i].items[j], packages[i]);
-
- return; // so we don't keep looping over the rest of the packages
+ } else {
+ packages[i].items[j].deliverytimestamp = null;
+ appendActivityLog("Undelivered", SETTINGS.itemtypes[packages[i].items[j].type].name, packages[i].items[j].address, "fas fa-undo");
}
+ setStorage("packages", JSON.stringify(packages));
+ return; // so we don't keep looping over the rest of the packages
}
}
}
@@ -343,6 +347,9 @@ function deletePackage(id, callback) {
for (var i = 0; i < packages.length; i++) {
for (var j = 0; j < packages[i].items.length; j++) {
if (packages[i].items[j].id == id) {
+
+ appendActivityLog("Deleted", SETTINGS.itemtypes[packages[i].items[j].type].name, packages[i].items[j].address, "fas fa-trash");
+
packages[i].items.splice(j, 1);
if (packages[i].items.length == 0) {
diff --git a/www/assets/js/toolbox_log.js b/www/assets/js/toolbox_log.js
new file mode 100644
index 0000000..387be00
--- /dev/null
+++ b/www/assets/js/toolbox_log.js
@@ -0,0 +1,21 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+
+function confirmDeleteActivityLog() {
+ app.dialog.confirm(
+ "Really delete all log entries?",
+ "Clear Log",
+ function () {
+ // clear
+ clearActivityLog();
+ router.refreshPage();
+ },
+ function () {
+ // cancel
+ }
+ );
+}
\ No newline at end of file
diff --git a/www/assets/js/toolbox_scanner.js b/www/assets/js/toolbox_scanner.js
index 14d1a09..8d358a3 100644
--- a/www/assets/js/toolbox_scanner.js
+++ b/www/assets/js/toolbox_scanner.js
@@ -101,7 +101,7 @@ function addCodeToScannerList(code) {
} else {
app.dialog.confirm(
"It looks like this item might require a signature or other special procedures. Add it anyways?",
- "Signature Item",
+ "Special Item",
function () {
$("#codelist").append(codeEntryTemplate({
code: code
@@ -195,6 +195,8 @@ function saveScanCode(code) {
var events = JSON.parse(getStorage("scanevents"));
events.push(code);
setStorage("scanevents", JSON.stringify(events));
+
+ appendActivityLog("Scanned Item", code.event.join(' '), code.code + (code.form3849 == "" ? "" : "
Form 3849: " + code.form3849), "fas fa-barcode");
}
$(".view-main").off("click", "#codelist li.codelist-entry");
diff --git a/www/assets/js/toolbox_sharelist.js b/www/assets/js/toolbox_sharelist.js
index 7a84db1..93394b9 100644
--- a/www/assets/js/toolbox_sharelist.js
+++ b/www/assets/js/toolbox_sharelist.js
@@ -33,6 +33,7 @@ function uploadList() {
height: 40
});
$("#listidbarcodeli").css("display", "");
+ appendActivityLog("Shared List", countPackages() + " items sent", "", "fas fa-file-upload");
} else {
app.dialog.alert(resp.message, "Error");
}
diff --git a/www/assets/js/util.js b/www/assets/js/util.js
index 2982c4c..e0819ee 100644
--- a/www/assets/js/util.js
+++ b/www/assets/js/util.js
@@ -1,4 +1,4 @@
-/*
+/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
@@ -16,16 +16,233 @@ function uuidv4() {
});
}
-function timestampToDateTimeString(timestamp) {
+/**
+ * Take a UNIX timestamp (seconds since Jan 1 1970) and format it.
+ * (Mostly) compatible with PHP's date() function.
+ * @param {String} date format string, see https://www.php.net/manual/en/function.date.php
+ * @param {Integer} timestamp UNIX timestamp
+ * @return {String}
+ */
+function formatTimestamp(format, timestamp) {
+ if (typeof timestamp == "undefined") {
+ timestamp = time();
+ }
var date = new Date(timestamp * 1000);
- var pm = date.getHours() >= 12;
- var hours = date.getHours() > 12 ? date.getHours() - 12 : date.getHours();
- hours = (hours == 0 ? 12 : hours);
- var minutes = date.getMinutes();
- var time = hours + ":" + (minutes < 10 ? "0" + minutes : minutes) + " " + (pm ? "PM" : "AM");
+ var out = "";
- return date.toLocaleDateString() + " " + time;
+ var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
+ var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
+ for (var i = 0; i < format.length; i++) {
+ var c = format.charAt(i);
+ // Handle backslash-escaped characters
+ if (c == "\\" && i < format.length - 1) {
+ out += format.charAt(i + 1);
+ i++;
+ continue;
+ }
+ switch (c) {
+ case "d":
+ var d = date.getDate();
+ if (d < 10) {
+ out += "0";
+ }
+ out += d;
+ break;
+ case "D":
+ out += days[date.getDay()].substring(0, 3);
+ break;
+ case "j":
+ out += date.getDate();
+ break;
+ case "l":
+ out += days[date.getDay()];
+ break;
+ case "N":
+ // TODO
+ break;
+ case "S":
+ // TODO
+ break;
+ case "w":
+ out += date.getDay();
+ break;
+ case "z":
+ // TODO
+ break;
+ case "W":
+ // TODO
+ break;
+ case "F":
+ out += months[date.getMonth()];
+ break;
+ case "m":
+ var m = date.getMonth() + 1;
+ if (m < 10) {
+ out += "0";
+ }
+ out += m;
+ break;
+ case "M":
+ out += months[date.getMonth()].substring(0, 3);
+ break;
+ case "n":
+ out += date.getMonth() + 1;
+ break;
+ case "t":
+ // TODO
+ break;
+ case "L":
+ // TODO
+ break;
+ case "o":
+ // TODO
+ break;
+ case "Y":
+ out += date.getFullYear();
+ break;
+ case "y":
+ var y = (date.getFullYear() + "");
+ out += y.substring(y.length - 2);
+ break;
+ case "a":
+ if (date.getHours() < 12) {
+ out += "am";
+ } else {
+ out += "pm";
+ }
+ break;
+ case "A":
+ if (date.getHours() < 12) {
+ out += "AM";
+ } else {
+ out += "PM";
+ }
+ break;
+ case "B":
+ // TODO
+ break;
+ case "g":
+ var h = date.getHours() % 12;
+ if (h == 0) {
+ h = 12;
+ }
+ out += h;
+ break;
+ case "G":
+ out += date.getHours();
+ break;
+ case "h":
+ var h = date.getHours() % 12;
+ if (h == 0) {
+ h = 12;
+ }
+ if (h < 10) {
+ out += "0";
+ }
+ out += h;
+ break;
+ case "H":
+ var h = date.getHours();
+ if (h < 10) {
+ out += "0";
+ }
+ out += h;
+ break;
+ case "i":
+ var ii = date.getMinutes();
+ if (ii < 10) {
+ out += "0";
+ }
+ out += ii;
+ break;
+ case "s":
+ var s = date.getSeconds();
+ if (s < 10) {
+ out += "0";
+ }
+ out += s;
+ break;
+ case "u":
+ out += date.getMilliseconds() * 1000;
+ break;
+ case "v":
+ out += date.getMilliseconds();
+ break;
+ case "e":
+ // TODO
+ break;
+ case "I":
+ // TODO
+ break;
+ case "O":
+ var off = date.getTimezoneOffset();
+ var m = off % 60;
+ var h = (off - m) / 60;
+ if (off >= 0) {
+ out += "+";
+ } else {
+ out += "-";
+ }
+ if (h < 10) {
+ out += "0";
+ }
+ out += h;
+ if (m < 10) {
+ out += "0";
+ }
+ out += m;
+ break;
+ case "P":
+ var off = date.getTimezoneOffset();
+ var m = off % 60;
+ var h = (off - m) / 60;
+ if (off >= 0) {
+ out += "+";
+ } else {
+ out += "-";
+ }
+ if (h < 10) {
+ out += "0";
+ }
+ out += h;
+ out += ":";
+ if (m < 10) {
+ out += "0";
+ }
+ out += m;
+ break;
+ case "T":
+ // TODO
+ break;
+ case "Z":
+ out += date.getTimezoneOffset() * 60;
+ break;
+ case "c":
+ out += formatTimestamp(timestamp, "Y-m-d\\TH:i:sP");
+ break;
+ case "r":
+ out += formatTimestamp(timestamp, "D, j M Y G:i:s O");
+ break;
+ case "U":
+ out += Math.round(timestamp);
+ break;
+ default:
+ out += c;
+ }
+ }
+
+ return out;
+}
+
+function timestampToDateTimeString(timestamp) {
+ return timestampToDateString(timestamp) + " " + timestampToTimeString(timestamp);
+}
+
+function timestampToDateString(timestamp) {
+ var date = new Date(timestamp * 1000);
+
+ return date.toLocaleDateString();
}
function timestampToTimeString(timestamp) {
diff --git a/www/index.html b/www/index.html
index bacdf9e..7bb39f2 100644
--- a/www/index.html
+++ b/www/index.html
@@ -53,6 +53,7 @@
+
@@ -61,7 +62,6 @@
-
diff --git a/www/pages/toolbox.html b/www/pages/toolbox.html
index 4549a25..47686d6 100644
--- a/www/pages/toolbox.html
+++ b/www/pages/toolbox.html
@@ -53,6 +53,14 @@
+