From f4a6e14dba5c44ad43e4342f54f30637ecc0068f Mon Sep 17 00:00:00 2001 From: Dan Gowans Date: Wed, 14 Sep 2022 16:08:18 -0400 Subject: [PATCH] development - close work order - delete work order - milestone calendar --- app.js | 1 + app.ts | 5 + data/config.cemetery.ssm.js | 4 +- data/config.cemetery.ssm.ts | 4 +- handlers/workOrders-get/edit.js | 6 +- handlers/workOrders-get/edit.ts | 6 +- handlers/workOrders-get/view.js | 6 +- handlers/workOrders-get/view.ts | 6 +- .../doAddWorkOrderMilestone.js | 2 + .../doAddWorkOrderMilestone.ts | 11 +- .../workOrders-post/doCloseWorkOrder.d.ts | 3 + handlers/workOrders-post/doCloseWorkOrder.js | 8 + handlers/workOrders-post/doCloseWorkOrder.ts | 16 ++ .../doCompleteWorkOrderMilestone.js | 2 + .../doCompleteWorkOrderMilestone.ts | 11 +- .../workOrders-post/doDeleteWorkOrder.d.ts | 3 + handlers/workOrders-post/doDeleteWorkOrder.js | 8 + handlers/workOrders-post/doDeleteWorkOrder.ts | 13 ++ .../doDeleteWorkOrderMilestone.js | 2 + .../doDeleteWorkOrderMilestone.ts | 11 +- .../doGetWorkOrderMilestones.d.ts | 3 + .../doGetWorkOrderMilestones.js | 11 ++ .../doGetWorkOrderMilestones.ts | 16 ++ .../doReopenWorkOrderMilestone.js | 2 + .../doReopenWorkOrderMilestone.ts | 11 +- .../doUpdateWorkOrderMilestone.js | 2 + .../doUpdateWorkOrderMilestone.ts | 11 +- helpers/functions.config.d.ts | 2 + helpers/functions.config.js | 2 + helpers/functions.config.ts | 16 ++ helpers/lotOccupancyDB/closeWorkOrder.d.ts | 2 +- helpers/lotOccupancyDB/closeWorkOrder.js | 2 +- helpers/lotOccupancyDB/closeWorkOrder.ts | 4 +- helpers/lotOccupancyDB/deleteWorkOrder.d.ts | 3 + helpers/lotOccupancyDB/deleteWorkOrder.js | 15 ++ helpers/lotOccupancyDB/deleteWorkOrder.ts | 29 +++ helpers/lotOccupancyDB/getWorkOrder.d.ts | 8 +- helpers/lotOccupancyDB/getWorkOrder.js | 66 ++++--- helpers/lotOccupancyDB/getWorkOrder.ts | 112 ++++++++---- .../getWorkOrderMilestones.d.ts | 8 +- .../lotOccupancyDB/getWorkOrderMilestones.js | 63 ++++++- .../lotOccupancyDB/getWorkOrderMilestones.ts | 107 ++++++++++- package-lock.json | 22 +++ package.json | 2 + public-typescript/main.js | 16 +- public-typescript/main.ts | 21 ++- public-typescript/workOrderEdit.js | 118 +++++++++++- public-typescript/workOrderEdit.ts | 170 +++++++++++++++-- .../workOrderMilestoneCalendar.js | 101 +++++++++- .../workOrderMilestoneCalendar.ts | 173 ++++++++++++++++-- public/javascripts/main.min.js | 2 +- public/javascripts/workOrderEdit.min.js | 2 +- .../workOrderMilestoneCalendar.min.js | 2 +- routes/workOrders.js | 6 + routes/workOrders.ts | 19 ++ temp/legacy.importFromCSV.js | 12 +- temp/legacy.importFromCSV.ts | 13 +- types/configTypes.d.ts | 2 + types/configTypes.ts | 2 + types/globalTypes.d.ts | 1 + types/globalTypes.js | 3 +- types/globalTypes.ts | 1 + types/recordTypes.d.ts | 1 + types/recordTypes.ts | 2 + views/_footerA.ejs | 1 + views/workOrder-edit.ejs | 8 +- views/workOrder-milestoneCalendar.ejs | 6 +- views/workOrder-search.ejs | 2 - 68 files changed, 1167 insertions(+), 164 deletions(-) create mode 100644 handlers/workOrders-post/doCloseWorkOrder.d.ts create mode 100644 handlers/workOrders-post/doCloseWorkOrder.js create mode 100644 handlers/workOrders-post/doCloseWorkOrder.ts create mode 100644 handlers/workOrders-post/doDeleteWorkOrder.d.ts create mode 100644 handlers/workOrders-post/doDeleteWorkOrder.js create mode 100644 handlers/workOrders-post/doDeleteWorkOrder.ts create mode 100644 helpers/lotOccupancyDB/deleteWorkOrder.d.ts create mode 100644 helpers/lotOccupancyDB/deleteWorkOrder.js create mode 100644 helpers/lotOccupancyDB/deleteWorkOrder.ts 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) + + "" + + "
") + + ('
' + + "" + + ' ' + + cityssm.escapeHTML(milestone.workOrder.workOrderNumber) + + "
" + + '' + + cityssm.escapeHTML(milestone.workOrder.workOrderDescription) + + "" + + "
") + + ('
' + + 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 + ) + + "" + + "
") + + ('
' + + "" + + ' ' + + cityssm.escapeHTML(milestone.workOrder.workOrderNumber) + + "
" + + '' + + cityssm.escapeHTML( + milestone.workOrder.workOrderDescription + ) + + "" + + "
") + + ('
' + + 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 DateEnd 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+' TypeStatus
';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 DateEnd 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+" TypeStatus
";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 DateEnd 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+' TypeStatus
';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 DateEnd 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+" TypeStatus
";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)+'
'+cityssm.escapeHTML(n.workOrder.workOrderNumber)+'
'+cityssm.escapeHTML(n.workOrder.workOrderDescription)+'
'+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 @@ +