/* eslint-disable 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 urlPrefix = document.querySelector("main").dataset.urlPrefix; 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") .closest(".field") ); los.initializeUnlockFieldButtons(workOrderFormElement); workOrderFormElement.addEventListener("submit", (submitEvent) => { submitEvent.preventDefault(); cityssm.postJSON( urlPrefix + "/workOrders/" + (isCreate ? "doCreateWorkOrder" : "doUpdateWorkOrder"), submitEvent.currentTarget, (responseJSON: { success: boolean; workOrderId?: number; errorMessage?: string }) => { if (responseJSON.success) { if (isCreate) { window.location.href = 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" }); } } ); }); /* * 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 */ 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( 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( 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( 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").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) + ")" : '' + cityssm.escapeHTML( lotOccupancy.lotOccupancyOccupants[0].occupantName ) + "") + (lotOccupancy.lotOccupancyOccupants.length > 1 ? " plus " + (lotOccupancy.lotOccupancyOccupants.length - 1) : "") + "") + ("" + '" + "") ); if (lotOccupancy.lotId && !hasLotRecord) { rowElement .querySelector(".button--addLot") .addEventListener("click", addLotFromLotOccupancy); } rowElement .querySelector(".button--deleteLotOccupancy") .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( 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( 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").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") .addEventListener("click", openEditLotStatus); rowElement.querySelector(".button--deleteLot").addEventListener("click", deleteLot); lotsContainerElement.querySelector("tbody").append(rowElement); } }; const renderRelatedLotsAndOccupancies = () => { renderRelatedOccupancies(); renderRelatedLots(); }; renderRelatedLotsAndOccupancies(); document.querySelector("#button--addLotOccupancy").addEventListener("click", () => { let searchFormElement: HTMLFormElement; let searchResultsContainerElement: HTMLElement; const doAddLotOccupancy = (clickEvent: Event) => { const rowElement = (clickEvent.currentTarget as HTMLElement).closest("tr"); const lotOccupancyId = rowElement.dataset.lotOccupancyId; addLotOccupancy(lotOccupancyId, (success) => { if (success) { rowElement.remove(); } }); }; const doSearch = (event?: Event) => { if (event) { event.preventDefault(); } searchResultsContainerElement.innerHTML = '

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

"; cityssm.postJSON( 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") .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" ); ( 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") .addEventListener("change", doSearch); modalElement .querySelector("#lotOccupancySearch--lotName") .addEventListener("change", doSearch); searchFormElement.addEventListener("submit", doSearch); }, onremoved: () => { bulmaJS.toggleHtmlClipped(); } }); }); document.querySelector("#button--addLot").addEventListener("click", () => { let searchFormElement: HTMLFormElement; let searchResultsContainerElement: HTMLElement; const doAddLot = (clickEvent: Event) => { const rowElement = (clickEvent.currentTarget as HTMLElement).closest("tr"); const lotId = rowElement.dataset.lotId; addLot(lotId, (success) => { if (success) { rowElement.remove(); } }); }; const doSearch = (event?: Event) => { if (event) { event.preventDefault(); } searchResultsContainerElement.innerHTML = '

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

"; cityssm.postJSON( 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") .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" ); ( 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") .addEventListener("change", doSearch); modalElement .querySelector("#lotSearch--lotStatusId") .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( 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( 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") .addEventListener("click", openEditWorkOrderComment); tableRowElement .querySelector(".button--delete") .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( 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") .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( 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( 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( 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( 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") .addEventListener("click", reopenMilestone); } else { panelBlockElement .querySelector(".button--editMilestone") .addEventListener("click", editMilestone); panelBlockElement .querySelector(".button--completeMilestone") .addEventListener("click", completeMilestone); } panelBlockElement .querySelector(".button--deleteMilestone") .addEventListener("click", deleteMilestone); milestonesPanelElement.append(panelBlockElement); } bulmaJS.init(milestonesPanelElement); }; renderMilestones(); document.querySelector("#button--addMilestone").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( 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(); } }); }); } })();