diff --git a/app.js b/app.js
index 5216c67f..55222a44 100644
--- a/app.js
+++ b/app.js
@@ -60,6 +60,7 @@ app.use(urlPrefix + "/lib/fa", express.static(path.join("node_modules", "@fortaw
app.use(urlPrefix + "/lib/cityssm-bulma-webapp-js", express.static(path.join("node_modules", "@cityssm", "bulma-webapp-js")));
app.use(urlPrefix + "/lib/cityssm-bulma-js", express.static(path.join("node_modules", "@cityssm", "bulma-js", "dist")));
app.use(urlPrefix + "/lib/leaflet", express.static(path.join("node_modules", "leaflet", "dist")));
+app.use(urlPrefix + "/lib/randomcolor", express.static(path.join("node_modules", "randomcolor")));
const sessionCookieName = configFunctions.getProperty("session.cookieName");
const FileStoreSession = FileStore(session);
app.use(session({
diff --git a/app.ts b/app.ts
index a6d00a73..b5e643ad 100644
--- a/app.ts
+++ b/app.ts
@@ -122,6 +122,11 @@ app.use(
express.static(path.join("node_modules", "leaflet", "dist"))
);
+app.use(
+ urlPrefix + "/lib/randomcolor",
+ express.static(path.join("node_modules", "randomcolor"))
+);
+
/*
* SESSION MANAGEMENT
*/
diff --git a/data/config.cemetery.ssm.js b/data/config.cemetery.ssm.js
index e2b52164..e3b0f37e 100644
--- a/data/config.cemetery.ssm.js
+++ b/data/config.cemetery.ssm.js
@@ -25,7 +25,9 @@ config.settings.lot = {
config.settings.lotOccupancy.occupantCityDefault = "Sault Ste. Marie";
config.settings.map.mapCityDefault = "Sault Ste. Marie";
config.settings.workOrders = {
- workOrderNumberLength: 6
+ workOrderNumberLength: 6,
+ workOrderMilestoneDateRecentBeforeDays: 7,
+ workOrderMilestoneDateRecentAfterDays: 30
};
config.aliases.externalReceiptNumber = "GP Receipt Number";
export default config;
diff --git a/data/config.cemetery.ssm.ts b/data/config.cemetery.ssm.ts
index 2f46b8d9..fbc77ad1 100644
--- a/data/config.cemetery.ssm.ts
+++ b/data/config.cemetery.ssm.ts
@@ -39,7 +39,9 @@ config.settings.lotOccupancy.occupantCityDefault = "Sault Ste. Marie";
config.settings.map.mapCityDefault = "Sault Ste. Marie";
config.settings.workOrders = {
- workOrderNumberLength: 6
+ workOrderNumberLength: 6,
+ workOrderMilestoneDateRecentBeforeDays: 7,
+ workOrderMilestoneDateRecentAfterDays: 30
};
config.aliases.externalReceiptNumber = "GP Receipt Number";
diff --git a/handlers/workOrders-get/edit.js b/handlers/workOrders-get/edit.js
index 2d0e0746..1010d10a 100644
--- a/handlers/workOrders-get/edit.js
+++ b/handlers/workOrders-get/edit.js
@@ -2,7 +2,11 @@ import { getLotStatuses, getWorkOrderMilestoneTypes, getWorkOrderTypes } from ".
import * as configFunctions from "../../helpers/functions.config.js";
import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js";
export const handler = (request, response) => {
- const workOrder = getWorkOrder(request.params.workOrderId);
+ const workOrder = getWorkOrder(request.params.workOrderId, {
+ includeLotsAndLotOccupancies: true,
+ includeComments: true,
+ includeMilestones: true
+ });
if (!workOrder) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") +
"/workOrders/?error=workOrderIdNotFound");
diff --git a/handlers/workOrders-get/edit.ts b/handlers/workOrders-get/edit.ts
index b90a287a..44d02d09 100644
--- a/handlers/workOrders-get/edit.ts
+++ b/handlers/workOrders-get/edit.ts
@@ -11,7 +11,11 @@ import * as configFunctions from "../../helpers/functions.config.js";
import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js";
export const handler: RequestHandler = (request, response) => {
- const workOrder = getWorkOrder(request.params.workOrderId);
+ const workOrder = getWorkOrder(request.params.workOrderId, {
+ includeLotsAndLotOccupancies: true,
+ includeComments: true,
+ includeMilestones: true
+ });
if (!workOrder) {
return response.redirect(
diff --git a/handlers/workOrders-get/view.js b/handlers/workOrders-get/view.js
index 8c16271e..2c1af188 100644
--- a/handlers/workOrders-get/view.js
+++ b/handlers/workOrders-get/view.js
@@ -1,7 +1,11 @@
import * as configFunctions from "../../helpers/functions.config.js";
import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js";
export const handler = (request, response) => {
- const workOrder = getWorkOrder(request.params.workOrderId);
+ const workOrder = getWorkOrder(request.params.workOrderId, {
+ includeLotsAndLotOccupancies: true,
+ includeComments: true,
+ includeMilestones: true
+ });
if (!workOrder) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") +
"/workOrders/?error=workOrderIdNotFound");
diff --git a/handlers/workOrders-get/view.ts b/handlers/workOrders-get/view.ts
index 35812786..4ea9eb04 100644
--- a/handlers/workOrders-get/view.ts
+++ b/handlers/workOrders-get/view.ts
@@ -5,7 +5,11 @@ import * as configFunctions from "../../helpers/functions.config.js";
import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js";
export const handler: RequestHandler = (request, response) => {
- const workOrder = getWorkOrder(request.params.workOrderId);
+ const workOrder = getWorkOrder(request.params.workOrderId, {
+ includeLotsAndLotOccupancies: true,
+ includeComments: true,
+ includeMilestones: true
+ });
if (!workOrder) {
return response.redirect(
diff --git a/handlers/workOrders-post/doAddWorkOrderMilestone.js b/handlers/workOrders-post/doAddWorkOrderMilestone.js
index 0b2bc029..719a64d7 100644
--- a/handlers/workOrders-post/doAddWorkOrderMilestone.js
+++ b/handlers/workOrders-post/doAddWorkOrderMilestone.js
@@ -4,6 +4,8 @@ export const handler = async (request, response) => {
const success = addWorkOrderMilestone(request.body, request.session);
const workOrderMilestones = getWorkOrderMilestones({
workOrderId: request.body.workOrderId
+ }, {
+ orderBy: "completion"
});
response.json({
success,
diff --git a/handlers/workOrders-post/doAddWorkOrderMilestone.ts b/handlers/workOrders-post/doAddWorkOrderMilestone.ts
index 782bdc58..4d30aadd 100644
--- a/handlers/workOrders-post/doAddWorkOrderMilestone.ts
+++ b/handlers/workOrders-post/doAddWorkOrderMilestone.ts
@@ -6,9 +6,14 @@ import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrde
export const handler: RequestHandler = async (request, response) => {
const success = addWorkOrderMilestone(request.body, request.session);
- const workOrderMilestones = getWorkOrderMilestones({
- workOrderId: request.body.workOrderId
- });
+ const workOrderMilestones = getWorkOrderMilestones(
+ {
+ workOrderId: request.body.workOrderId
+ },
+ {
+ orderBy: "completion"
+ }
+ );
response.json({
success,
diff --git a/handlers/workOrders-post/doCloseWorkOrder.d.ts b/handlers/workOrders-post/doCloseWorkOrder.d.ts
new file mode 100644
index 00000000..9621c611
--- /dev/null
+++ b/handlers/workOrders-post/doCloseWorkOrder.d.ts
@@ -0,0 +1,3 @@
+import type { RequestHandler } from "express";
+export declare const handler: RequestHandler;
+export default handler;
diff --git a/handlers/workOrders-post/doCloseWorkOrder.js b/handlers/workOrders-post/doCloseWorkOrder.js
new file mode 100644
index 00000000..c4c1a095
--- /dev/null
+++ b/handlers/workOrders-post/doCloseWorkOrder.js
@@ -0,0 +1,8 @@
+import { closeWorkOrder } from "../../helpers/lotOccupancyDB/closeWorkOrder.js";
+export const handler = async (request, response) => {
+ const success = closeWorkOrder(request.body, request.session);
+ response.json({
+ success
+ });
+};
+export default handler;
diff --git a/handlers/workOrders-post/doCloseWorkOrder.ts b/handlers/workOrders-post/doCloseWorkOrder.ts
new file mode 100644
index 00000000..d4b798fd
--- /dev/null
+++ b/handlers/workOrders-post/doCloseWorkOrder.ts
@@ -0,0 +1,16 @@
+import type { RequestHandler } from "express";
+
+import { closeWorkOrder } from "../../helpers/lotOccupancyDB/closeWorkOrder.js";
+
+export const handler: RequestHandler = async (request, response) => {
+ const success = closeWorkOrder(
+ request.body,
+ request.session
+ );
+
+ response.json({
+ success
+ });
+};
+
+export default handler;
diff --git a/handlers/workOrders-post/doCompleteWorkOrderMilestone.js b/handlers/workOrders-post/doCompleteWorkOrderMilestone.js
index a8ac95fe..bacc1961 100644
--- a/handlers/workOrders-post/doCompleteWorkOrderMilestone.js
+++ b/handlers/workOrders-post/doCompleteWorkOrderMilestone.js
@@ -6,6 +6,8 @@ export const handler = async (request, response) => {
}, request.session);
const workOrderMilestones = getWorkOrderMilestones({
workOrderId: request.body.workOrderId
+ }, {
+ orderBy: "completion"
});
response.json({
success,
diff --git a/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts b/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts
index 1b7f1158..5318521e 100644
--- a/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts
+++ b/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts
@@ -12,9 +12,14 @@ export const handler: RequestHandler = async (request, response) => {
request.session
);
- const workOrderMilestones = getWorkOrderMilestones({
- workOrderId: request.body.workOrderId
- });
+ const workOrderMilestones = getWorkOrderMilestones(
+ {
+ workOrderId: request.body.workOrderId
+ },
+ {
+ orderBy: "completion"
+ }
+ );
response.json({
success,
diff --git a/handlers/workOrders-post/doDeleteWorkOrder.d.ts b/handlers/workOrders-post/doDeleteWorkOrder.d.ts
new file mode 100644
index 00000000..9621c611
--- /dev/null
+++ b/handlers/workOrders-post/doDeleteWorkOrder.d.ts
@@ -0,0 +1,3 @@
+import type { RequestHandler } from "express";
+export declare const handler: RequestHandler;
+export default handler;
diff --git a/handlers/workOrders-post/doDeleteWorkOrder.js b/handlers/workOrders-post/doDeleteWorkOrder.js
new file mode 100644
index 00000000..0753e3d1
--- /dev/null
+++ b/handlers/workOrders-post/doDeleteWorkOrder.js
@@ -0,0 +1,8 @@
+import { deleteWorkOrder } from "../../helpers/lotOccupancyDB/deleteWorkOrder.js";
+export const handler = async (request, response) => {
+ const success = deleteWorkOrder(request.body.workOrderId, request.session);
+ response.json({
+ success
+ });
+};
+export default handler;
diff --git a/handlers/workOrders-post/doDeleteWorkOrder.ts b/handlers/workOrders-post/doDeleteWorkOrder.ts
new file mode 100644
index 00000000..83e9bc9e
--- /dev/null
+++ b/handlers/workOrders-post/doDeleteWorkOrder.ts
@@ -0,0 +1,13 @@
+import type { RequestHandler } from "express";
+
+import { deleteWorkOrder } from "../../helpers/lotOccupancyDB/deleteWorkOrder.js";
+
+export const handler: RequestHandler = async (request, response) => {
+ const success = deleteWorkOrder(request.body.workOrderId, request.session);
+
+ response.json({
+ success
+ });
+};
+
+export default handler;
diff --git a/handlers/workOrders-post/doDeleteWorkOrderMilestone.js b/handlers/workOrders-post/doDeleteWorkOrderMilestone.js
index 1b440168..0285b37c 100644
--- a/handlers/workOrders-post/doDeleteWorkOrderMilestone.js
+++ b/handlers/workOrders-post/doDeleteWorkOrderMilestone.js
@@ -4,6 +4,8 @@ export const handler = async (request, response) => {
const success = deleteWorkOrderMilestone(request.body.workOrderMilestoneId, request.session);
const workOrderMilestones = getWorkOrderMilestones({
workOrderId: request.body.workOrderId
+ }, {
+ orderBy: "completion"
});
response.json({
success,
diff --git a/handlers/workOrders-post/doDeleteWorkOrderMilestone.ts b/handlers/workOrders-post/doDeleteWorkOrderMilestone.ts
index d3dccaf8..72256117 100644
--- a/handlers/workOrders-post/doDeleteWorkOrderMilestone.ts
+++ b/handlers/workOrders-post/doDeleteWorkOrderMilestone.ts
@@ -10,9 +10,14 @@ export const handler: RequestHandler = async (request, response) => {
request.session
);
- const workOrderMilestones = getWorkOrderMilestones({
- workOrderId: request.body.workOrderId
- });
+ const workOrderMilestones = getWorkOrderMilestones(
+ {
+ workOrderId: request.body.workOrderId
+ },
+ {
+ orderBy: "completion"
+ }
+ );
response.json({
success,
diff --git a/handlers/workOrders-post/doGetWorkOrderMilestones.d.ts b/handlers/workOrders-post/doGetWorkOrderMilestones.d.ts
index e69de29b..9621c611 100644
--- a/handlers/workOrders-post/doGetWorkOrderMilestones.d.ts
+++ b/handlers/workOrders-post/doGetWorkOrderMilestones.d.ts
@@ -0,0 +1,3 @@
+import type { RequestHandler } from "express";
+export declare const handler: RequestHandler;
+export default handler;
diff --git a/handlers/workOrders-post/doGetWorkOrderMilestones.js b/handlers/workOrders-post/doGetWorkOrderMilestones.js
index e69de29b..f24fe58f 100644
--- a/handlers/workOrders-post/doGetWorkOrderMilestones.js
+++ b/handlers/workOrders-post/doGetWorkOrderMilestones.js
@@ -0,0 +1,11 @@
+import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrderMilestones.js";
+export const handler = async (request, response) => {
+ const workOrderMilestones = getWorkOrderMilestones(request.body, {
+ includeWorkOrders: true,
+ orderBy: "date"
+ });
+ response.json({
+ workOrderMilestones
+ });
+};
+export default handler;
diff --git a/handlers/workOrders-post/doGetWorkOrderMilestones.ts b/handlers/workOrders-post/doGetWorkOrderMilestones.ts
index e69de29b..d7750869 100644
--- a/handlers/workOrders-post/doGetWorkOrderMilestones.ts
+++ b/handlers/workOrders-post/doGetWorkOrderMilestones.ts
@@ -0,0 +1,16 @@
+import type { RequestHandler } from "express";
+
+import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrderMilestones.js";
+
+export const handler: RequestHandler = async (request, response) => {
+ const workOrderMilestones = getWorkOrderMilestones(request.body, {
+ includeWorkOrders: true,
+ orderBy: "date"
+ });
+
+ response.json({
+ workOrderMilestones
+ });
+};
+
+export default handler;
diff --git a/handlers/workOrders-post/doReopenWorkOrderMilestone.js b/handlers/workOrders-post/doReopenWorkOrderMilestone.js
index 5003576c..740ae963 100644
--- a/handlers/workOrders-post/doReopenWorkOrderMilestone.js
+++ b/handlers/workOrders-post/doReopenWorkOrderMilestone.js
@@ -4,6 +4,8 @@ export const handler = async (request, response) => {
const success = reopenWorkOrderMilestone(request.body.workOrderMilestoneId, request.session);
const workOrderMilestones = getWorkOrderMilestones({
workOrderId: request.body.workOrderId
+ }, {
+ orderBy: "completion"
});
response.json({
success,
diff --git a/handlers/workOrders-post/doReopenWorkOrderMilestone.ts b/handlers/workOrders-post/doReopenWorkOrderMilestone.ts
index d8c4cef2..8aa83a3b 100644
--- a/handlers/workOrders-post/doReopenWorkOrderMilestone.ts
+++ b/handlers/workOrders-post/doReopenWorkOrderMilestone.ts
@@ -10,9 +10,14 @@ export const handler: RequestHandler = async (request, response) => {
request.session
);
- const workOrderMilestones = getWorkOrderMilestones({
- workOrderId: request.body.workOrderId
- });
+ const workOrderMilestones = getWorkOrderMilestones(
+ {
+ workOrderId: request.body.workOrderId
+ },
+ {
+ orderBy: "completion"
+ }
+ );
response.json({
success,
diff --git a/handlers/workOrders-post/doUpdateWorkOrderMilestone.js b/handlers/workOrders-post/doUpdateWorkOrderMilestone.js
index 211da0c4..0a146036 100644
--- a/handlers/workOrders-post/doUpdateWorkOrderMilestone.js
+++ b/handlers/workOrders-post/doUpdateWorkOrderMilestone.js
@@ -4,6 +4,8 @@ export const handler = async (request, response) => {
const success = updateWorkOrderMilestone(request.body, request.session);
const workOrderMilestones = getWorkOrderMilestones({
workOrderId: request.body.workOrderId
+ }, {
+ orderBy: "completion"
});
response.json({
success,
diff --git a/handlers/workOrders-post/doUpdateWorkOrderMilestone.ts b/handlers/workOrders-post/doUpdateWorkOrderMilestone.ts
index 371d227e..0de1bb89 100644
--- a/handlers/workOrders-post/doUpdateWorkOrderMilestone.ts
+++ b/handlers/workOrders-post/doUpdateWorkOrderMilestone.ts
@@ -6,9 +6,14 @@ import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrde
export const handler: RequestHandler = async (request, response) => {
const success = updateWorkOrderMilestone(request.body, request.session);
- const workOrderMilestones = getWorkOrderMilestones({
- workOrderId: request.body.workOrderId
- });
+ const workOrderMilestones = getWorkOrderMilestones(
+ {
+ workOrderId: request.body.workOrderId
+ },
+ {
+ orderBy: "completion"
+ }
+ );
response.json({
success,
diff --git a/helpers/functions.config.d.ts b/helpers/functions.config.d.ts
index aefb50a3..c20c5e6d 100644
--- a/helpers/functions.config.d.ts
+++ b/helpers/functions.config.d.ts
@@ -33,4 +33,6 @@ export declare function getProperty(propertyName: "settings.lotOccupancy.occupan
export declare function getProperty(propertyName: "settings.lotOccupancy.occupantProvinceDefault"): string;
export declare function getProperty(propertyName: "settings.fees.taxPercentageDefault"): number;
export declare function getProperty(propertyName: "settings.workOrders.workOrderNumberLength"): number;
+export declare function getProperty(propertyName: "settings.workOrders.workOrderMilestoneDateRecentBeforeDays"): number;
+export declare function getProperty(propertyName: "settings.workOrders.workOrderMilestoneDateRecentAfterDays"): number;
export declare const keepAliveMillis: number;
diff --git a/helpers/functions.config.js b/helpers/functions.config.js
index 3c60725a..3899a880 100644
--- a/helpers/functions.config.js
+++ b/helpers/functions.config.js
@@ -32,6 +32,8 @@ configFallbackValues.set("settings.lotOccupancy.occupantCityDefault", "");
configFallbackValues.set("settings.lotOccupancy.occupantProvinceDefault", "");
configFallbackValues.set("settings.fees.taxPercentageDefault", 0);
configFallbackValues.set("settings.workOrders.workOrderNumberLength", 6);
+configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentBeforeDays", 5);
+configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentAfterDays", 60);
export function getProperty(propertyName) {
const propertyNameSplit = propertyName.split(".");
let currentObject = config;
diff --git a/helpers/functions.config.ts b/helpers/functions.config.ts
index 5614abf3..79fea77a 100644
--- a/helpers/functions.config.ts
+++ b/helpers/functions.config.ts
@@ -63,6 +63,14 @@ configFallbackValues.set("settings.lotOccupancy.occupantProvinceDefault", "");
configFallbackValues.set("settings.fees.taxPercentageDefault", 0);
configFallbackValues.set("settings.workOrders.workOrderNumberLength", 6);
+configFallbackValues.set(
+ "settings.workOrders.workOrderMilestoneDateRecentBeforeDays",
+ 5
+);
+configFallbackValues.set(
+ "settings.workOrders.workOrderMilestoneDateRecentAfterDays",
+ 60
+);
/*
* Set up function overloads
@@ -148,6 +156,14 @@ export function getProperty(
propertyName: "settings.workOrders.workOrderNumberLength"
): number;
+export function getProperty(
+ propertyName: "settings.workOrders.workOrderMilestoneDateRecentBeforeDays"
+): number;
+
+export function getProperty(
+ propertyName: "settings.workOrders.workOrderMilestoneDateRecentAfterDays"
+): number;
+
export function getProperty(propertyName: string): unknown {
const propertyNameSplit = propertyName.split(".");
diff --git a/helpers/lotOccupancyDB/closeWorkOrder.d.ts b/helpers/lotOccupancyDB/closeWorkOrder.d.ts
index 52d8a610..617afe74 100644
--- a/helpers/lotOccupancyDB/closeWorkOrder.d.ts
+++ b/helpers/lotOccupancyDB/closeWorkOrder.d.ts
@@ -3,5 +3,5 @@ interface AddWorkOrderForm {
workOrderId: number | string;
workOrderCloseDateString?: string;
}
-export declare const closeWorkOrder: (workOrderForm: AddWorkOrderForm, requestSession: recordTypes.PartialSession) => number;
+export declare const closeWorkOrder: (workOrderForm: AddWorkOrderForm, requestSession: recordTypes.PartialSession) => boolean;
export default closeWorkOrder;
diff --git a/helpers/lotOccupancyDB/closeWorkOrder.js b/helpers/lotOccupancyDB/closeWorkOrder.js
index 4fee09c9..c67c3971 100644
--- a/helpers/lotOccupancyDB/closeWorkOrder.js
+++ b/helpers/lotOccupancyDB/closeWorkOrder.js
@@ -14,6 +14,6 @@ export const closeWorkOrder = (workOrderForm, requestSession) => {
? dateStringToInteger(workOrderForm.workOrderCloseDateString)
: dateToInteger(new Date()), requestSession.user.userName, rightNow.getTime(), workOrderForm.workOrderId);
database.close();
- return result.lastInsertRowid;
+ return result.changes > 0;
};
export default closeWorkOrder;
diff --git a/helpers/lotOccupancyDB/closeWorkOrder.ts b/helpers/lotOccupancyDB/closeWorkOrder.ts
index 4354d231..a5b36043 100644
--- a/helpers/lotOccupancyDB/closeWorkOrder.ts
+++ b/helpers/lotOccupancyDB/closeWorkOrder.ts
@@ -17,7 +17,7 @@ interface AddWorkOrderForm {
export const closeWorkOrder = (
workOrderForm: AddWorkOrderForm,
requestSession: recordTypes.PartialSession
-): number => {
+): boolean => {
const database = sqlite(databasePath);
const rightNow = new Date();
@@ -41,7 +41,7 @@ export const closeWorkOrder = (
database.close();
- return result.lastInsertRowid as number;
+ return result.changes > 0;
};
export default closeWorkOrder;
diff --git a/helpers/lotOccupancyDB/deleteWorkOrder.d.ts b/helpers/lotOccupancyDB/deleteWorkOrder.d.ts
new file mode 100644
index 00000000..df1df960
--- /dev/null
+++ b/helpers/lotOccupancyDB/deleteWorkOrder.d.ts
@@ -0,0 +1,3 @@
+import type * as recordTypes from "../../types/recordTypes";
+export declare const deleteWorkOrder: (workOrderId: number | string, requestSession: recordTypes.PartialSession) => boolean;
+export default deleteWorkOrder;
diff --git a/helpers/lotOccupancyDB/deleteWorkOrder.js b/helpers/lotOccupancyDB/deleteWorkOrder.js
new file mode 100644
index 00000000..c828e65c
--- /dev/null
+++ b/helpers/lotOccupancyDB/deleteWorkOrder.js
@@ -0,0 +1,15 @@
+import sqlite from "better-sqlite3";
+import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
+export const deleteWorkOrder = (workOrderId, requestSession) => {
+ const database = sqlite(databasePath);
+ const rightNowMillis = Date.now();
+ const result = database
+ .prepare("update WorkOrders" +
+ " set recordDelete_userName = ?," +
+ " recordDelete_timeMillis = ?" +
+ " where workOrderId = ?")
+ .run(requestSession.user.userName, rightNowMillis, workOrderId);
+ database.close();
+ return result.changes > 0;
+};
+export default deleteWorkOrder;
diff --git a/helpers/lotOccupancyDB/deleteWorkOrder.ts b/helpers/lotOccupancyDB/deleteWorkOrder.ts
new file mode 100644
index 00000000..2c0f8751
--- /dev/null
+++ b/helpers/lotOccupancyDB/deleteWorkOrder.ts
@@ -0,0 +1,29 @@
+import sqlite from "better-sqlite3";
+
+import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
+
+import type * as recordTypes from "../../types/recordTypes";
+
+export const deleteWorkOrder = (
+ workOrderId: number | string,
+ requestSession: recordTypes.PartialSession
+): boolean => {
+ const database = sqlite(databasePath);
+
+ const rightNowMillis = Date.now();
+
+ const result = database
+ .prepare(
+ "update WorkOrders" +
+ " set recordDelete_userName = ?," +
+ " recordDelete_timeMillis = ?" +
+ " where workOrderId = ?"
+ )
+ .run(requestSession.user.userName, rightNowMillis, workOrderId);
+
+ database.close();
+
+ return result.changes > 0;
+};
+
+export default deleteWorkOrder;
diff --git a/helpers/lotOccupancyDB/getWorkOrder.d.ts b/helpers/lotOccupancyDB/getWorkOrder.d.ts
index ce46f3d5..072c87e8 100644
--- a/helpers/lotOccupancyDB/getWorkOrder.d.ts
+++ b/helpers/lotOccupancyDB/getWorkOrder.d.ts
@@ -1,4 +1,10 @@
+import sqlite from "better-sqlite3";
import type * as recordTypes from "../../types/recordTypes";
+interface WorkOrderOptions {
+ includeLotsAndLotOccupancies: boolean;
+ includeComments: boolean;
+ includeMilestones: boolean;
+}
export declare const getWorkOrderByWorkOrderNumber: (workOrderNumber: string) => recordTypes.WorkOrder;
-export declare const getWorkOrder: (workOrderId: number | string) => recordTypes.WorkOrder;
+export declare const getWorkOrder: (workOrderId: number | string, options: WorkOrderOptions, connectedDatabase?: sqlite.Database) => recordTypes.WorkOrder;
export default getWorkOrder;
diff --git a/helpers/lotOccupancyDB/getWorkOrder.js b/helpers/lotOccupancyDB/getWorkOrder.js
index 7db34f24..f3710800 100644
--- a/helpers/lotOccupancyDB/getWorkOrder.js
+++ b/helpers/lotOccupancyDB/getWorkOrder.js
@@ -13,40 +13,56 @@ const baseSQL = "select w.workOrderId," +
" from WorkOrders w" +
" left join WorkOrderTypes t on w.workOrderTypeId = t.workOrderTypeId" +
" where w.recordDelete_timeMillis is null";
-const _getWorkOrder = (sql, workOrderId_or_workOrderNumber) => {
- const database = sqlite(databasePath, {
- readonly: true
- });
+const _getWorkOrder = (sql, workOrderId_or_workOrderNumber, options, connectedDatabase) => {
+ const database = connectedDatabase ||
+ sqlite(databasePath, {
+ readonly: true
+ });
database.function("userFn_dateIntegerToString", dateIntegerToString);
const workOrder = database
.prepare(sql)
.get(workOrderId_or_workOrderNumber);
if (workOrder) {
- workOrder.workOrderLots = getLots({
- workOrderId: workOrder.workOrderId
- }, {
- limit: -1,
- offset: 0
- }, database).lots;
- workOrder.workOrderLotOccupancies = getLotOccupancies({
- workOrderId: workOrder.workOrderId
- }, {
- limit: -1,
- offset: 0,
- includeOccupants: true
- }, database).lotOccupancies;
- workOrder.workOrderComments = getWorkOrderComments(workOrder.workOrderId, database);
- workOrder.workOrderMilestones = getWorkOrderMilestones({
- workOrderId: workOrder.workOrderId
- }, database);
+ if (options.includeLotsAndLotOccupancies) {
+ workOrder.workOrderLots = getLots({
+ workOrderId: workOrder.workOrderId
+ }, {
+ limit: -1,
+ offset: 0
+ }, database).lots;
+ workOrder.workOrderLotOccupancies = getLotOccupancies({
+ workOrderId: workOrder.workOrderId
+ }, {
+ limit: -1,
+ offset: 0,
+ includeOccupants: true
+ }, database).lotOccupancies;
+ }
+ if (options.includeComments) {
+ workOrder.workOrderComments = getWorkOrderComments(workOrder.workOrderId, database);
+ }
+ if (options.includeMilestones) {
+ workOrder.workOrderMilestones = getWorkOrderMilestones({
+ workOrderId: workOrder.workOrderId
+ }, {
+ includeWorkOrders: false,
+ orderBy: "completion"
+ }, database);
+ }
+ }
+ if (!connectedDatabase) {
+ database.close();
}
- database.close();
return workOrder;
};
export const getWorkOrderByWorkOrderNumber = (workOrderNumber) => {
- return _getWorkOrder(baseSQL + " and w.workOrderNumber = ?", workOrderNumber);
+ return _getWorkOrder(baseSQL + " and w.workOrderNumber = ?", workOrderNumber, {
+ includeLotsAndLotOccupancies: true,
+ includeComments: true,
+ includeMilestones: true
+ });
};
-export const getWorkOrder = (workOrderId) => {
- return _getWorkOrder(baseSQL + " and w.workOrderId = ?", workOrderId);
+export const getWorkOrder = (workOrderId, options, connectedDatabase) => {
+ return _getWorkOrder(baseSQL + " and w.workOrderId = ?", workOrderId, options, connectedDatabase);
};
export default getWorkOrder;
diff --git a/helpers/lotOccupancyDB/getWorkOrder.ts b/helpers/lotOccupancyDB/getWorkOrder.ts
index 41413c82..49214108 100644
--- a/helpers/lotOccupancyDB/getWorkOrder.ts
+++ b/helpers/lotOccupancyDB/getWorkOrder.ts
@@ -14,6 +14,12 @@ import { getWorkOrderMilestones } from "./getWorkOrderMilestones.js";
import type * as recordTypes from "../../types/recordTypes";
+interface WorkOrderOptions {
+ includeLotsAndLotOccupancies: boolean;
+ includeComments: boolean;
+ includeMilestones: boolean;
+}
+
const baseSQL =
"select w.workOrderId," +
" w.workOrderTypeId, t.workOrderType," +
@@ -26,11 +32,15 @@ const baseSQL =
const _getWorkOrder = (
sql: string,
- workOrderId_or_workOrderNumber: number | string
+ workOrderId_or_workOrderNumber: number | string,
+ options: WorkOrderOptions,
+ connectedDatabase?: sqlite.Database
): recordTypes.WorkOrder => {
- const database = sqlite(databasePath, {
- readonly: true
- });
+ const database =
+ connectedDatabase ||
+ sqlite(databasePath, {
+ readonly: true
+ });
database.function("userFn_dateIntegerToString", dateIntegerToString);
@@ -39,43 +49,55 @@ const _getWorkOrder = (
.get(workOrderId_or_workOrderNumber);
if (workOrder) {
- workOrder.workOrderLots = getLots(
- {
- workOrderId: workOrder.workOrderId
- },
- {
- limit: -1,
- offset: 0
- },
- database
- ).lots;
+ if (options.includeLotsAndLotOccupancies) {
+ workOrder.workOrderLots = getLots(
+ {
+ workOrderId: workOrder.workOrderId
+ },
+ {
+ limit: -1,
+ offset: 0
+ },
+ database
+ ).lots;
- workOrder.workOrderLotOccupancies = getLotOccupancies(
- {
- workOrderId: workOrder.workOrderId
- },
- {
- limit: -1,
- offset: 0,
- includeOccupants: true
- },
- database
- ).lotOccupancies;
+ workOrder.workOrderLotOccupancies = getLotOccupancies(
+ {
+ workOrderId: workOrder.workOrderId
+ },
+ {
+ limit: -1,
+ offset: 0,
+ includeOccupants: true
+ },
+ database
+ ).lotOccupancies;
+ }
- workOrder.workOrderComments = getWorkOrderComments(
- workOrder.workOrderId,
- database
- );
+ if (options.includeComments) {
+ workOrder.workOrderComments = getWorkOrderComments(
+ workOrder.workOrderId,
+ database
+ );
+ }
- workOrder.workOrderMilestones = getWorkOrderMilestones(
- {
- workOrderId: workOrder.workOrderId
- },
- database
- );
+ if (options.includeMilestones) {
+ workOrder.workOrderMilestones = getWorkOrderMilestones(
+ {
+ workOrderId: workOrder.workOrderId
+ },
+ {
+ includeWorkOrders: false,
+ orderBy: "completion"
+ },
+ database
+ );
+ }
}
- database.close();
+ if (!connectedDatabase) {
+ database.close();
+ }
return workOrder;
};
@@ -85,14 +107,26 @@ export const getWorkOrderByWorkOrderNumber = (
): recordTypes.WorkOrder => {
return _getWorkOrder(
baseSQL + " and w.workOrderNumber = ?",
- workOrderNumber
+ workOrderNumber,
+ {
+ includeLotsAndLotOccupancies: true,
+ includeComments: true,
+ includeMilestones: true
+ }
);
};
export const getWorkOrder = (
- workOrderId: number | string
+ workOrderId: number | string,
+ options: WorkOrderOptions,
+ connectedDatabase?: sqlite.Database
): recordTypes.WorkOrder => {
- return _getWorkOrder(baseSQL + " and w.workOrderId = ?", workOrderId);
+ return _getWorkOrder(
+ baseSQL + " and w.workOrderId = ?",
+ workOrderId,
+ options,
+ connectedDatabase
+ );
};
export default getWorkOrder;
diff --git a/helpers/lotOccupancyDB/getWorkOrderMilestones.d.ts b/helpers/lotOccupancyDB/getWorkOrderMilestones.d.ts
index 8a26ffe3..801ae46f 100644
--- a/helpers/lotOccupancyDB/getWorkOrderMilestones.d.ts
+++ b/helpers/lotOccupancyDB/getWorkOrderMilestones.d.ts
@@ -2,6 +2,12 @@ import sqlite from "better-sqlite3";
import type * as recordTypes from "../../types/recordTypes";
interface WorkOrderMilestoneFilters {
workOrderId?: number | string;
+ workOrderMilestoneDateFilter?: "upcomingMissed" | "recent" | "date";
+ workOrderMilestoneDateString?: string;
}
-export declare const getWorkOrderMilestones: (filters: WorkOrderMilestoneFilters, connectedDatabase?: sqlite.Database) => recordTypes.WorkOrderMilestone[];
+interface WorkOrderMilestoneOptions {
+ includeWorkOrders?: boolean;
+ orderBy: "completion" | "date";
+}
+export declare const getWorkOrderMilestones: (filters: WorkOrderMilestoneFilters, options: WorkOrderMilestoneOptions, connectedDatabase?: sqlite.Database) => recordTypes.WorkOrderMilestone[];
export default getWorkOrderMilestones;
diff --git a/helpers/lotOccupancyDB/getWorkOrderMilestones.js b/helpers/lotOccupancyDB/getWorkOrderMilestones.js
index 41655efb..1254d37e 100644
--- a/helpers/lotOccupancyDB/getWorkOrderMilestones.js
+++ b/helpers/lotOccupancyDB/getWorkOrderMilestones.js
@@ -1,7 +1,9 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
-import { dateIntegerToString, timeIntegerToString } from "@cityssm/expressjs-server-js/dateTimeFns.js";
-export const getWorkOrderMilestones = (filters, connectedDatabase) => {
+import { getWorkOrder } from "./getWorkOrder.js";
+import { dateIntegerToString, dateStringToInteger, dateToInteger, timeIntegerToString } from "@cityssm/expressjs-server-js/dateTimeFns.js";
+import * as configFunctions from "../functions.config.js";
+export const getWorkOrderMilestones = (filters, options, connectedDatabase) => {
const database = connectedDatabase ||
sqlite(databasePath, {
readonly: true
@@ -14,9 +16,48 @@ export const getWorkOrderMilestones = (filters, connectedDatabase) => {
sqlWhereClause += " and m.workOrderId = ?";
sqlParameters.push(filters.workOrderId);
}
+ const date = new Date();
+ const currentDateNumber = dateToInteger(date);
+ date.setDate(date.getDate() -
+ configFunctions.getProperty("settings.workOrders.workOrderMilestoneDateRecentBeforeDays"));
+ const recentBeforeDateNumber = dateToInteger(date);
+ date.setDate(date.getDate() +
+ configFunctions.getProperty("settings.workOrders.workOrderMilestoneDateRecentBeforeDays") +
+ configFunctions.getProperty("settings.workOrders.workOrderMilestoneDateRecentAfterDays"));
+ const recentAfterDateNumber = dateToInteger(date);
+ switch (filters.workOrderMilestoneDateFilter) {
+ case "upcomingMissed":
+ sqlWhereClause +=
+ " and (m.workOrderMilestoneCompletionDate is null or m.workOrderMilestoneDate >= ?)";
+ sqlParameters.push(currentDateNumber);
+ break;
+ case "recent":
+ sqlWhereClause +=
+ " and m.workOrderMilestoneDate >= ? and m.workOrderMilestoneDate <= ?";
+ sqlParameters.push(recentBeforeDateNumber, recentAfterDateNumber);
+ break;
+ }
+ if (filters.workOrderMilestoneDateString) {
+ sqlWhereClause += " and m.workOrderMilestoneDate = ?";
+ sqlParameters.push(dateStringToInteger(filters.workOrderMilestoneDateString));
+ }
+ let orderByClause = "";
+ switch (options.orderBy) {
+ case "completion":
+ orderByClause =
+ " order by" +
+ " m.workOrderMilestoneCompletionDate, m.workOrderMilestoneCompletionTime," +
+ " m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
+ " t.orderNumber, m.workOrderMilestoneId";
+ break;
+ case "date":
+ orderByClause =
+ " order by m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
+ " t.orderNumber, m.workOrderId, m.workOrderMilestoneId";
+ }
const workOrderMilestones = database
- .prepare("select m.workOrderMilestoneId," +
- " m.workOrderMilestoneTypeId, t.workORderMilestoneType," +
+ .prepare("select m.workOrderId, m.workOrderMilestoneId," +
+ " m.workOrderMilestoneTypeId, t.workOrderMilestoneType," +
" m.workOrderMilestoneDate, userFn_dateIntegerToString(m.workOrderMilestoneDate) as workOrderMilestoneDateString," +
" m.workOrderMilestoneTime, userFn_timeIntegerToString(m.workOrderMilestoneTime) as workOrderMilestoneTimeString," +
" m.workOrderMilestoneDescription," +
@@ -26,11 +67,17 @@ export const getWorkOrderMilestones = (filters, connectedDatabase) => {
" from WorkOrderMilestones m" +
" left join WorkOrderMilestoneTypes t on m.workOrderMilestoneTypeId = t.workOrderMilestoneTypeId" +
sqlWhereClause +
- " order by" +
- " m.workOrderMilestoneCompletionDate, m.workOrderMilestoneCompletionTime," +
- " m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
- " t.orderNumber, m.workOrderMilestoneId")
+ orderByClause)
.all(sqlParameters);
+ if (options.includeWorkOrders) {
+ for (const workOrderMilestone of workOrderMilestones) {
+ workOrderMilestone.workOrder = getWorkOrder(workOrderMilestone.workOrderId, {
+ includeLotsAndLotOccupancies: true,
+ includeComments: false,
+ includeMilestones: false
+ }, database);
+ }
+ }
if (!connectedDatabase) {
database.close();
}
diff --git a/helpers/lotOccupancyDB/getWorkOrderMilestones.ts b/helpers/lotOccupancyDB/getWorkOrderMilestones.ts
index 546ff482..dc98cce8 100644
--- a/helpers/lotOccupancyDB/getWorkOrderMilestones.ts
+++ b/helpers/lotOccupancyDB/getWorkOrderMilestones.ts
@@ -2,19 +2,33 @@ import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
+import { getWorkOrder } from "./getWorkOrder.js";
+
import {
dateIntegerToString,
+ dateStringToInteger,
+ dateToInteger,
timeIntegerToString
} from "@cityssm/expressjs-server-js/dateTimeFns.js";
+import * as configFunctions from "../functions.config.js";
+
import type * as recordTypes from "../../types/recordTypes";
interface WorkOrderMilestoneFilters {
workOrderId?: number | string;
+ workOrderMilestoneDateFilter?: "upcomingMissed" | "recent" | "date";
+ workOrderMilestoneDateString?: string;
+}
+
+interface WorkOrderMilestoneOptions {
+ includeWorkOrders?: boolean;
+ orderBy: "completion" | "date";
}
export const getWorkOrderMilestones = (
filters: WorkOrderMilestoneFilters,
+ options: WorkOrderMilestoneOptions,
connectedDatabase?: sqlite.Database
): recordTypes.WorkOrderMilestone[] => {
const database =
@@ -26,6 +40,8 @@ export const getWorkOrderMilestones = (
database.function("userFn_dateIntegerToString", dateIntegerToString);
database.function("userFn_timeIntegerToString", timeIntegerToString);
+ // Filters
+
let sqlWhereClause = " where m.recordDelete_timeMillis is null";
const sqlParameters = [];
@@ -34,10 +50,76 @@ export const getWorkOrderMilestones = (
sqlParameters.push(filters.workOrderId);
}
- const workOrderMilestones = database
+ const date = new Date();
+ const currentDateNumber = dateToInteger(date);
+
+ date.setDate(
+ date.getDate() -
+ configFunctions.getProperty(
+ "settings.workOrders.workOrderMilestoneDateRecentBeforeDays"
+ )
+ );
+
+ const recentBeforeDateNumber = dateToInteger(date);
+
+ date.setDate(
+ date.getDate() +
+ configFunctions.getProperty(
+ "settings.workOrders.workOrderMilestoneDateRecentBeforeDays"
+ ) +
+ configFunctions.getProperty(
+ "settings.workOrders.workOrderMilestoneDateRecentAfterDays"
+ )
+ );
+
+ const recentAfterDateNumber = dateToInteger(date);
+
+ switch (filters.workOrderMilestoneDateFilter) {
+ case "upcomingMissed":
+ sqlWhereClause +=
+ " and (m.workOrderMilestoneCompletionDate is null or m.workOrderMilestoneDate >= ?)";
+ sqlParameters.push(currentDateNumber);
+ break;
+
+ case "recent":
+ sqlWhereClause +=
+ " and m.workOrderMilestoneDate >= ? and m.workOrderMilestoneDate <= ?";
+ sqlParameters.push(recentBeforeDateNumber, recentAfterDateNumber);
+ break;
+ }
+
+ if (filters.workOrderMilestoneDateString) {
+ sqlWhereClause += " and m.workOrderMilestoneDate = ?";
+ sqlParameters.push(
+ dateStringToInteger(filters.workOrderMilestoneDateString)
+ );
+ }
+
+ // Order By
+
+ let orderByClause = "";
+
+ switch (options.orderBy) {
+ case "completion":
+ orderByClause =
+ " order by" +
+ " m.workOrderMilestoneCompletionDate, m.workOrderMilestoneCompletionTime," +
+ " m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
+ " t.orderNumber, m.workOrderMilestoneId";
+ break;
+
+ case "date":
+ orderByClause =
+ " order by m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
+ " t.orderNumber, m.workOrderId, m.workOrderMilestoneId";
+ }
+
+ // Query
+
+ const workOrderMilestones: recordTypes.WorkOrderMilestone[] = database
.prepare(
- "select m.workOrderMilestoneId," +
- " m.workOrderMilestoneTypeId, t.workORderMilestoneType," +
+ "select m.workOrderId, m.workOrderMilestoneId," +
+ " m.workOrderMilestoneTypeId, t.workOrderMilestoneType," +
" m.workOrderMilestoneDate, userFn_dateIntegerToString(m.workOrderMilestoneDate) as workOrderMilestoneDateString," +
" m.workOrderMilestoneTime, userFn_timeIntegerToString(m.workOrderMilestoneTime) as workOrderMilestoneTimeString," +
" m.workOrderMilestoneDescription," +
@@ -47,13 +129,24 @@ export const getWorkOrderMilestones = (
" from WorkOrderMilestones m" +
" left join WorkOrderMilestoneTypes t on m.workOrderMilestoneTypeId = t.workOrderMilestoneTypeId" +
sqlWhereClause +
- " order by" +
- " m.workOrderMilestoneCompletionDate, m.workOrderMilestoneCompletionTime," +
- " m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
- " t.orderNumber, m.workOrderMilestoneId"
+ orderByClause
)
.all(sqlParameters);
+ if (options.includeWorkOrders) {
+ for (const workOrderMilestone of workOrderMilestones) {
+ workOrderMilestone.workOrder = getWorkOrder(
+ workOrderMilestone.workOrderId,
+ {
+ includeLotsAndLotOccupancies: true,
+ includeComments: false,
+ includeMilestones: false
+ },
+ database
+ );
+ }
+ }
+
if (!connectedDatabase) {
database.close();
}
diff --git a/package-lock.json b/package-lock.json
index 6fa8af55..4ba7273b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
"@cityssm/date-diff": "^2.2.3",
"@cityssm/expressjs-server-js": "^2.3.2",
"@fortawesome/fontawesome-free": "^5.15.4",
+ "@types/randomcolor": "^0.5.6",
"activedirectory2": "^2.1.0",
"better-sqlite3": "^7.6.2",
"camelcase": "^7.0.0",
@@ -30,6 +31,7 @@
"http-errors": "^2.0.0",
"leaflet": "^1.8.0",
"papaparse": "^5.3.2",
+ "randomcolor": "^0.6.2",
"session-file-store": "^1.5.0"
},
"devDependencies": {
@@ -1165,6 +1167,11 @@
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
"dev": true
},
+ "node_modules/@types/randomcolor": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/@types/randomcolor/-/randomcolor-0.5.6.tgz",
+ "integrity": "sha512-lKkW8DGUQpZldTrwa+HM5rY+7eTyaHOMTsnj9ewt7AAwXuMPwBmkLlfh8+SXdgOhBW9iNI4x4zRjQ/TQZkdycQ=="
+ },
"node_modules/@types/range-parser": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
@@ -9141,6 +9148,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/randomcolor": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/randomcolor/-/randomcolor-0.6.2.tgz",
+ "integrity": "sha512-Mn6TbyYpFgwFuQ8KJKqf3bqqY9O1y37/0jgSK/61PUxV4QfIMv0+K2ioq8DfOjkBslcjwSzRfIDEXfzA9aCx7A=="
+ },
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -12856,6 +12868,11 @@
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
"dev": true
},
+ "@types/randomcolor": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/@types/randomcolor/-/randomcolor-0.5.6.tgz",
+ "integrity": "sha512-lKkW8DGUQpZldTrwa+HM5rY+7eTyaHOMTsnj9ewt7AAwXuMPwBmkLlfh8+SXdgOhBW9iNI4x4zRjQ/TQZkdycQ=="
+ },
"@types/range-parser": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
@@ -18967,6 +18984,11 @@
"resolved": "https://registry.npmjs.org/random-item/-/random-item-4.0.1.tgz",
"integrity": "sha512-52SyTkFhFm6YP6MN9U5+txr8lBN5/fE2+xjzp1snaDzDNHN8a6Lu/G9fSc3gvD1+bmT+kIS7A0EP9QJQgRBfsg=="
},
+ "randomcolor": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/randomcolor/-/randomcolor-0.6.2.tgz",
+ "integrity": "sha512-Mn6TbyYpFgwFuQ8KJKqf3bqqY9O1y37/0jgSK/61PUxV4QfIMv0+K2ioq8DfOjkBslcjwSzRfIDEXfzA9aCx7A=="
+ },
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
diff --git a/package.json b/package.json
index 8e6ed7fb..f8ccc6eb 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
"@cityssm/date-diff": "^2.2.3",
"@cityssm/expressjs-server-js": "^2.3.2",
"@fortawesome/fontawesome-free": "^5.15.4",
+ "@types/randomcolor": "^0.5.6",
"activedirectory2": "^2.1.0",
"better-sqlite3": "^7.6.2",
"camelcase": "^7.0.0",
@@ -54,6 +55,7 @@
"http-errors": "^2.0.0",
"leaflet": "^1.8.0",
"papaparse": "^5.3.2",
+ "randomcolor": "^0.6.2",
"session-file-store": "^1.5.0"
},
"devDependencies": {
diff --git a/public-typescript/main.js b/public-typescript/main.js
index 3f8dc438..e14c387c 100644
--- a/public-typescript/main.js
+++ b/public-typescript/main.js
@@ -72,10 +72,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
}
}
};
+ const hues = ["red", "green", "orange", "blue", "pink", "yellow", "purple"];
+ const luminosity = ["bright", "light", "dark"];
+ const getRandomColor = (seedString) => {
+ let actualSeedString = seedString;
+ if (actualSeedString.length < 2) {
+ actualSeedString = actualSeedString + "a1";
+ }
+ return exports.randomColor({
+ seed: actualSeedString + actualSeedString,
+ hue: hues[actualSeedString.codePointAt(actualSeedString.length - 1) % hues.length],
+ luminosity: luminosity[actualSeedString.codePointAt(actualSeedString.length - 2) % luminosity.length]
+ });
+ };
const los = {
highlightMap,
initializeUnlockFieldButtons,
- populateAliases
+ populateAliases,
+ getRandomColor
};
exports.los = los;
})();
diff --git a/public-typescript/main.ts b/public-typescript/main.ts
index 03cc1f7a..2fd1904f 100644
--- a/public-typescript/main.ts
+++ b/public-typescript/main.ts
@@ -118,10 +118,29 @@ import type * as globalTypes from "../types/globalTypes";
}
};
+ const hues = ["red", "green", "orange", "blue", "pink", "yellow", "purple"];
+ const luminosity = ["bright", "light", "dark"];
+
+ const getRandomColor = (seedString: string) => {
+
+ let actualSeedString = seedString;
+
+ if (actualSeedString.length < 2) {
+ actualSeedString = actualSeedString + "a1";
+ }
+
+ return exports.randomColor({
+ seed: actualSeedString + actualSeedString,
+ hue: hues[actualSeedString.codePointAt(actualSeedString.length - 1) % hues.length],
+ luminosity: luminosity[actualSeedString.codePointAt(actualSeedString.length - 2) % luminosity.length]
+ });
+ };
+
const los: globalTypes.LOS = {
highlightMap,
initializeUnlockFieldButtons,
- populateAliases
+ populateAliases,
+ getRandomColor
};
exports.los = los;
diff --git a/public-typescript/workOrderEdit.js b/public-typescript/workOrderEdit.js
index d56e1aef..2e9aba8d 100644
--- a/public-typescript/workOrderEdit.js
+++ b/public-typescript/workOrderEdit.js
@@ -37,6 +37,85 @@ Object.defineProperty(exports, "__esModule", { value: true });
}
});
});
+ let workOrderMilestones;
+ if (!isCreate) {
+ const doClose = () => {
+ cityssm.postJSON(urlPrefix + "/workOrders/doCloseWorkOrder", {
+ workOrderId
+ }, (responseJSON) => {
+ if (responseJSON.success) {
+ window.location.href =
+ urlPrefix + "/workOrders/" + workOrderId;
+ }
+ else {
+ bulmaJS.alert({
+ title: "Error Closing Work Order",
+ message: responseJSON.errorMessage,
+ contextualColorName: "danger"
+ });
+ }
+ });
+ };
+ document
+ .querySelector("#button--closeWorkOrder")
+ .addEventListener("click", () => {
+ const hasOpenMilestones = workOrderMilestones.some((milestone) => {
+ return !milestone.workOrderMilestoneCompletionDate;
+ });
+ if (hasOpenMilestones) {
+ bulmaJS.confirm({
+ title: "Close Work Order with Outstanding Milestones",
+ message: "Are you sure you want to close this work order with outstanding milestones?",
+ contextualColorName: "danger",
+ okButton: {
+ text: "Yes, Close Work Order",
+ callbackFunction: doClose
+ }
+ });
+ }
+ else {
+ bulmaJS.confirm({
+ title: "Close Work Order",
+ message: "Are you sure you want to close this work order?",
+ contextualColorName: "info",
+ okButton: {
+ text: "Yes, Close Work Order",
+ callbackFunction: doClose
+ }
+ });
+ }
+ });
+ const doDelete = () => {
+ cityssm.postJSON(urlPrefix + "/workOrders/doDeleteWorkOrder", {
+ workOrderId
+ }, (responseJSON) => {
+ if (responseJSON.success) {
+ window.location.href = urlPrefix + "/workOrders";
+ }
+ else {
+ bulmaJS.alert({
+ title: "Error Deleting Work Order",
+ message: responseJSON.errorMessage,
+ contextualColorName: "danger"
+ });
+ }
+ });
+ };
+ document
+ .querySelector("#button--deleteWorkOrder")
+ .addEventListener("click", (clickEvent) => {
+ clickEvent.preventDefault();
+ bulmaJS.confirm({
+ title: "Delete Work Order",
+ message: "Are you sure you want to delete this work order?",
+ contextualColorName: "warning",
+ okButton: {
+ text: "Yes, Delete Work Order",
+ callbackFunction: doDelete
+ }
+ });
+ });
+ }
if (!isCreate) {
let workOrderLots = exports.workOrderLots;
delete exports.workOrderLots;
@@ -593,7 +672,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
});
}
if (!isCreate) {
- let workOrderMilestones = exports.workOrderMilestones;
+ workOrderMilestones =
+ exports.workOrderMilestones;
delete exports.workOrderMilestones;
const processMilestoneResponse = (responseJSON) => {
if (responseJSON.success) {
@@ -723,7 +803,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
milestoneTypeElement.append(optionElement);
}
modalElement.querySelector("#milestoneEdit--workOrderMilestoneDateString").value = workOrderMilestone.workOrderMilestoneDateString;
- modalElement.querySelector("#milestoneEdit--workOrderMilestoneTimeString").value = workOrderMilestone.workOrderMilestoneTimeString;
+ if (workOrderMilestone.workOrderMilestoneTime) {
+ modalElement.querySelector("#milestoneEdit--workOrderMilestoneTimeString").value =
+ workOrderMilestone.workOrderMilestoneTimeString;
+ }
modalElement.querySelector("#milestoneEdit--workOrderMilestoneDescription").value = workOrderMilestone.workOrderMilestoneDescription;
},
onshown: (modalElement, closeModalFunction) => {
@@ -832,15 +915,33 @@ Object.defineProperty(exports, "__esModule", { value: true });
document
.querySelector("#button--addMilestone")
.addEventListener("click", () => {
+ let addModalElement;
let addCloseModalFunction;
const doAdd = (submitEvent) => {
submitEvent.preventDefault();
- cityssm.postJSON(urlPrefix + "/workOrders/doAddWorkOrderMilestone", submitEvent.currentTarget, (responseJSON) => {
- processMilestoneResponse(responseJSON);
- if (responseJSON.success) {
- addCloseModalFunction();
- }
- });
+ const currentDateString = cityssm.dateToString(new Date());
+ const _doAdd = () => {
+ cityssm.postJSON(urlPrefix + "/workOrders/doAddWorkOrderMilestone", submitEvent.currentTarget, (responseJSON) => {
+ processMilestoneResponse(responseJSON);
+ if (responseJSON.success) {
+ addCloseModalFunction();
+ }
+ });
+ };
+ if (addModalElement.querySelector("#milestoneAdd--workOrderMilestoneDateString").value < currentDateString) {
+ bulmaJS.confirm({
+ title: "Milestone Date in the Past",
+ message: "Are you sure you want to create a milestone with a date in the past?",
+ contextualColorName: "warning",
+ okButton: {
+ text: "Yes, Create a Past Milestone",
+ callbackFunction: _doAdd
+ }
+ });
+ }
+ else {
+ _doAdd();
+ }
};
cityssm.openHtmlModal("workOrder-addMilestone", {
onshow: (modalElement) => {
@@ -857,6 +958,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
modalElement.querySelector("#milestoneAdd--workOrderMilestoneDateString").valueAsDate = new Date();
},
onshown: (modalElement, closeModalFunction) => {
+ addModalElement = modalElement;
addCloseModalFunction = closeModalFunction;
bulmaJS.toggleHtmlClipped();
modalElement
diff --git a/public-typescript/workOrderEdit.ts b/public-typescript/workOrderEdit.ts
index 556fecf4..3f7d86e7 100644
--- a/public-typescript/workOrderEdit.ts
+++ b/public-typescript/workOrderEdit.ts
@@ -65,6 +65,105 @@ declare const bulmaJS: BulmaJS;
);
});
+ /*
+ * Work Order Options
+ */
+
+ let workOrderMilestones: recordTypes.WorkOrderMilestone[];
+
+ if (!isCreate) {
+ const doClose = () => {
+ cityssm.postJSON(
+ urlPrefix + "/workOrders/doCloseWorkOrder",
+ {
+ workOrderId
+ },
+ (responseJSON: { success: boolean; errorMessage?: string }) => {
+ if (responseJSON.success) {
+ window.location.href =
+ urlPrefix + "/workOrders/" + workOrderId;
+ } else {
+ bulmaJS.alert({
+ title: "Error Closing Work Order",
+ message: responseJSON.errorMessage,
+ contextualColorName: "danger"
+ });
+ }
+ }
+ );
+ };
+
+ document
+ .querySelector("#button--closeWorkOrder")
+ .addEventListener("click", () => {
+ const hasOpenMilestones = workOrderMilestones.some(
+ (milestone) => {
+ return !milestone.workOrderMilestoneCompletionDate;
+ }
+ );
+
+ if (hasOpenMilestones) {
+ bulmaJS.confirm({
+ title: "Close Work Order with Outstanding Milestones",
+ message:
+ "Are you sure you want to close this work order with outstanding milestones?",
+ contextualColorName: "danger",
+ okButton: {
+ text: "Yes, Close Work Order",
+ callbackFunction: doClose
+ }
+ });
+ } else {
+ bulmaJS.confirm({
+ title: "Close Work Order",
+ message:
+ "Are you sure you want to close this work order?",
+ contextualColorName: "info",
+ okButton: {
+ text: "Yes, Close Work Order",
+ callbackFunction: doClose
+ }
+ });
+ }
+ });
+
+ const doDelete = () => {
+ cityssm.postJSON(
+ urlPrefix + "/workOrders/doDeleteWorkOrder",
+ {
+ workOrderId
+ },
+ (responseJSON: { success: boolean; errorMessage?: string }) => {
+ if (responseJSON.success) {
+ window.location.href = urlPrefix + "/workOrders";
+ } else {
+ bulmaJS.alert({
+ title: "Error Deleting Work Order",
+ message: responseJSON.errorMessage,
+ contextualColorName: "danger"
+ });
+ }
+ }
+ );
+ };
+
+ document
+ .querySelector("#button--deleteWorkOrder")
+ .addEventListener("click", (clickEvent: Event) => {
+ clickEvent.preventDefault();
+
+ bulmaJS.confirm({
+ title: "Delete Work Order",
+ message: "Are you sure you want to delete this work order?",
+ contextualColorName: "warning",
+ okButton: {
+ text: "Yes, Delete Work Order",
+ callbackFunction: doDelete
+ }
+ });
+ });
+ }
+
/*
* Related Lots
*/
@@ -843,7 +942,7 @@ declare const bulmaJS: BulmaJS;
*/
if (!isCreate) {
- let workOrderMilestones =
+ workOrderMilestones =
exports.workOrderMilestones as recordTypes.WorkOrderMilestone[];
delete exports.workOrderMilestones;
@@ -1079,11 +1178,15 @@ declare const bulmaJS: BulmaJS;
) as HTMLInputElement
).value = workOrderMilestone.workOrderMilestoneDateString;
- (
- modalElement.querySelector(
- "#milestoneEdit--workOrderMilestoneTimeString"
- ) as HTMLInputElement
- ).value = workOrderMilestone.workOrderMilestoneTimeString;
+ if (workOrderMilestone.workOrderMilestoneTime) {
+ (
+ modalElement.querySelector(
+ "#milestoneEdit--workOrderMilestoneTimeString"
+ ) as HTMLInputElement
+ ).value =
+ workOrderMilestone.workOrderMilestoneTimeString;
+ }
+
(
modalElement.querySelector(
"#milestoneEdit--workOrderMilestoneDescription"
@@ -1219,26 +1322,52 @@ declare const bulmaJS: BulmaJS;
document
.querySelector("#button--addMilestone")
.addEventListener("click", () => {
+ let addModalElement: HTMLElement;
let addCloseModalFunction: () => void;
const doAdd = (submitEvent: SubmitEvent) => {
submitEvent.preventDefault();
- cityssm.postJSON(
- urlPrefix + "/workOrders/doAddWorkOrderMilestone",
- submitEvent.currentTarget,
- (responseJSON: {
- success: boolean;
- errorMessage?: string;
- workOrderMilestones?: recordTypes.WorkOrderMilestone[];
- }) => {
- processMilestoneResponse(responseJSON);
+ const currentDateString = cityssm.dateToString(new Date());
- if (responseJSON.success) {
- addCloseModalFunction();
+ const _doAdd = () => {
+ cityssm.postJSON(
+ urlPrefix + "/workOrders/doAddWorkOrderMilestone",
+ submitEvent.currentTarget,
+ (responseJSON: {
+ success: boolean;
+ errorMessage?: string;
+ workOrderMilestones?: recordTypes.WorkOrderMilestone[];
+ }) => {
+ processMilestoneResponse(responseJSON);
+
+ if (responseJSON.success) {
+ addCloseModalFunction();
+ }
}
- }
- );
+ );
+ };
+
+ if (
+ (
+ addModalElement.querySelector(
+ "#milestoneAdd--workOrderMilestoneDateString"
+ ) as HTMLInputElement
+ ).value < currentDateString
+ ) {
+ bulmaJS.confirm({
+ title: "Milestone Date in the Past",
+ message:
+ "Are you sure you want to create a milestone with a date in the past?",
+ contextualColorName: "warning",
+ okButton: {
+ text: "Yes, Create a Past Milestone",
+ callbackFunction: _doAdd
+ }
+ });
+ } else {
+ _doAdd();
+ }
};
cityssm.openHtmlModal("workOrder-addMilestone", {
@@ -1272,8 +1401,11 @@ declare const bulmaJS: BulmaJS;
).valueAsDate = new Date();
},
onshown: (modalElement, closeModalFunction) => {
+ addModalElement = modalElement;
addCloseModalFunction = closeModalFunction;
+
bulmaJS.toggleHtmlClipped();
+
modalElement
.querySelector("form")
.addEventListener("submit", doAdd);
diff --git a/public-typescript/workOrderMilestoneCalendar.js b/public-typescript/workOrderMilestoneCalendar.js
index 2e16c5fb..d98de1aa 100644
--- a/public-typescript/workOrderMilestoneCalendar.js
+++ b/public-typescript/workOrderMilestoneCalendar.js
@@ -1,22 +1,121 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
(() => {
+ const los = exports.los;
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const workOrderSearchFiltersFormElement = document.querySelector("#form--searchFilters");
const workOrderMilestoneDateFilterElement = workOrderSearchFiltersFormElement.querySelector("#searchFilter--workOrderMilestoneDateFilter");
const workOrderMilestoneDateStringElement = workOrderSearchFiltersFormElement.querySelector("#searchFilter--workOrderMilestoneDateString");
+ const milestoneCalendarContainerElement = document.querySelector("#container--milestoneCalendar");
const renderMilestones = (workOrderMilestones) => {
+ if (workOrderMilestones.length === 0) {
+ milestoneCalendarContainerElement.innerHTML =
+ '
' +
+ '
There are no milestones that meet the search criteria.
' +
+ "
";
+ return;
+ }
+ milestoneCalendarContainerElement.innerHTML = "";
+ let currentDate = cityssm.dateToString(new Date());
+ let currentPanelElement;
+ let currentPanelDateString = "";
+ for (const milestone of workOrderMilestones) {
+ if (currentPanelDateString !== milestone.workOrderMilestoneDateString) {
+ if (currentPanelElement) {
+ milestoneCalendarContainerElement.append(currentPanelElement);
+ }
+ currentPanelElement = document.createElement("div");
+ currentPanelElement.className = "panel";
+ currentPanelElement.innerHTML =
+ '' +
+ milestone.workOrderMilestoneDateString +
+ " ";
+ currentPanelDateString = milestone.workOrderMilestoneDateString;
+ }
+ const panelBlockElement = document.createElement("div");
+ panelBlockElement.className = "panel-block is-block";
+ if (!milestone.workOrderMilestoneCompletionDate && milestone.workOrderMilestoneDateString < currentDate) {
+ panelBlockElement.classList.add("has-background-warning-light");
+ }
+ let lotOccupancyHTML = "";
+ for (const lot of milestone.workOrder.workOrderLots) {
+ lotOccupancyHTML +=
+ ' ' +
+ cityssm.escapeHTML(lot.lotName) +
+ " ";
+ }
+ for (const lotOccupancy of milestone.workOrder
+ .workOrderLotOccupancies) {
+ if (lotOccupancy.lotOccupancyOccupants.length > 0) {
+ lotOccupancyHTML +=
+ ' ' +
+ cityssm.escapeHTML(lotOccupancy.lotOccupancyOccupants[0].occupantName) +
+ " ";
+ }
+ }
+ panelBlockElement.innerHTML =
+ '' +
+ ('
' +
+ '' +
+ (milestone.workOrderMilestoneCompletionDate
+ ? ' '
+ : ' ') +
+ " " +
+ "
") +
+ ('
' +
+ (milestone.workOrderMilestoneTime === 0
+ ? ""
+ : milestone.workOrderMilestoneTimeString + " ") +
+ (milestone.workOrderMilestoneTypeId
+ ? "" +
+ cityssm.escapeHTML(milestone.workOrderMilestoneType) +
+ " "
+ : "") +
+ '' +
+ cityssm.escapeHTML(milestone.workOrderMilestoneDescription) +
+ " " +
+ "
") +
+ ('
") +
+ ('
' +
+ lotOccupancyHTML +
+ "
") +
+ "
";
+ currentPanelElement.append(panelBlockElement);
+ }
+ milestoneCalendarContainerElement.append(currentPanelElement);
};
const getMilestones = (event) => {
if (event) {
event.preventDefault();
}
+ milestoneCalendarContainerElement.innerHTML =
+ '' +
+ ' ' +
+ "Loading Milestones..." +
+ "
";
cityssm.postJSON(urlPrefix + "/workOrders/doGetWorkOrderMilestones", workOrderSearchFiltersFormElement, (responseJSON) => {
renderMilestones(responseJSON.workOrderMilestones);
});
};
workOrderMilestoneDateFilterElement.addEventListener("change", () => {
- workOrderMilestoneDateStringElement.disabled = (workOrderMilestoneDateFilterElement.value !== "date");
+ workOrderMilestoneDateStringElement.disabled =
+ workOrderMilestoneDateFilterElement.value !== "date";
getMilestones();
});
workOrderMilestoneDateStringElement.addEventListener("change", getMilestones);
diff --git a/public-typescript/workOrderMilestoneCalendar.ts b/public-typescript/workOrderMilestoneCalendar.ts
index 80c30139..4333a35e 100644
--- a/public-typescript/workOrderMilestoneCalendar.ts
+++ b/public-typescript/workOrderMilestoneCalendar.ts
@@ -1,21 +1,152 @@
/* eslint-disable unicorn/prefer-module */
import type * as recordTypes from "../types/recordTypes";
-
+import type * as globalTypes from "../types/globalTypes";
import type { cityssmGlobal } from "@cityssm/bulma-webapp-js/src/types";
declare const cityssm: cityssmGlobal;
(() => {
+ const los = exports.los as globalTypes.LOS;
+
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
- const workOrderSearchFiltersFormElement = document.querySelector("#form--searchFilters") as HTMLFormElement;
+ const workOrderSearchFiltersFormElement = document.querySelector(
+ "#form--searchFilters"
+ ) as HTMLFormElement;
- const workOrderMilestoneDateFilterElement = workOrderSearchFiltersFormElement.querySelector("#searchFilter--workOrderMilestoneDateFilter") as HTMLSelectElement;
- const workOrderMilestoneDateStringElement = workOrderSearchFiltersFormElement.querySelector("#searchFilter--workOrderMilestoneDateString") as HTMLInputElement;
+ const workOrderMilestoneDateFilterElement =
+ workOrderSearchFiltersFormElement.querySelector(
+ "#searchFilter--workOrderMilestoneDateFilter"
+ ) as HTMLSelectElement;
- const renderMilestones = (workOrderMilestones: recordTypes.WorkOrderMilestone[]) => {
+ const workOrderMilestoneDateStringElement =
+ workOrderSearchFiltersFormElement.querySelector(
+ "#searchFilter--workOrderMilestoneDateString"
+ ) as HTMLInputElement;
+ const milestoneCalendarContainerElement = document.querySelector(
+ "#container--milestoneCalendar"
+ ) as HTMLElement;
+
+ const renderMilestones = (
+ workOrderMilestones: recordTypes.WorkOrderMilestone[]
+ ) => {
+ if (workOrderMilestones.length === 0) {
+ milestoneCalendarContainerElement.innerHTML =
+ '' +
+ '
There are no milestones that meet the search criteria.
' +
+ "
";
+ return;
+ }
+
+ milestoneCalendarContainerElement.innerHTML = "";
+
+ let currentDate = cityssm.dateToString(new Date());
+
+ let currentPanelElement: HTMLElement;
+ let currentPanelDateString = "";
+
+ for (const milestone of workOrderMilestones) {
+ if (currentPanelDateString !== milestone.workOrderMilestoneDateString) {
+ if (currentPanelElement) {
+ milestoneCalendarContainerElement.append(
+ currentPanelElement
+ );
+ }
+
+ currentPanelElement = document.createElement("div");
+ currentPanelElement.className = "panel";
+
+ currentPanelElement.innerHTML =
+ '' +
+ milestone.workOrderMilestoneDateString +
+ " ";
+
+ currentPanelDateString = milestone.workOrderMilestoneDateString;
+ }
+
+ const panelBlockElement = document.createElement("div");
+
+ panelBlockElement.className = "panel-block is-block";
+
+ if (!milestone.workOrderMilestoneCompletionDate && milestone.workOrderMilestoneDateString < currentDate) {
+ panelBlockElement.classList.add("has-background-warning-light");
+ }
+
+ let lotOccupancyHTML = "";
+
+ for (const lot of milestone.workOrder.workOrderLots) {
+ lotOccupancyHTML +=
+ ' ' +
+ cityssm.escapeHTML(lot.lotName) +
+ " ";
+ }
+
+ for (const lotOccupancy of milestone.workOrder
+ .workOrderLotOccupancies) {
+ if (lotOccupancy.lotOccupancyOccupants.length > 0) {
+ lotOccupancyHTML +=
+ ' ' +
+ cityssm.escapeHTML(
+ lotOccupancy.lotOccupancyOccupants[0].occupantName
+ ) +
+ " ";
+ }
+ }
+
+ panelBlockElement.innerHTML =
+ '' +
+ ('
' +
+ '' +
+ (milestone.workOrderMilestoneCompletionDate
+ ? ' '
+ : ' ') +
+ " " +
+ "
") +
+ ('
' +
+ (milestone.workOrderMilestoneTime === 0
+ ? ""
+ : milestone.workOrderMilestoneTimeString + " ") +
+ (milestone.workOrderMilestoneTypeId
+ ? "" +
+ cityssm.escapeHTML(milestone.workOrderMilestoneType) +
+ " "
+ : "") +
+ '' +
+ cityssm.escapeHTML(
+ milestone.workOrderMilestoneDescription
+ ) +
+ " " +
+ "
") +
+ ('
") +
+ ('
' +
+ lotOccupancyHTML +
+ "
") +
+ "
";
+
+ currentPanelElement.append(panelBlockElement);
+ }
+
+ milestoneCalendarContainerElement.append(currentPanelElement);
};
const getMilestones = (event?: Event) => {
@@ -23,20 +154,34 @@ declare const cityssm: cityssmGlobal;
event.preventDefault();
}
- cityssm.postJSON(urlPrefix + "/workOrders/doGetWorkOrderMilestones",
- workOrderSearchFiltersFormElement,
- (responseJSON: {workOrderMilestones: recordTypes.WorkOrderMilestone[]}) => {
- renderMilestones(responseJSON.workOrderMilestones);
- })
- } ;
+ milestoneCalendarContainerElement.innerHTML =
+ '' +
+ ' ' +
+ "Loading Milestones..." +
+ "
";
+
+ cityssm.postJSON(
+ urlPrefix + "/workOrders/doGetWorkOrderMilestones",
+ workOrderSearchFiltersFormElement,
+ (responseJSON: {
+ workOrderMilestones: recordTypes.WorkOrderMilestone[];
+ }) => {
+ renderMilestones(responseJSON.workOrderMilestones);
+ }
+ );
+ };
workOrderMilestoneDateFilterElement.addEventListener("change", () => {
- workOrderMilestoneDateStringElement.disabled = (workOrderMilestoneDateFilterElement.value !== "date");
+ workOrderMilestoneDateStringElement.disabled =
+ workOrderMilestoneDateFilterElement.value !== "date";
getMilestones();
});
- workOrderMilestoneDateStringElement.addEventListener("change", getMilestones);
+ workOrderMilestoneDateStringElement.addEventListener(
+ "change",
+ getMilestones
+ );
workOrderSearchFiltersFormElement.addEventListener("submit", getMilestones);
getMilestones();
-})();
\ No newline at end of file
+})();
diff --git a/public/javascripts/main.min.js b/public/javascripts/main.min.js
index 465813a1..75d45c43 100644
--- a/public/javascripts/main.min.js
+++ b/public/javascripts/main.min.js
@@ -1 +1 @@
-"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=e=>{const t=e.currentTarget.closest(".field").querySelector("input, select");if("INPUT"===t.tagName)t.readOnly=!1;else{const e=t.querySelectorAll("option");for(const t of e)t.disabled=!1}t.focus()},t={highlightMap:(e,t,s)=>{let o,a=t;for(;!(o=e.querySelector("#"+a))&&a.includes("-");)a=a.slice(0,Math.max(0,a.lastIndexOf("-")));if(o){o.style.fill=null,o.classList.add("highlight","is-"+s);const e=o.querySelectorAll("path");for(const t of e)t.style.fill=null}},initializeUnlockFieldButtons:t=>{const s=t.querySelectorAll(".is-unlock-field-button");for(const t of s)t.addEventListener("click",e)},populateAliases:e=>{const t=e.querySelectorAll(".alias");for(const e of t)switch(e.dataset.alias){case"Lot":e.textContent=exports.aliases.lot;break;case"lot":e.textContent=exports.aliases.lot.toLowerCase();break;case"Occupancy":e.textContent=exports.aliases.occupancy;break;case"occupancy":e.textContent=exports.aliases.occupancy.toLowerCase();break;case"Occupant":e.textContent=exports.aliases.occupant;break;case"occupant":e.textContent=exports.aliases.occupant.toLowerCase();break;case"ExternalReceiptNumber":e.textContent=exports.aliases.externalReceiptNumber}}};exports.los=t})();
\ No newline at end of file
+"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=e=>{const t=e.currentTarget.closest(".field").querySelector("input, select");if("INPUT"===t.tagName)t.readOnly=!1;else{const e=t.querySelectorAll("option");for(const t of e)t.disabled=!1}t.focus()},t=["red","green","orange","blue","pink","yellow","purple"],o=["bright","light","dark"],l={highlightMap:(e,t,o)=>{let l,s=t;for(;!(l=e.querySelector("#"+s))&&s.includes("-");)s=s.slice(0,Math.max(0,s.lastIndexOf("-")));if(l){l.style.fill=null,l.classList.add("highlight","is-"+o);const e=l.querySelectorAll("path");for(const t of e)t.style.fill=null}},initializeUnlockFieldButtons:t=>{const o=t.querySelectorAll(".is-unlock-field-button");for(const t of o)t.addEventListener("click",e)},populateAliases:e=>{const t=e.querySelectorAll(".alias");for(const e of t)switch(e.dataset.alias){case"Lot":e.textContent=exports.aliases.lot;break;case"lot":e.textContent=exports.aliases.lot.toLowerCase();break;case"Occupancy":e.textContent=exports.aliases.occupancy;break;case"occupancy":e.textContent=exports.aliases.occupancy.toLowerCase();break;case"Occupant":e.textContent=exports.aliases.occupant;break;case"occupant":e.textContent=exports.aliases.occupant.toLowerCase();break;case"ExternalReceiptNumber":e.textContent=exports.aliases.externalReceiptNumber}},getRandomColor:e=>{let l=e;return l.length<2&&(l+="a1"),exports.randomColor({seed:l+l,hue:t[l.codePointAt(l.length-1)%t.length],luminosity:o[l.codePointAt(l.length-2)%o.length]})}};exports.los=l})();
\ No newline at end of file
diff --git a/public/javascripts/workOrderEdit.min.js b/public/javascripts/workOrderEdit.min.js
index 7cca5a04..7bcc6950 100644
--- a/public/javascripts/workOrderEdit.min.js
+++ b/public/javascripts/workOrderEdit.min.js
@@ -1 +1 @@
-"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=exports.los,t=document.querySelector("main").dataset.urlPrefix,o=document.querySelector("#workOrderEdit--workOrderId").value,s=""===o;if(e.initializeUnlockFieldButtons(document.querySelector("#form--workOrderEdit")),document.querySelector("#form--workOrderEdit").addEventListener("submit",e=>{e.preventDefault(),cityssm.postJSON(t+"/workOrders/"+(s?"doCreateWorkOrder":"doUpdateWorkOrder"),e.currentTarget,e=>{e.success?s?window.location.href=t+"/workOrders/"+e.workOrderId+"/edit":bulmaJS.alert({message:"Work Order Updated Successfully",contextualColorName:"success"}):bulmaJS.alert({title:"Error Updating Work Order",message:e.errorMessage,contextualColorName:"danger"})})}),!s){let s=exports.workOrderLots;delete exports.workOrderLots;let r=exports.workOrderLotOccupancies;delete exports.workOrderLotOccupancies;const a=e=>{const s=e.currentTarget.closest(".container--lotOccupancy").dataset.lotOccupancyId;bulmaJS.confirm({title:"Delete "+exports.aliases.lot+" "+exports.aliases.occupancy+" Relationship",message:"Are you sure you want to remove the relationship to this "+exports.aliases.lot.toLowerCase()+" "+exports.aliases.occupancy.toLowerCase()+" record from this work order? Note that the record will remain.",contextualColorName:"warning",okButton:{text:"Yes, Delete Relationship",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrderLotOccupancy",{workOrderId:o,lotOccupancyId:s},e=>{e.success?(r=e.workOrderLotOccupancies,u()):bulmaJS.alert({title:"Error Deleting Relationship",message:e.errorMessage,contextualColorName:"danger"})})}}})},n=(e,r)=>{cityssm.postJSON(t+"/workOrders/doAddWorkOrderLot",{workOrderId:o,lotId:e},e=>{e.success?(s=e.workOrderLots,u()):bulmaJS.alert({title:"Error Adding "+exports.aliases.lot,message:e.errorMessage,contextualColorName:"danger"}),r&&r(e.success)})},l=(e,s)=>{cityssm.postJSON(t+"/workOrders/doAddWorkOrderLotOccupancy",{workOrderId:o,lotOccupancyId:e},e=>{e.success?(r=e.workOrderLotOccupancies,u()):bulmaJS.alert({title:"Error Adding "+exports.aliases.occupancy,message:e.errorMessage,contextualColorName:"danger"}),s&&s(e.success)})},c=e=>{const t=e.currentTarget.dataset.lotId;n(t)},i=()=>{const e=document.querySelector("#container--lotOccupancies");if(document.querySelector(".tabs a[href='#relatedTab--lotOccupancies'] .tag").textContent=r.length.toString(),0===r.length)return void(e.innerHTML='There are no '+exports.aliases.occupancies.toLowerCase()+" associated with this work order.
");e.innerHTML=''+exports.aliases.occupancy+" Type "+exports.aliases.lot+" Start Date End Date "+exports.aliases.occupants+'
';const o=cityssm.dateToString(new Date);for(const n of r){const r=document.createElement("tr");r.className="container--lotOccupancy",r.dataset.lotOccupancyId=n.lotOccupancyId.toString();const l=!(n.occupancyEndDate&&n.occupancyEndDateStringn.lotId===e.lotId);r.innerHTML=''+(l?' ':' ')+' '+cityssm.escapeHTML(n.occupancyType)+" ",n.lotId?r.insertAdjacentHTML("beforeend",""+cityssm.escapeHTML(n.lotName)+(i?"":' ')+" "):r.insertAdjacentHTML("beforeend",'(No '+exports.aliases.lot+") "),r.insertAdjacentHTML("beforeend",""+n.occupancyStartDateString+" "+(n.occupancyEndDate?n.occupancyEndDateString:'(No End Date) ')+" "+(0===n.lotOccupancyOccupants.length?'(No '+cityssm.escapeHTML(exports.aliases.occupants)+") ":cityssm.escapeHTML(n.lotOccupancyOccupants[0].occupantName)+(n.lotOccupancyOccupants.length>1?" plus "+(n.lotOccupancyOccupants.length-1):""))+' '),n.lotId&&!i&&r.querySelector(".button--addLot").addEventListener("click",c),r.querySelector(".button--deleteLotOccupancy").addEventListener("click",a),e.querySelector("tbody").append(r)}},d=e=>{const r=e.currentTarget.closest(".container--lot").dataset.lotId;bulmaJS.confirm({title:"Delete "+exports.aliases.lot+" "+exports.aliases.occupancy+" Relationship",message:"Are you sure you want to remove the relationship to this "+exports.aliases.lot.toLowerCase()+" "+exports.aliases.occupancy.toLowerCase()+" record from this work order? Note that the record will remain.",contextualColorName:"warning",okButton:{text:"Yes, Delete Relationship",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrderLot",{workOrderId:o,lotId:r},e=>{e.success?(s=e.workOrderLots,u()):bulmaJS.alert({title:"Error Deleting Relationship",message:e.errorMessage,contextualColorName:"danger"})})}}})},p=()=>{const e=document.querySelector("#container--lots");if(document.querySelector(".tabs a[href='#relatedTab--lots'] .tag").textContent=s.length.toString(),0!==s.length){e.innerHTML=''+exports.aliases.lot+" "+exports.aliases.map+" "+exports.aliases.lot+' Type Status
';for(const o of s){const s=document.createElement("tr");s.className="container--lot",s.dataset.lotId=o.lotId.toString(),s.innerHTML=''+cityssm.escapeHTML(o.lotName)+" "+cityssm.escapeHTML(o.mapName)+" "+cityssm.escapeHTML(o.lotType)+" "+cityssm.escapeHTML(o.lotStatus)+' ',s.querySelector(".button--deleteLot").addEventListener("click",d),e.querySelector("tbody").append(s)}}else e.innerHTML='There are no '+exports.aliases.lots.toLowerCase()+" associated with this work order.
"},u=()=>{i(),p()};u(),document.querySelector("#button--addLotOccupancy").addEventListener("click",()=>{let s,r;const a=e=>{const t=e.currentTarget.closest("tr"),o=t.dataset.lotOccupancyId;l(o,e=>{e&&t.remove()})},n=e=>{e&&e.preventDefault(),r.innerHTML=' Searching...
',cityssm.postJSON(t+"/lotOccupancies/doSearchLotOccupancies",s,e=>{if(0!==e.lotOccupancies.length){r.innerHTML=''+exports.aliases.occupancy+" Type "+exports.aliases.lot+" Start Date End Date "+exports.aliases.occupants+"
";for(const t of e.lotOccupancies){const e=document.createElement("tr");e.className="container--lotOccupancy",e.dataset.lotOccupancyId=t.lotOccupancyId.toString(),e.innerHTML=''+cityssm.escapeHTML(t.occupancyType)+" ",t.lotId?e.insertAdjacentHTML("beforeend",""+cityssm.escapeHTML(t.lotName)+" "):e.insertAdjacentHTML("beforeend",'(No '+exports.aliases.lot+") "),e.insertAdjacentHTML("beforeend",""+t.occupancyStartDateString+" "+(t.occupancyEndDate?t.occupancyEndDateString:'(No End Date) ')+" "+(0===t.lotOccupancyOccupants.length?'(No '+cityssm.escapeHTML(exports.aliases.occupants)+") ":cityssm.escapeHTML(t.lotOccupancyOccupants[0].occupantName)+(t.lotOccupancyOccupants.length>1?" plus "+(t.lotOccupancyOccupants.length-1):""))+" "),e.querySelector(".button--addLotOccupancy").addEventListener("click",a),r.querySelector("tbody").append(e)}}else r.innerHTML='There are no records that meet the search criteria.
'})};cityssm.openHtmlModal("workOrder-addLotOccupancy",{onshow:t=>{e.populateAliases(t),s=t.querySelector("form"),r=t.querySelector("#resultsContainer--lotOccupancyAdd"),t.querySelector("#lotOccupancySearch--notWorkOrderId").value=o,t.querySelector("#lotOccupancySearch--occupancyEffectiveDateString").value=document.querySelector("#workOrderEdit--workOrderOpenDateString").value,n()},onshown:e=>{bulmaJS.toggleHtmlClipped(),e.querySelector("#lotOccupancySearch--occupantName").addEventListener("change",n),e.querySelector("#lotOccupancySearch--lotName").addEventListener("change",n),s.addEventListener("submit",n)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),document.querySelector("#button--addLot").addEventListener("click",()=>{let s,r;const a=e=>{const t=e.currentTarget.closest("tr"),o=t.dataset.lotId;n(o,e=>{e&&t.remove()})},l=e=>{e&&e.preventDefault(),r.innerHTML=' Searching...
',cityssm.postJSON(t+"/lots/doSearchLots",s,e=>{if(0!==e.lots.length){r.innerHTML=''+exports.aliases.lot+" "+exports.aliases.map+" "+exports.aliases.lot+" Type Status
";for(const t of e.lots){const e=document.createElement("tr");e.className="container--lot",e.dataset.lotId=t.lotId.toString(),e.innerHTML=''+cityssm.escapeHTML(t.lotName)+" "+cityssm.escapeHTML(t.mapName)+" "+cityssm.escapeHTML(t.lotType)+" "+cityssm.escapeHTML(t.lotStatus)+" ",e.querySelector(".button--addLot").addEventListener("click",a),r.querySelector("tbody").append(e)}}else r.innerHTML='There are no records that meet the search criteria.
'})};cityssm.openHtmlModal("workOrder-addLot",{onshow:t=>{e.populateAliases(t),s=t.querySelector("form"),r=t.querySelector("#resultsContainer--lotAdd"),t.querySelector("#lotSearch--notWorkOrderId").value=o;const a=t.querySelector("#lotSearch--lotStatusId");for(const e of exports.lotStatuses){const t=document.createElement("option");t.value=e.lotStatusId.toString(),t.textContent=e.lotStatus,a.append(t)}l()},onshown:e=>{bulmaJS.toggleHtmlClipped(),e.querySelector("#lotSearch--lotName").addEventListener("change",l),e.querySelector("#lotSearch--lotStatusId").addEventListener("change",l),s.addEventListener("submit",l)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})})}if(!s){let e=exports.workOrderMilestones;delete exports.workOrderMilestones;const s=t=>{t.success?(e=t.workOrderMilestones,c()):bulmaJS.alert({title:"Error Reopening Milestone",message:t.errorMessage,contextualColorName:"danger"})},r=r=>{r.preventDefault();const a=cityssm.dateToString(new Date),n=Number.parseInt(r.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId,10),l=e.find(e=>e.workOrderMilestoneId===n);bulmaJS.confirm({title:"Complete Milestone",message:"Are you sure you want to complete this milestone?"+(l.workOrderMilestoneDateString>a?"Note that this milestone is expected to be completed in the future. ":""),messageIsHtml:!0,contextualColorName:"warning",okButton:{text:"Yes, Complete Milestone",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doCompleteWorkOrderMilestone",{workOrderId:o,workOrderMilestoneId:n},s)}}})},a=e=>{e.preventDefault();const r=e.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId;bulmaJS.confirm({title:"Reopen Milestone",message:"Are you sure you want to remove the completion status from this milestone, and reopen it?",contextualColorName:"warning",okButton:{text:"Yes, Reopen Milestone",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doReopenWorkOrderMilestone",{workOrderId:o,workOrderMilestoneId:r},s)}}})},n=e=>{e.preventDefault();const r=e.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId;bulmaJS.confirm({title:"Delete Milestone",message:"Are you sure you want to delete this milestone?",contextualColorName:"warning",okButton:{text:"Yes, Delete Milestone",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrderMilestone",{workOrderMilestoneId:r,workOrderId:o},s)}}})},l=r=>{r.preventDefault();const a=Number.parseInt(r.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId,10),n=e.find(e=>e.workOrderMilestoneId===a);let l;const c=e=>{e.preventDefault(),cityssm.postJSON(t+"/workOrders/doUpdateWorkOrderMilestone",e.currentTarget,e=>{s(e),e.success&&l()})};cityssm.openHtmlModal("workOrder-editMilestone",{onshow:e=>{e.querySelector("#milestoneEdit--workOrderId").value=o,e.querySelector("#milestoneEdit--workOrderMilestoneId").value=n.workOrderMilestoneId.toString();const t=e.querySelector("#milestoneEdit--workOrderMilestoneTypeId");let s=!1;for(const e of exports.workOrderMilestoneTypes){const o=document.createElement("option");o.value=e.workOrderMilestoneTypeId.toString(),o.textContent=e.workOrderMilestoneType,e.workOrderMilestoneTypeId===n.workOrderMilestoneTypeId&&(o.selected=!0,s=!0),t.append(o)}if(!s&&n.workOrderMilestoneTypeId){const e=document.createElement("option");e.value=n.workOrderMilestoneTypeId.toString(),e.textContent=n.workOrderMilestoneType,e.selected=!0,t.append(e)}e.querySelector("#milestoneEdit--workOrderMilestoneDateString").value=n.workOrderMilestoneDateString,e.querySelector("#milestoneEdit--workOrderMilestoneTimeString").value=n.workOrderMilestoneTimeString,e.querySelector("#milestoneEdit--workOrderMilestoneDescription").value=n.workOrderMilestoneDescription},onshown:(e,t)=>{l=t,bulmaJS.toggleHtmlClipped(),e.querySelector("form").addEventListener("submit",c)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})},c=()=>{const t=document.querySelector("#panel--milestones"),o=t.querySelectorAll(".panel-block");for(const e of o)e.remove();for(const o of e){const e=document.createElement("div");e.className="panel-block is-block container--milestone",e.dataset.workOrderMilestoneId=o.workOrderMilestoneId.toString(),e.innerHTML=''+(o.workOrderMilestoneCompletionDate?' ':' ')+'
'+(o.workOrderMilestoneTypeId?""+cityssm.escapeHTML(o.workOrderMilestoneType)+" ":"")+o.workOrderMilestoneDateString+(o.workOrderMilestoneTime?" "+o.workOrderMilestoneTimeString:"")+''+cityssm.escapeHTML(o.workOrderMilestoneDescription)+'
',o.workOrderMilestoneCompletionDate?e.querySelector(".button--reopenMilestone").addEventListener("click",a):(e.querySelector(".button--editMilestone").addEventListener("click",l),e.querySelector(".button--completeMilestone").addEventListener("click",r)),e.querySelector(".button--deleteMilestone").addEventListener("click",n),t.append(e)}bulmaJS.init(t)};c(),document.querySelector("#button--addMilestone").addEventListener("click",()=>{let e;const r=o=>{o.preventDefault(),cityssm.postJSON(t+"/workOrders/doAddWorkOrderMilestone",o.currentTarget,t=>{s(t),t.success&&e()})};cityssm.openHtmlModal("workOrder-addMilestone",{onshow:e=>{e.querySelector("#milestoneAdd--workOrderId").value=o;const t=e.querySelector("#milestoneAdd--workOrderMilestoneTypeId");for(const e of exports.workOrderMilestoneTypes){const o=document.createElement("option");o.value=e.workOrderMilestoneTypeId.toString(),o.textContent=e.workOrderMilestoneType,t.append(o)}e.querySelector("#milestoneAdd--workOrderMilestoneDateString").valueAsDate=new Date},onshown:(t,o)=>{e=o,bulmaJS.toggleHtmlClipped(),t.querySelector("form").addEventListener("submit",r)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})})}})();
\ No newline at end of file
+"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=exports.los,t=document.querySelector("main").dataset.urlPrefix,o=document.querySelector("#workOrderEdit--workOrderId").value,s=""===o;let r;if(e.initializeUnlockFieldButtons(document.querySelector("#form--workOrderEdit")),document.querySelector("#form--workOrderEdit").addEventListener("submit",e=>{e.preventDefault(),cityssm.postJSON(t+"/workOrders/"+(s?"doCreateWorkOrder":"doUpdateWorkOrder"),e.currentTarget,e=>{e.success?s?window.location.href=t+"/workOrders/"+e.workOrderId+"/edit":bulmaJS.alert({message:"Work Order Updated Successfully",contextualColorName:"success"}):bulmaJS.alert({title:"Error Updating Work Order",message:e.errorMessage,contextualColorName:"danger"})})}),!s){const e=()=>{cityssm.postJSON(t+"/workOrders/doCloseWorkOrder",{workOrderId:o},e=>{e.success?window.location.href=t+"/workOrders/"+o:bulmaJS.alert({title:"Error Closing Work Order",message:e.errorMessage,contextualColorName:"danger"})})};document.querySelector("#button--closeWorkOrder").addEventListener("click",()=>{r.some(e=>!e.workOrderMilestoneCompletionDate)?bulmaJS.confirm({title:"Close Work Order with Outstanding Milestones",message:"Are you sure you want to close this work order with outstanding milestones?",contextualColorName:"danger",okButton:{text:"Yes, Close Work Order",callbackFunction:e}}):bulmaJS.confirm({title:"Close Work Order",message:"Are you sure you want to close this work order?",contextualColorName:"info",okButton:{text:"Yes, Close Work Order",callbackFunction:e}})});const s=()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrder",{workOrderId:o},e=>{e.success?window.location.href=t+"/workOrders":bulmaJS.alert({title:"Error Deleting Work Order",message:e.errorMessage,contextualColorName:"danger"})})};document.querySelector("#button--deleteWorkOrder").addEventListener("click",e=>{e.preventDefault(),bulmaJS.confirm({title:"Delete Work Order",message:"Are you sure you want to delete this work order?",contextualColorName:"warning",okButton:{text:"Yes, Delete Work Order",callbackFunction:s}})})}if(!s){let s=exports.workOrderLots;delete exports.workOrderLots;let r=exports.workOrderLotOccupancies;delete exports.workOrderLotOccupancies;const a=e=>{const s=e.currentTarget.closest(".container--lotOccupancy").dataset.lotOccupancyId;bulmaJS.confirm({title:"Delete "+exports.aliases.lot+" "+exports.aliases.occupancy+" Relationship",message:"Are you sure you want to remove the relationship to this "+exports.aliases.lot.toLowerCase()+" "+exports.aliases.occupancy.toLowerCase()+" record from this work order? Note that the record will remain.",contextualColorName:"warning",okButton:{text:"Yes, Delete Relationship",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrderLotOccupancy",{workOrderId:o,lotOccupancyId:s},e=>{e.success?(r=e.workOrderLotOccupancies,p()):bulmaJS.alert({title:"Error Deleting Relationship",message:e.errorMessage,contextualColorName:"danger"})})}}})},n=(e,r)=>{cityssm.postJSON(t+"/workOrders/doAddWorkOrderLot",{workOrderId:o,lotId:e},e=>{e.success?(s=e.workOrderLots,p()):bulmaJS.alert({title:"Error Adding "+exports.aliases.lot,message:e.errorMessage,contextualColorName:"danger"}),r&&r(e.success)})},l=(e,s)=>{cityssm.postJSON(t+"/workOrders/doAddWorkOrderLotOccupancy",{workOrderId:o,lotOccupancyId:e},e=>{e.success?(r=e.workOrderLotOccupancies,p()):bulmaJS.alert({title:"Error Adding "+exports.aliases.occupancy,message:e.errorMessage,contextualColorName:"danger"}),s&&s(e.success)})},c=e=>{const t=e.currentTarget.dataset.lotId;n(t)},i=()=>{const e=document.querySelector("#container--lotOccupancies");if(document.querySelector(".tabs a[href='#relatedTab--lotOccupancies'] .tag").textContent=r.length.toString(),0===r.length)return void(e.innerHTML='There are no '+exports.aliases.occupancies.toLowerCase()+" associated with this work order.
");e.innerHTML=''+exports.aliases.occupancy+" Type "+exports.aliases.lot+" Start Date End Date "+exports.aliases.occupants+'
';const o=cityssm.dateToString(new Date);for(const n of r){const r=document.createElement("tr");r.className="container--lotOccupancy",r.dataset.lotOccupancyId=n.lotOccupancyId.toString();const l=!(n.occupancyEndDate&&n.occupancyEndDateStringn.lotId===e.lotId);r.innerHTML=''+(l?' ':' ')+' '+cityssm.escapeHTML(n.occupancyType)+" ",n.lotId?r.insertAdjacentHTML("beforeend",""+cityssm.escapeHTML(n.lotName)+(i?"":' ')+" "):r.insertAdjacentHTML("beforeend",'(No '+exports.aliases.lot+") "),r.insertAdjacentHTML("beforeend",""+n.occupancyStartDateString+" "+(n.occupancyEndDate?n.occupancyEndDateString:'(No End Date) ')+" "+(0===n.lotOccupancyOccupants.length?'(No '+cityssm.escapeHTML(exports.aliases.occupants)+") ":cityssm.escapeHTML(n.lotOccupancyOccupants[0].occupantName)+(n.lotOccupancyOccupants.length>1?" plus "+(n.lotOccupancyOccupants.length-1):""))+' '),n.lotId&&!i&&r.querySelector(".button--addLot").addEventListener("click",c),r.querySelector(".button--deleteLotOccupancy").addEventListener("click",a),e.querySelector("tbody").append(r)}},d=e=>{const r=e.currentTarget.closest(".container--lot").dataset.lotId;bulmaJS.confirm({title:"Delete "+exports.aliases.lot+" "+exports.aliases.occupancy+" Relationship",message:"Are you sure you want to remove the relationship to this "+exports.aliases.lot.toLowerCase()+" "+exports.aliases.occupancy.toLowerCase()+" record from this work order? Note that the record will remain.",contextualColorName:"warning",okButton:{text:"Yes, Delete Relationship",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrderLot",{workOrderId:o,lotId:r},e=>{e.success?(s=e.workOrderLots,p()):bulmaJS.alert({title:"Error Deleting Relationship",message:e.errorMessage,contextualColorName:"danger"})})}}})},u=()=>{const e=document.querySelector("#container--lots");if(document.querySelector(".tabs a[href='#relatedTab--lots'] .tag").textContent=s.length.toString(),0!==s.length){e.innerHTML=''+exports.aliases.lot+" "+exports.aliases.map+" "+exports.aliases.lot+' Type Status
';for(const o of s){const s=document.createElement("tr");s.className="container--lot",s.dataset.lotId=o.lotId.toString(),s.innerHTML=''+cityssm.escapeHTML(o.lotName)+" "+cityssm.escapeHTML(o.mapName)+" "+cityssm.escapeHTML(o.lotType)+" "+cityssm.escapeHTML(o.lotStatus)+' ',s.querySelector(".button--deleteLot").addEventListener("click",d),e.querySelector("tbody").append(s)}}else e.innerHTML='There are no '+exports.aliases.lots.toLowerCase()+" associated with this work order.
"},p=()=>{i(),u()};p(),document.querySelector("#button--addLotOccupancy").addEventListener("click",()=>{let s,r;const a=e=>{const t=e.currentTarget.closest("tr"),o=t.dataset.lotOccupancyId;l(o,e=>{e&&t.remove()})},n=e=>{e&&e.preventDefault(),r.innerHTML=' Searching...
',cityssm.postJSON(t+"/lotOccupancies/doSearchLotOccupancies",s,e=>{if(0!==e.lotOccupancies.length){r.innerHTML=''+exports.aliases.occupancy+" Type "+exports.aliases.lot+" Start Date End Date "+exports.aliases.occupants+"
";for(const t of e.lotOccupancies){const e=document.createElement("tr");e.className="container--lotOccupancy",e.dataset.lotOccupancyId=t.lotOccupancyId.toString(),e.innerHTML=''+cityssm.escapeHTML(t.occupancyType)+" ",t.lotId?e.insertAdjacentHTML("beforeend",""+cityssm.escapeHTML(t.lotName)+" "):e.insertAdjacentHTML("beforeend",'(No '+exports.aliases.lot+") "),e.insertAdjacentHTML("beforeend",""+t.occupancyStartDateString+" "+(t.occupancyEndDate?t.occupancyEndDateString:'(No End Date) ')+" "+(0===t.lotOccupancyOccupants.length?'(No '+cityssm.escapeHTML(exports.aliases.occupants)+") ":cityssm.escapeHTML(t.lotOccupancyOccupants[0].occupantName)+(t.lotOccupancyOccupants.length>1?" plus "+(t.lotOccupancyOccupants.length-1):""))+" "),e.querySelector(".button--addLotOccupancy").addEventListener("click",a),r.querySelector("tbody").append(e)}}else r.innerHTML='There are no records that meet the search criteria.
'})};cityssm.openHtmlModal("workOrder-addLotOccupancy",{onshow:t=>{e.populateAliases(t),s=t.querySelector("form"),r=t.querySelector("#resultsContainer--lotOccupancyAdd"),t.querySelector("#lotOccupancySearch--notWorkOrderId").value=o,t.querySelector("#lotOccupancySearch--occupancyEffectiveDateString").value=document.querySelector("#workOrderEdit--workOrderOpenDateString").value,n()},onshown:e=>{bulmaJS.toggleHtmlClipped(),e.querySelector("#lotOccupancySearch--occupantName").addEventListener("change",n),e.querySelector("#lotOccupancySearch--lotName").addEventListener("change",n),s.addEventListener("submit",n)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),document.querySelector("#button--addLot").addEventListener("click",()=>{let s,r;const a=e=>{const t=e.currentTarget.closest("tr"),o=t.dataset.lotId;n(o,e=>{e&&t.remove()})},l=e=>{e&&e.preventDefault(),r.innerHTML=' Searching...
',cityssm.postJSON(t+"/lots/doSearchLots",s,e=>{if(0!==e.lots.length){r.innerHTML=''+exports.aliases.lot+" "+exports.aliases.map+" "+exports.aliases.lot+" Type Status
";for(const t of e.lots){const e=document.createElement("tr");e.className="container--lot",e.dataset.lotId=t.lotId.toString(),e.innerHTML=''+cityssm.escapeHTML(t.lotName)+" "+cityssm.escapeHTML(t.mapName)+" "+cityssm.escapeHTML(t.lotType)+" "+cityssm.escapeHTML(t.lotStatus)+" ",e.querySelector(".button--addLot").addEventListener("click",a),r.querySelector("tbody").append(e)}}else r.innerHTML='There are no records that meet the search criteria.
'})};cityssm.openHtmlModal("workOrder-addLot",{onshow:t=>{e.populateAliases(t),s=t.querySelector("form"),r=t.querySelector("#resultsContainer--lotAdd"),t.querySelector("#lotSearch--notWorkOrderId").value=o;const a=t.querySelector("#lotSearch--lotStatusId");for(const e of exports.lotStatuses){const t=document.createElement("option");t.value=e.lotStatusId.toString(),t.textContent=e.lotStatus,a.append(t)}l()},onshown:e=>{bulmaJS.toggleHtmlClipped(),e.querySelector("#lotSearch--lotName").addEventListener("change",l),e.querySelector("#lotSearch--lotStatusId").addEventListener("change",l),s.addEventListener("submit",l)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})})}if(!s){r=exports.workOrderMilestones,delete exports.workOrderMilestones;const e=e=>{e.success?(r=e.workOrderMilestones,c()):bulmaJS.alert({title:"Error Reopening Milestone",message:e.errorMessage,contextualColorName:"danger"})},s=s=>{s.preventDefault();const a=cityssm.dateToString(new Date),n=Number.parseInt(s.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId,10),l=r.find(e=>e.workOrderMilestoneId===n);bulmaJS.confirm({title:"Complete Milestone",message:"Are you sure you want to complete this milestone?"+(l.workOrderMilestoneDateString>a?"Note that this milestone is expected to be completed in the future. ":""),messageIsHtml:!0,contextualColorName:"warning",okButton:{text:"Yes, Complete Milestone",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doCompleteWorkOrderMilestone",{workOrderId:o,workOrderMilestoneId:n},e)}}})},a=s=>{s.preventDefault();const r=s.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId;bulmaJS.confirm({title:"Reopen Milestone",message:"Are you sure you want to remove the completion status from this milestone, and reopen it?",contextualColorName:"warning",okButton:{text:"Yes, Reopen Milestone",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doReopenWorkOrderMilestone",{workOrderId:o,workOrderMilestoneId:r},e)}}})},n=s=>{s.preventDefault();const r=s.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId;bulmaJS.confirm({title:"Delete Milestone",message:"Are you sure you want to delete this milestone?",contextualColorName:"warning",okButton:{text:"Yes, Delete Milestone",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrderMilestone",{workOrderMilestoneId:r,workOrderId:o},e)}}})},l=s=>{s.preventDefault();const a=Number.parseInt(s.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId,10),n=r.find(e=>e.workOrderMilestoneId===a);let l;const c=o=>{o.preventDefault(),cityssm.postJSON(t+"/workOrders/doUpdateWorkOrderMilestone",o.currentTarget,t=>{e(t),t.success&&l()})};cityssm.openHtmlModal("workOrder-editMilestone",{onshow:e=>{e.querySelector("#milestoneEdit--workOrderId").value=o,e.querySelector("#milestoneEdit--workOrderMilestoneId").value=n.workOrderMilestoneId.toString();const t=e.querySelector("#milestoneEdit--workOrderMilestoneTypeId");let s=!1;for(const e of exports.workOrderMilestoneTypes){const o=document.createElement("option");o.value=e.workOrderMilestoneTypeId.toString(),o.textContent=e.workOrderMilestoneType,e.workOrderMilestoneTypeId===n.workOrderMilestoneTypeId&&(o.selected=!0,s=!0),t.append(o)}if(!s&&n.workOrderMilestoneTypeId){const e=document.createElement("option");e.value=n.workOrderMilestoneTypeId.toString(),e.textContent=n.workOrderMilestoneType,e.selected=!0,t.append(e)}e.querySelector("#milestoneEdit--workOrderMilestoneDateString").value=n.workOrderMilestoneDateString,n.workOrderMilestoneTime&&(e.querySelector("#milestoneEdit--workOrderMilestoneTimeString").value=n.workOrderMilestoneTimeString),e.querySelector("#milestoneEdit--workOrderMilestoneDescription").value=n.workOrderMilestoneDescription},onshown:(e,t)=>{l=t,bulmaJS.toggleHtmlClipped(),e.querySelector("form").addEventListener("submit",c)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})},c=()=>{const e=document.querySelector("#panel--milestones"),t=e.querySelectorAll(".panel-block");for(const e of t)e.remove();for(const t of r){const o=document.createElement("div");o.className="panel-block is-block container--milestone",o.dataset.workOrderMilestoneId=t.workOrderMilestoneId.toString(),o.innerHTML=''+(t.workOrderMilestoneCompletionDate?' ':' ')+'
'+(t.workOrderMilestoneTypeId?""+cityssm.escapeHTML(t.workOrderMilestoneType)+" ":"")+t.workOrderMilestoneDateString+(t.workOrderMilestoneTime?" "+t.workOrderMilestoneTimeString:"")+''+cityssm.escapeHTML(t.workOrderMilestoneDescription)+'
',t.workOrderMilestoneCompletionDate?o.querySelector(".button--reopenMilestone").addEventListener("click",a):(o.querySelector(".button--editMilestone").addEventListener("click",l),o.querySelector(".button--completeMilestone").addEventListener("click",s)),o.querySelector(".button--deleteMilestone").addEventListener("click",n),e.append(o)}bulmaJS.init(e)};c(),document.querySelector("#button--addMilestone").addEventListener("click",()=>{let s,r;const a=o=>{o.preventDefault();const a=cityssm.dateToString(new Date),n=()=>{cityssm.postJSON(t+"/workOrders/doAddWorkOrderMilestone",o.currentTarget,t=>{e(t),t.success&&r()})};s.querySelector("#milestoneAdd--workOrderMilestoneDateString").value{e.querySelector("#milestoneAdd--workOrderId").value=o;const t=e.querySelector("#milestoneAdd--workOrderMilestoneTypeId");for(const e of exports.workOrderMilestoneTypes){const o=document.createElement("option");o.value=e.workOrderMilestoneTypeId.toString(),o.textContent=e.workOrderMilestoneType,t.append(o)}e.querySelector("#milestoneAdd--workOrderMilestoneDateString").valueAsDate=new Date},onshown:(e,t)=>{s=e,r=t,bulmaJS.toggleHtmlClipped(),e.querySelector("form").addEventListener("submit",a)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})})}})();
\ No newline at end of file
diff --git a/public/javascripts/workOrderMilestoneCalendar.min.js b/public/javascripts/workOrderMilestoneCalendar.min.js
index c2d8304e..dcfa4244 100644
--- a/public/javascripts/workOrderMilestoneCalendar.min.js
+++ b/public/javascripts/workOrderMilestoneCalendar.min.js
@@ -1 +1 @@
-"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=document.querySelector("main").dataset.urlPrefix,r=document.querySelector("#form--searchFilters"),t=r.querySelector("#searchFilter--workOrderMilestoneDateFilter"),s=r.querySelector("#searchFilter--workOrderMilestoneDateString"),o=t=>{t&&t.preventDefault(),cityssm.postJSON(e+"/workOrders/doGetWorkOrderMilestones",r,e=>{e.workOrderMilestones})};t.addEventListener("change",()=>{s.disabled="date"!==t.value,o()}),s.addEventListener("change",o),r.addEventListener("submit",o),o()})();
\ No newline at end of file
+"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=exports.los,r=document.querySelector("main").dataset.urlPrefix,s=document.querySelector("#form--searchFilters"),a=s.querySelector("#searchFilter--workOrderMilestoneDateFilter"),t=s.querySelector("#searchFilter--workOrderMilestoneDateString"),i=document.querySelector("#container--milestoneCalendar"),o=a=>{a&&a.preventDefault(),i.innerHTML=' Loading Milestones...
',cityssm.postJSON(r+"/workOrders/doGetWorkOrderMilestones",s,s=>{(s=>{if(0===s.length)return void(i.innerHTML='There are no milestones that meet the search criteria.
');i.innerHTML="";let a,t=cityssm.dateToString(new Date),o="";for(const n of s){o!==n.workOrderMilestoneDateString&&(a&&i.append(a),(a=document.createElement("div")).className="panel",a.innerHTML=''+n.workOrderMilestoneDateString+" ",o=n.workOrderMilestoneDateString);const s=document.createElement("div");s.className="panel-block is-block",!n.workOrderMilestoneCompletionDate&&n.workOrderMilestoneDateString '+cityssm.escapeHTML(e.lotName)+" ";for(const e of n.workOrder.workOrderLotOccupancies)e.lotOccupancyOccupants.length>0&&(c+=' '+cityssm.escapeHTML(e.lotOccupancyOccupants[0].occupantName)+" ");s.innerHTML=''+(n.workOrderMilestoneCompletionDate?' ':' ')+'
'+(0===n.workOrderMilestoneTime?"":n.workOrderMilestoneTimeString+" ")+(n.workOrderMilestoneTypeId?""+cityssm.escapeHTML(n.workOrderMilestoneType)+" ":"")+''+cityssm.escapeHTML(n.workOrderMilestoneDescription)+'
'+c+"
",a.append(s)}i.append(a)})(s.workOrderMilestones)})};a.addEventListener("change",()=>{t.disabled="date"!==a.value,o()}),t.addEventListener("change",o),s.addEventListener("submit",o),o()})();
\ No newline at end of file
diff --git a/routes/workOrders.js b/routes/workOrders.js
index 5f6d326b..05dd8928 100644
--- a/routes/workOrders.js
+++ b/routes/workOrders.js
@@ -3,12 +3,15 @@ import * as permissionHandlers from "../handlers/permissions.js";
import handler_search from "../handlers/workOrders-get/search.js";
import handler_doSearchWorkOrders from "../handlers/workOrders-post/doSearchWorkOrders.js";
import handler_milestoneCalendar from "../handlers/workOrders-get/milestoneCalendar.js";
+import handler_doGetWorkOrderMilestones from "../handlers/workOrders-post/doGetWorkOrderMilestones.js";
import handler_view from "../handlers/workOrders-get/view.js";
import handler_doReopenWorkOrder from "../handlers/workOrders-post/doReopenWorkOrder.js";
import handler_new from "../handlers/workOrders-get/new.js";
import handler_doCreateWorkOrder from "../handlers/workOrders-post/doCreateWorkOrder.js";
import handler_edit from "../handlers/workOrders-get/edit.js";
import handler_doUpdateWorkOrder from "../handlers/workOrders-post/doUpdateWorkOrder.js";
+import handler_doCloseWorkOrder from "../handlers/workOrders-post/doCloseWorkOrder.js";
+import handler_doDeleteWorkOrder from "../handlers/workOrders-post/doDeleteWorkOrder.js";
import handler_doAddWorkOrderLotOccupancy from "../handlers/workOrders-post/doAddWorkOrderLotOccupancy.js";
import handler_doDeleteWorkOrderLotOccupancy from "../handlers/workOrders-post/doDeleteWorkOrderLotOccupancy.js";
import handler_doAddWorkOrderLot from "../handlers/workOrders-post/doAddWorkOrderLot.js";
@@ -22,12 +25,15 @@ export const router = Router();
router.get("/", handler_search);
router.post("/doSearchWorkOrders", handler_doSearchWorkOrders);
router.get("/milestoneCalendar", handler_milestoneCalendar);
+router.post("/doGetWorkOrderMilestones", handler_doGetWorkOrderMilestones);
router.get("/new", permissionHandlers.adminGetHandler, handler_new);
router.post("/doCreateWorkOrder", permissionHandlers.updatePostHandler, handler_doCreateWorkOrder);
router.get("/:workOrderId", handler_view);
router.post("/doReopenWorkOrder", permissionHandlers.updatePostHandler, handler_doReopenWorkOrder);
router.get("/:workOrderId/edit", permissionHandlers.updateGetHandler, handler_edit);
router.post("/doUpdateWorkOrder", permissionHandlers.updatePostHandler, handler_doUpdateWorkOrder);
+router.post("/doCloseWorkOrder", permissionHandlers.updatePostHandler, handler_doCloseWorkOrder);
+router.post("/doDeleteWorkOrder", permissionHandlers.updatePostHandler, handler_doDeleteWorkOrder);
router.post("/doAddWorkOrderLotOccupancy", permissionHandlers.updatePostHandler, handler_doAddWorkOrderLotOccupancy);
router.post("/doDeleteWorkOrderLotOccupancy", permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLotOccupancy);
router.post("/doAddWorkOrderLot", permissionHandlers.updatePostHandler, handler_doAddWorkOrderLot);
diff --git a/routes/workOrders.ts b/routes/workOrders.ts
index f0f1d01b..994f05e4 100644
--- a/routes/workOrders.ts
+++ b/routes/workOrders.ts
@@ -6,6 +6,7 @@ import handler_search from "../handlers/workOrders-get/search.js";
import handler_doSearchWorkOrders from "../handlers/workOrders-post/doSearchWorkOrders.js";
import handler_milestoneCalendar from "../handlers/workOrders-get/milestoneCalendar.js";
+import handler_doGetWorkOrderMilestones from "../handlers/workOrders-post/doGetWorkOrderMilestones.js";
import handler_view from "../handlers/workOrders-get/view.js";
import handler_doReopenWorkOrder from "../handlers/workOrders-post/doReopenWorkOrder.js";
@@ -15,6 +16,8 @@ import handler_doCreateWorkOrder from "../handlers/workOrders-post/doCreateWorkO
import handler_edit from "../handlers/workOrders-get/edit.js";
import handler_doUpdateWorkOrder from "../handlers/workOrders-post/doUpdateWorkOrder.js";
+import handler_doCloseWorkOrder from "../handlers/workOrders-post/doCloseWorkOrder.js";
+import handler_doDeleteWorkOrder from "../handlers/workOrders-post/doDeleteWorkOrder.js";
import handler_doAddWorkOrderLotOccupancy from "../handlers/workOrders-post/doAddWorkOrderLotOccupancy.js";
import handler_doDeleteWorkOrderLotOccupancy from "../handlers/workOrders-post/doDeleteWorkOrderLotOccupancy.js";
@@ -40,6 +43,8 @@ router.post("/doSearchWorkOrders", handler_doSearchWorkOrders);
router.get("/milestoneCalendar", handler_milestoneCalendar);
+router.post("/doGetWorkOrderMilestones", handler_doGetWorkOrderMilestones);
+
// New
router.get("/new", permissionHandlers.adminGetHandler, handler_new);
@@ -74,6 +79,20 @@ router.post(
handler_doUpdateWorkOrder
);
+router.post(
+ "/doCloseWorkOrder",
+ permissionHandlers.updatePostHandler,
+ handler_doCloseWorkOrder
+);
+
+router.post(
+ "/doDeleteWorkOrder",
+ permissionHandlers.updatePostHandler,
+ handler_doDeleteWorkOrder
+);
+
+// Lot Occupancy
+
router.post(
"/doAddWorkOrderLotOccupancy",
permissionHandlers.updatePostHandler,
diff --git a/temp/legacy.importFromCSV.js b/temp/legacy.importFromCSV.js
index 541fedbd..4944da6d 100644
--- a/temp/legacy.importFromCSV.js
+++ b/temp/legacy.importFromCSV.js
@@ -728,7 +728,11 @@ function importFromWorkOrderCSV() {
workOrderRow.WO_REMARK3).trim(),
workOrderOpenDateString
}, user);
- workOrder = getWorkOrder(workOrderId);
+ workOrder = getWorkOrder(workOrderId, {
+ includeLotsAndLotOccupancies: true,
+ includeComments: true,
+ includeMilestones: true
+ });
}
let lot;
if (workOrderRow.WO_CEMETERY !== "00") {
@@ -912,7 +916,11 @@ function importFromWorkOrderCSV() {
}
if (workOrderRow.WO_FUNERAL_YR) {
const workOrderMilestoneDateString = formatDateString(workOrderRow.WO_FUNERAL_YR, workOrderRow.WO_FUNERAL_MON, workOrderRow.WO_FUNERAL_DAY);
- const workOrderMilestoneTimeString = formatTimeString(workOrderRow.WO_FUNERAL_HR, workOrderRow.WO_FUNERAL_MIN);
+ let funeralHour = Number.parseInt(workOrderRow.WO_FUNERAL_HR, 10);
+ if (funeralHour <= 6) {
+ funeralHour += 12;
+ }
+ const workOrderMilestoneTimeString = formatTimeString(funeralHour.toString(), workOrderRow.WO_FUNERAL_MIN);
addWorkOrderMilestone({
workOrderId: workOrder.workOrderId,
workOrderMilestoneTypeId: funeralWorkOrderMilestoneType.workOrderMilestoneTypeId,
diff --git a/temp/legacy.importFromCSV.ts b/temp/legacy.importFromCSV.ts
index eb93af86..66a21062 100644
--- a/temp/legacy.importFromCSV.ts
+++ b/temp/legacy.importFromCSV.ts
@@ -1295,7 +1295,11 @@ function importFromWorkOrderCSV() {
user
);
- workOrder = getWorkOrder(workOrderId);
+ workOrder = getWorkOrder(workOrderId, {
+ includeLotsAndLotOccupancies: true,
+ includeComments: true,
+ includeMilestones: true
+ });
}
let lot: recordTypes.Lot;
@@ -1628,8 +1632,13 @@ function importFromWorkOrderCSV() {
workOrderRow.WO_FUNERAL_DAY
);
+ let funeralHour = Number.parseInt(workOrderRow.WO_FUNERAL_HR, 10);
+ if (funeralHour <= 6) {
+ funeralHour += 12;
+ }
+
const workOrderMilestoneTimeString = formatTimeString(
- workOrderRow.WO_FUNERAL_HR,
+ funeralHour.toString(),
workOrderRow.WO_FUNERAL_MIN
);
diff --git a/types/configTypes.d.ts b/types/configTypes.d.ts
index 27744677..9298db04 100644
--- a/types/configTypes.d.ts
+++ b/types/configTypes.d.ts
@@ -44,6 +44,8 @@ export interface Config {
};
workOrders?: {
workOrderNumberLength?: number;
+ workOrderMilestoneDateRecentBeforeDays?: number;
+ workOrderMilestoneDateRecentAfterDays?: number;
};
};
}
diff --git a/types/configTypes.ts b/types/configTypes.ts
index 2c2ed79c..4589b076 100644
--- a/types/configTypes.ts
+++ b/types/configTypes.ts
@@ -44,6 +44,8 @@ export interface Config {
};
workOrders?: {
workOrderNumberLength?: number;
+ workOrderMilestoneDateRecentBeforeDays?: number;
+ workOrderMilestoneDateRecentAfterDays?: number;
};
};
}
diff --git a/types/globalTypes.d.ts b/types/globalTypes.d.ts
index 6b2becc9..a8076535 100644
--- a/types/globalTypes.d.ts
+++ b/types/globalTypes.d.ts
@@ -2,4 +2,5 @@ export interface LOS {
highlightMap: (mapContainerElement: HTMLElement, mapKey: string, contextualClass: "success" | "danger") => void;
initializeUnlockFieldButtons: (containerElement: HTMLElement) => void;
populateAliases: (containerElement: HTMLElement) => void;
+ getRandomColor: (seedString: string) => string;
}
diff --git a/types/globalTypes.js b/types/globalTypes.js
index cb0ff5c3..c8ad2e54 100644
--- a/types/globalTypes.js
+++ b/types/globalTypes.js
@@ -1 +1,2 @@
-export {};
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
diff --git a/types/globalTypes.ts b/types/globalTypes.ts
index bf00e63c..035498f9 100644
--- a/types/globalTypes.ts
+++ b/types/globalTypes.ts
@@ -6,4 +6,5 @@ export interface LOS {
) => void;
initializeUnlockFieldButtons: (containerElement: HTMLElement) => void;
populateAliases: (containerElement: HTMLElement) => void;
+ getRandomColor: (seedString: string) => string;
}
diff --git a/types/recordTypes.d.ts b/types/recordTypes.d.ts
index 12989950..4bf0d1a9 100644
--- a/types/recordTypes.d.ts
+++ b/types/recordTypes.d.ts
@@ -209,6 +209,7 @@ export interface WorkOrderComment extends Record {
export interface WorkOrderMilestone extends Record {
workOrderMilestoneId?: number;
workOrderId?: number;
+ workOrder?: WorkOrder;
workOrderMilestoneTypeId?: number;
workOrderMilestoneType?: string;
workOrderMilestoneDate?: number;
diff --git a/types/recordTypes.ts b/types/recordTypes.ts
index 87f4ed7b..fc0f817f 100644
--- a/types/recordTypes.ts
+++ b/types/recordTypes.ts
@@ -279,7 +279,9 @@ export interface WorkOrderComment extends Record {
export interface WorkOrderMilestone extends Record {
workOrderMilestoneId?: number;
+
workOrderId?: number;
+ workOrder?: WorkOrder;
workOrderMilestoneTypeId?: number;
workOrderMilestoneType?: string;
diff --git a/views/_footerA.ejs b/views/_footerA.ejs
index fa0ced3f..2e0f2e08 100644
--- a/views/_footerA.ejs
+++ b/views/_footerA.ejs
@@ -28,6 +28,7 @@
+