From 817c32502c9409a9d1a805708f7427d417a01e20 Mon Sep 17 00:00:00 2001 From: Dan Gowans Date: Wed, 24 Aug 2022 15:41:38 -0400 Subject: [PATCH] lot comments --- handlers/lots-get/print.js | 6 - handlers/lots-get/print.ts | 59 ------- .../doAddLotComment.d.ts} | 0 handlers/lots-post/doAddLotComment.js | 11 ++ handlers/lots-post/doAddLotComment.ts | 27 +++ handlers/lots-post/doDeleteLotComment.d.ts | 3 + handlers/lots-post/doDeleteLotComment.js | 11 ++ handlers/lots-post/doDeleteLotComment.ts | 27 +++ handlers/lots-post/doUpdateLotComment.d.ts | 3 + handlers/lots-post/doUpdateLotComment.js | 11 ++ handlers/lots-post/doUpdateLotComment.ts | 27 +++ helpers/lotOccupancyDB/deleteLotComment.d.ts | 3 + helpers/lotOccupancyDB/deleteLotComment.js | 15 ++ helpers/lotOccupancyDB/deleteLotComment.ts | 33 ++++ helpers/lotOccupancyDB/getLotComments.js | 3 +- helpers/lotOccupancyDB/getLotComments.ts | 3 +- helpers/lotOccupancyDB/updateLotComment.d.ts | 9 + helpers/lotOccupancyDB/updateLotComment.js | 20 +++ helpers/lotOccupancyDB/updateLotComment.ts | 51 ++++++ public-typescript/lotEdit.js | 117 ++++++++++++- public-typescript/lotEdit.ts | 154 +++++++++++++++++- ...otComment-add.html => lot-addComment.html} | 0 public/html/lot-editComment.html | 48 ++++++ public/javascripts/lotEdit.min.js | 2 +- routes/lots.js | 8 +- routes/lots.ts | 20 ++- views/lot-view.ejs | 29 ++++ 27 files changed, 620 insertions(+), 80 deletions(-) delete mode 100644 handlers/lots-get/print.js delete mode 100644 handlers/lots-get/print.ts rename handlers/{lots-get/print.d.ts => lots-post/doAddLotComment.d.ts} (100%) create mode 100644 handlers/lots-post/doAddLotComment.js create mode 100644 handlers/lots-post/doAddLotComment.ts create mode 100644 handlers/lots-post/doDeleteLotComment.d.ts create mode 100644 handlers/lots-post/doDeleteLotComment.js create mode 100644 handlers/lots-post/doDeleteLotComment.ts create mode 100644 handlers/lots-post/doUpdateLotComment.d.ts create mode 100644 handlers/lots-post/doUpdateLotComment.js create mode 100644 handlers/lots-post/doUpdateLotComment.ts create mode 100644 helpers/lotOccupancyDB/deleteLotComment.d.ts create mode 100644 helpers/lotOccupancyDB/deleteLotComment.js create mode 100644 helpers/lotOccupancyDB/deleteLotComment.ts create mode 100644 helpers/lotOccupancyDB/updateLotComment.d.ts create mode 100644 helpers/lotOccupancyDB/updateLotComment.js create mode 100644 helpers/lotOccupancyDB/updateLotComment.ts rename public/html/{lotComment-add.html => lot-addComment.html} (100%) create mode 100644 public/html/lot-editComment.html diff --git a/handlers/lots-get/print.js b/handlers/lots-get/print.js deleted file mode 100644 index c2fa3413..00000000 --- a/handlers/lots-get/print.js +++ /dev/null @@ -1,6 +0,0 @@ -import * as configFunctions from "../../helpers/functions.config.js"; -const urlPrefix = configFunctions.getProperty("reverseProxy.urlPrefix"); -const __dirname = "."; -export const handler = async (request, response, next) => { -}; -export default handler; diff --git a/handlers/lots-get/print.ts b/handlers/lots-get/print.ts deleted file mode 100644 index 401134b8..00000000 --- a/handlers/lots-get/print.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { RequestHandler } from "express"; - -import path from "path"; -import * as ejs from "ejs"; - -import * as configFunctions from "../../helpers/functions.config.js"; - -// import convertHTMLToPDF from "pdf-puppeteer"; - - -const urlPrefix = configFunctions.getProperty("reverseProxy.urlPrefix"); - -const __dirname = "."; - - -export const handler: RequestHandler = async(request, response, next) => { - -/* - const reportPath = path.join(__dirname, "reports", printTemplate); - - const pdfCallbackFunction = (pdf: Buffer) => { - - response.setHeader("Content-Disposition", - "attachment;" + - " filename=licence-" + licenceID.toString() + "-" + licence.recordUpdate_timeMillis.toString() + ".pdf" - ); - - response.setHeader("Content-Type", "application/pdf"); - - response.send(pdf); - }; - - await ejs.renderFile( - reportPath, { - configFunctions, - licence, - licenceTicketTypeSummary, - organization - }, {}, - async(ejsError, ejsData) => { - - if (ejsError) { - return next(ejsError); - } - - await convertHTMLToPDF(ejsData, pdfCallbackFunction, { - format: "letter", - printBackground: true, - preferCSSPageSize: true - }); - - return; - } - ); - */ -}; - - -export default handler; diff --git a/handlers/lots-get/print.d.ts b/handlers/lots-post/doAddLotComment.d.ts similarity index 100% rename from handlers/lots-get/print.d.ts rename to handlers/lots-post/doAddLotComment.d.ts diff --git a/handlers/lots-post/doAddLotComment.js b/handlers/lots-post/doAddLotComment.js new file mode 100644 index 00000000..2e3bd458 --- /dev/null +++ b/handlers/lots-post/doAddLotComment.js @@ -0,0 +1,11 @@ +import { addLotComment } from "../../helpers/lotOccupancyDB/addLotComment.js"; +import { getLotComments } from "../../helpers/lotOccupancyDB/getLotComments.js"; +export const handler = async (request, response) => { + addLotComment(request.body, request.session); + const lotComments = getLotComments(request.body.lotId); + response.json({ + success: true, + lotComments + }); +}; +export default handler; diff --git a/handlers/lots-post/doAddLotComment.ts b/handlers/lots-post/doAddLotComment.ts new file mode 100644 index 00000000..9f817b9f --- /dev/null +++ b/handlers/lots-post/doAddLotComment.ts @@ -0,0 +1,27 @@ +import type { + RequestHandler +} from "express"; + +import { + addLotComment +} from "../../helpers/lotOccupancyDB/addLotComment.js"; + +import { + getLotComments +} from "../../helpers/lotOccupancyDB/getLotComments.js"; + + +export const handler: RequestHandler = async (request, response) => { + + addLotComment(request.body, request.session); + + const lotComments = getLotComments(request.body.lotId); + + response.json({ + success: true, + lotComments + }); +}; + + +export default handler; \ No newline at end of file diff --git a/handlers/lots-post/doDeleteLotComment.d.ts b/handlers/lots-post/doDeleteLotComment.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lots-post/doDeleteLotComment.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/lots-post/doDeleteLotComment.js b/handlers/lots-post/doDeleteLotComment.js new file mode 100644 index 00000000..9af54d2f --- /dev/null +++ b/handlers/lots-post/doDeleteLotComment.js @@ -0,0 +1,11 @@ +import { deleteLotComment } from "../../helpers/lotOccupancyDB/deleteLotComment.js"; +import { getLotComments } from "../../helpers/lotOccupancyDB/getLotComments.js"; +export const handler = async (request, response) => { + const success = deleteLotComment(request.body.lotCommentId, request.session); + const lotComments = getLotComments(request.body.lotId); + response.json({ + success, + lotComments + }); +}; +export default handler; diff --git a/handlers/lots-post/doDeleteLotComment.ts b/handlers/lots-post/doDeleteLotComment.ts new file mode 100644 index 00000000..1e9aab56 --- /dev/null +++ b/handlers/lots-post/doDeleteLotComment.ts @@ -0,0 +1,27 @@ +import type { + RequestHandler +} from "express"; + +import { + deleteLotComment +} from "../../helpers/lotOccupancyDB/deleteLotComment.js"; + +import { + getLotComments +} from "../../helpers/lotOccupancyDB/getLotComments.js"; + + +export const handler: RequestHandler = async (request, response) => { + + const success = deleteLotComment(request.body.lotCommentId, request.session); + + const lotComments = getLotComments(request.body.lotId); + + response.json({ + success, + lotComments + }); +}; + + +export default handler; \ No newline at end of file diff --git a/handlers/lots-post/doUpdateLotComment.d.ts b/handlers/lots-post/doUpdateLotComment.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lots-post/doUpdateLotComment.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/lots-post/doUpdateLotComment.js b/handlers/lots-post/doUpdateLotComment.js new file mode 100644 index 00000000..30ca5b6b --- /dev/null +++ b/handlers/lots-post/doUpdateLotComment.js @@ -0,0 +1,11 @@ +import { updateLotComment } from "../../helpers/lotOccupancyDB/updateLotComment.js"; +import { getLotComments } from "../../helpers/lotOccupancyDB/getLotComments.js"; +export const handler = async (request, response) => { + const success = updateLotComment(request.body, request.session); + const lotComments = getLotComments(request.body.lotId); + response.json({ + success, + lotComments + }); +}; +export default handler; diff --git a/handlers/lots-post/doUpdateLotComment.ts b/handlers/lots-post/doUpdateLotComment.ts new file mode 100644 index 00000000..4535e53b --- /dev/null +++ b/handlers/lots-post/doUpdateLotComment.ts @@ -0,0 +1,27 @@ +import type { + RequestHandler +} from "express"; + +import { + updateLotComment +} from "../../helpers/lotOccupancyDB/updateLotComment.js"; + +import { + getLotComments +} from "../../helpers/lotOccupancyDB/getLotComments.js"; + + +export const handler: RequestHandler = async (request, response) => { + + const success = updateLotComment(request.body, request.session); + + const lotComments = getLotComments(request.body.lotId); + + response.json({ + success, + lotComments + }); +}; + + +export default handler; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/deleteLotComment.d.ts b/helpers/lotOccupancyDB/deleteLotComment.d.ts new file mode 100644 index 00000000..0292f9a0 --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotComment.d.ts @@ -0,0 +1,3 @@ +import type * as recordTypes from "../../types/recordTypes"; +export declare const deleteLotComment: (lotCommentId: number | string, requestSession: recordTypes.PartialSession) => boolean; +export default deleteLotComment; diff --git a/helpers/lotOccupancyDB/deleteLotComment.js b/helpers/lotOccupancyDB/deleteLotComment.js new file mode 100644 index 00000000..2fbda89f --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotComment.js @@ -0,0 +1,15 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +export const deleteLotComment = (lotCommentId, requestSession) => { + const database = sqlite(databasePath); + const rightNowMillis = Date.now(); + const result = database + .prepare("update LotComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotCommentId = ?") + .run(requestSession.user.userName, rightNowMillis, lotCommentId); + database.close(); + return (result.changes > 0); +}; +export default deleteLotComment; diff --git a/helpers/lotOccupancyDB/deleteLotComment.ts b/helpers/lotOccupancyDB/deleteLotComment.ts new file mode 100644 index 00000000..46439e3e --- /dev/null +++ b/helpers/lotOccupancyDB/deleteLotComment.ts @@ -0,0 +1,33 @@ +import sqlite from "better-sqlite3"; + +import { + lotOccupancyDB as databasePath +} from "../../data/databasePaths.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +export const deleteLotComment = + (lotCommentId: number | string, + requestSession: recordTypes.PartialSession): boolean => { + + const database = sqlite(databasePath); + + const rightNowMillis = Date.now(); + + const result = database + .prepare("update LotComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotCommentId = ?") + .run(requestSession.user.userName, + rightNowMillis, + lotCommentId); + + database.close(); + + return (result.changes > 0); + }; + + +export default deleteLotComment; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/getLotComments.js b/helpers/lotOccupancyDB/getLotComments.js index 86c6d2ea..2a01d5f5 100644 --- a/helpers/lotOccupancyDB/getLotComments.js +++ b/helpers/lotOccupancyDB/getLotComments.js @@ -11,7 +11,8 @@ export const getLotComments = (lotId, connectedDatabase) => { .prepare("select lotCommentId," + " lotCommentDate, userFn_dateIntegerToString(lotCommentDate) as lotCommentDateString," + " lotCommentTime, userFn_timeIntegerToString(lotCommentTime) as lotCommentTimeString," + - " lotComment" + + " lotComment," + + " recordCreate_userName, recordUpdate_userName" + " from LotComments" + " where recordDelete_timeMillis is null" + " and lotId = ?" + diff --git a/helpers/lotOccupancyDB/getLotComments.ts b/helpers/lotOccupancyDB/getLotComments.ts index f8d9baba..6031e06b 100644 --- a/helpers/lotOccupancyDB/getLotComments.ts +++ b/helpers/lotOccupancyDB/getLotComments.ts @@ -20,7 +20,8 @@ export const getLotComments = (lotId: number | string, connectedDatabase ? : sql .prepare("select lotCommentId," + " lotCommentDate, userFn_dateIntegerToString(lotCommentDate) as lotCommentDateString," + " lotCommentTime, userFn_timeIntegerToString(lotCommentTime) as lotCommentTimeString," + - " lotComment" + + " lotComment," + + " recordCreate_userName, recordUpdate_userName" + " from LotComments" + " where recordDelete_timeMillis is null" + " and lotId = ?" + diff --git a/helpers/lotOccupancyDB/updateLotComment.d.ts b/helpers/lotOccupancyDB/updateLotComment.d.ts new file mode 100644 index 00000000..3b95fc34 --- /dev/null +++ b/helpers/lotOccupancyDB/updateLotComment.d.ts @@ -0,0 +1,9 @@ +import type * as recordTypes from "../../types/recordTypes"; +interface UpdateLotCommentForm { + lotCommentId: string | number; + lotCommentDateString: string; + lotCommentTimeString: string; + lotComment: string; +} +export declare const updateLotComment: (commentForm: UpdateLotCommentForm, requestSession: recordTypes.PartialSession) => boolean; +export default updateLotComment; diff --git a/helpers/lotOccupancyDB/updateLotComment.js b/helpers/lotOccupancyDB/updateLotComment.js new file mode 100644 index 00000000..56f5de01 --- /dev/null +++ b/helpers/lotOccupancyDB/updateLotComment.js @@ -0,0 +1,20 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +import { dateStringToInteger } from "@cityssm/expressjs-server-js/dateTimeFns.js"; +export const updateLotComment = (commentForm, requestSession) => { + const rightNowMillis = Date.now(); + const database = sqlite(databasePath); + const result = database + .prepare("update LotComments" + + " set lotCommentDate = ?," + + " lotCommentTime = ?," + + " lotComment = ?," + + " recordUpdate_userName = ?," + + " recordUpdate_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotCommentId = ?") + .run(dateStringToInteger(commentForm.lotCommentDateString), dateStringToInteger(commentForm.lotCommentTimeString), commentForm.lotComment, requestSession.user.userName, rightNowMillis, commentForm.lotCommentId); + database.close(); + return result.changes > 0; +}; +export default updateLotComment; diff --git a/helpers/lotOccupancyDB/updateLotComment.ts b/helpers/lotOccupancyDB/updateLotComment.ts new file mode 100644 index 00000000..7a4cd6d9 --- /dev/null +++ b/helpers/lotOccupancyDB/updateLotComment.ts @@ -0,0 +1,51 @@ +import sqlite from "better-sqlite3"; + +import { + lotOccupancyDB as databasePath +} from "../../data/databasePaths.js"; + +import { + dateStringToInteger +} from "@cityssm/expressjs-server-js/dateTimeFns.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +interface UpdateLotCommentForm { + lotCommentId: string | number; + lotCommentDateString: string; + lotCommentTimeString: string; + lotComment: string; +} + + +export const updateLotComment = + (commentForm: UpdateLotCommentForm, requestSession: recordTypes.PartialSession): boolean => { + + const rightNowMillis = Date.now(); + + const database = sqlite(databasePath); + + const result = database + .prepare("update LotComments" + + " set lotCommentDate = ?," + + " lotCommentTime = ?," + + " lotComment = ?," + + " recordUpdate_userName = ?," + + " recordUpdate_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotCommentId = ?") + .run(dateStringToInteger(commentForm.lotCommentDateString), + dateStringToInteger(commentForm.lotCommentTimeString), + commentForm.lotComment, + requestSession.user.userName, + rightNowMillis, + commentForm.lotCommentId); + + database.close(); + + return result.changes > 0; + }; + + +export default updateLotComment; \ No newline at end of file diff --git a/public-typescript/lotEdit.js b/public-typescript/lotEdit.js index 631bedbe..ce11f30f 100644 --- a/public-typescript/lotEdit.js +++ b/public-typescript/lotEdit.js @@ -32,13 +32,124 @@ Object.defineProperty(exports, "__esModule", { value: true }); formElement.addEventListener("submit", updateLot); los.initializeUnlockFieldButtons(formElement); let lotComments = exports.lotComments; + const openEditLotComment = (clickEvent) => { + const lotCommentId = Number.parseInt(clickEvent.currentTarget.closest("tr").dataset.lotCommentId, 10); + const lotComment = lotComments.find((currentLotComment) => { + return currentLotComment.lotCommentId === lotCommentId; + }); + let editFormElement; + let editCloseModalFunction; + const editComment = (submitEvent) => { + submitEvent.preventDefault(); + cityssm.postJSON(urlPrefix + "/lots/doUpdateLotComment", editFormElement, (responseJSON) => { + if (responseJSON.success) { + lotComments = responseJSON.lotComments; + editCloseModalFunction(); + renderLotComments(); + } + else { + bulmaJS.alert({ + title: "Error Updating Comment", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + cityssm.openHtmlModal("lot-editComment", { + onshow: (modalElement) => { + los.populateAliases(modalElement); + modalElement.querySelector("#lotCommentEdit--lotId").value = lotId; + modalElement.querySelector("#lotCommentEdit--lotCommentId").value = lotCommentId.toString(); + modalElement.querySelector("#lotCommentEdit--lotComment").value = lotComment.lotComment; + modalElement.querySelector("#lotCommentEdit--lotCommentDateString").value = lotComment.lotCommentDateString; + modalElement.querySelector("#lotCommentEdit--lotCommentTimeString").value = lotComment.lotCommentTimeString; + }, + onshown: (modalElement, closeModalFunction) => { + bulmaJS.toggleHtmlClipped(); + modalElement.querySelector("#lotCommentEdit--lotComment").focus(); + editFormElement = modalElement.querySelector("form"); + editFormElement.addEventListener("submit", editComment); + editCloseModalFunction = closeModalFunction; + }, + onremoved: () => { + bulmaJS.toggleHtmlClipped(); + } + }); + }; + const deleteLotComment = (clickEvent) => { + const lotCommentId = Number.parseInt(clickEvent.currentTarget.closest("tr").dataset.lotCommentId, 10); + const doDelete = () => { + cityssm.postJSON(urlPrefix + "/lots/doDeleteLotComment", { + lotId, + lotCommentId + }, (responseJSON) => { + if (responseJSON.success) { + lotComments = responseJSON.lotComments; + renderLotComments(); + } + 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 renderLotComments = () => { - const lotCommentsContainerElement = document.querySelector("#container--lotComments"); + const containerElement = document.querySelector("#container--lotComments"); if (lotComments.length === 0) { - lotCommentsContainerElement.innerHTML = "
" + + 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 lotComment of lotComments) { + const tableRowElement = document.createElement("tr"); + tableRowElement.dataset.lotCommentId = lotComment.lotCommentId.toString(); + tableRowElement.innerHTML = "" + cityssm.escapeHTML(lotComment.recordCreate_userName) + "" + + "" + + lotComment.lotCommentDateString + + (lotComment.lotCommentTime === 0 ? "" : " " + lotComment.lotCommentTimeString) + + "" + + "" + cityssm.escapeHTML(lotComment.lotComment) + "" + + ("" + + "
" + + ("") + + ("") + + "
" + + ""); + tableRowElement.querySelector(".button--edit").addEventListener("click", openEditLotComment); + tableRowElement.querySelector(".button--delete").addEventListener("click", deleteLotComment); + tableElement.querySelector("tbody").append(tableRowElement); + } + containerElement.innerHTML = ""; + containerElement.append(tableElement); }; const openAddCommentModal = () => { let addCommentCloseModalFunction; @@ -52,7 +163,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); } }); }; - cityssm.openHtmlModal("lotComment-add", { + cityssm.openHtmlModal("lot-addComment", { onshow(modalElement) { los.populateAliases(modalElement); modalElement.querySelector("#lotCommentAdd--lotId").value = lotId; diff --git a/public-typescript/lotEdit.ts b/public-typescript/lotEdit.ts index 898e9d7d..80e04f18 100644 --- a/public-typescript/lotEdit.ts +++ b/public-typescript/lotEdit.ts @@ -63,15 +63,163 @@ declare const bulmaJS: BulmaJS; let lotComments: recordTypes.LotComment[] = exports.lotComments; + + const openEditLotComment = (clickEvent: Event) => { + + const lotCommentId = Number.parseInt((clickEvent.currentTarget as HTMLElement).closest("tr").dataset.lotCommentId, 10); + + const lotComment = lotComments.find((currentLotComment) => { + return currentLotComment.lotCommentId === lotCommentId; + }); + + let editFormElement: HTMLFormElement; + let editCloseModalFunction: () => void; + + const editComment = (submitEvent: SubmitEvent) => { + + submitEvent.preventDefault(); + + cityssm.postJSON(urlPrefix + "/lots/doUpdateLotComment", + editFormElement, + (responseJSON: { + success: boolean; + errorMessage ? : string; + lotComments ? : recordTypes.LotComment[]; + }) => { + + if (responseJSON.success) { + lotComments = responseJSON.lotComments; + editCloseModalFunction(); + renderLotComments(); + } else { + bulmaJS.alert({ + title: "Error Updating Comment", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + + cityssm.openHtmlModal("lot-editComment", { + onshow: (modalElement) => { + los.populateAliases(modalElement); + + (modalElement.querySelector("#lotCommentEdit--lotId") as HTMLInputElement).value = lotId; + (modalElement.querySelector("#lotCommentEdit--lotCommentId") as HTMLInputElement).value = lotCommentId.toString(); + + (modalElement.querySelector("#lotCommentEdit--lotComment") as HTMLInputElement).value = lotComment.lotComment; + (modalElement.querySelector("#lotCommentEdit--lotCommentDateString") as HTMLInputElement).value = lotComment.lotCommentDateString; + (modalElement.querySelector("#lotCommentEdit--lotCommentTimeString") as HTMLInputElement).value = lotComment.lotCommentTimeString; + }, + onshown: (modalElement, closeModalFunction) => { + + bulmaJS.toggleHtmlClipped(); + + (modalElement.querySelector("#lotCommentEdit--lotComment") as HTMLTextAreaElement).focus(); + + editFormElement = modalElement.querySelector("form"); + editFormElement.addEventListener("submit", editComment); + + editCloseModalFunction = closeModalFunction; + }, + onremoved: () => { + bulmaJS.toggleHtmlClipped(); + } + }); + }; + + const deleteLotComment = (clickEvent: Event) => { + + const lotCommentId = Number.parseInt((clickEvent.currentTarget as HTMLElement).closest("tr").dataset.lotCommentId, 10); + + const doDelete = () => { + cityssm.postJSON(urlPrefix + "/lots/doDeleteLotComment", { + lotId, + lotCommentId + }, + (responseJSON: { + success: boolean; + errorMessage ? : string; + lotComments: recordTypes.LotComment[]; + }) => { + if (responseJSON.success) { + lotComments = responseJSON.lotComments; + renderLotComments(); + } 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 renderLotComments = () => { - const lotCommentsContainerElement = document.querySelector("#container--lotComments") as HTMLElement; + const containerElement = document.querySelector("#container--lotComments") as HTMLElement; if (lotComments.length === 0) { - lotCommentsContainerElement.innerHTML = "
" + + 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 lotComment of lotComments) { + + const tableRowElement = document.createElement("tr"); + tableRowElement.dataset.lotCommentId = lotComment.lotCommentId.toString(); + + tableRowElement.innerHTML = "" + cityssm.escapeHTML(lotComment.recordCreate_userName) + "" + + "" + + lotComment.lotCommentDateString + + (lotComment.lotCommentTime === 0 ? "" : " " + lotComment.lotCommentTimeString) + + "" + + "" + cityssm.escapeHTML(lotComment.lotComment) + "" + + ("" + + "
" + + ("") + + ("") + + "
" + + ""); + + tableRowElement.querySelector(".button--edit").addEventListener("click", openEditLotComment); + tableRowElement.querySelector(".button--delete").addEventListener("click", deleteLotComment); + + tableElement.querySelector("tbody").append(tableRowElement); + } + + containerElement.innerHTML = ""; + containerElement.append(tableElement); }; const openAddCommentModal = () => { @@ -93,7 +241,7 @@ declare const bulmaJS: BulmaJS; }); }; - cityssm.openHtmlModal("lotComment-add", { + cityssm.openHtmlModal("lot-addComment", { onshow(modalElement) { los.populateAliases(modalElement); (modalElement.querySelector("#lotCommentAdd--lotId") as HTMLInputElement).value = lotId; diff --git a/public/html/lotComment-add.html b/public/html/lot-addComment.html similarity index 100% rename from public/html/lotComment-add.html rename to public/html/lot-addComment.html diff --git a/public/html/lot-editComment.html b/public/html/lot-editComment.html new file mode 100644 index 00000000..e47dc672 --- /dev/null +++ b/public/html/lot-editComment.html @@ -0,0 +1,48 @@ + \ No newline at end of file diff --git a/public/javascripts/lotEdit.min.js b/public/javascripts/lotEdit.min.js index cc39ce04..088a5945 100644 --- a/public/javascripts/lotEdit.min.js +++ b/public/javascripts/lotEdit.min.js @@ -1 +1 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=exports.los,t=document.querySelector("main").dataset.urlPrefix,o=document.querySelector("#lot--lotId").value,s=""===o,l=document.querySelector("#form--lot");l.addEventListener("submit",e=>{e.preventDefault(),cityssm.postJSON(t+"/lots/"+(s?"doCreateLot":"doUpdateLot"),l,e=>{e.success?s?window.location.href=t+"/lots/"+e.lotId+"/edit":bulmaJS.alert({message:exports.aliases.lot+" Updated Successfully",contextualColorName:"success"}):bulmaJS.alert({title:"Error Updating "+exports.aliases.lot,message:e.errorMessage,contextualColorName:"danger"})})}),e.initializeUnlockFieldButtons(l);let r=exports.lotComments;const n=()=>{const e=document.querySelector("#container--lotComments");0===r.length&&(e.innerHTML='

There are no comments to display.

')},m=()=>{let s;const l=e=>{e.preventDefault(),cityssm.postJSON(t+"/lots/doAddLotComment",e.currentTarget,e=>{e.success&&(r=e.lotComments,n(),s())})};cityssm.openHtmlModal("lotComment-add",{onshow(t){e.populateAliases(t),t.querySelector("#lotCommentAdd--lotId").value=o,t.querySelector("form").addEventListener("submit",l)},onshown(e,t){bulmaJS.toggleHtmlClipped(),s=t,e.querySelector("#lotCommentAdd--lotComment").focus()},onremoved(){bulmaJS.toggleHtmlClipped(),document.querySelector("#lotComments--add").focus()}})};s||(document.querySelector("#lotComments--add").addEventListener("click",m),n())})(); \ No newline at end of file +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const t=exports.los,e=document.querySelector("main").dataset.urlPrefix,o=document.querySelector("#lot--lotId").value,l=""===o,s=document.querySelector("#form--lot");s.addEventListener("submit",t=>{t.preventDefault(),cityssm.postJSON(e+"/lots/"+(l?"doCreateLot":"doUpdateLot"),s,t=>{t.success?l?window.location.href=e+"/lots/"+t.lotId+"/edit":bulmaJS.alert({message:exports.aliases.lot+" Updated Successfully",contextualColorName:"success"}):bulmaJS.alert({title:"Error Updating "+exports.aliases.lot,message:t.errorMessage,contextualColorName:"danger"})})}),t.initializeUnlockFieldButtons(s);let n=exports.lotComments;const m=l=>{const s=Number.parseInt(l.currentTarget.closest("tr").dataset.lotCommentId,10),m=n.find(t=>t.lotCommentId===s);let r,d;const i=t=>{t.preventDefault(),cityssm.postJSON(e+"/lots/doUpdateLotComment",r,t=>{t.success?(n=t.lotComments,d(),a()):bulmaJS.alert({title:"Error Updating Comment",message:t.errorMessage,contextualColorName:"danger"})})};cityssm.openHtmlModal("lot-editComment",{onshow:e=>{t.populateAliases(e),e.querySelector("#lotCommentEdit--lotId").value=o,e.querySelector("#lotCommentEdit--lotCommentId").value=s.toString(),e.querySelector("#lotCommentEdit--lotComment").value=m.lotComment,e.querySelector("#lotCommentEdit--lotCommentDateString").value=m.lotCommentDateString,e.querySelector("#lotCommentEdit--lotCommentTimeString").value=m.lotCommentTimeString},onshown:(t,e)=>{bulmaJS.toggleHtmlClipped(),t.querySelector("#lotCommentEdit--lotComment").focus(),(r=t.querySelector("form")).addEventListener("submit",i),d=e},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})},r=t=>{const l=Number.parseInt(t.currentTarget.closest("tr").dataset.lotCommentId,10);bulmaJS.confirm({title:"Remove Comment?",message:"Are you sure you want to remove this comment?",okButton:{text:"Yes, Remove Comment",callbackFunction:()=>{cityssm.postJSON(e+"/lots/doDeleteLotComment",{lotId:o,lotCommentId:l},t=>{t.success?(n=t.lotComments,a()):bulmaJS.alert({title:"Error Removing Comment",message:t.errorMessage,contextualColorName:"danger"})})}},contextualColorName:"warning"})},a=()=>{const t=document.querySelector("#container--lotComments");if(0===n.length)return void(t.innerHTML='

There are no comments to display.

');const e=document.createElement("table");e.className="table is-fullwidth is-striped is-hoverable",e.innerHTML='CommentorComment DateCommentOptions';for(const t of n){const o=document.createElement("tr");o.dataset.lotCommentId=t.lotCommentId.toString(),o.innerHTML=""+cityssm.escapeHTML(t.recordCreate_userName)+""+t.lotCommentDateString+(0===t.lotCommentTime?"":" "+t.lotCommentTimeString)+""+cityssm.escapeHTML(t.lotComment)+'
',o.querySelector(".button--edit").addEventListener("click",m),o.querySelector(".button--delete").addEventListener("click",r),e.querySelector("tbody").append(o)}t.innerHTML="",t.append(e)},d=()=>{let l;const s=t=>{t.preventDefault(),cityssm.postJSON(e+"/lots/doAddLotComment",t.currentTarget,t=>{t.success&&(n=t.lotComments,a(),l())})};cityssm.openHtmlModal("lot-addComment",{onshow(e){t.populateAliases(e),e.querySelector("#lotCommentAdd--lotId").value=o,e.querySelector("form").addEventListener("submit",s)},onshown(t,e){bulmaJS.toggleHtmlClipped(),l=e,t.querySelector("#lotCommentAdd--lotComment").focus()},onremoved(){bulmaJS.toggleHtmlClipped(),document.querySelector("#lotComments--add").focus()}})};l||(document.querySelector("#lotComments--add").addEventListener("click",d),a())})(); \ No newline at end of file diff --git a/routes/lots.js b/routes/lots.js index 1b69498b..aaad5517 100644 --- a/routes/lots.js +++ b/routes/lots.js @@ -5,9 +5,11 @@ import handler_doSearchLots from "../handlers/lots-post/doSearchLots.js"; import handler_view from "../handlers/lots-get/view.js"; import handler_new from "../handlers/lots-get/new.js"; import handler_edit from "../handlers/lots-get/edit.js"; -import handler_print from "../handlers/lots-get/print.js"; import handler_doCreateLot from "../handlers/lots-post/doCreateLot.js"; import handler_doUpdateLot from "../handlers/lots-post/doUpdateLot.js"; +import handler_doAddLotComment from "../handlers/lots-post/doAddLotComment.js"; +import handler_doUpdateLotComment from "../handlers/lots-post/doUpdateLotComment.js"; +import handler_doDeleteLotComment from "../handlers/lots-post/doDeleteLotComment.js"; export const router = Router(); router.get("/", handler_search); router.post("/doSearchLots", handler_doSearchLots); @@ -16,5 +18,7 @@ router.get("/:lotId", handler_view); router.get("/:lotId/edit", permissionHandlers.updateGetHandler, handler_edit); router.post("/doCreateLot", permissionHandlers.updatePostHandler, handler_doCreateLot); router.post("/doUpdateLot", permissionHandlers.updatePostHandler, handler_doUpdateLot); -router.get("/:lotId/print", handler_print); +router.post("/doAddLotComment", permissionHandlers.updatePostHandler, handler_doAddLotComment); +router.post("/doUpdateLotComment", permissionHandlers.updatePostHandler, handler_doUpdateLotComment); +router.post("/doDeleteLotComment", permissionHandlers.updatePostHandler, handler_doDeleteLotComment); export default router; diff --git a/routes/lots.ts b/routes/lots.ts index a84a35af..bf303680 100644 --- a/routes/lots.ts +++ b/routes/lots.ts @@ -8,11 +8,14 @@ import handler_doSearchLots from "../handlers/lots-post/doSearchLots.js"; import handler_view from "../handlers/lots-get/view.js"; import handler_new from "../handlers/lots-get/new.js"; import handler_edit from "../handlers/lots-get/edit.js"; -import handler_print from "../handlers/lots-get/print.js"; import handler_doCreateLot from "../handlers/lots-post/doCreateLot.js"; import handler_doUpdateLot from "../handlers/lots-post/doUpdateLot.js"; +import handler_doAddLotComment from "../handlers/lots-post/doAddLotComment.js"; +import handler_doUpdateLotComment from "../handlers/lots-post/doUpdateLotComment.js"; +import handler_doDeleteLotComment from "../handlers/lots-post/doDeleteLotComment.js"; + export const router = Router(); @@ -39,7 +42,6 @@ router.get("/new", handler_new); - router.get("/:lotId", handler_view); @@ -59,9 +61,19 @@ router.post("/doUpdateLot", handler_doUpdateLot); +router.post("/doAddLotComment", + permissionHandlers.updatePostHandler, + handler_doAddLotComment); -router.get("/:lotId/print", - handler_print); + +router.post("/doUpdateLotComment", + permissionHandlers.updatePostHandler, + handler_doUpdateLotComment); + + +router.post("/doDeleteLotComment", + permissionHandlers.updatePostHandler, + handler_doDeleteLotComment); export default router; diff --git a/views/lot-view.ejs b/views/lot-view.ejs index 4fc4431c..e0a4ae37 100644 --- a/views/lot-view.ejs +++ b/views/lot-view.ejs @@ -71,6 +71,35 @@
+<% if (lot.lotComments.length > 0) { %> +
+

Comments

+
+ + + + + + + + + + <% for (const lotComment of lot.lotComments) { %> + + + + + + <% } %> + +
CommentorComment DateComment
<%= lotComment.recordCreate_userName %> + <%= lotComment.lotCommentDateString %> + <%= (lotComment.lotCommentTime === 0 ? "" : lotComment.lotCommentTimeString) %> + <%= lotComment.lotComment %>
+
+
+<% } %> +

<%= configFunctions.getProperty("aliases.occupancies") %>