/* eslint-disable @typescript-eslint/no-non-null-assertion, unicorn/prefer-module */ import type { cityssmGlobal } from "@cityssm/bulma-webapp-js/src/types"; import type { BulmaJS } from "@cityssm/bulma-js/types"; import type * as globalTypes from "../types/globalTypes"; import type * as recordTypes from "../types/recordTypes"; declare const cityssm: cityssmGlobal; declare const bulmaJS: BulmaJS; (() => { const los = exports.los as globalTypes.LOS; const workOrderId = (document.querySelector("#workOrderEdit--workOrderId") as HTMLInputElement) .value; const isCreate = workOrderId === ""; const workOrderFormElement = document.querySelector("#form--workOrderEdit") as HTMLFormElement; los.initializeDatePickers( ( workOrderFormElement.querySelector( "#workOrderEdit--workOrderOpenDateString" ) as HTMLInputElement ).closest(".field") as HTMLElement ); los.initializeUnlockFieldButtons(workOrderFormElement); workOrderFormElement.addEventListener("submit", (submitEvent) => { submitEvent.preventDefault(); cityssm.postJSON( los.urlPrefix + "/workOrders/" + (isCreate ? "doCreateWorkOrder" : "doUpdateWorkOrder"), submitEvent.currentTarget, (responseJSON: { success: boolean; workOrderId?: number; errorMessage?: string }) => { if (responseJSON.success) { cityssm.disableNavBlocker(); if (isCreate) { window.location.href = los.urlPrefix + "/workOrders/" + responseJSON.workOrderId + "/edit"; } else { bulmaJS.alert({ message: "Work Order Updated Successfully", contextualColorName: "success" }); } } else { bulmaJS.alert({ title: "Error Updating Work Order", message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } } ); }); const inputElements = workOrderFormElement.querySelectorAll("input, select") as NodeListOf< HTMLInputElement | HTMLSelectElement >; for (const inputElement of inputElements) { inputElement.addEventListener("change", cityssm.enableNavBlocker); } /* * Work Order Options */ let workOrderMilestones: recordTypes.WorkOrderMilestone[]; if (!isCreate) { const doClose = () => { cityssm.postJSON( los.urlPrefix + "/workOrders/doCloseWorkOrder", { workOrderId }, (responseJSON: { success: boolean; errorMessage?: string }) => { if (responseJSON.success) { window.location.href = los.urlPrefix + "/workOrders/" + encodeURIComponent(workOrderId); } else { bulmaJS.alert({ title: "Error Closing Work Order", message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } } ); }; (document.querySelector("#button--closeWorkOrder") as HTMLButtonElement).addEventListener( "click", () => { const hasOpenMilestones = workOrderMilestones.some((milestone) => { return !milestone.workOrderMilestoneCompletionDate; }); if (hasOpenMilestones) { bulmaJS.alert({ title: "Outstanding Milestones", message: "You cannot close a work order with outstanding milestones." + " Either complete the outstanding milestones, or remove them from the work order.", contextualColorName: "warning" }); /* // Disable closing work orders with open milestones 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( los.urlPrefix + "/workOrders/doDeleteWorkOrder", { workOrderId }, (responseJSON: { success: boolean; errorMessage?: string }) => { if (responseJSON.success) { window.location.href = los.urlPrefix + "/workOrders"; } else { bulmaJS.alert({ title: "Error Deleting Work Order", message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } } ); }; (document.querySelector("#button--deleteWorkOrder") as HTMLButtonElement).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 */ if (!isCreate) { let workOrderLots: recordTypes.Lot[] = exports.workOrderLots; delete exports.workOrderLots; let workOrderLotOccupancies: recordTypes.LotOccupancy[] = exports.workOrderLotOccupancies; delete exports.workOrderLotOccupancies; const deleteLotOccupancy = (clickEvent: Event) => { const lotOccupancyId = ( (clickEvent.currentTarget as HTMLElement).closest( ".container--lotOccupancy" ) as HTMLElement ).dataset.lotOccupancyId; const doDelete = () => { cityssm.postJSON( los.urlPrefix + "/workOrders/doDeleteWorkOrderLotOccupancy", { workOrderId, lotOccupancyId }, (responseJSON: { success: boolean; errorMessage?: string; workOrderLotOccupancies?: recordTypes.LotOccupancy[]; }) => { if (responseJSON.success) { workOrderLotOccupancies = responseJSON.workOrderLotOccupancies!; renderRelatedLotsAndOccupancies(); } else { bulmaJS.alert({ title: "Error Deleting Relationship", message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } } ); }; 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: doDelete } }); }; const addLot = (lotId: number | string, callbackFunction?: (success?: boolean) => void) => { cityssm.postJSON( los.urlPrefix + "/workOrders/doAddWorkOrderLot", { workOrderId, lotId }, (responseJSON: { success: boolean; errorMessage?: string; workOrderLots?: recordTypes.Lot[]; }) => { if (responseJSON.success) { workOrderLots = responseJSON.workOrderLots!; renderRelatedLotsAndOccupancies(); } else { bulmaJS.alert({ title: "Error Adding " + exports.aliases.lot, message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } if (callbackFunction) { callbackFunction(responseJSON.success); } } ); }; const addLotOccupancy = ( lotOccupancyId: number | string, callbackFunction?: (success?: boolean) => void ) => { cityssm.postJSON( los.urlPrefix + "/workOrders/doAddWorkOrderLotOccupancy", { workOrderId, lotOccupancyId }, (responseJSON: { success: boolean; errorMessage?: string; workOrderLotOccupancies?: recordTypes.LotOccupancy[]; }) => { if (responseJSON.success) { workOrderLotOccupancies = responseJSON.workOrderLotOccupancies!; renderRelatedLotsAndOccupancies(); } else { bulmaJS.alert({ title: "Error Adding " + exports.aliases.occupancy, message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } if (callbackFunction) { callbackFunction(responseJSON.success); } } ); }; const addLotFromLotOccupancy = (clickEvent: Event) => { const lotId = (clickEvent.currentTarget as HTMLElement).dataset.lotId!; addLot(lotId); }; const renderRelatedOccupancies = () => { const occupanciesContainerElement = document.querySelector( "#container--lotOccupancies" ) as HTMLElement; ( document.querySelector( ".tabs a[href='#relatedTab--lotOccupancies'] .tag" ) as HTMLElement ).textContent = workOrderLotOccupancies.length.toString(); if (workOrderLotOccupancies.length === 0) { occupanciesContainerElement.innerHTML = '
' + '

There are no ' + exports.aliases.occupancies.toLowerCase() + " associated with this work order.

" + "
"; return; } occupanciesContainerElement.innerHTML = '' + "" + "" + '' + ("") + ("") + ("") + "" + ("") + '' + "" + "" + "" + "
" + exports.aliases.occupancy + " Type" + exports.aliases.lot + "" + exports.aliases.occupancyStartDate + "End Date" + exports.aliases.occupants + "
"; const currentDateString = cityssm.dateToString(new Date()); for (const lotOccupancy of workOrderLotOccupancies) { const rowElement = document.createElement("tr"); rowElement.className = "container--lotOccupancy"; rowElement.dataset.lotOccupancyId = lotOccupancy.lotOccupancyId!.toString(); const isActive = !( lotOccupancy.occupancyEndDate && lotOccupancy.occupancyEndDateString! < currentDateString ); const hasLotRecord = lotOccupancy.lotId && workOrderLots.some((lot) => { return lotOccupancy.lotId === lot.lotId; }); rowElement.innerHTML = '' + (isActive ? '' : '') + "" + ("" + '' + cityssm.escapeHTML(lotOccupancy.occupancyType || "") + "" + ""); if (lotOccupancy.lotId) { rowElement.insertAdjacentHTML( "beforeend", "" + cityssm.escapeHTML(lotOccupancy.lotName || "") + (hasLotRecord ? "" : ' ") + "" ); } else { rowElement.insertAdjacentHTML( "beforeend", "" + '(No ' + exports.aliases.lot + ")" + "" ); } rowElement.insertAdjacentHTML( "beforeend", "" + lotOccupancy.occupancyStartDateString + "" + ("" + (lotOccupancy.occupancyEndDate ? lotOccupancy.occupancyEndDateString : '(No End Date)') + "") + ("" + (lotOccupancy.lotOccupancyOccupants!.length === 0 ? '(No ' + cityssm.escapeHTML(exports.aliases.occupants) + ")" : lotOccupancy.lotOccupancyOccupants?.reduce((soFar, occupant) => { return ( soFar + '' + ' ' + cityssm.escapeHTML(occupant.occupantName!) + "
" ); }, "")) + "") + ("" + '" + "") ); if (lotOccupancy.lotId && !hasLotRecord) { ( rowElement.querySelector(".button--addLot") as HTMLButtonElement ).addEventListener("click", addLotFromLotOccupancy); } ( rowElement.querySelector(".button--deleteLotOccupancy") as HTMLButtonElement ).addEventListener("click", deleteLotOccupancy); occupanciesContainerElement.querySelector("tbody")!.append(rowElement); } }; const openEditLotStatus = (clickEvent: Event) => { const lotId = Number.parseInt( ( (clickEvent.currentTarget as HTMLElement).closest( ".container--lot" ) as HTMLElement ).dataset.lotId!, 10 ); const lot = workOrderLots.find((possibleLot) => { return possibleLot.lotId === lotId; })!; let editCloseModalFunction: () => void; const doUpdateLotStatus = (submitEvent: SubmitEvent) => { submitEvent.preventDefault(); cityssm.postJSON( los.urlPrefix + "/workOrders/doUpdateLotStatus", submitEvent.currentTarget, (responseJSON: { success: boolean; errorMessage?: string; workOrderLots?: recordTypes.Lot[]; }) => { if (responseJSON.success) { workOrderLots = responseJSON.workOrderLots!; renderRelatedLotsAndOccupancies(); editCloseModalFunction(); } else { bulmaJS.alert({ title: "Error Deleting Relationship", message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } } ); }; cityssm.openHtmlModal("lot-editLotStatus", { onshow: (modalElement) => { los.populateAliases(modalElement); ( modalElement.querySelector("#lotStatusEdit--lotId") as HTMLInputElement ).value = lotId.toString(); ( modalElement.querySelector("#lotStatusEdit--lotName") as HTMLInputElement ).value = lot.lotName!; const lotStatusElement = modalElement.querySelector( "#lotStatusEdit--lotStatusId" ) as HTMLSelectElement; let lotStatusFound = false; for (const lotStatus of exports.lotStatuses as recordTypes.LotStatus[]) { const optionElement = document.createElement("option"); optionElement.value = lotStatus.lotStatusId.toString(); optionElement.textContent = lotStatus.lotStatus; if (lotStatus.lotStatusId === lot.lotStatusId) { lotStatusFound = true; } lotStatusElement.append(optionElement); } if (!lotStatusFound && lot.lotStatusId) { const optionElement = document.createElement("option"); optionElement.value = lot.lotStatusId.toString(); optionElement.textContent = lot.lotStatus!; lotStatusElement.append(optionElement); } if (lot.lotStatusId) { lotStatusElement.value = lot.lotStatusId.toString(); } modalElement .querySelector("form")! .insertAdjacentHTML( "beforeend", '' ); }, onshown: (modalElement, closeModalFunction) => { editCloseModalFunction = closeModalFunction; bulmaJS.toggleHtmlClipped(); modalElement .querySelector("form")! .addEventListener("submit", doUpdateLotStatus); }, onremoved: () => { bulmaJS.toggleHtmlClipped(); } }); }; const deleteLot = (clickEvent: Event) => { const lotId = ( (clickEvent.currentTarget as HTMLElement).closest(".container--lot") as HTMLElement ).dataset.lotId; const doDelete = () => { cityssm.postJSON( los.urlPrefix + "/workOrders/doDeleteWorkOrderLot", { workOrderId, lotId }, (responseJSON: { success: boolean; errorMessage?: string; workOrderLots?: recordTypes.Lot[]; }) => { if (responseJSON.success) { workOrderLots = responseJSON.workOrderLots!; renderRelatedLotsAndOccupancies(); } else { bulmaJS.alert({ title: "Error Deleting Relationship", message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } } ); }; 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: doDelete } }); }; const renderRelatedLots = () => { const lotsContainerElement = document.querySelector("#container--lots") as HTMLElement; ( document.querySelector(".tabs a[href='#relatedTab--lots'] .tag") as HTMLElement ).textContent = workOrderLots.length.toString(); if (workOrderLots.length === 0) { lotsContainerElement.innerHTML = '
' + '

There are no ' + exports.aliases.lots.toLowerCase() + " associated with this work order.

" + "
"; return; } lotsContainerElement.innerHTML = '' + "" + "" + ("") + ("") + ("") + "" + '' + "" + "" + "" + "
" + exports.aliases.lot + "" + exports.aliases.map + "" + exports.aliases.lot + " TypeStatus
"; for (const lot of workOrderLots) { const rowElement = document.createElement("tr"); rowElement.className = "container--lot"; rowElement.dataset.lotId = lot.lotId.toString(); rowElement.innerHTML = "" + '' + cityssm.escapeHTML(lot.lotName || "") + "" + "" + ("" + cityssm.escapeHTML(lot.mapName || "") + "") + ("" + cityssm.escapeHTML(lot.lotType || "") + "") + ("" + (lot.lotStatusId ? cityssm.escapeHTML(lot.lotStatus || "") : '(No Status)') + "") + ('' + '" + ' " + ""); ( rowElement.querySelector(".button--editLotStatus") as HTMLButtonElement ).addEventListener("click", openEditLotStatus); ( rowElement.querySelector(".button--deleteLot") as HTMLButtonElement ).addEventListener("click", deleteLot); lotsContainerElement.querySelector("tbody")!.append(rowElement); } }; const renderRelatedLotsAndOccupancies = () => { renderRelatedOccupancies(); renderRelatedLots(); }; renderRelatedLotsAndOccupancies(); const doAddLotOccupancy = (clickEvent: Event) => { const rowElement = (clickEvent.currentTarget as HTMLElement).closest("tr")!; const lotOccupancyId = rowElement.dataset.lotOccupancyId!; addLotOccupancy(lotOccupancyId, (success) => { if (success) { rowElement.remove(); } }); }; (document.querySelector("#button--addLotOccupancy") as HTMLButtonElement).addEventListener( "click", () => { let searchFormElement: HTMLFormElement; let searchResultsContainerElement: HTMLElement; const doSearch = (event?: Event) => { if (event) { event.preventDefault(); } searchResultsContainerElement.innerHTML = '

' + '
' + "Searching..." + "

"; cityssm.postJSON( los.urlPrefix + "/lotOccupancies/doSearchLotOccupancies", searchFormElement, (responseJSON: { lotOccupancies: recordTypes.LotOccupancy[] }) => { if (responseJSON.lotOccupancies.length === 0) { searchResultsContainerElement.innerHTML = '
' + '

There are no records that meet the search criteria.

' + "
"; return; } searchResultsContainerElement.innerHTML = '' + "" + "" + '' + ("") + ("") + "" + "" + ("") + "" + "" + "" + "
" + exports.aliases.occupancy + " Type" + exports.aliases.lot + "Start DateEnd Date" + exports.aliases.occupants + "
"; for (const lotOccupancy of responseJSON.lotOccupancies) { const rowElement = document.createElement("tr"); rowElement.className = "container--lotOccupancy"; rowElement.dataset.lotOccupancyId = lotOccupancy.lotOccupancyId!.toString(); rowElement.innerHTML = '' + '" + "" + ('' + cityssm.escapeHTML(lotOccupancy.occupancyType || "") + ""); if (lotOccupancy.lotId) { rowElement.insertAdjacentHTML( "beforeend", "" + cityssm.escapeHTML(lotOccupancy.lotName || "") + "" ); } else { rowElement.insertAdjacentHTML( "beforeend", "" + '(No ' + exports.aliases.lot + ")" + "" ); } rowElement.insertAdjacentHTML( "beforeend", "" + lotOccupancy.occupancyStartDateString + "" + ("" + (lotOccupancy.occupancyEndDate ? lotOccupancy.occupancyEndDateString : '(No End Date)') + "") + ("" + (lotOccupancy.lotOccupancyOccupants!.length === 0 ? '(No ' + cityssm.escapeHTML(exports.aliases.occupants) + ")" : cityssm.escapeHTML( lotOccupancy.lotOccupancyOccupants![0] .occupantName! ) + (lotOccupancy.lotOccupancyOccupants!.length > 1 ? " plus " + (lotOccupancy.lotOccupancyOccupants! .length - 1) : "")) + "") ); ( rowElement.querySelector( ".button--addLotOccupancy" ) as HTMLButtonElement ).addEventListener("click", doAddLotOccupancy); searchResultsContainerElement .querySelector("tbody")! .append(rowElement); } } ); }; cityssm.openHtmlModal("workOrder-addLotOccupancy", { onshow: (modalElement) => { los.populateAliases(modalElement); searchFormElement = modalElement.querySelector("form")!; searchResultsContainerElement = modalElement.querySelector( "#resultsContainer--lotOccupancyAdd" ) as HTMLElement; ( modalElement.querySelector( "#lotOccupancySearch--notWorkOrderId" ) as HTMLInputElement ).value = workOrderId; ( modalElement.querySelector( "#lotOccupancySearch--occupancyEffectiveDateString" ) as HTMLInputElement ).value = ( document.querySelector( "#workOrderEdit--workOrderOpenDateString" ) as HTMLInputElement ).value; doSearch(); }, onshown: (modalElement) => { bulmaJS.toggleHtmlClipped(); ( modalElement.querySelector( "#lotOccupancySearch--occupantName" ) as HTMLInputElement ).addEventListener("change", doSearch); ( modalElement.querySelector( "#lotOccupancySearch--lotName" ) as HTMLInputElement ).addEventListener("change", doSearch); searchFormElement.addEventListener("submit", doSearch); }, onremoved: () => { bulmaJS.toggleHtmlClipped(); } }); } ); const doAddLot = (clickEvent: Event) => { const rowElement = (clickEvent.currentTarget as HTMLElement).closest("tr")!; const lotId = rowElement.dataset.lotId!; addLot(lotId, (success) => { if (success) { rowElement.remove(); } }); }; (document.querySelector("#button--addLot") as HTMLButtonElement).addEventListener( "click", () => { let searchFormElement: HTMLFormElement; let searchResultsContainerElement: HTMLElement; const doSearch = (event?: Event) => { if (event) { event.preventDefault(); } searchResultsContainerElement.innerHTML = '

' + '
' + "Searching..." + "

"; cityssm.postJSON( los.urlPrefix + "/lots/doSearchLots", searchFormElement, (responseJSON: { lots: recordTypes.Lot[] }) => { if (responseJSON.lots.length === 0) { searchResultsContainerElement.innerHTML = '
' + '

There are no records that meet the search criteria.

' + "
"; return; } searchResultsContainerElement.innerHTML = '' + "" + "" + '' + ("") + ("") + ("") + "" + "" + "" + "" + "
" + exports.aliases.lot + "" + exports.aliases.map + "" + exports.aliases.lot + " TypeStatus
"; for (const lot of responseJSON.lots) { const rowElement = document.createElement("tr"); rowElement.className = "container--lot"; rowElement.dataset.lotId = lot.lotId.toString(); rowElement.innerHTML = '' + '" + "" + ('' + cityssm.escapeHTML(lot.lotName || "") + "") + "" + cityssm.escapeHTML(lot.mapName || "") + "" + ("" + cityssm.escapeHTML(lot.lotType || "") + "") + ("" + cityssm.escapeHTML(lot.lotStatus || "") + ""); ( rowElement.querySelector(".button--addLot") as HTMLButtonElement ).addEventListener("click", doAddLot); searchResultsContainerElement .querySelector("tbody")! .append(rowElement); } } ); }; cityssm.openHtmlModal("workOrder-addLot", { onshow: (modalElement) => { los.populateAliases(modalElement); searchFormElement = modalElement.querySelector("form")!; searchResultsContainerElement = modalElement.querySelector( "#resultsContainer--lotAdd" ) as HTMLElement; ( modalElement.querySelector( "#lotSearch--notWorkOrderId" ) as HTMLInputElement ).value = workOrderId; const lotStatusElement = modalElement.querySelector( "#lotSearch--lotStatusId" ) as HTMLSelectElement; for (const lotStatus of exports.lotStatuses as recordTypes.LotStatus[]) { const optionElement = document.createElement("option"); optionElement.value = lotStatus.lotStatusId.toString(); optionElement.textContent = lotStatus.lotStatus; lotStatusElement.append(optionElement); } doSearch(); }, onshown: (modalElement) => { bulmaJS.toggleHtmlClipped(); ( modalElement.querySelector("#lotSearch--lotName") as HTMLInputElement ).addEventListener("change", doSearch); ( modalElement.querySelector( "#lotSearch--lotStatusId" ) as HTMLSelectElement ).addEventListener("change", doSearch); searchFormElement.addEventListener("submit", doSearch); }, onremoved: () => { bulmaJS.toggleHtmlClipped(); } }); } ); } /* * Comments */ let workOrderComments: recordTypes.WorkOrderComment[] = exports.workOrderComments; delete exports.workOrderComments; const openEditWorkOrderComment = (clickEvent: Event) => { const workOrderCommentId = Number.parseInt( (clickEvent.currentTarget as HTMLElement).closest("tr")!.dataset.workOrderCommentId!, 10 ); const workOrderComment = workOrderComments.find((currentComment) => { return currentComment.workOrderCommentId === workOrderCommentId; })!; let editFormElement: HTMLFormElement; let editCloseModalFunction: () => void; const editComment = (submitEvent: SubmitEvent) => { submitEvent.preventDefault(); cityssm.postJSON( los.urlPrefix + "/workOrders/doUpdateWorkOrderComment", editFormElement, (responseJSON: { success: boolean; errorMessage?: string; workOrderComments?: recordTypes.WorkOrderComment[]; }) => { if (responseJSON.success) { workOrderComments = responseJSON.workOrderComments!; editCloseModalFunction(); renderWorkOrderComments(); } else { bulmaJS.alert({ title: "Error Updating Comment", message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } } ); }; cityssm.openHtmlModal("workOrder-editComment", { onshow: (modalElement) => { ( modalElement.querySelector( "#workOrderCommentEdit--workOrderId" ) as HTMLInputElement ).value = workOrderId; ( modalElement.querySelector( "#workOrderCommentEdit--workOrderCommentId" ) as HTMLInputElement ).value = workOrderCommentId.toString(); ( modalElement.querySelector( "#workOrderCommentEdit--workOrderComment" ) as HTMLInputElement ).value = workOrderComment.workOrderComment!; const workOrderCommentDateStringElement = modalElement.querySelector( "#workOrderCommentEdit--workOrderCommentDateString" ) as HTMLInputElement; workOrderCommentDateStringElement.value = workOrderComment.workOrderCommentDateString!; const currentDateString = cityssm.dateToString(new Date()); workOrderCommentDateStringElement.max = workOrderComment.workOrderCommentDateString! <= currentDateString ? currentDateString : workOrderComment.workOrderCommentDateString!; ( modalElement.querySelector( "#workOrderCommentEdit--workOrderCommentTimeString" ) as HTMLInputElement ).value = workOrderComment.workOrderCommentTimeString!; }, onshown: (modalElement, closeModalFunction) => { bulmaJS.toggleHtmlClipped(); los.initializeDatePickers(modalElement); // los.initializeTimePickers(modalElement); ( modalElement.querySelector( "#workOrderCommentEdit--workOrderComment" ) as HTMLTextAreaElement ).focus(); editFormElement = modalElement.querySelector("form")!; editFormElement.addEventListener("submit", editComment); editCloseModalFunction = closeModalFunction; }, onremoved: () => { bulmaJS.toggleHtmlClipped(); } }); }; const deleteWorkOrderComment = (clickEvent: Event) => { const workOrderCommentId = Number.parseInt( (clickEvent.currentTarget as HTMLElement).closest("tr")!.dataset.workOrderCommentId!, 10 ); const doDelete = () => { cityssm.postJSON( los.urlPrefix + "/workOrders/doDeleteWorkOrderComment", { workOrderId, workOrderCommentId }, (responseJSON: { success: boolean; errorMessage?: string; workOrderComments: recordTypes.WorkOrderComment[]; }) => { if (responseJSON.success) { workOrderComments = responseJSON.workOrderComments; renderWorkOrderComments(); } else { bulmaJS.alert({ title: "Error Removing Comment", message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } } ); }; bulmaJS.confirm({ title: "Remove Comment?", message: "Are you sure you want to remove this comment?", okButton: { text: "Yes, Remove Comment", callbackFunction: doDelete }, contextualColorName: "warning" }); }; const renderWorkOrderComments = () => { const containerElement = document.querySelector( "#container--workOrderComments" ) as HTMLElement; if (workOrderComments.length === 0) { containerElement.innerHTML = '
' + '

There are no comments to display.

' + "
"; return; } const tableElement = document.createElement("table"); tableElement.className = "table is-fullwidth is-striped is-hoverable"; tableElement.innerHTML = "" + "Commentor" + "Comment Date" + "Comment" + 'Options' + "" + ""; for (const workOrderComment of workOrderComments) { const tableRowElement = document.createElement("tr"); tableRowElement.dataset.workOrderCommentId = workOrderComment.workOrderCommentId!.toString(); tableRowElement.innerHTML = "" + cityssm.escapeHTML(workOrderComment.recordCreate_userName || "") + "" + "" + workOrderComment.workOrderCommentDateString + (workOrderComment.workOrderCommentTime === 0 ? "" : " " + workOrderComment.workOrderCommentTimeString) + "" + "" + cityssm.escapeHTML(workOrderComment.workOrderComment || "") + "" + ('' + '
' + ('") + ('") + "
" + ""); (tableRowElement.querySelector(".button--edit") as HTMLButtonElement).addEventListener( "click", openEditWorkOrderComment ); ( tableRowElement.querySelector(".button--delete") as HTMLButtonElement ).addEventListener("click", deleteWorkOrderComment); tableElement.querySelector("tbody")!.append(tableRowElement); } containerElement.innerHTML = ""; containerElement.append(tableElement); }; const openAddCommentModal = () => { let addCommentCloseModalFunction: () => void; const doAddComment = (formEvent: SubmitEvent) => { formEvent.preventDefault(); cityssm.postJSON( los.urlPrefix + "/workOrders/doAddWorkOrderComment", formEvent.currentTarget, (responseJSON: { success: boolean; workOrderComments?: recordTypes.WorkOrderComment[]; }) => { if (responseJSON.success) { workOrderComments = responseJSON.workOrderComments!; renderWorkOrderComments(); addCommentCloseModalFunction(); } } ); }; cityssm.openHtmlModal("workOrder-addComment", { onshow(modalElement) { los.populateAliases(modalElement); ( modalElement.querySelector( "#workOrderCommentAdd--workOrderId" ) as HTMLInputElement ).value = workOrderId; modalElement.querySelector("form")!.addEventListener("submit", doAddComment); }, onshown(modalElement, closeModalFunction) { bulmaJS.toggleHtmlClipped(); addCommentCloseModalFunction = closeModalFunction; ( modalElement.querySelector( "#workOrderCommentAdd--workOrderComment" ) as HTMLTextAreaElement ).focus(); }, onremoved() { bulmaJS.toggleHtmlClipped(); (document.querySelector("#workOrderComments--add") as HTMLButtonElement).focus(); } }); }; if (!isCreate) { (document.querySelector("#workOrderComments--add") as HTMLButtonElement).addEventListener( "click", openAddCommentModal ); renderWorkOrderComments(); } /* * Milestones */ if (!isCreate) { workOrderMilestones = exports.workOrderMilestones as recordTypes.WorkOrderMilestone[]; delete exports.workOrderMilestones; const processMilestoneResponse = (responseJSON: { success: boolean; errorMessage?: string; workOrderMilestones?: recordTypes.WorkOrderMilestone[]; }) => { if (responseJSON.success) { workOrderMilestones = responseJSON.workOrderMilestones!; renderMilestones(); } else { bulmaJS.alert({ title: "Error Reopening Milestone", message: responseJSON.errorMessage || "", contextualColorName: "danger" }); } }; const completeMilestone = (clickEvent: Event) => { clickEvent.preventDefault(); const currentDateString = cityssm.dateToString(new Date()); const workOrderMilestoneId = Number.parseInt( ( (clickEvent.currentTarget as HTMLElement).closest( ".container--milestone" ) as HTMLElement ).dataset.workOrderMilestoneId!, 10 ); const workOrderMilestone = workOrderMilestones.find((currentMilestone) => { return currentMilestone.workOrderMilestoneId === workOrderMilestoneId; })!; const doComplete = () => { cityssm.postJSON( los.urlPrefix + "/workOrders/doCompleteWorkOrderMilestone", { workOrderId, workOrderMilestoneId }, processMilestoneResponse ); }; bulmaJS.confirm({ title: "Complete Milestone", message: "Are you sure you want to complete this milestone?" + (workOrderMilestone.workOrderMilestoneDateString! > currentDateString ? "
Note that this milestone is expected to be completed in the future." : ""), messageIsHtml: true, contextualColorName: "warning", okButton: { text: "Yes, Complete Milestone", callbackFunction: doComplete } }); }; const reopenMilestone = (clickEvent: Event) => { clickEvent.preventDefault(); const workOrderMilestoneId = ( (clickEvent.currentTarget as HTMLElement).closest( ".container--milestone" ) as HTMLElement ).dataset.workOrderMilestoneId; const doReopen = () => { cityssm.postJSON( los.urlPrefix + "/workOrders/doReopenWorkOrderMilestone", { workOrderId, workOrderMilestoneId }, processMilestoneResponse ); }; 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: doReopen } }); }; const deleteMilestone = (clickEvent: Event) => { clickEvent.preventDefault(); const workOrderMilestoneId = ( (clickEvent.currentTarget as HTMLElement).closest( ".container--milestone" ) as HTMLElement ).dataset.workOrderMilestoneId; const doDelete = () => { cityssm.postJSON( los.urlPrefix + "/workOrders/doDeleteWorkOrderMilestone", { workOrderMilestoneId, workOrderId }, processMilestoneResponse ); }; bulmaJS.confirm({ title: "Delete Milestone", message: "Are you sure you want to delete this milestone?", contextualColorName: "warning", okButton: { text: "Yes, Delete Milestone", callbackFunction: doDelete } }); }; const editMilestone = (clickEvent: Event) => { clickEvent.preventDefault(); const workOrderMilestoneId = Number.parseInt( ( (clickEvent.currentTarget as HTMLElement).closest( ".container--milestone" ) as HTMLElement ).dataset.workOrderMilestoneId!, 10 ); const workOrderMilestone = workOrderMilestones.find((currentMilestone) => { return currentMilestone.workOrderMilestoneId === workOrderMilestoneId; })!; let editCloseModalFunction: () => void; const doEdit = (submitEvent: SubmitEvent) => { submitEvent.preventDefault(); cityssm.postJSON( los.urlPrefix + "/workOrders/doUpdateWorkOrderMilestone", submitEvent.currentTarget, (responseJSON: { success: boolean; errorMessage?: string; workOrderMilestones?: recordTypes.WorkOrderMilestone[]; }) => { processMilestoneResponse(responseJSON); if (responseJSON.success) { editCloseModalFunction(); } } ); }; cityssm.openHtmlModal("workOrder-editMilestone", { onshow: (modalElement) => { ( modalElement.querySelector( "#milestoneEdit--workOrderId" ) as HTMLInputElement ).value = workOrderId; ( modalElement.querySelector( "#milestoneEdit--workOrderMilestoneId" ) as HTMLInputElement ).value = workOrderMilestone.workOrderMilestoneId!.toString(); const milestoneTypeElement = modalElement.querySelector( "#milestoneEdit--workOrderMilestoneTypeId" ) as HTMLSelectElement; let milestoneTypeFound = false; for (const milestoneType of exports.workOrderMilestoneTypes as recordTypes.WorkOrderMilestoneType[]) { const optionElement = document.createElement("option"); optionElement.value = milestoneType.workOrderMilestoneTypeId.toString(); optionElement.textContent = milestoneType.workOrderMilestoneType; if ( milestoneType.workOrderMilestoneTypeId === workOrderMilestone.workOrderMilestoneTypeId ) { optionElement.selected = true; milestoneTypeFound = true; } milestoneTypeElement.append(optionElement); } if (!milestoneTypeFound && workOrderMilestone.workOrderMilestoneTypeId) { const optionElement = document.createElement("option"); optionElement.value = workOrderMilestone.workOrderMilestoneTypeId.toString(); optionElement.textContent = workOrderMilestone.workOrderMilestoneType!; optionElement.selected = true; milestoneTypeElement.append(optionElement); } ( modalElement.querySelector( "#milestoneEdit--workOrderMilestoneDateString" ) as HTMLInputElement ).value = workOrderMilestone.workOrderMilestoneDateString!; if (workOrderMilestone.workOrderMilestoneTime) { ( modalElement.querySelector( "#milestoneEdit--workOrderMilestoneTimeString" ) as HTMLInputElement ).value = workOrderMilestone.workOrderMilestoneTimeString!; } ( modalElement.querySelector( "#milestoneEdit--workOrderMilestoneDescription" ) as HTMLTextAreaElement ).value = workOrderMilestone.workOrderMilestoneDescription!; }, onshown: (modalElement, closeModalFunction) => { editCloseModalFunction = closeModalFunction; bulmaJS.toggleHtmlClipped(); los.initializeDatePickers(modalElement); // los.initializeTimePickers(modalElement); modalElement.querySelector("form")!.addEventListener("submit", doEdit); }, onremoved: () => { bulmaJS.toggleHtmlClipped(); } }); }; const renderMilestones = () => { // Clear milestones panel const milestonesPanelElement = document.querySelector( "#panel--milestones" ) as HTMLElement; const panelBlockElementsToDelete = milestonesPanelElement.querySelectorAll(".panel-block"); for (const panelBlockToDelete of panelBlockElementsToDelete) { panelBlockToDelete.remove(); } for (const milestone of workOrderMilestones) { const panelBlockElement = document.createElement("div"); panelBlockElement.className = "panel-block is-block container--milestone"; panelBlockElement.dataset.workOrderMilestoneId = milestone.workOrderMilestoneId!.toString(); panelBlockElement.innerHTML = '
' + ('
' + (milestone.workOrderMilestoneCompletionDate ? '' + '' + "" : '") + "
") + ('
' + (milestone.workOrderMilestoneTypeId ? "" + cityssm.escapeHTML(milestone.workOrderMilestoneType || "") + "
" : "") + milestone.workOrderMilestoneDateString + (milestone.workOrderMilestoneTime ? " " + milestone.workOrderMilestoneTimeString : "") + "
" + '' + cityssm.escapeHTML(milestone.workOrderMilestoneDescription || "") + "" + "
") + ('
' + '" + "
") + "
"; if (milestone.workOrderMilestoneCompletionDate) { ( panelBlockElement.querySelector( ".button--reopenMilestone" ) as HTMLButtonElement ).addEventListener("click", reopenMilestone); } else { ( panelBlockElement.querySelector( ".button--editMilestone" ) as HTMLButtonElement ).addEventListener("click", editMilestone); ( panelBlockElement.querySelector( ".button--completeMilestone" ) as HTMLButtonElement ).addEventListener("click", completeMilestone); } ( panelBlockElement.querySelector(".button--deleteMilestone") as HTMLButtonElement ).addEventListener("click", deleteMilestone); milestonesPanelElement.append(panelBlockElement); } bulmaJS.init(milestonesPanelElement); }; renderMilestones(); (document.querySelector("#button--addMilestone") as HTMLButtonElement).addEventListener( "click", () => { let addModalElement: HTMLElement; let addFormElement: HTMLFormElement; let addCloseModalFunction: () => void; const doAdd = (submitEvent: SubmitEvent) => { if (submitEvent) { submitEvent.preventDefault(); } const currentDateString = cityssm.dateToString(new Date()); const _doAdd = () => { cityssm.postJSON( los.urlPrefix + "/workOrders/doAddWorkOrderMilestone", addFormElement, (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", { onshow: (modalElement) => { ( modalElement.querySelector( "#milestoneAdd--workOrderId" ) as HTMLInputElement ).value = workOrderId; const milestoneTypeElement = modalElement.querySelector( "#milestoneAdd--workOrderMilestoneTypeId" ) as HTMLSelectElement; for (const milestoneType of exports.workOrderMilestoneTypes as recordTypes.WorkOrderMilestoneType[]) { const optionElement = document.createElement("option"); optionElement.value = milestoneType.workOrderMilestoneTypeId.toString(); optionElement.textContent = milestoneType.workOrderMilestoneType; milestoneTypeElement.append(optionElement); } ( modalElement.querySelector( "#milestoneAdd--workOrderMilestoneDateString" ) as HTMLInputElement ).valueAsDate = new Date(); }, onshown: (modalElement, closeModalFunction) => { addModalElement = modalElement; addCloseModalFunction = closeModalFunction; los.initializeDatePickers(modalElement); // los.initializeTimePickers(modalElement); bulmaJS.toggleHtmlClipped(); addFormElement = modalElement.querySelector("form")!; addFormElement.addEventListener("submit", doAdd); }, onremoved: () => { bulmaJS.toggleHtmlClipped(); } }); } ); } })();