diff --git a/handlers/workOrders-get/edit.js b/handlers/workOrders-get/edit.js
index 2d53fdca..4f5bc25d 100644
--- a/handlers/workOrders-get/edit.js
+++ b/handlers/workOrders-get/edit.js
@@ -1,4 +1,4 @@
-import { getWorkOrderTypes } from "../../helpers/functions.cache.js";
+import { getLotStatuses, getWorkOrderTypes } from "../../helpers/functions.cache.js";
import * as configFunctions from "../../helpers/functions.config.js";
import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js";
export const handler = (request, response) => {
@@ -9,14 +9,18 @@ export const handler = (request, response) => {
}
if (workOrder.workOrderCloseDate) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") +
- "/workOrders/" + workOrder.workOrderId.toString() + "/?error=workOrderIsClosed");
+ "/workOrders/" +
+ workOrder.workOrderId.toString() +
+ "/?error=workOrderIsClosed");
}
const workOrderTypes = getWorkOrderTypes();
+ const lotStatuses = getLotStatuses();
response.render("workOrder-edit", {
headTitle: "Work Order #" + workOrder.workOrderNumber,
workOrder,
isCreate: false,
- workOrderTypes
+ workOrderTypes,
+ lotStatuses
});
};
export default handler;
diff --git a/handlers/workOrders-get/edit.ts b/handlers/workOrders-get/edit.ts
index f4cb7953..c986543d 100644
--- a/handlers/workOrders-get/edit.ts
+++ b/handlers/workOrders-get/edit.ts
@@ -1,5 +1,9 @@
import type { RequestHandler } from "express";
-import { getWorkOrderTypes } from "../../helpers/functions.cache.js";
+
+import {
+ getLotStatuses,
+ getWorkOrderTypes
+} from "../../helpers/functions.cache.js";
import * as configFunctions from "../../helpers/functions.config.js";
@@ -18,17 +22,22 @@ export const handler: RequestHandler = (request, response) => {
if (workOrder.workOrderCloseDate) {
return response.redirect(
configFunctions.getProperty("reverseProxy.urlPrefix") +
- "/workOrders/" + workOrder.workOrderId.toString() + "/?error=workOrderIsClosed"
+ "/workOrders/" +
+ workOrder.workOrderId.toString() +
+ "/?error=workOrderIsClosed"
);
}
const workOrderTypes = getWorkOrderTypes();
+ const lotStatuses = getLotStatuses();
+
response.render("workOrder-edit", {
headTitle: "Work Order #" + workOrder.workOrderNumber,
workOrder,
isCreate: false,
- workOrderTypes
+ workOrderTypes,
+ lotStatuses
});
};
diff --git a/handlers/workOrders-post/doAddWorkOrderLot.d.ts b/handlers/workOrders-post/doAddWorkOrderLot.d.ts
new file mode 100644
index 00000000..9621c611
--- /dev/null
+++ b/handlers/workOrders-post/doAddWorkOrderLot.d.ts
@@ -0,0 +1,3 @@
+import type { RequestHandler } from "express";
+export declare const handler: RequestHandler;
+export default handler;
diff --git a/handlers/workOrders-post/doAddWorkOrderLot.js b/handlers/workOrders-post/doAddWorkOrderLot.js
new file mode 100644
index 00000000..cc05bbdb
--- /dev/null
+++ b/handlers/workOrders-post/doAddWorkOrderLot.js
@@ -0,0 +1,19 @@
+import { addWorkOrderLot } from "../../helpers/lotOccupancyDB/addWorkOrderLot.js";
+import { getLots } from "../../helpers/lotOccupancyDB/getLots.js";
+export const handler = async (request, response) => {
+ const success = addWorkOrderLot({
+ workOrderId: request.body.workOrderId,
+ lotId: request.body.lotId
+ }, request.session);
+ const workOrderLots = getLots({
+ workOrderId: request.body.workOrderId
+ }, {
+ limit: -1,
+ offset: 0
+ }).lots;
+ response.json({
+ success,
+ workOrderLots
+ });
+};
+export default handler;
diff --git a/handlers/workOrders-post/doAddWorkOrderLot.ts b/handlers/workOrders-post/doAddWorkOrderLot.ts
new file mode 100644
index 00000000..d671e381
--- /dev/null
+++ b/handlers/workOrders-post/doAddWorkOrderLot.ts
@@ -0,0 +1,31 @@
+import type { RequestHandler } from "express";
+
+import { addWorkOrderLot } from "../../helpers/lotOccupancyDB/addWorkOrderLot.js";
+import { getLots } from "../../helpers/lotOccupancyDB/getLots.js";
+
+export const handler: RequestHandler = async (request, response) => {
+ const success = addWorkOrderLot(
+ {
+ workOrderId: request.body.workOrderId,
+ lotId: request.body.lotId
+ },
+ request.session
+ );
+
+ const workOrderLots = getLots(
+ {
+ workOrderId: request.body.workOrderId
+ },
+ {
+ limit: -1,
+ offset: 0
+ }
+ ).lots;
+
+ response.json({
+ success,
+ workOrderLots
+ });
+};
+
+export default handler;
diff --git a/handlers/workOrders-post/doAddWorkOrderLotOccupancy.d.ts b/handlers/workOrders-post/doAddWorkOrderLotOccupancy.d.ts
new file mode 100644
index 00000000..9621c611
--- /dev/null
+++ b/handlers/workOrders-post/doAddWorkOrderLotOccupancy.d.ts
@@ -0,0 +1,3 @@
+import type { RequestHandler } from "express";
+export declare const handler: RequestHandler;
+export default handler;
diff --git a/handlers/workOrders-post/doAddWorkOrderLotOccupancy.js b/handlers/workOrders-post/doAddWorkOrderLotOccupancy.js
new file mode 100644
index 00000000..e6e3b2af
--- /dev/null
+++ b/handlers/workOrders-post/doAddWorkOrderLotOccupancy.js
@@ -0,0 +1,20 @@
+import { addWorkOrderLotOccupancy } from "../../helpers/lotOccupancyDB/addWorkOrderLotOccupancy.js";
+import { getLotOccupancies } from "../../helpers/lotOccupancyDB/getLotOccupancies.js";
+export const handler = async (request, response) => {
+ const success = addWorkOrderLotOccupancy({
+ workOrderId: request.body.workOrderId,
+ lotOccupancyId: request.body.lotOccupancyId
+ }, request.session);
+ const workOrderLotOccupancies = getLotOccupancies({
+ workOrderId: request.body.workOrderId
+ }, {
+ limit: -1,
+ offset: 0,
+ includeOccupants: true
+ }).lotOccupancies;
+ response.json({
+ success,
+ workOrderLotOccupancies
+ });
+};
+export default handler;
diff --git a/handlers/workOrders-post/doAddWorkOrderLotOccupancy.ts b/handlers/workOrders-post/doAddWorkOrderLotOccupancy.ts
new file mode 100644
index 00000000..d8fc276a
--- /dev/null
+++ b/handlers/workOrders-post/doAddWorkOrderLotOccupancy.ts
@@ -0,0 +1,32 @@
+import type { RequestHandler } from "express";
+
+import { addWorkOrderLotOccupancy } from "../../helpers/lotOccupancyDB/addWorkOrderLotOccupancy.js";
+import { getLotOccupancies } from "../../helpers/lotOccupancyDB/getLotOccupancies.js";
+
+export const handler: RequestHandler = async (request, response) => {
+ const success = addWorkOrderLotOccupancy(
+ {
+ workOrderId: request.body.workOrderId,
+ lotOccupancyId: request.body.lotOccupancyId
+ },
+ request.session
+ );
+
+ const workOrderLotOccupancies = getLotOccupancies(
+ {
+ workOrderId: request.body.workOrderId
+ },
+ {
+ limit: -1,
+ offset: 0,
+ includeOccupants: true
+ }
+ ).lotOccupancies;
+
+ response.json({
+ success,
+ workOrderLotOccupancies
+ });
+};
+
+export default handler;
diff --git a/handlers/workOrders-post/doCompleteWorkOrderMilestone.d.ts b/handlers/workOrders-post/doCompleteWorkOrderMilestone.d.ts
new file mode 100644
index 00000000..9621c611
--- /dev/null
+++ b/handlers/workOrders-post/doCompleteWorkOrderMilestone.d.ts
@@ -0,0 +1,3 @@
+import type { RequestHandler } from "express";
+export declare const handler: RequestHandler;
+export default handler;
diff --git a/handlers/workOrders-post/doCompleteWorkOrderMilestone.js b/handlers/workOrders-post/doCompleteWorkOrderMilestone.js
new file mode 100644
index 00000000..0e18c572
--- /dev/null
+++ b/handlers/workOrders-post/doCompleteWorkOrderMilestone.js
@@ -0,0 +1,13 @@
+import { completeWorkOrderMilestone } from "../../helpers/lotOccupancyDB/completeWorkOrderMiletstone.js";
+import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrderMilestones.js";
+export const handler = async (request, response) => {
+ const success = completeWorkOrderMilestone({
+ workOrderMilestoneId: request.body.workOrderMilestoneId
+ }, request.session);
+ const workOrderMilestones = getWorkOrderMilestones(request.body.workOrderId);
+ response.json({
+ success,
+ workOrderMilestones
+ });
+};
+export default handler;
diff --git a/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts b/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts
new file mode 100644
index 00000000..324c0550
--- /dev/null
+++ b/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts
@@ -0,0 +1,25 @@
+import type { RequestHandler } from "express";
+
+import { completeWorkOrderMilestone } from "../../helpers/lotOccupancyDB/completeWorkOrderMiletstone.js";
+
+import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrderMilestones.js";
+
+export const handler: RequestHandler = async (request, response) => {
+ const success = completeWorkOrderMilestone(
+ {
+ workOrderMilestoneId: request.body.workOrderMilestoneId
+ },
+ request.session
+ );
+
+ const workOrderMilestones = getWorkOrderMilestones(
+ request.body.workOrderId
+ );
+
+ response.json({
+ success,
+ workOrderMilestones
+ });
+};
+
+export default handler;
diff --git a/handlers/workOrders-post/doReopenWorkOrderMilestone.d.ts b/handlers/workOrders-post/doReopenWorkOrderMilestone.d.ts
new file mode 100644
index 00000000..9621c611
--- /dev/null
+++ b/handlers/workOrders-post/doReopenWorkOrderMilestone.d.ts
@@ -0,0 +1,3 @@
+import type { RequestHandler } from "express";
+export declare const handler: RequestHandler;
+export default handler;
diff --git a/handlers/workOrders-post/doReopenWorkOrderMilestone.js b/handlers/workOrders-post/doReopenWorkOrderMilestone.js
new file mode 100644
index 00000000..8ea0cd7e
--- /dev/null
+++ b/handlers/workOrders-post/doReopenWorkOrderMilestone.js
@@ -0,0 +1,11 @@
+import { reopenWorkOrderMilestone } from "../../helpers/lotOccupancyDB/reopenWorkOrderMilestone.js";
+import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrderMilestones.js";
+export const handler = async (request, response) => {
+ const success = reopenWorkOrderMilestone(request.body.workOrderMilestoneId, request.session);
+ const workOrderMilestones = getWorkOrderMilestones(request.body.workOrderId);
+ response.json({
+ success,
+ workOrderMilestones
+ });
+};
+export default handler;
diff --git a/handlers/workOrders-post/doReopenWorkOrderMilestone.ts b/handlers/workOrders-post/doReopenWorkOrderMilestone.ts
new file mode 100644
index 00000000..72dc1250
--- /dev/null
+++ b/handlers/workOrders-post/doReopenWorkOrderMilestone.ts
@@ -0,0 +1,23 @@
+import type { RequestHandler } from "express";
+
+import { reopenWorkOrderMilestone } from "../../helpers/lotOccupancyDB/reopenWorkOrderMilestone.js";
+
+import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrderMilestones.js";
+
+export const handler: RequestHandler = async (request, response) => {
+ const success = reopenWorkOrderMilestone(
+ request.body.workOrderMilestoneId,
+ request.session
+ );
+
+ const workOrderMilestones = getWorkOrderMilestones(
+ request.body.workOrderId
+ );
+
+ response.json({
+ success,
+ workOrderMilestones
+ });
+};
+
+export default handler;
diff --git a/helpers/lotOccupancyDB/addWorkOrderLotOccupancy.js b/helpers/lotOccupancyDB/addWorkOrderLotOccupancy.js
index 30527029..809fdc5f 100644
--- a/helpers/lotOccupancyDB/addWorkOrderLotOccupancy.js
+++ b/helpers/lotOccupancyDB/addWorkOrderLotOccupancy.js
@@ -3,14 +3,37 @@ import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const addWorkOrderLotOccupancy = (workOrderLotOccupancyForm, requestSession) => {
const database = sqlite(databasePath);
const rightNowMillis = Date.now();
- const result = database
- .prepare("insert into WorkOrderLotOccupancies (" +
- "workOrderId, lotOccupancyId," +
- " recordCreate_userName, recordCreate_timeMillis," +
- " recordUpdate_userName, recordUpdate_timeMillis)" +
- " values (?, ?, ?, ?, ?, ?)")
- .run(workOrderLotOccupancyForm.workOrderId, workOrderLotOccupancyForm.lotOccupancyId, requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis);
+ const row = database
+ .prepare("select recordDelete_timeMillis" +
+ " from WorkOrderLotOccupancies" +
+ " where workOrderId = ?" +
+ " and lotOccupancyId = ?")
+ .get(workOrderLotOccupancyForm.workOrderId, workOrderLotOccupancyForm.lotOccupancyId);
+ if (row) {
+ if (row.recordDelete_timeMillis) {
+ database
+ .prepare("update WorkOrderLotOccupancies" +
+ " set recordCreate_userName = ?," +
+ " recordCreate_timeMillis = ?," +
+ " recordUpdate_userName = ?," +
+ " recordUpdate_timeMillis = ?," +
+ " recordDelete_userName = null," +
+ " recordDelete_timeMillis = null" +
+ " where workOrderId = ?" +
+ " and lotOccupancyId = ?")
+ .run(requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis, workOrderLotOccupancyForm.workOrderId, workOrderLotOccupancyForm.lotOccupancyId);
+ }
+ }
+ else {
+ database
+ .prepare("insert into WorkOrderLotOccupancies (" +
+ "workOrderId, lotOccupancyId," +
+ " recordCreate_userName, recordCreate_timeMillis," +
+ " recordUpdate_userName, recordUpdate_timeMillis)" +
+ " values (?, ?, ?, ?, ?, ?)")
+ .run(workOrderLotOccupancyForm.workOrderId, workOrderLotOccupancyForm.lotOccupancyId, requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis);
+ }
database.close();
- return result.changes > 0;
+ return true;
};
export default addWorkOrderLotOccupancy;
diff --git a/helpers/lotOccupancyDB/addWorkOrderLotOccupancy.ts b/helpers/lotOccupancyDB/addWorkOrderLotOccupancy.ts
index 01652e39..34ccfe84 100644
--- a/helpers/lotOccupancyDB/addWorkOrderLotOccupancy.ts
+++ b/helpers/lotOccupancyDB/addWorkOrderLotOccupancy.ts
@@ -17,26 +17,63 @@ export const addWorkOrderLotOccupancy = (
const rightNowMillis = Date.now();
- const result = database
+ const row: { recordDelete_timeMillis?: number } = database
.prepare(
- "insert into WorkOrderLotOccupancies (" +
- "workOrderId, lotOccupancyId," +
- " recordCreate_userName, recordCreate_timeMillis," +
- " recordUpdate_userName, recordUpdate_timeMillis)" +
- " values (?, ?, ?, ?, ?, ?)"
+ "select recordDelete_timeMillis" +
+ " from WorkOrderLotOccupancies" +
+ " where workOrderId = ?" +
+ " and lotOccupancyId = ?"
)
- .run(
+ .get(
workOrderLotOccupancyForm.workOrderId,
- workOrderLotOccupancyForm.lotOccupancyId,
- requestSession.user.userName,
- rightNowMillis,
- requestSession.user.userName,
- rightNowMillis
+ workOrderLotOccupancyForm.lotOccupancyId
);
+ if (row) {
+ if (row.recordDelete_timeMillis) {
+ database
+ .prepare(
+ "update WorkOrderLotOccupancies" +
+ " set recordCreate_userName = ?," +
+ " recordCreate_timeMillis = ?," +
+ " recordUpdate_userName = ?," +
+ " recordUpdate_timeMillis = ?," +
+ " recordDelete_userName = null," +
+ " recordDelete_timeMillis = null" +
+ " where workOrderId = ?" +
+ " and lotOccupancyId = ?"
+ )
+ .run(
+ requestSession.user.userName,
+ rightNowMillis,
+ requestSession.user.userName,
+ rightNowMillis,
+ workOrderLotOccupancyForm.workOrderId,
+ workOrderLotOccupancyForm.lotOccupancyId
+ );
+ }
+ } else {
+ database
+ .prepare(
+ "insert into WorkOrderLotOccupancies (" +
+ "workOrderId, lotOccupancyId," +
+ " recordCreate_userName, recordCreate_timeMillis," +
+ " recordUpdate_userName, recordUpdate_timeMillis)" +
+ " values (?, ?, ?, ?, ?, ?)"
+ )
+ .run(
+ workOrderLotOccupancyForm.workOrderId,
+ workOrderLotOccupancyForm.lotOccupancyId,
+ requestSession.user.userName,
+ rightNowMillis,
+ requestSession.user.userName,
+ rightNowMillis
+ );
+ }
+
database.close();
- return result.changes > 0;
+ return true;
};
export default addWorkOrderLotOccupancy;
diff --git a/helpers/lotOccupancyDB/completeWorkOrderMiletstone.d.ts b/helpers/lotOccupancyDB/completeWorkOrderMiletstone.d.ts
new file mode 100644
index 00000000..f9da97bf
--- /dev/null
+++ b/helpers/lotOccupancyDB/completeWorkOrderMiletstone.d.ts
@@ -0,0 +1,8 @@
+import type * as recordTypes from "../../types/recordTypes";
+interface CompleteWorkOrderMilestoneForm {
+ workOrderMilestoneId: string | number;
+ workOrderMilestoneCompletionDateString?: string;
+ workOrderMilestoneCompletionTimeString?: string;
+}
+export declare const completeWorkOrderMilestone: (milestoneForm: CompleteWorkOrderMilestoneForm, requestSession: recordTypes.PartialSession) => boolean;
+export default completeWorkOrderMilestone;
diff --git a/helpers/lotOccupancyDB/completeWorkOrderMiletstone.js b/helpers/lotOccupancyDB/completeWorkOrderMiletstone.js
new file mode 100644
index 00000000..07b3b1bd
--- /dev/null
+++ b/helpers/lotOccupancyDB/completeWorkOrderMiletstone.js
@@ -0,0 +1,22 @@
+import sqlite from "better-sqlite3";
+import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
+import { dateStringToInteger, dateToInteger, dateToTimeInteger, timeStringToInteger } from "@cityssm/expressjs-server-js/dateTimeFns.js";
+export const completeWorkOrderMilestone = (milestoneForm, requestSession) => {
+ const rightNow = new Date();
+ const database = sqlite(databasePath);
+ const result = database
+ .prepare("update WorkOrderMilestones" +
+ " set workOrderMilestoneCompletionDate = ?," +
+ " workOrderMilestoneCompletionTime = ?," +
+ " recordUpdate_userName = ?," +
+ " recordUpdate_timeMillis = ?" +
+ " where workOrderMilestoneId = ?")
+ .run(milestoneForm.workOrderMilestoneCompletionDateString
+ ? dateStringToInteger(milestoneForm.workOrderMilestoneCompletionDateString)
+ : dateToInteger(rightNow), milestoneForm.workOrderMilestoneCompletionTimeString
+ ? timeStringToInteger(milestoneForm.workOrderMilestoneCompletionTimeString)
+ : dateToTimeInteger(rightNow), requestSession.user.userName, rightNow.getTime(), milestoneForm.workOrderMilestoneId);
+ database.close();
+ return result.changes > 0;
+};
+export default completeWorkOrderMilestone;
diff --git a/helpers/lotOccupancyDB/completeWorkOrderMiletstone.ts b/helpers/lotOccupancyDB/completeWorkOrderMiletstone.ts
new file mode 100644
index 00000000..4152de7e
--- /dev/null
+++ b/helpers/lotOccupancyDB/completeWorkOrderMiletstone.ts
@@ -0,0 +1,58 @@
+import sqlite from "better-sqlite3";
+
+import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
+
+import {
+ dateStringToInteger,
+ dateToInteger,
+ dateToTimeInteger,
+ timeStringToInteger
+} from "@cityssm/expressjs-server-js/dateTimeFns.js";
+
+import type * as recordTypes from "../../types/recordTypes";
+
+interface CompleteWorkOrderMilestoneForm {
+ workOrderMilestoneId: string | number;
+ workOrderMilestoneCompletionDateString?: string;
+ workOrderMilestoneCompletionTimeString?: string;
+}
+
+export const completeWorkOrderMilestone = (
+ milestoneForm: CompleteWorkOrderMilestoneForm,
+ requestSession: recordTypes.PartialSession
+): boolean => {
+ const rightNow = new Date();
+
+ const database = sqlite(databasePath);
+
+ const result = database
+ .prepare(
+ "update WorkOrderMilestones" +
+ " set workOrderMilestoneCompletionDate = ?," +
+ " workOrderMilestoneCompletionTime = ?," +
+ " recordUpdate_userName = ?," +
+ " recordUpdate_timeMillis = ?" +
+ " where workOrderMilestoneId = ?"
+ )
+ .run(
+ milestoneForm.workOrderMilestoneCompletionDateString
+ ? dateStringToInteger(
+ milestoneForm.workOrderMilestoneCompletionDateString
+ )
+ : dateToInteger(rightNow),
+ milestoneForm.workOrderMilestoneCompletionTimeString
+ ? timeStringToInteger(
+ milestoneForm.workOrderMilestoneCompletionTimeString
+ )
+ : dateToTimeInteger(rightNow),
+ requestSession.user.userName,
+ rightNow.getTime(),
+ milestoneForm.workOrderMilestoneId
+ );
+
+ database.close();
+
+ return result.changes > 0;
+};
+
+export default completeWorkOrderMilestone;
diff --git a/helpers/lotOccupancyDB/getLotOccupancies.d.ts b/helpers/lotOccupancyDB/getLotOccupancies.d.ts
index 6618220e..fe7300d6 100644
--- a/helpers/lotOccupancyDB/getLotOccupancies.d.ts
+++ b/helpers/lotOccupancyDB/getLotOccupancies.d.ts
@@ -4,12 +4,14 @@ interface GetLotOccupanciesFilters {
lotId?: number | string;
occupancyTime?: "" | "past" | "current" | "future";
occupancyStartDateString?: string;
+ occupancyEffectiveDateString?: string;
occupantName?: string;
occupancyTypeId?: number | string;
mapId?: number | string;
lotName?: string;
lotTypeId?: number | string;
workOrderId?: number | string;
+ notWorkOrderId?: number | string;
}
interface GetLotOccupanciesOptions {
limit: -1 | number;
diff --git a/helpers/lotOccupancyDB/getLotOccupancies.js b/helpers/lotOccupancyDB/getLotOccupancies.js
index 9fe2de3b..b64e2686 100644
--- a/helpers/lotOccupancyDB/getLotOccupancies.js
+++ b/helpers/lotOccupancyDB/getLotOccupancies.js
@@ -57,6 +57,11 @@ export const getLotOccupancies = (filters, options, connectedDatabase) => {
sqlWhereClause += " and o.occupancyStartDate = ?";
sqlParameters.push(dateStringToInteger(filters.occupancyStartDateString));
}
+ if (filters.occupancyEffectiveDateString) {
+ sqlWhereClause +=
+ " and (o.occupancyStartDate <= ? and (o.occupancyEndDate is null or o.occupancyEndDate >= ?))";
+ sqlParameters.push(dateStringToInteger(filters.occupancyEffectiveDateString), dateStringToInteger(filters.occupancyEffectiveDateString));
+ }
if (filters.mapId) {
sqlWhereClause += " and l.mapId = ?";
sqlParameters.push(filters.mapId);
@@ -70,6 +75,11 @@ export const getLotOccupancies = (filters, options, connectedDatabase) => {
" and o.lotOccupancyId in (select lotOccupancyId from WorkOrderLotOccupancies where recordDelete_timeMillis is null and workOrderId = ?)";
sqlParameters.push(filters.workOrderId);
}
+ if (filters.notWorkOrderId) {
+ sqlWhereClause +=
+ " and o.lotOccupancyId not in (select lotOccupancyId from WorkOrderLotOccupancies where recordDelete_timeMillis is null and workOrderId = ?)";
+ sqlParameters.push(filters.notWorkOrderId);
+ }
const count = database
.prepare("select count(*) as recordCount" +
" from LotOccupancies o" +
diff --git a/helpers/lotOccupancyDB/getLotOccupancies.ts b/helpers/lotOccupancyDB/getLotOccupancies.ts
index 381564f0..601e1339 100644
--- a/helpers/lotOccupancyDB/getLotOccupancies.ts
+++ b/helpers/lotOccupancyDB/getLotOccupancies.ts
@@ -16,12 +16,14 @@ interface GetLotOccupanciesFilters {
lotId?: number | string;
occupancyTime?: "" | "past" | "current" | "future";
occupancyStartDateString?: string;
+ occupancyEffectiveDateString?: string;
occupantName?: string;
occupancyTypeId?: number | string;
mapId?: number | string;
lotName?: string;
lotTypeId?: number | string;
workOrderId?: number | string;
+ notWorkOrderId?: number | string;
}
interface GetLotOccupanciesOptions {
@@ -107,6 +109,15 @@ export const getLotOccupancies = (
);
}
+ if (filters.occupancyEffectiveDateString) {
+ sqlWhereClause +=
+ " and (o.occupancyStartDate <= ? and (o.occupancyEndDate is null or o.occupancyEndDate >= ?))";
+ sqlParameters.push(
+ dateStringToInteger(filters.occupancyEffectiveDateString),
+ dateStringToInteger(filters.occupancyEffectiveDateString)
+ );
+ }
+
if (filters.mapId) {
sqlWhereClause += " and l.mapId = ?";
sqlParameters.push(filters.mapId);
@@ -123,6 +134,12 @@ export const getLotOccupancies = (
sqlParameters.push(filters.workOrderId);
}
+ if (filters.notWorkOrderId) {
+ sqlWhereClause +=
+ " and o.lotOccupancyId not in (select lotOccupancyId from WorkOrderLotOccupancies where recordDelete_timeMillis is null and workOrderId = ?)";
+ sqlParameters.push(filters.notWorkOrderId);
+ }
+
const count: number = database
.prepare(
"select count(*) as recordCount" +
diff --git a/helpers/lotOccupancyDB/reopenWorkOrderMilestone.d.ts b/helpers/lotOccupancyDB/reopenWorkOrderMilestone.d.ts
new file mode 100644
index 00000000..3852c19c
--- /dev/null
+++ b/helpers/lotOccupancyDB/reopenWorkOrderMilestone.d.ts
@@ -0,0 +1,3 @@
+import type * as recordTypes from "../../types/recordTypes";
+export declare const reopenWorkOrderMilestone: (workOrderMilestoneId: number | string, requestSession: recordTypes.PartialSession) => boolean;
+export default reopenWorkOrderMilestone;
diff --git a/helpers/lotOccupancyDB/reopenWorkOrderMilestone.js b/helpers/lotOccupancyDB/reopenWorkOrderMilestone.js
new file mode 100644
index 00000000..02a15ae5
--- /dev/null
+++ b/helpers/lotOccupancyDB/reopenWorkOrderMilestone.js
@@ -0,0 +1,18 @@
+import sqlite from "better-sqlite3";
+import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
+export const reopenWorkOrderMilestone = (workOrderMilestoneId, requestSession) => {
+ const database = sqlite(databasePath);
+ const rightNowMillis = Date.now();
+ const result = database
+ .prepare("update WorkOrderMilestones" +
+ " set workOrderMilestoneCompletionDate = null," +
+ " workOrderMilestoneCompletionTime = null," +
+ " recordUpdate_userName = ?," +
+ " recordUpdate_timeMillis = ?" +
+ " where workOrderMilestoneId = ?" +
+ " and workOrderMilestoneCompletionDate is not null")
+ .run(requestSession.user.userName, rightNowMillis, workOrderMilestoneId);
+ database.close();
+ return result.changes > 0;
+};
+export default reopenWorkOrderMilestone;
diff --git a/helpers/lotOccupancyDB/reopenWorkOrderMilestone.ts b/helpers/lotOccupancyDB/reopenWorkOrderMilestone.ts
new file mode 100644
index 00000000..1441a10a
--- /dev/null
+++ b/helpers/lotOccupancyDB/reopenWorkOrderMilestone.ts
@@ -0,0 +1,36 @@
+import sqlite from "better-sqlite3";
+
+import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
+
+import type * as recordTypes from "../../types/recordTypes";
+
+export const reopenWorkOrderMilestone = (
+ workOrderMilestoneId: number | string,
+ requestSession: recordTypes.PartialSession
+): boolean => {
+ const database = sqlite(databasePath);
+
+ const rightNowMillis = Date.now();
+
+ const result = database
+ .prepare(
+ "update WorkOrderMilestones" +
+ " set workOrderMilestoneCompletionDate = null," +
+ " workOrderMilestoneCompletionTime = null," +
+ " recordUpdate_userName = ?," +
+ " recordUpdate_timeMillis = ?" +
+ " where workOrderMilestoneId = ?" +
+ " and workOrderMilestoneCompletionDate is not null"
+ )
+ .run(
+ requestSession.user.userName,
+ rightNowMillis,
+ workOrderMilestoneId
+ );
+
+ database.close();
+
+ return result.changes > 0;
+};
+
+export default reopenWorkOrderMilestone;
diff --git a/public-typescript/workOrderEdit.js b/public-typescript/workOrderEdit.js
index 5ceb190f..7485f1a5 100644
--- a/public-typescript/workOrderEdit.js
+++ b/public-typescript/workOrderEdit.js
@@ -1,6 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
(() => {
+ const los = exports.los;
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const workOrderId = document.querySelector("#workOrderEdit--workOrderId").value;
const isCreate = workOrderId === "";
@@ -79,8 +80,56 @@ Object.defineProperty(exports, "__esModule", { value: true });
}
});
};
+ const addLot = (lotId, callbackFunction) => {
+ cityssm.postJSON(urlPrefix + "/workOrders/doAddWorkOrderLot", {
+ workOrderId,
+ lotId
+ }, (responseJSON) => {
+ 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, callbackFunction) => {
+ cityssm.postJSON(urlPrefix + "/workOrders/doAddWorkOrderLotOccupancy", {
+ workOrderId,
+ lotOccupancyId
+ }, (responseJSON) => {
+ 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) => {
+ const lotId = clickEvent.currentTarget.dataset
+ .lotId;
+ addLot(lotId);
+ };
const renderRelatedOccupancies = () => {
- const occupanciesContainerElement = document.querySelector("#relatedTab--lotOccupancies");
+ const occupanciesContainerElement = document.querySelector("#container--lotOccupancies");
document.querySelector(".tabs a[href='#relatedTab--lotOccupancies'] .tag").textContent = workOrderLotOccupancies.length.toString();
if (workOrderLotOccupancies.length === 0) {
occupanciesContainerElement.innerHTML =
@@ -114,6 +163,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
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
@@ -132,38 +185,65 @@ Object.defineProperty(exports, "__esModule", { value: true });
'">' +
cityssm.escapeHTML(lotOccupancy.occupancyType) +
"" +
- " ") +
- ("" +
- (lotOccupancy.lotId
- ? cityssm.escapeHTML(lotOccupancy.lotName)
- : '(No ' +
- exports.aliases.lot +
- ") ") +
- " ") +
- ("" + 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) {
+ 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);
@@ -180,8 +260,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
lotId
}, (responseJSON) => {
if (responseJSON.success) {
- workOrderLots =
- responseJSON.workOrderLots;
+ workOrderLots = responseJSON.workOrderLots;
renderRelatedLotsAndOccupancies();
}
else {
@@ -212,7 +291,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
});
};
const renderRelatedLots = () => {
- const lotsContainerElement = document.querySelector("#relatedTab--lots");
+ const lotsContainerElement = document.querySelector("#container--lots");
document.querySelector(".tabs a[href='#relatedTab--lots'] .tag").textContent = workOrderLots.length.toString();
if (workOrderLots.length === 0) {
lotsContainerElement.innerHTML =
@@ -269,5 +348,398 @@ Object.defineProperty(exports, "__esModule", { value: true });
renderRelatedLots();
};
renderRelatedLotsAndOccupancies();
+ document
+ .querySelector("#button--addLotOccupancy")
+ .addEventListener("click", () => {
+ let searchFormElement;
+ let searchResultsContainerElement;
+ const doAddLotOccupancy = (clickEvent) => {
+ const rowElement = clickEvent.currentTarget.closest("tr");
+ const lotOccupancyId = rowElement.dataset.lotOccupancyId;
+ addLotOccupancy(lotOccupancyId, (success) => {
+ if (success) {
+ rowElement.remove();
+ }
+ });
+ };
+ const doSearch = (event) => {
+ if (event) {
+ event.preventDefault();
+ }
+ searchResultsContainerElement.innerHTML =
+ '' +
+ ' ' +
+ "Searching..." +
+ "
";
+ cityssm.postJSON(urlPrefix + "/lotOccupancies/doSearchLotOccupancies", searchFormElement, (responseJSON) => {
+ 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 Date " +
+ "End 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").value = workOrderId;
+ modalElement.querySelector("#lotOccupancySearch--occupancyEffectiveDateString").value = document.querySelector("#workOrderEdit--workOrderOpenDateString").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;
+ let searchResultsContainerElement;
+ const doAddLot = (clickEvent) => {
+ const rowElement = clickEvent.currentTarget.closest("tr");
+ const lotId = rowElement.dataset.lotId;
+ addLot(lotId, (success) => {
+ if (success) {
+ rowElement.remove();
+ }
+ });
+ };
+ const doSearch = (event) => {
+ if (event) {
+ event.preventDefault();
+ }
+ searchResultsContainerElement.innerHTML =
+ '' +
+ ' ' +
+ "Searching..." +
+ "
";
+ cityssm.postJSON(urlPrefix + "/lots/doSearchLots", searchFormElement, (responseJSON) => {
+ 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 + " Type ") +
+ "Status " +
+ " " +
+ " " +
+ " " +
+ "
";
+ 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").value = workOrderId;
+ const lotStatusElement = modalElement.querySelector("#lotSearch--lotStatusId");
+ for (const lotStatus of exports.lotStatuses) {
+ 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();
+ }
+ });
+ });
+ }
+ if (!isCreate) {
+ let workOrderMilestones = exports.workOrderMilestones;
+ delete exports.workOrderMilestones;
+ const completeMilestone = (clickEvent) => {
+ clickEvent.preventDefault();
+ const workOrderMilestoneId = clickEvent.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId;
+ const doComplete = () => {
+ cityssm.postJSON(urlPrefix + "/workOrders/doCompleteWorkOrderMilestone", {
+ workOrderId,
+ workOrderMilestoneId
+ }, (responseJSON) => {
+ if (responseJSON.success) {
+ workOrderMilestones = responseJSON.workOrderMilestones;
+ renderMilestones();
+ }
+ else {
+ bulmaJS.alert({
+ title: "Error Completing Milestone",
+ message: responseJSON.errorMessage,
+ contextualColorName: "danger"
+ });
+ }
+ });
+ };
+ bulmaJS.confirm({
+ title: "Complete Milestone",
+ message: "Are you sure you want to complete this milestone?",
+ contextualColorName: "warning",
+ okButton: {
+ text: "Yes, Complete Milestone",
+ callbackFunction: doComplete
+ }
+ });
+ };
+ const reopenMilestone = (clickEvent) => {
+ clickEvent.preventDefault();
+ const workOrderMilestoneId = clickEvent.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId;
+ const doReopen = () => {
+ cityssm.postJSON(urlPrefix + "/workOrders/doReopenWorkOrderMilestone", {
+ workOrderId,
+ workOrderMilestoneId
+ }, (responseJSON) => {
+ if (responseJSON.success) {
+ workOrderMilestones = responseJSON.workOrderMilestones;
+ renderMilestones();
+ }
+ else {
+ bulmaJS.alert({
+ title: "Error Reopening Milestone",
+ message: responseJSON.errorMessage,
+ contextualColorName: "danger"
+ });
+ }
+ });
+ };
+ 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) => {
+ clickEvent.preventDefault();
+ };
+ const renderMilestones = () => {
+ const milestonesPanelElement = document.querySelector("#panel--milestones");
+ 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 +
+ " " +
+ '' +
+ cityssm.escapeHTML(milestone.workOrderMilestoneDescription) +
+ " " +
+ "
") +
+ ('
' +
+ '
' +
+ ('
' +
+ '' +
+ ' ' +
+ " " +
+ "
") +
+ ('") +
+ "
" +
+ "
") +
+ "
";
+ if (milestone.workOrderMilestoneCompletionDate) {
+ panelBlockElement
+ .querySelector(".button--reopenMilestone")
+ .addEventListener("click", reopenMilestone);
+ }
+ else {
+ panelBlockElement
+ .querySelector(".button--completeMilestone")
+ .addEventListener("click", completeMilestone);
+ }
+ panelBlockElement
+ .querySelector(".button--deleteMilestone")
+ .addEventListener("click", deleteMilestone);
+ milestonesPanelElement.append(panelBlockElement);
+ }
+ bulmaJS.init(milestonesPanelElement);
+ };
+ renderMilestones();
}
})();
diff --git a/public-typescript/workOrderEdit.ts b/public-typescript/workOrderEdit.ts
index a5d2a739..28e82abc 100644
--- a/public-typescript/workOrderEdit.ts
+++ b/public-typescript/workOrderEdit.ts
@@ -2,12 +2,17 @@
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";
+import { response } from "express";
declare const cityssm: cityssmGlobal;
declare const bulmaJS: BulmaJS;
(() => {
+ const los = exports.los as globalTypes.LOS;
+
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const workOrderId = (
@@ -124,9 +129,82 @@ declare const bulmaJS: BulmaJS;
});
};
+ 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(
- "#relatedTab--lotOccupancies"
+ "#container--lotOccupancies"
) as HTMLElement;
document.querySelector(
@@ -173,6 +251,12 @@ declare const bulmaJS: BulmaJS;
lotOccupancy.occupancyEndDateString < currentDateString
);
+ const hasLotRecord =
+ lotOccupancy.lotId &&
+ workOrderLots.some((lot) => {
+ return lotOccupancy.lotId === lot.lotId;
+ });
+
rowElement.innerHTML =
'' +
(isActive
@@ -191,41 +275,81 @@ declare const bulmaJS: BulmaJS;
'">' +
cityssm.escapeHTML(lotOccupancy.occupancyType) +
"" +
- " ") +
- ("" +
- (lotOccupancy.lotId
- ? cityssm.escapeHTML(lotOccupancy.lotName)
- : '(No ' +
- exports.aliases.lot +
- ") ") +
- " ") +
- ("" + 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) {
+ 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);
@@ -256,8 +380,7 @@ declare const bulmaJS: BulmaJS;
workOrderLots?: recordTypes.Lot[];
}) => {
if (responseJSON.success) {
- workOrderLots =
- responseJSON.workOrderLots;
+ workOrderLots = responseJSON.workOrderLots;
renderRelatedLotsAndOccupancies();
} else {
bulmaJS.alert({
@@ -293,7 +416,7 @@ declare const bulmaJS: BulmaJS;
const renderRelatedLots = () => {
const lotsContainerElement = document.querySelector(
- "#relatedTab--lots"
+ "#container--lots"
) as HTMLElement;
document.querySelector(
@@ -364,6 +487,348 @@ declare const bulmaJS: BulmaJS;
};
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 Date " +
+ "End 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 + " Type ") +
+ "Status " +
+ " " +
+ " " +
+ " " +
+ "
";
+
+ 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();
+ }
+ });
+ });
}
/*
@@ -373,4 +838,193 @@ declare const bulmaJS: BulmaJS;
/*
* Milestones
*/
+
+ if (!isCreate) {
+ let workOrderMilestones =
+ exports.workOrderMilestones as recordTypes.WorkOrderMilestone[];
+ delete exports.workOrderMilestones;
+
+ const completeMilestone = (clickEvent: Event) => {
+ clickEvent.preventDefault();
+
+ const workOrderMilestoneId = (
+ (clickEvent.currentTarget as HTMLElement).closest(
+ ".container--milestone"
+ ) as HTMLElement
+ ).dataset.workOrderMilestoneId;
+
+ const doComplete = () => {
+ cityssm.postJSON(urlPrefix + "/workOrders/doCompleteWorkOrderMilestone", {
+ workOrderId,
+ workOrderMilestoneId
+ },
+ (responseJSON: {success: boolean; errorMessage?: string; workOrderMilestones?: recordTypes.WorkOrderMilestone[];}) => {
+ if (responseJSON.success) {
+ workOrderMilestones = responseJSON.workOrderMilestones;
+ renderMilestones();
+ } else {
+ bulmaJS.alert({
+ title: "Error Completing Milestone",
+ message: responseJSON.errorMessage,
+ contextualColorName: "danger"
+ });
+ }
+ });
+ };
+
+ bulmaJS.confirm({
+ title: "Complete Milestone",
+ message: "Are you sure you want to complete this milestone?",
+ 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
+ },
+ (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"
+ });
+ }
+ });
+ };
+
+ 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 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 +
+ " " +
+ '' +
+ cityssm.escapeHTML(
+ milestone.workOrderMilestoneDescription
+ ) +
+ " " +
+ "
") +
+ ('
' +
+ '
' +
+ ('
' +
+ '' +
+ ' ' +
+ " " +
+ "
") +
+ ('") +
+ "
" +
+ "
") +
+ "
";
+
+ if (milestone.workOrderMilestoneCompletionDate) {
+ panelBlockElement
+ .querySelector(".button--reopenMilestone")
+ .addEventListener("click", reopenMilestone);
+ } else {
+ panelBlockElement
+ .querySelector(".button--completeMilestone")
+ .addEventListener("click", completeMilestone);
+ }
+
+ panelBlockElement
+ .querySelector(".button--deleteMilestone")
+ .addEventListener("click", deleteMilestone);
+
+ milestonesPanelElement.append(panelBlockElement);
+ }
+
+ bulmaJS.init(milestonesPanelElement);
+ };
+
+ renderMilestones();
+ }
})();
diff --git a/public/html/workOrder-addLot.html b/public/html/workOrder-addLot.html
new file mode 100644
index 00000000..66f2d1b7
--- /dev/null
+++ b/public/html/workOrder-addLot.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+ Add Related to Work Order
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/html/workOrder-addLotOccupancy.html b/public/html/workOrder-addLotOccupancy.html
new file mode 100644
index 00000000..7b9a0e7c
--- /dev/null
+++ b/public/html/workOrder-addLotOccupancy.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+ Add Related to Work Order
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/javascripts/workOrderEdit.min.js b/public/javascripts/workOrderEdit.min.js
index 0e04ec49..0202b950 100644
--- a/public/javascripts/workOrderEdit.min.js
+++ b/public/javascripts/workOrderEdit.min.js
@@ -1 +1 @@
-"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const t=document.querySelector("main").dataset.urlPrefix,e=document.querySelector("#workOrderEdit--workOrderId").value,s=""===e;if(document.querySelector("#form--workOrderEdit").addEventListener("submit",e=>{e.preventDefault(),cityssm.postJSON(t+"/workOrders/"+(s?"doCreateWorkOrder":"doUpdateWorkOrder"),e.currentTarget,e=>{e.success?s?window.location.href=t+"/workOrders/"+e.workOrderId+"/edit":bulmaJS.alert({message:"Work Order Updated Successfully",contextualColorName:"success"}):bulmaJS.alert({title:"Error Updating Work Order",message:e.errorMessage,contextualColorName:"danger"})})}),!s){let s=exports.workOrderLots;delete exports.workOrderLots;let a=exports.workOrderLotOccupancies;delete exports.workOrderLotOccupancies;const o=s=>{const o=s.currentTarget.closest(".container--lotOccupancy").dataset.lotOccupancyId;bulmaJS.confirm({title:"Delete "+exports.aliases.lot+" "+exports.aliases.occupancy+" Relationship",message:"Are you sure you want to remove the relationship to this "+exports.aliases.lot.toLowerCase()+" "+exports.aliases.occupancy.toLowerCase()+" record from this work order? Note that the record will remain.",contextualColorName:"warning",okButton:{text:"Yes, Delete Relationship",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrderLotOccupancy",{workOrderId:e,lotOccupancyId:o},t=>{t.success?(a=t.workOrderLotOccupancies,n()):bulmaJS.alert({title:"Error Deleting Relationship",message:t.errorMessage,contextualColorName:"danger"})})}}})},r=()=>{const e=document.querySelector("#relatedTab--lotOccupancies");if(document.querySelector(".tabs a[href='#relatedTab--lotOccupancies'] .tag").textContent=a.length.toString(),0===a.length)return void(e.innerHTML='There are no '+exports.aliases.occupancies.toLowerCase()+" associated with this work order.
");e.innerHTML=''+exports.aliases.occupancy+" Type "+exports.aliases.lot+" Start Date End Date "+exports.aliases.occupants+'
';const s=cityssm.dateToString(new Date);for(const r of a){const a=document.createElement("tr");a.className="container--lotOccupancy",a.dataset.lotOccupancyId=r.lotOccupancyId.toString();const c=!(r.occupancyEndDate&&r.occupancyEndDateString'+(c?' ':' ')+''+cityssm.escapeHTML(r.occupancyType)+" "+(r.lotId?cityssm.escapeHTML(r.lotName):'(No '+exports.aliases.lot+") ")+" "+r.occupancyStartDateString+" "+(r.occupancyEndDate?r.occupancyEndDateString:'(No End Date) ')+" "+(0===r.lotOccupancyOccupants.length?'(No '+cityssm.escapeHTML(exports.aliases.occupants)+") ":cityssm.escapeHTML(r.lotOccupancyOccupants[0].occupantName)+(r.lotOccupancyOccupants.length>1?" plus "+(r.lotOccupancyOccupants.length-1):""))+' ',a.querySelector(".button--deleteLotOccupancy").addEventListener("click",o),e.querySelector("tbody").append(a)}},c=a=>{const o=a.currentTarget.closest(".container--lot").dataset.lotId;bulmaJS.confirm({title:"Delete "+exports.aliases.lot+" "+exports.aliases.occupancy+" Relationship",message:"Are you sure you want to remove the relationship to this "+exports.aliases.lot.toLowerCase()+" "+exports.aliases.occupancy.toLowerCase()+" record from this work order? Note that the record will remain.",contextualColorName:"warning",okButton:{text:"Yes, Delete Relationship",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrderLot",{workOrderId:e,lotId:o},t=>{t.success?(s=t.workOrderLots,n()):bulmaJS.alert({title:"Error Deleting Relationship",message:t.errorMessage,contextualColorName:"danger"})})}}})},l=()=>{const e=document.querySelector("#relatedTab--lots");if(document.querySelector(".tabs a[href='#relatedTab--lots'] .tag").textContent=s.length.toString(),0!==s.length){e.innerHTML=''+exports.aliases.lot+" "+exports.aliases.map+" "+exports.aliases.lot+' Type Status
';for(const a of s){const s=document.createElement("tr");s.className="container--lot",s.dataset.lotId=a.lotId.toString(),s.innerHTML=''+cityssm.escapeHTML(a.lotName)+" "+cityssm.escapeHTML(a.mapName)+" "+cityssm.escapeHTML(a.lotType)+" "+cityssm.escapeHTML(a.lotStatus)+' ',s.querySelector(".button--deleteLot").addEventListener("click",c),e.querySelector("tbody").append(s)}}else e.innerHTML='There are no '+exports.aliases.lots.toLowerCase()+" associated with this work order.
"},n=()=>{r(),l()};n()}})();
\ No newline at end of file
+"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=exports.los,t=document.querySelector("main").dataset.urlPrefix,s=document.querySelector("#workOrderEdit--workOrderId").value,o=""===s;if(document.querySelector("#form--workOrderEdit").addEventListener("submit",e=>{e.preventDefault(),cityssm.postJSON(t+"/workOrders/"+(o?"doCreateWorkOrder":"doUpdateWorkOrder"),e.currentTarget,e=>{e.success?o?window.location.href=t+"/workOrders/"+e.workOrderId+"/edit":bulmaJS.alert({message:"Work Order Updated Successfully",contextualColorName:"success"}):bulmaJS.alert({title:"Error Updating Work Order",message:e.errorMessage,contextualColorName:"danger"})})}),!o){let o=exports.workOrderLots;delete exports.workOrderLots;let a=exports.workOrderLotOccupancies;delete exports.workOrderLotOccupancies;const r=e=>{const o=e.currentTarget.closest(".container--lotOccupancy").dataset.lotOccupancyId;bulmaJS.confirm({title:"Delete "+exports.aliases.lot+" "+exports.aliases.occupancy+" Relationship",message:"Are you sure you want to remove the relationship to this "+exports.aliases.lot.toLowerCase()+" "+exports.aliases.occupancy.toLowerCase()+" record from this work order? Note that the record will remain.",contextualColorName:"warning",okButton:{text:"Yes, Delete Relationship",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrderLotOccupancy",{workOrderId:s,lotOccupancyId:o},e=>{e.success?(a=e.workOrderLotOccupancies,u()):bulmaJS.alert({title:"Error Deleting Relationship",message:e.errorMessage,contextualColorName:"danger"})})}}})},c=(e,a)=>{cityssm.postJSON(t+"/workOrders/doAddWorkOrderLot",{workOrderId:s,lotId:e},e=>{e.success?(o=e.workOrderLots,u()):bulmaJS.alert({title:"Error Adding "+exports.aliases.lot,message:e.errorMessage,contextualColorName:"danger"}),a&&a(e.success)})},n=(e,o)=>{cityssm.postJSON(t+"/workOrders/doAddWorkOrderLotOccupancy",{workOrderId:s,lotOccupancyId:e},e=>{e.success?(a=e.workOrderLotOccupancies,u()):bulmaJS.alert({title:"Error Adding "+exports.aliases.occupancy,message:e.errorMessage,contextualColorName:"danger"}),o&&o(e.success)})},l=e=>{const t=e.currentTarget.dataset.lotId;c(t)},i=()=>{const e=document.querySelector("#container--lotOccupancies");if(document.querySelector(".tabs a[href='#relatedTab--lotOccupancies'] .tag").textContent=a.length.toString(),0===a.length)return void(e.innerHTML='There are no '+exports.aliases.occupancies.toLowerCase()+" associated with this work order.
");e.innerHTML=''+exports.aliases.occupancy+" Type "+exports.aliases.lot+" Start Date End Date "+exports.aliases.occupants+'
';const s=cityssm.dateToString(new Date);for(const c of a){const a=document.createElement("tr");a.className="container--lotOccupancy",a.dataset.lotOccupancyId=c.lotOccupancyId.toString();const n=!(c.occupancyEndDate&&c.occupancyEndDateStringc.lotId===e.lotId);a.innerHTML=''+(n?' ':' ')+' '+cityssm.escapeHTML(c.occupancyType)+" ",c.lotId?a.insertAdjacentHTML("beforeend",""+cityssm.escapeHTML(c.lotName)+(i?"":' ')+" "):a.insertAdjacentHTML("beforeend",'(No '+exports.aliases.lot+") "),a.insertAdjacentHTML("beforeend",""+c.occupancyStartDateString+" "+(c.occupancyEndDate?c.occupancyEndDateString:'(No End Date) ')+" "+(0===c.lotOccupancyOccupants.length?'(No '+cityssm.escapeHTML(exports.aliases.occupants)+") ":cityssm.escapeHTML(c.lotOccupancyOccupants[0].occupantName)+(c.lotOccupancyOccupants.length>1?" plus "+(c.lotOccupancyOccupants.length-1):""))+' '),c.lotId&&!i&&a.querySelector(".button--addLot").addEventListener("click",l),a.querySelector(".button--deleteLotOccupancy").addEventListener("click",r),e.querySelector("tbody").append(a)}},d=e=>{const a=e.currentTarget.closest(".container--lot").dataset.lotId;bulmaJS.confirm({title:"Delete "+exports.aliases.lot+" "+exports.aliases.occupancy+" Relationship",message:"Are you sure you want to remove the relationship to this "+exports.aliases.lot.toLowerCase()+" "+exports.aliases.occupancy.toLowerCase()+" record from this work order? Note that the record will remain.",contextualColorName:"warning",okButton:{text:"Yes, Delete Relationship",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doDeleteWorkOrderLot",{workOrderId:s,lotId:a},e=>{e.success?(o=e.workOrderLots,u()):bulmaJS.alert({title:"Error Deleting Relationship",message:e.errorMessage,contextualColorName:"danger"})})}}})},p=()=>{const e=document.querySelector("#container--lots");if(document.querySelector(".tabs a[href='#relatedTab--lots'] .tag").textContent=o.length.toString(),0!==o.length){e.innerHTML=''+exports.aliases.lot+" "+exports.aliases.map+" "+exports.aliases.lot+' Type Status
';for(const s of o){const o=document.createElement("tr");o.className="container--lot",o.dataset.lotId=s.lotId.toString(),o.innerHTML=''+cityssm.escapeHTML(s.lotName)+" "+cityssm.escapeHTML(s.mapName)+" "+cityssm.escapeHTML(s.lotType)+" "+cityssm.escapeHTML(s.lotStatus)+' ',o.querySelector(".button--deleteLot").addEventListener("click",d),e.querySelector("tbody").append(o)}}else e.innerHTML='There are no '+exports.aliases.lots.toLowerCase()+" associated with this work order.
"},u=()=>{i(),p()};u(),document.querySelector("#button--addLotOccupancy").addEventListener("click",()=>{let o,a;const r=e=>{const t=e.currentTarget.closest("tr"),s=t.dataset.lotOccupancyId;n(s,e=>{e&&t.remove()})},c=e=>{e&&e.preventDefault(),a.innerHTML=' Searching...
',cityssm.postJSON(t+"/lotOccupancies/doSearchLotOccupancies",o,e=>{if(0!==e.lotOccupancies.length){a.innerHTML=''+exports.aliases.occupancy+" Type "+exports.aliases.lot+" Start Date End Date "+exports.aliases.occupants+"
";for(const t of e.lotOccupancies){const e=document.createElement("tr");e.className="container--lotOccupancy",e.dataset.lotOccupancyId=t.lotOccupancyId.toString(),e.innerHTML=''+cityssm.escapeHTML(t.occupancyType)+" ",t.lotId?e.insertAdjacentHTML("beforeend",""+cityssm.escapeHTML(t.lotName)+" "):e.insertAdjacentHTML("beforeend",'(No '+exports.aliases.lot+") "),e.insertAdjacentHTML("beforeend",""+t.occupancyStartDateString+" "+(t.occupancyEndDate?t.occupancyEndDateString:'(No End Date) ')+" "+(0===t.lotOccupancyOccupants.length?'(No '+cityssm.escapeHTML(exports.aliases.occupants)+") ":cityssm.escapeHTML(t.lotOccupancyOccupants[0].occupantName)+(t.lotOccupancyOccupants.length>1?" plus "+(t.lotOccupancyOccupants.length-1):""))+" "),e.querySelector(".button--addLotOccupancy").addEventListener("click",r),a.querySelector("tbody").append(e)}}else a.innerHTML='There are no records that meet the search criteria.
'})};cityssm.openHtmlModal("workOrder-addLotOccupancy",{onshow:t=>{e.populateAliases(t),o=t.querySelector("form"),a=t.querySelector("#resultsContainer--lotOccupancyAdd"),t.querySelector("#lotOccupancySearch--notWorkOrderId").value=s,t.querySelector("#lotOccupancySearch--occupancyEffectiveDateString").value=document.querySelector("#workOrderEdit--workOrderOpenDateString").value,c()},onshown:e=>{bulmaJS.toggleHtmlClipped(),e.querySelector("#lotOccupancySearch--occupantName").addEventListener("change",c),e.querySelector("#lotOccupancySearch--lotName").addEventListener("change",c),o.addEventListener("submit",c)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})}),document.querySelector("#button--addLot").addEventListener("click",()=>{let o,a;const r=e=>{const t=e.currentTarget.closest("tr"),s=t.dataset.lotId;c(s,e=>{e&&t.remove()})},n=e=>{e&&e.preventDefault(),a.innerHTML=' Searching...
',cityssm.postJSON(t+"/lots/doSearchLots",o,e=>{if(0!==e.lots.length){a.innerHTML=''+exports.aliases.lot+" "+exports.aliases.map+" "+exports.aliases.lot+" Type Status
";for(const t of e.lots){const e=document.createElement("tr");e.className="container--lot",e.dataset.lotId=t.lotId.toString(),e.innerHTML=''+cityssm.escapeHTML(t.lotName)+" "+cityssm.escapeHTML(t.mapName)+" "+cityssm.escapeHTML(t.lotType)+" "+cityssm.escapeHTML(t.lotStatus)+" ",e.querySelector(".button--addLot").addEventListener("click",r),a.querySelector("tbody").append(e)}}else a.innerHTML='There are no records that meet the search criteria.
'})};cityssm.openHtmlModal("workOrder-addLot",{onshow:t=>{e.populateAliases(t),o=t.querySelector("form"),a=t.querySelector("#resultsContainer--lotAdd"),t.querySelector("#lotSearch--notWorkOrderId").value=s;const r=t.querySelector("#lotSearch--lotStatusId");for(const e of exports.lotStatuses){const t=document.createElement("option");t.value=e.lotStatusId.toString(),t.textContent=e.lotStatus,r.append(t)}n()},onshown:e=>{bulmaJS.toggleHtmlClipped(),e.querySelector("#lotSearch--lotName").addEventListener("change",n),e.querySelector("#lotSearch--lotStatusId").addEventListener("change",n),o.addEventListener("submit",n)},onremoved:()=>{bulmaJS.toggleHtmlClipped()}})})}if(!o){let e=exports.workOrderMilestones;delete exports.workOrderMilestones;const o=o=>{o.preventDefault();const a=o.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId;bulmaJS.confirm({title:"Complete Milestone",message:"Are you sure you want to complete this milestone?",contextualColorName:"warning",okButton:{text:"Yes, Complete Milestone",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doCompleteWorkOrderMilestone",{workOrderId:s,workOrderMilestoneId:a},t=>{t.success?(e=t.workOrderMilestones,c()):bulmaJS.alert({title:"Error Completing Milestone",message:t.errorMessage,contextualColorName:"danger"})})}}})},a=o=>{o.preventDefault();const a=o.currentTarget.closest(".container--milestone").dataset.workOrderMilestoneId;bulmaJS.confirm({title:"Reopen Milestone",message:"Are you sure you want to remove the completion status from this milestone, and reopen it?",contextualColorName:"warning",okButton:{text:"Yes, Reopen Milestone",callbackFunction:()=>{cityssm.postJSON(t+"/workOrders/doReopenWorkOrderMilestone",{workOrderId:s,workOrderMilestoneId:a},t=>{t.success?(e=t.workOrderMilestones,c()):bulmaJS.alert({title:"Error Reopening Milestone",message:t.errorMessage,contextualColorName:"danger"})})}}})},r=e=>{e.preventDefault()},c=()=>{const t=document.querySelector("#panel--milestones"),s=t.querySelectorAll(".panel-block");for(const e of s)e.remove();for(const s of e){const e=document.createElement("div");e.className="panel-block is-block container--milestone",e.dataset.workOrderMilestoneId=s.workOrderMilestoneId.toString(),e.innerHTML=''+(s.workOrderMilestoneCompletionDate?' ':' ')+'
'+(s.workOrderMilestoneTypeId?""+cityssm.escapeHTML(s.workOrderMilestoneType)+" ":"")+s.workOrderMilestoneDateString+''+cityssm.escapeHTML(s.workOrderMilestoneDescription)+'
',s.workOrderMilestoneCompletionDate?e.querySelector(".button--reopenMilestone").addEventListener("click",a):e.querySelector(".button--completeMilestone").addEventListener("click",o),e.querySelector(".button--deleteMilestone").addEventListener("click",r),t.append(e)}bulmaJS.init(t)};c()}})();
\ No newline at end of file
diff --git a/routes/workOrders.js b/routes/workOrders.js
index 43ce6e7c..489c170d 100644
--- a/routes/workOrders.js
+++ b/routes/workOrders.js
@@ -6,8 +6,12 @@ import handler_view from "../handlers/workOrders-get/view.js";
import handler_doReopenWorkOrder from "../handlers/workOrders-post/doReopenWorkOrder.js";
import handler_edit from "../handlers/workOrders-get/edit.js";
import handler_doUpdateWorkOrder from "../handlers/workOrders-post/doUpdateWorkOrder.js";
+import handler_doAddWorkOrderLotOccupancy from "../handlers/workOrders-post/doAddWorkOrderLotOccupancy.js";
import handler_doDeleteWorkOrderLotOccupancy from "../handlers/workOrders-post/doDeleteWorkOrderLotOccupancy.js";
+import handler_doAddWorkOrderLot from "../handlers/workOrders-post/doAddWorkOrderLot.js";
import handler_doDeleteWorkOrderLot from "../handlers/workOrders-post/doDeleteWorkOrderLot.js";
+import handler_doCompleteWorkOrderMilestone from "../handlers/workOrders-post/doCompleteWorkOrderMilestone.js";
+import handler_doReopenWorkOrderMilestone from "../handlers/workOrders-post/doReopenWorkOrderMilestone.js";
export const router = Router();
router.get("/", handler_search);
router.post("/doSearchWorkOrders", handler_doSearchWorkOrders);
@@ -15,6 +19,10 @@ router.get("/:workOrderId", handler_view);
router.post("/doReopenWorkOrder", permissionHandlers.updatePostHandler, handler_doReopenWorkOrder);
router.get("/:workOrderId/edit", permissionHandlers.updateGetHandler, handler_edit);
router.post("/doUpdateWorkOrder", permissionHandlers.updatePostHandler, handler_doUpdateWorkOrder);
+router.post("/doAddWorkOrderLotOccupancy", permissionHandlers.updatePostHandler, handler_doAddWorkOrderLotOccupancy);
router.post("/doDeleteWorkOrderLotOccupancy", permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLotOccupancy);
+router.post("/doAddWorkOrderLot", permissionHandlers.updatePostHandler, handler_doAddWorkOrderLot);
router.post("/doDeleteWorkOrderLot", permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLot);
+router.post("/doCompleteWorkOrderMilestone", permissionHandlers.updatePostHandler, handler_doCompleteWorkOrderMilestone);
+router.post("/doReopenWorkOrderMilestone", permissionHandlers.updatePostHandler, handler_doReopenWorkOrderMilestone);
export default router;
diff --git a/routes/workOrders.ts b/routes/workOrders.ts
index d4f44cde..17b63aca 100644
--- a/routes/workOrders.ts
+++ b/routes/workOrders.ts
@@ -10,9 +10,16 @@ import handler_doReopenWorkOrder from "../handlers/workOrders-post/doReopenWorkO
import handler_edit from "../handlers/workOrders-get/edit.js";
import handler_doUpdateWorkOrder from "../handlers/workOrders-post/doUpdateWorkOrder.js";
+
+import handler_doAddWorkOrderLotOccupancy from "../handlers/workOrders-post/doAddWorkOrderLotOccupancy.js";
import handler_doDeleteWorkOrderLotOccupancy from "../handlers/workOrders-post/doDeleteWorkOrderLotOccupancy.js";
+
+import handler_doAddWorkOrderLot from "../handlers/workOrders-post/doAddWorkOrderLot.js";
import handler_doDeleteWorkOrderLot from "../handlers/workOrders-post/doDeleteWorkOrderLot.js";
+import handler_doCompleteWorkOrderMilestone from "../handlers/workOrders-post/doCompleteWorkOrderMilestone.js";
+import handler_doReopenWorkOrderMilestone from "../handlers/workOrders-post/doReopenWorkOrderMilestone.js";
+
export const router = Router();
router.get("/", handler_search);
@@ -39,16 +46,40 @@ router.post(
handler_doUpdateWorkOrder
);
+router.post(
+ "/doAddWorkOrderLotOccupancy",
+ permissionHandlers.updatePostHandler,
+ handler_doAddWorkOrderLotOccupancy
+);
+
router.post(
"/doDeleteWorkOrderLotOccupancy",
permissionHandlers.updatePostHandler,
handler_doDeleteWorkOrderLotOccupancy
);
+router.post(
+ "/doAddWorkOrderLot",
+ permissionHandlers.updatePostHandler,
+ handler_doAddWorkOrderLot
+);
+
router.post(
"/doDeleteWorkOrderLot",
permissionHandlers.updatePostHandler,
handler_doDeleteWorkOrderLot
);
+router.post(
+ "/doCompleteWorkOrderMilestone",
+ permissionHandlers.updatePostHandler,
+ handler_doCompleteWorkOrderMilestone
+);
+
+router.post(
+ "/doReopenWorkOrderMilestone",
+ permissionHandlers.updatePostHandler,
+ handler_doReopenWorkOrderMilestone
+);
+
export default router;
diff --git a/views/workOrder-edit.ejs b/views/workOrder-edit.ejs
index 082888de..aa726297 100644
--- a/views/workOrder-edit.ejs
+++ b/views/workOrder-edit.ejs
@@ -38,143 +38,170 @@
<% } %>
-