diff --git a/data/config.cemetery.ssm.js b/data/config.cemetery.ssm.js index 7a26c9c0..e2b52164 100644 --- a/data/config.cemetery.ssm.js +++ b/data/config.cemetery.ssm.js @@ -24,5 +24,8 @@ config.settings.lot = { }; config.settings.lotOccupancy.occupantCityDefault = "Sault Ste. Marie"; config.settings.map.mapCityDefault = "Sault Ste. Marie"; +config.settings.workOrders = { + workOrderNumberLength: 6 +}; config.aliases.externalReceiptNumber = "GP Receipt Number"; export default config; diff --git a/data/config.cemetery.ssm.ts b/data/config.cemetery.ssm.ts index 05453ddc..2f46b8d9 100644 --- a/data/config.cemetery.ssm.ts +++ b/data/config.cemetery.ssm.ts @@ -38,6 +38,10 @@ config.settings.lot = { config.settings.lotOccupancy.occupantCityDefault = "Sault Ste. Marie"; config.settings.map.mapCityDefault = "Sault Ste. Marie"; +config.settings.workOrders = { + workOrderNumberLength: 6 +}; + config.aliases.externalReceiptNumber = "GP Receipt Number"; export default config; \ No newline at end of file diff --git a/handlers/workOrders-get/new.d.ts b/handlers/workOrders-get/new.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/workOrders-get/new.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/workOrders-get/new.js b/handlers/workOrders-get/new.js new file mode 100644 index 00000000..84409fb1 --- /dev/null +++ b/handlers/workOrders-get/new.js @@ -0,0 +1,17 @@ +import { dateToInteger, dateToString } from "@cityssm/expressjs-server-js/dateTimeFns.js"; +import { getWorkOrderTypes } from "../../helpers/functions.cache.js"; +export const handler = (request, response) => { + const currentDate = new Date(); + const workOrder = { + workOrderOpenDate: dateToInteger(currentDate), + workOrderOpenDateString: dateToString(currentDate) + }; + const workOrderTypes = getWorkOrderTypes(); + response.render("workOrder-edit", { + headTitle: "New Work Order", + workOrder, + isCreate: true, + workOrderTypes + }); +}; +export default handler; diff --git a/handlers/workOrders-get/new.ts b/handlers/workOrders-get/new.ts new file mode 100644 index 00000000..fcd1c8ae --- /dev/null +++ b/handlers/workOrders-get/new.ts @@ -0,0 +1,29 @@ +import { dateToInteger, dateToString } from "@cityssm/expressjs-server-js/dateTimeFns.js"; +import type { RequestHandler } from "express"; + +import { + getWorkOrderTypes +} from "../../helpers/functions.cache.js"; + +import * as recordTypes from "../../types/recordTypes"; + +export const handler: RequestHandler = (request, response) => { + + const currentDate = new Date(); + + const workOrder: recordTypes.WorkOrder = { + workOrderOpenDate: dateToInteger(currentDate), + workOrderOpenDateString: dateToString(currentDate) + }; + + const workOrderTypes = getWorkOrderTypes(); + + response.render("workOrder-edit", { + headTitle: "New Work Order", + workOrder, + isCreate: true, + workOrderTypes + }); +}; + +export default handler; diff --git a/handlers/workOrders-post/doCreateWorkOrder.d.ts b/handlers/workOrders-post/doCreateWorkOrder.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/workOrders-post/doCreateWorkOrder.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/doCreateWorkOrder.js b/handlers/workOrders-post/doCreateWorkOrder.js new file mode 100644 index 00000000..2946e597 --- /dev/null +++ b/handlers/workOrders-post/doCreateWorkOrder.js @@ -0,0 +1,9 @@ +import { addWorkOrder } from "../../helpers/lotOccupancyDB/addWorkOrder.js"; +export const handler = async (request, response) => { + const workOrderId = addWorkOrder(request.body, request.session); + response.json({ + success: true, + workOrderId + }); +}; +export default handler; diff --git a/handlers/workOrders-post/doCreateWorkOrder.ts b/handlers/workOrders-post/doCreateWorkOrder.ts new file mode 100644 index 00000000..3da2da52 --- /dev/null +++ b/handlers/workOrders-post/doCreateWorkOrder.ts @@ -0,0 +1,14 @@ +import type { RequestHandler } from "express"; + +import { addWorkOrder } from "../../helpers/lotOccupancyDB/addWorkOrder.js"; + +export const handler: RequestHandler = async (request, response) => { + const workOrderId = addWorkOrder(request.body, request.session); + + response.json({ + success: true, + workOrderId + }); +}; + +export default handler; diff --git a/helpers/functions.config.d.ts b/helpers/functions.config.d.ts index 567edf0d..aefb50a3 100644 --- a/helpers/functions.config.d.ts +++ b/helpers/functions.config.d.ts @@ -32,4 +32,5 @@ export declare function getProperty(propertyName: "settings.lotOccupancy.occupan export declare function getProperty(propertyName: "settings.lotOccupancy.occupantCityDefault"): string; 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 const keepAliveMillis: number; diff --git a/helpers/functions.config.js b/helpers/functions.config.js index 353493b0..3c60725a 100644 --- a/helpers/functions.config.js +++ b/helpers/functions.config.js @@ -31,6 +31,7 @@ configFallbackValues.set("settings.lotOccupancy.occupancyEndDateIsRequired", tru configFallbackValues.set("settings.lotOccupancy.occupantCityDefault", ""); configFallbackValues.set("settings.lotOccupancy.occupantProvinceDefault", ""); configFallbackValues.set("settings.fees.taxPercentageDefault", 0); +configFallbackValues.set("settings.workOrders.workOrderNumberLength", 6); export function getProperty(propertyName) { const propertyNameSplit = propertyName.split("."); let currentObject = config; diff --git a/helpers/functions.config.ts b/helpers/functions.config.ts index 70de2832..5614abf3 100644 --- a/helpers/functions.config.ts +++ b/helpers/functions.config.ts @@ -62,6 +62,8 @@ configFallbackValues.set("settings.lotOccupancy.occupantProvinceDefault", ""); configFallbackValues.set("settings.fees.taxPercentageDefault", 0); +configFallbackValues.set("settings.workOrders.workOrderNumberLength", 6); + /* * Set up function overloads */ @@ -142,6 +144,10 @@ export function getProperty( propertyName: "settings.fees.taxPercentageDefault" ): number; +export function getProperty( + propertyName: "settings.workOrders.workOrderNumberLength" +): number; + export function getProperty(propertyName: string): unknown { const propertyNameSplit = propertyName.split("."); diff --git a/helpers/lotOccupancyDB/addWorkOrder.d.ts b/helpers/lotOccupancyDB/addWorkOrder.d.ts index e36afd7b..4e92f917 100644 --- a/helpers/lotOccupancyDB/addWorkOrder.d.ts +++ b/helpers/lotOccupancyDB/addWorkOrder.d.ts @@ -1,7 +1,7 @@ import type * as recordTypes from "../../types/recordTypes"; interface AddWorkOrderForm { workOrderTypeId: number | string; - workOrderNumber: string; + workOrderNumber?: string; workOrderDescription: string; workOrderOpenDateString?: string; workOrderCloseDateString?: string; diff --git a/helpers/lotOccupancyDB/addWorkOrder.js b/helpers/lotOccupancyDB/addWorkOrder.js index 3f0bcc3a..af0bf254 100644 --- a/helpers/lotOccupancyDB/addWorkOrder.js +++ b/helpers/lotOccupancyDB/addWorkOrder.js @@ -1,9 +1,14 @@ import sqlite from "better-sqlite3"; import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +import { getNextWorkOrderNumber } from "./getNextWorkOrderNumber.js"; import { dateStringToInteger, dateToInteger } from "@cityssm/expressjs-server-js/dateTimeFns.js"; export const addWorkOrder = (workOrderForm, requestSession) => { const database = sqlite(databasePath); const rightNow = new Date(); + let workOrderNumber = workOrderForm.workOrderNumber; + if (!workOrderNumber) { + workOrderNumber = getNextWorkOrderNumber(database); + } const result = database .prepare("insert into WorkOrders (" + "workOrderTypeId, workOrderNumber, workOrderDescription," + @@ -11,7 +16,7 @@ export const addWorkOrder = (workOrderForm, requestSession) => { " recordCreate_userName, recordCreate_timeMillis," + " recordUpdate_userName, recordUpdate_timeMillis)" + " values (?, ?, ?, ?, ?, ?, ?, ?, ?)") - .run(workOrderForm.workOrderTypeId, workOrderForm.workOrderNumber, workOrderForm.workOrderDescription, workOrderForm.workOrderOpenDateString + .run(workOrderForm.workOrderTypeId, workOrderNumber, workOrderForm.workOrderDescription, workOrderForm.workOrderOpenDateString ? dateStringToInteger(workOrderForm.workOrderOpenDateString) : dateToInteger(rightNow), workOrderForm.workOrderCloseDateString ? dateStringToInteger(workOrderForm.workOrderCloseDateString) diff --git a/helpers/lotOccupancyDB/addWorkOrder.ts b/helpers/lotOccupancyDB/addWorkOrder.ts index 0365f335..4f840c4c 100644 --- a/helpers/lotOccupancyDB/addWorkOrder.ts +++ b/helpers/lotOccupancyDB/addWorkOrder.ts @@ -2,6 +2,8 @@ import sqlite from "better-sqlite3"; import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +import { getNextWorkOrderNumber } from "./getNextWorkOrderNumber.js"; + import { dateStringToInteger, dateToInteger @@ -11,7 +13,7 @@ import type * as recordTypes from "../../types/recordTypes"; interface AddWorkOrderForm { workOrderTypeId: number | string; - workOrderNumber: string; + workOrderNumber?: string; workOrderDescription: string; workOrderOpenDateString?: string; workOrderCloseDateString?: string; @@ -25,6 +27,12 @@ export const addWorkOrder = ( const rightNow = new Date(); + let workOrderNumber = workOrderForm.workOrderNumber; + + if (!workOrderNumber) { + workOrderNumber = getNextWorkOrderNumber(database); + } + const result = database .prepare( "insert into WorkOrders (" + @@ -36,7 +44,7 @@ export const addWorkOrder = ( ) .run( workOrderForm.workOrderTypeId, - workOrderForm.workOrderNumber, + workOrderNumber, workOrderForm.workOrderDescription, workOrderForm.workOrderOpenDateString ? dateStringToInteger(workOrderForm.workOrderOpenDateString) diff --git a/helpers/lotOccupancyDB/getNextWorkOrderNumber.d.ts b/helpers/lotOccupancyDB/getNextWorkOrderNumber.d.ts new file mode 100644 index 00000000..c91ef8ef --- /dev/null +++ b/helpers/lotOccupancyDB/getNextWorkOrderNumber.d.ts @@ -0,0 +1,3 @@ +import sqlite from "better-sqlite3"; +export declare const getNextWorkOrderNumber: (connectedDatabase?: sqlite.Database) => string; +export default getNextWorkOrderNumber; diff --git a/helpers/lotOccupancyDB/getNextWorkOrderNumber.js b/helpers/lotOccupancyDB/getNextWorkOrderNumber.js new file mode 100644 index 00000000..2179c3ff --- /dev/null +++ b/helpers/lotOccupancyDB/getNextWorkOrderNumber.js @@ -0,0 +1,30 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +import * as configFunctions from "../functions.config.js"; +export const getNextWorkOrderNumber = (connectedDatabase) => { + const database = connectedDatabase || + sqlite(databasePath, { + readonly: true + }); + const paddingLength = configFunctions.getProperty("settings.workOrders.workOrderNumberLength"); + const currentYearString = new Date().getFullYear().toString(); + const regex = new RegExp("^" + currentYearString + "-\\d+$"); + database.function("userFn_matchesWorkOrderNumberSyntax", (workOrderNumber) => { + return regex.test(workOrderNumber) ? 1 : 0; + }); + const workOrderNumberRecord = database + .prepare("select workOrderNumber from WorkOrders" + + " where userFn_matchesWorkOrderNumberSyntax(workOrderNumber) = 1" + + " order by cast(substr(workOrderNumber, instr(workOrderNumber, '-') + 1) as integer) desc") + .get(); + if (!connectedDatabase) { + database.close(); + } + let workOrderNumberIndex = 0; + if (workOrderNumberRecord) { + workOrderNumberIndex = Number.parseInt(workOrderNumberRecord.workOrderNumber.split("-")[1], 10); + } + workOrderNumberIndex += 1; + return currentYearString + "-" + workOrderNumberIndex.toString().padStart(paddingLength, "0"); +}; +export default getNextWorkOrderNumber; diff --git a/helpers/lotOccupancyDB/getNextWorkOrderNumber.ts b/helpers/lotOccupancyDB/getNextWorkOrderNumber.ts new file mode 100644 index 00000000..62a36fb8 --- /dev/null +++ b/helpers/lotOccupancyDB/getNextWorkOrderNumber.ts @@ -0,0 +1,56 @@ +import sqlite from "better-sqlite3"; + +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; + +import * as configFunctions from "../functions.config.js"; + +export const getNextWorkOrderNumber = ( + connectedDatabase?: sqlite.Database +): string => { + const database = + connectedDatabase || + sqlite(databasePath, { + readonly: true + }); + + const paddingLength = configFunctions.getProperty( + "settings.workOrders.workOrderNumberLength" + ); + const currentYearString = new Date().getFullYear().toString(); + + const regex = new RegExp("^" + currentYearString + "-\\d+$"); + + database.function( + "userFn_matchesWorkOrderNumberSyntax", + (workOrderNumber: string) => { + return regex.test(workOrderNumber) ? 1 : 0; + } + ); + + const workOrderNumberRecord = database + .prepare( + "select workOrderNumber from WorkOrders" + + " where userFn_matchesWorkOrderNumberSyntax(workOrderNumber) = 1" + + " order by cast(substr(workOrderNumber, instr(workOrderNumber, '-') + 1) as integer) desc" + ) + .get(); + + if (!connectedDatabase) { + database.close(); + } + + let workOrderNumberIndex = 0; + + if (workOrderNumberRecord) { + workOrderNumberIndex = Number.parseInt( + workOrderNumberRecord.workOrderNumber.split("-")[1], + 10 + ); + } + + workOrderNumberIndex += 1; + + return currentYearString + "-" + workOrderNumberIndex.toString().padStart(paddingLength, "0"); +}; + +export default getNextWorkOrderNumber; diff --git a/public-typescript/main.js b/public-typescript/main.js index ce01a2dc..3f8dc438 100644 --- a/public-typescript/main.js +++ b/public-typescript/main.js @@ -24,7 +24,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); const fieldElement = clickEvent.currentTarget.closest(".field"); const inputOrSelectElement = fieldElement.querySelector("input, select"); if (inputOrSelectElement.tagName === "INPUT") { - inputOrSelectElement.disabled = false; + inputOrSelectElement.readOnly = false; } else { const optionElements = inputOrSelectElement.querySelectorAll("option"); diff --git a/public-typescript/main.ts b/public-typescript/main.ts index a654eaa7..03cc1f7a 100644 --- a/public-typescript/main.ts +++ b/public-typescript/main.ts @@ -54,7 +54,7 @@ import type * as globalTypes from "../types/globalTypes"; ) as HTMLInputElement | HTMLSelectElement; if (inputOrSelectElement.tagName === "INPUT") { - inputOrSelectElement.disabled = false; + (inputOrSelectElement as HTMLInputElement).readOnly = false; } else { const optionElements = inputOrSelectElement.querySelectorAll("option"); diff --git a/public-typescript/workOrderEdit.js b/public-typescript/workOrderEdit.js index 581683f8..d56e1aef 100644 --- a/public-typescript/workOrderEdit.js +++ b/public-typescript/workOrderEdit.js @@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); const urlPrefix = document.querySelector("main").dataset.urlPrefix; const workOrderId = document.querySelector("#workOrderEdit--workOrderId").value; const isCreate = workOrderId === ""; + los.initializeUnlockFieldButtons(document.querySelector("#form--workOrderEdit")); document .querySelector("#form--workOrderEdit") .addEventListener("submit", (submitEvent) => { diff --git a/public-typescript/workOrderEdit.ts b/public-typescript/workOrderEdit.ts index 070d2720..556fecf4 100644 --- a/public-typescript/workOrderEdit.ts +++ b/public-typescript/workOrderEdit.ts @@ -5,8 +5,6 @@ import type { BulmaJS } from "@cityssm/bulma-js/types"; import type * as globalTypes from "../types/globalTypes"; import type * as recordTypes from "../types/recordTypes"; -import { response } from "express"; -import { closeDelimiter } from "ejs"; declare const cityssm: cityssmGlobal; declare const bulmaJS: BulmaJS; @@ -24,6 +22,10 @@ declare const bulmaJS: BulmaJS; const isCreate = workOrderId === ""; + los.initializeUnlockFieldButtons( + document.querySelector("#form--workOrderEdit") + ); + document .querySelector("#form--workOrderEdit") .addEventListener("submit", (submitEvent) => { diff --git a/public/javascripts/main.min.js b/public/javascripts/main.min.js index 0de7f811..465813a1 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.disabled=!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={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 diff --git a/public/javascripts/workOrderEdit.min.js b/public/javascripts/workOrderEdit.min.js index 9001ac89..7cca5a04 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,s=document.querySelector("#workOrderEdit--workOrderId").value,o=""===s;if(document.querySelector("#form--workOrderEdit").addEventListener("submit",e=>{e.preventDefault(),cityssm.postJSON(t+"/workOrders/"+(o?"doCreateWorkOrder":"doUpdateWorkOrder"),e.currentTarget,e=>{e.success?o?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"})})}),!o){let o=exports.workOrderLots;delete exports.workOrderLots;let r=exports.workOrderLotOccupancies;delete exports.workOrderLotOccupancies;const a=e=>{const o=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:s,lotOccupancyId:o},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:s,lotId:e},e=>{e.success?(o=e.workOrderLots,u()):bulmaJS.alert({title:"Error Adding "+exports.aliases.lot,message:e.errorMessage,contextualColorName:"danger"}),r&&r(e.success)})},l=(e,o)=>{cityssm.postJSON(t+"/workOrders/doAddWorkOrderLotOccupancy",{workOrderId:s,lotOccupancyId:e},e=>{e.success?(r=e.workOrderLotOccupancies,u()):bulmaJS.alert({title:"Error Adding "+exports.aliases.occupancy,message:e.errorMessage,contextualColorName:"danger"}),o&&o(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 s=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:s,lotId:r},e=>{e.success?(o=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=o.length.toString(),0!==o.length){e.innerHTML='
'+exports.aliases.lot+""+exports.aliases.map+""+exports.aliases.lot+' TypeStatus
';for(const s of o){const o=document.createElement("tr");o.className="container--lot",o.dataset.lotId=s.lotId.toString(),o.innerHTML=''+cityssm.escapeHTML(s.lotName)+""+cityssm.escapeHTML(s.mapName)+""+cityssm.escapeHTML(s.lotType)+""+cityssm.escapeHTML(s.lotStatus)+'',o.querySelector(".button--deleteLot").addEventListener("click",d),e.querySelector("tbody").append(o)}}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 o,r;const a=e=>{const t=e.currentTarget.closest("tr"),s=t.dataset.lotOccupancyId;l(s,e=>{e&&t.remove()})},n=e=>{e&&e.preventDefault(),r.innerHTML='


Searching...

',cityssm.postJSON(t+"/lotOccupancies/doSearchLotOccupancies",o,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),o=t.querySelector("form"),r=t.querySelector("#resultsContainer--lotOccupancyAdd"),t.querySelector("#lotOccupancySearch--notWorkOrderId").value=s,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),o.addEventListener("submit",n)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),document.querySelector("#button--addLot").addEventListener("click",()=>{let o,r;const a=e=>{const t=e.currentTarget.closest("tr"),s=t.dataset.lotId;n(s,e=>{e&&t.remove()})},l=e=>{e&&e.preventDefault(),r.innerHTML='


Searching...

',cityssm.postJSON(t+"/lots/doSearchLots",o,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),o=t.querySelector("form"),r=t.querySelector("#resultsContainer--lotAdd"),t.querySelector("#lotSearch--notWorkOrderId").value=s;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),o.addEventListener("submit",l)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})})}if(!o){let e=exports.workOrderMilestones;delete exports.workOrderMilestones;const o=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:s,workOrderMilestoneId:n},o)}}})},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:s,workOrderMilestoneId:r},o)}}})},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:s},o)}}})},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=>{o(e),e.success&&l()})};cityssm.openHtmlModal("workOrder-editMilestone",{onshow:e=>{e.querySelector("#milestoneEdit--workOrderId").value=s,e.querySelector("#milestoneEdit--workOrderMilestoneId").value=n.workOrderMilestoneId.toString();const t=e.querySelector("#milestoneEdit--workOrderMilestoneTypeId");let o=!1;for(const e of exports.workOrderMilestoneTypes){const s=document.createElement("option");s.value=e.workOrderMilestoneTypeId.toString(),s.textContent=e.workOrderMilestoneType,e.workOrderMilestoneTypeId===n.workOrderMilestoneTypeId&&(s.selected=!0,o=!0),t.append(s)}if(!o&&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"),s=t.querySelectorAll(".panel-block");for(const e of s)e.remove();for(const s of e){const e=document.createElement("div");e.className="panel-block is-block container--milestone",e.dataset.workOrderMilestoneId=s.workOrderMilestoneId.toString(),e.innerHTML='
'+(s.workOrderMilestoneCompletionDate?'':'')+'
'+(s.workOrderMilestoneTypeId?""+cityssm.escapeHTML(s.workOrderMilestoneType)+"
":"")+s.workOrderMilestoneDateString+(s.workOrderMilestoneTime?" "+s.workOrderMilestoneTimeString:"")+'
'+cityssm.escapeHTML(s.workOrderMilestoneDescription)+'
',s.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=s=>{s.preventDefault(),cityssm.postJSON(t+"/workOrders/doAddWorkOrderMilestone",s.currentTarget,t=>{o(t),t.success&&e()})};cityssm.openHtmlModal("workOrder-addMilestone",{onshow:e=>{e.querySelector("#milestoneAdd--workOrderId").value=s;const t=e.querySelector("#milestoneAdd--workOrderMilestoneTypeId");for(const e of exports.workOrderMilestoneTypes){const s=document.createElement("option");s.value=e.workOrderMilestoneTypeId.toString(),s.textContent=e.workOrderMilestoneType,t.append(s)}e.querySelector("#milestoneAdd--workOrderMilestoneDateString").valueAsDate=new Date},onshown:(t,s)=>{e=s,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;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 diff --git a/routes/workOrders.js b/routes/workOrders.js index 69a3029f..42925b81 100644 --- a/routes/workOrders.js +++ b/routes/workOrders.js @@ -4,6 +4,8 @@ import handler_search from "../handlers/workOrders-get/search.js"; import handler_doSearchWorkOrders from "../handlers/workOrders-post/doSearchWorkOrders.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_doAddWorkOrderLotOccupancy from "../handlers/workOrders-post/doAddWorkOrderLotOccupancy.js"; @@ -18,6 +20,8 @@ import handler_doDeleteWorkOrderMilestone from "../handlers/workOrders-post/doDe export const router = Router(); router.get("/", handler_search); router.post("/doSearchWorkOrders", handler_doSearchWorkOrders); +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); diff --git a/routes/workOrders.ts b/routes/workOrders.ts index d7755814..10a9c019 100644 --- a/routes/workOrders.ts +++ b/routes/workOrders.ts @@ -8,6 +8,9 @@ import handler_doSearchWorkOrders from "../handlers/workOrders-post/doSearchWork 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"; @@ -29,6 +32,14 @@ router.get("/", handler_search); router.post("/doSearchWorkOrders", handler_doSearchWorkOrders); +router.get("/new", permissionHandlers.adminGetHandler, handler_new); + +router.post( + "/doCreateWorkOrder", + permissionHandlers.updatePostHandler, + handler_doCreateWorkOrder +); + router.get("/:workOrderId", handler_view); router.post( diff --git a/types/configTypes.d.ts b/types/configTypes.d.ts index 5b731f24..27744677 100644 --- a/types/configTypes.d.ts +++ b/types/configTypes.d.ts @@ -42,6 +42,9 @@ export interface Config { occupantCityDefault?: string; occupantProvinceDefault?: string; }; + workOrders?: { + workOrderNumberLength?: number; + }; }; } interface ConfigApplication { diff --git a/types/configTypes.ts b/types/configTypes.ts index 8c0dae4c..2c2ed79c 100644 --- a/types/configTypes.ts +++ b/types/configTypes.ts @@ -42,6 +42,9 @@ export interface Config { occupantCityDefault?: string; occupantProvinceDefault?: string; }; + workOrders?: { + workOrderNumberLength?: number; + }; }; } diff --git a/views/workOrder-edit.ejs b/views/workOrder-edit.ejs index 3920fe06..1ef05bfe 100644 --- a/views/workOrder-edit.ejs +++ b/views/workOrder-edit.ejs @@ -46,12 +46,20 @@
-
- + +
+
+ +
- +
+ <% if (isCreate) { %> +

Leave work order number blank to autopopulate.

+ <% } %>
@@ -205,6 +213,7 @@ <%- include('_footerA'); -%> +<% if (!isCreate) { %> +<% } %> <%- include('_footerB'); -%> \ No newline at end of file