diff --git a/handlers/lotOccupancies-get/new.d.ts b/handlers/lotOccupancies-get/new.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lotOccupancies-get/new.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/lotOccupancies-get/new.js b/handlers/lotOccupancies-get/new.js new file mode 100644 index 00000000..8249fcff --- /dev/null +++ b/handlers/lotOccupancies-get/new.js @@ -0,0 +1,13 @@ +import { getOccupancyTypes } from "../../helpers/functions.cache.js"; +import * as configFunctions from "../../helpers/functions.config.js"; +export const handler = (request, response) => { + const lotOccupancy = {}; + const occupancyTypes = getOccupancyTypes(); + return response.render("lotOccupancy-edit", { + headTitle: "Create a New " + configFunctions.getProperty("aliases.lot") + " " + configFunctions.getProperty("aliases.occupancy") + " Record", + lotOccupancy, + occupancyTypes, + isCreate: true + }); +}; +export default handler; diff --git a/handlers/lotOccupancies-get/new.ts b/handlers/lotOccupancies-get/new.ts new file mode 100644 index 00000000..2554ae1a --- /dev/null +++ b/handlers/lotOccupancies-get/new.ts @@ -0,0 +1,33 @@ +import type { + RequestHandler +} from "express"; + +import { + getLotOccupantTypes, + getOccupancyTypes +} from "../../helpers/functions.cache.js"; + +import * as configFunctions from "../../helpers/functions.config.js"; + +import type * as recordTypes from "../../types/recordTypes"; + + +export const handler: RequestHandler = (request, response) => { + + const lotOccupancy: recordTypes.LotOccupancy = { + + }; + + const occupancyTypes = getOccupancyTypes(); + + return response.render("lotOccupancy-edit", { + headTitle: "Create a New " + configFunctions.getProperty("aliases.lot") + " " + configFunctions.getProperty("aliases.occupancy") + " Record", + lotOccupancy, + + occupancyTypes, + isCreate: true + }); +}; + + +export default handler; \ No newline at end of file diff --git a/handlers/lotOccupancies-post/doCreateLotOccupancy.d.ts b/handlers/lotOccupancies-post/doCreateLotOccupancy.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lotOccupancies-post/doCreateLotOccupancy.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/lotOccupancies-post/doCreateLotOccupancy.js b/handlers/lotOccupancies-post/doCreateLotOccupancy.js new file mode 100644 index 00000000..51d25141 --- /dev/null +++ b/handlers/lotOccupancies-post/doCreateLotOccupancy.js @@ -0,0 +1,9 @@ +import { addLotOccupancy } from "../../helpers/lotOccupancyDB/addLotOccupancy.js"; +export const handler = async (request, response) => { + const lotOccupancyId = addLotOccupancy(request.body, request.session); + response.json({ + success: true, + lotOccupancyId + }); +}; +export default handler; diff --git a/handlers/lotOccupancies-post/doCreateLotOccupancy.ts b/handlers/lotOccupancies-post/doCreateLotOccupancy.ts new file mode 100644 index 00000000..33ec0ac3 --- /dev/null +++ b/handlers/lotOccupancies-post/doCreateLotOccupancy.ts @@ -0,0 +1,21 @@ +import type { + RequestHandler +} from "express"; + +import { + addLotOccupancy +} from "../../helpers/lotOccupancyDB/addLotOccupancy.js"; + + +export const handler: RequestHandler = async (request, response) => { + + const lotOccupancyId = addLotOccupancy(request.body, request.session); + + response.json({ + success: true, + lotOccupancyId + }); +}; + + +export default handler; \ No newline at end of file diff --git a/handlers/lotOccupancies-post/doGetOccupancyTypeFields.d.ts b/handlers/lotOccupancies-post/doGetOccupancyTypeFields.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/lotOccupancies-post/doGetOccupancyTypeFields.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/lotOccupancies-post/doGetOccupancyTypeFields.js b/handlers/lotOccupancies-post/doGetOccupancyTypeFields.js new file mode 100644 index 00000000..a42925e4 --- /dev/null +++ b/handlers/lotOccupancies-post/doGetOccupancyTypeFields.js @@ -0,0 +1,8 @@ +import { getOccupancyTypeById } from "../../helpers/functions.cache.js"; +export const handler = async (request, response) => { + const result = getOccupancyTypeById(Number.parseInt(request.body.occupancyTypeId, 10)); + response.json({ + occupancyTypeFields: result.occupancyTypeFields + }); +}; +export default handler; diff --git a/handlers/lotOccupancies-post/doGetOccupancyTypeFields.ts b/handlers/lotOccupancies-post/doGetOccupancyTypeFields.ts new file mode 100644 index 00000000..fc20dc12 --- /dev/null +++ b/handlers/lotOccupancies-post/doGetOccupancyTypeFields.ts @@ -0,0 +1,18 @@ +import type { + RequestHandler +} from "express"; + +import { getOccupancyTypeById } from "../../helpers/functions.cache.js"; + + +export const handler: RequestHandler = async (request, response) => { + + const result = getOccupancyTypeById(Number.parseInt(request.body.occupancyTypeId, 10)); + + response.json({ + occupancyTypeFields: result.occupancyTypeFields + }); +}; + + +export default handler; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/addLotOccupancy.d.ts b/helpers/lotOccupancyDB/addLotOccupancy.d.ts index 8a5625ca..0ad517ce 100644 --- a/helpers/lotOccupancyDB/addLotOccupancy.d.ts +++ b/helpers/lotOccupancyDB/addLotOccupancy.d.ts @@ -4,6 +4,8 @@ interface AddLotOccupancyForm { lotId: string | number; occupancyStartDateString: string; occupancyEndDateString: string; + occupancyTypeFieldIds: string; + [lotOccupancyFieldValue_occupancyTypeFieldId: string]: unknown; } export declare const addLotOccupancy: (lotOccupancyForm: AddLotOccupancyForm, requestSession: recordTypes.PartialSession) => number; export default addLotOccupancy; diff --git a/helpers/lotOccupancyDB/addLotOccupancy.js b/helpers/lotOccupancyDB/addLotOccupancy.js index 7e2d2f5b..70400084 100644 --- a/helpers/lotOccupancyDB/addLotOccupancy.js +++ b/helpers/lotOccupancyDB/addLotOccupancy.js @@ -1,6 +1,7 @@ import sqlite from "better-sqlite3"; import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; import * as dateTimeFunctions from "@cityssm/expressjs-server-js/dateTimeFns.js"; +import { addOrUpdateLotOccupancyField } from "./addOrUpdateLotOccupancyField.js"; export const addLotOccupancy = (lotOccupancyForm, requestSession) => { const database = sqlite(databasePath); const rightNowMillis = Date.now(); @@ -20,7 +21,19 @@ export const addLotOccupancy = (lotOccupancyForm, requestSession) => { lotOccupancyForm.lotId), occupancyStartDate, (lotOccupancyForm.occupancyEndDateString === "" ? undefined : dateTimeFunctions.dateStringToInteger(lotOccupancyForm.occupancyEndDateString)), requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis); + const lotOccupancyId = result.lastInsertRowid; + const occupancyTypeFieldIds = lotOccupancyForm.occupancyTypeFieldIds.split(","); + for (const occupancyTypeFieldId of occupancyTypeFieldIds) { + const lotOccupancyFieldValue = lotOccupancyForm["lotOccupancyFieldValue_" + occupancyTypeFieldId]; + if (lotOccupancyFieldValue && lotOccupancyFieldValue !== "") { + addOrUpdateLotOccupancyField({ + lotOccupancyId, + occupancyTypeFieldId, + lotOccupancyFieldValue + }, requestSession, database); + } + } database.close(); - return result.lastInsertRowid; + return lotOccupancyId; }; export default addLotOccupancy; diff --git a/helpers/lotOccupancyDB/addLotOccupancy.ts b/helpers/lotOccupancyDB/addLotOccupancy.ts index 2ad72f64..cee7078b 100644 --- a/helpers/lotOccupancyDB/addLotOccupancy.ts +++ b/helpers/lotOccupancyDB/addLotOccupancy.ts @@ -1,59 +1,85 @@ import sqlite from "better-sqlite3"; + import { - lotOccupancyDB as databasePath + lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; import * as dateTimeFunctions from "@cityssm/expressjs-server-js/dateTimeFns.js"; +import { + addOrUpdateLotOccupancyField +} from "./addOrUpdateLotOccupancyField.js"; + import type * as recordTypes from "../../types/recordTypes"; interface AddLotOccupancyForm { - occupancyTypeId: string | number; - lotId: string | number; + occupancyTypeId: string | number; + lotId: string | number; - occupancyStartDateString: string; - occupancyEndDateString: string; + occupancyStartDateString: string; + occupancyEndDateString: string; + + occupancyTypeFieldIds: string; + [lotOccupancyFieldValue_occupancyTypeFieldId: string]: unknown; } export const addLotOccupancy = - (lotOccupancyForm: AddLotOccupancyForm, requestSession: recordTypes.PartialSession): number => { + (lotOccupancyForm: AddLotOccupancyForm, requestSession: recordTypes.PartialSession): number => { - const database = sqlite(databasePath); + const database = sqlite(databasePath); - const rightNowMillis = Date.now(); + const rightNowMillis = Date.now(); - const occupancyStartDate = dateTimeFunctions.dateStringToInteger(lotOccupancyForm.occupancyStartDateString); + const occupancyStartDate = dateTimeFunctions.dateStringToInteger(lotOccupancyForm.occupancyStartDateString); - if (occupancyStartDate <= 0) { - console.error(lotOccupancyForm); - } + if (occupancyStartDate <= 0) { + console.error(lotOccupancyForm); + } - const result = database - .prepare("insert into LotOccupancies (" + - "occupancyTypeId, lotId," + - " occupancyStartDate, occupancyEndDate," + - " recordCreate_userName, recordCreate_timeMillis," + - " recordUpdate_userName, recordUpdate_timeMillis)" + - " values (?, ?, ?, ?, ?, ?, ?, ?)") - .run(lotOccupancyForm.occupancyTypeId, - (lotOccupancyForm.lotId === "" ? - undefined : - lotOccupancyForm.lotId), - occupancyStartDate, - (lotOccupancyForm.occupancyEndDateString === "" ? - undefined : - dateTimeFunctions.dateStringToInteger(lotOccupancyForm.occupancyEndDateString)), - requestSession.user.userName, - rightNowMillis, - requestSession.user.userName, - rightNowMillis); + const result = database + .prepare("insert into LotOccupancies (" + + "occupancyTypeId, lotId," + + " occupancyStartDate, occupancyEndDate," + + " recordCreate_userName, recordCreate_timeMillis," + + " recordUpdate_userName, recordUpdate_timeMillis)" + + " values (?, ?, ?, ?, ?, ?, ?, ?)") + .run(lotOccupancyForm.occupancyTypeId, + (lotOccupancyForm.lotId === "" ? + undefined : + lotOccupancyForm.lotId), + occupancyStartDate, + (lotOccupancyForm.occupancyEndDateString === "" ? + undefined : + dateTimeFunctions.dateStringToInteger(lotOccupancyForm.occupancyEndDateString)), + requestSession.user.userName, + rightNowMillis, + requestSession.user.userName, + rightNowMillis); - database.close(); + const lotOccupancyId = result.lastInsertRowid as number; - return result.lastInsertRowid as number; - }; + const occupancyTypeFieldIds = lotOccupancyForm.occupancyTypeFieldIds.split(","); + + for (const occupancyTypeFieldId of occupancyTypeFieldIds) { + + const lotOccupancyFieldValue = lotOccupancyForm["lotOccupancyFieldValue_" + occupancyTypeFieldId] as string; + + if (lotOccupancyFieldValue && lotOccupancyFieldValue !== "") { + addOrUpdateLotOccupancyField({ + lotOccupancyId, + occupancyTypeFieldId, + lotOccupancyFieldValue + }, requestSession, database); + } + } + + + database.close(); + + return lotOccupancyId; + }; export default addLotOccupancy; \ No newline at end of file diff --git a/helpers/lotOccupancyDB/updateLotOccupancy.ts b/helpers/lotOccupancyDB/updateLotOccupancy.ts index dffacdb3..e41a4876 100644 --- a/helpers/lotOccupancyDB/updateLotOccupancy.ts +++ b/helpers/lotOccupancyDB/updateLotOccupancy.ts @@ -11,6 +11,7 @@ import { import { addOrUpdateLotOccupancyField } from "./addOrUpdateLotOccupancyField.js"; + import { deleteLotOccupancyField } from "./deleteLotOccupancyField.js"; diff --git a/public-typescript/lotOccupancyEdit.js b/public-typescript/lotOccupancyEdit.js index 6c815de5..59fb5df6 100644 --- a/public-typescript/lotOccupancyEdit.js +++ b/public-typescript/lotOccupancyEdit.js @@ -46,8 +46,53 @@ Object.defineProperty(exports, "__esModule", { value: true }); for (const formInputElement of formInputElements) { formInputElement.addEventListener("change", setUnsavedChanges); } - if (!isCreate) { - const occupancyTypeIdElement = document.querySelector("#lotOccupancy--occupancyTypeId"); + const occupancyTypeIdElement = document.querySelector("#lotOccupancy--occupancyTypeId"); + if (isCreate) { + const lotOccupancyFieldsContainerElement = document.querySelector("#container--lotOccupancyFields"); + occupancyTypeIdElement.addEventListener("change", () => { + if (occupancyTypeIdElement.value === "") { + lotOccupancyFieldsContainerElement.innerHTML = "
"; + return; + } + cityssm.postJSON(urlPrefix + "/lotOccupancies/doGetOccupancyTypeFields", { + occupancyTypeId: occupancyTypeIdElement.value + }, (responseJSON) => { + if (responseJSON.occupancyTypeFields.length === 0) { + lotOccupancyFieldsContainerElement.innerHTML = ""; + return; + } + lotOccupancyFieldsContainerElement.innerHTML = ""; + let occupancyTypeFieldIds = ""; + for (const occupancyTypeField of responseJSON.occupancyTypeFields) { + occupancyTypeFieldIds += "," + occupancyTypeField.occupancyTypeFieldId; + const fieldElement = document.createElement("div"); + fieldElement.className = "field"; + fieldElement.innerHTML = "" + + ""; + fieldElement.querySelector("label").textContent = occupancyTypeField.occupancyTypeField; + const inputElement = document.createElement("input"); + inputElement.className = "input"; + inputElement.id = "lotOccupancy--lotOccupancyFieldValue_" + occupancyTypeField.occupancyTypeFieldId; + inputElement.name = "lotOccupancyFieldValue_" + occupancyTypeField.occupancyTypeFieldId; + inputElement.type = "text"; + inputElement.required = occupancyTypeField.isRequired; + inputElement.minLength = occupancyTypeField.minimumLength; + inputElement.maxLength = occupancyTypeField.maximumLength; + if (occupancyTypeField.pattern && occupancyTypeField.pattern !== "") { + inputElement.pattern = occupancyTypeField.pattern; + } + fieldElement.querySelector(".control").append(inputElement); + lotOccupancyFieldsContainerElement.append(fieldElement); + } + lotOccupancyFieldsContainerElement.insertAdjacentHTML("beforeend", ""); + }); + }); + } + else { const originalOccupancyTypeId = occupancyTypeIdElement.value; occupancyTypeIdElement.addEventListener("change", () => { if (occupancyTypeIdElement.value !== originalOccupancyTypeId) { diff --git a/public-typescript/lotOccupancyEdit.ts b/public-typescript/lotOccupancyEdit.ts index cf438f00..ac14237b 100644 --- a/public-typescript/lotOccupancyEdit.ts +++ b/public-typescript/lotOccupancyEdit.ts @@ -8,7 +8,8 @@ import type { } from "@cityssm/bulma-webapp-js/src/types"; import type { - BulmaJS, StringConfigProperties + BulmaJS, + StringConfigProperties } from "@cityssm/bulma-js/types"; declare const cityssm: cityssmGlobal; @@ -86,9 +87,77 @@ declare const bulmaJS: BulmaJS; // Occupancy Type - if (!isCreate) { + const occupancyTypeIdElement = document.querySelector("#lotOccupancy--occupancyTypeId") as HTMLSelectElement; - const occupancyTypeIdElement = document.querySelector("#lotOccupancy--occupancyTypeId") as HTMLSelectElement; + if (isCreate) { + + const lotOccupancyFieldsContainerElement = document.querySelector("#container--lotOccupancyFields") as HTMLElement; + + occupancyTypeIdElement.addEventListener("change", () => { + + if (occupancyTypeIdElement.value === "") { + lotOccupancyFieldsContainerElement.innerHTML = ""; + + return + } + + cityssm.postJSON(urlPrefix + "/lotOccupancies/doGetOccupancyTypeFields", { + occupancyTypeId: occupancyTypeIdElement.value + }, + (responseJSON: { + occupancyTypeFields: recordTypes.OccupancyTypeField[] + }) => { + + if (responseJSON.occupancyTypeFields.length === 0) { + lotOccupancyFieldsContainerElement.innerHTML = ""; + + return + } + + lotOccupancyFieldsContainerElement.innerHTML = ""; + + let occupancyTypeFieldIds = ""; + + for (const occupancyTypeField of responseJSON.occupancyTypeFields) { + + occupancyTypeFieldIds += "," + occupancyTypeField.occupancyTypeFieldId; + + const fieldElement = document.createElement("div"); + fieldElement.className = "field"; + fieldElement.innerHTML = "" + + ""; + + fieldElement.querySelector("label").textContent = occupancyTypeField.occupancyTypeField; + + const inputElement = document.createElement("input"); + inputElement.className = "input"; + inputElement.id = "lotOccupancy--lotOccupancyFieldValue_" + occupancyTypeField.occupancyTypeFieldId; + inputElement.name = "lotOccupancyFieldValue_" + occupancyTypeField.occupancyTypeFieldId; + inputElement.type = "text"; + + inputElement.required = occupancyTypeField.isRequired; + inputElement.minLength = occupancyTypeField.minimumLength; + inputElement.maxLength = occupancyTypeField.maximumLength; + + if (occupancyTypeField.pattern && occupancyTypeField.pattern !== "") { + inputElement.pattern = occupancyTypeField.pattern; + } + + fieldElement.querySelector(".control").append(inputElement); + + lotOccupancyFieldsContainerElement.append(fieldElement); + } + + lotOccupancyFieldsContainerElement.insertAdjacentHTML("beforeend", + ""); + }); + }); + + } else { const originalOccupancyTypeId = occupancyTypeIdElement.value; @@ -264,8 +333,10 @@ declare const bulmaJS: BulmaJS; submitEvent.preventDefault(); cityssm.postJSON(urlPrefix + "/lotOccupancies/doUpdateLotOccupancyOccupant", - editFormElement, - (responseJSON: { success: boolean; errorMessage?: string; lotOccupancyOccupants?: recordTypes.LotOccupancyOccupant[]; }) => { + editFormElement, + (responseJSON: { + success: boolean;errorMessage ? : string;lotOccupancyOccupants ? : recordTypes.LotOccupancyOccupant[]; + }) => { if (responseJSON.success) { lotOccupancyOccupants = responseJSON.lotOccupancyOccupants; @@ -344,21 +415,23 @@ declare const bulmaJS: BulmaJS; const doDelete = () => { cityssm.postJSON(urlPrefix + "/lotOccupancies/doDeleteLotOccupancyOccupant", { - lotOccupancyId, - lotOccupantIndex - }, - (responseJSON: { success: boolean; errorMessage?: string; lotOccupancyOccupants: recordTypes.LotOccupancyOccupant[];}) => { - if (responseJSON.success) { - lotOccupancyOccupants = responseJSON.lotOccupancyOccupants; - renderLotOccupancyOccupants(); - } else { - bulmaJS.alert({ - title: "Error Removing " + exports.aliases.occupant, - message: responseJSON.errorMessage, - contextualColorName: "danger" - }); - } - }); + lotOccupancyId, + lotOccupantIndex + }, + (responseJSON: { + success: boolean;errorMessage ? : string;lotOccupancyOccupants: recordTypes.LotOccupancyOccupant[]; + }) => { + if (responseJSON.success) { + lotOccupancyOccupants = responseJSON.lotOccupancyOccupants; + renderLotOccupancyOccupants(); + } else { + bulmaJS.alert({ + title: "Error Removing " + exports.aliases.occupant, + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); }; bulmaJS.confirm({ @@ -444,7 +517,9 @@ declare const bulmaJS: BulmaJS; cityssm.postJSON(urlPrefix + "/lotOccupancies/doAddLotOccupancyOccupant", addFormElement, - (responseJSON: { success: boolean; errorMessage?: string; lotOccupancyOccupants?: recordTypes.LotOccupancyOccupant[]; }) => { + (responseJSON: { + success: boolean;errorMessage ? : string;lotOccupancyOccupants ? : recordTypes.LotOccupancyOccupant[]; + }) => { if (responseJSON.success) { lotOccupancyOccupants = responseJSON.lotOccupancyOccupants; diff --git a/public/javascripts/lotOccupancyEdit.min.js b/public/javascripts/lotOccupancyEdit.min.js index 11ef1f09..eaa45e35 100644 --- a/public/javascripts/lotOccupancyEdit.min.js +++ b/public/javascripts/lotOccupancyEdit.min.js @@ -1 +1 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=exports.los,t=document.querySelector("main").dataset.urlPrefix,c=document.querySelector("#lotOccupancy--lotOccupancyId").value,a=""===c;let o=!1,n=a;const s=()=>{o||(o=!0,cityssm.enableNavBlocker())},l=document.querySelector("#form--lotOccupancy");l.addEventListener("submit",e=>{e.preventDefault(),cityssm.postJSON(t+"/lotOccupancies/"+(a?"doCreateLotOccupancy":"doUpdateLotOccupancy"),l,e=>{e.success?(o=!1,cityssm.disableNavBlocker(),a||n?window.location.href=t+"/lotOccupancies/"+e.lotOccupancyId+"/edit?t="+Date.now():bulmaJS.alert({message:exports.aliases.occupancy+" Updated Successfully",contextualColorName:"success"})):bulmaJS.alert({title:"Error Saving "+exports.aliases.occupancy,message:e.errorMessage,contextualColorName:"danger"})})});const u=l.querySelectorAll("input, select");for(const e of u)e.addEventListener("change",s);if(!a){const e=document.querySelector("#lotOccupancy--occupancyTypeId"),t=e.value;e.addEventListener("change",()=>{e.value!==t&&bulmaJS.confirm({title:"Confirm Change",message:"Are you sure you want to change the "+exports.aliases.occupancy.toLowerCase()+" type?\nThis change affects the additional fields associated with this record, and may also affect the available fees.",contextualColorName:"warning",okButton:{text:"Yes, Keep the Change",callbackFunction:()=>{n=!0}},cancelButton:{text:"Revert the Change",callbackFunction:()=>{e.value=t}}})})}if(document.querySelector("#lotOccupancy--lotName").addEventListener("click",c=>{const a=c.currentTarget.value;let o,n,l;const u=e=>{e.preventDefault();const t=e.currentTarget;document.querySelector("#lotOccupancy--lotId").value=t.dataset.lotId,document.querySelector("#lotOccupancy--lotName").value=t.dataset.lotName,s(),o()},r=()=>{l.innerHTML='
Searching...
Searching...