work order development

- add lot occupancies
- add lots
- display milestones
- complete milestones
- reopen milestones
deepsource-autofix-76c6eb20
Dan Gowans 2022-09-12 15:50:44 -04:00
parent ab87f933c7
commit 2b38da7bea
33 changed files with 1963 additions and 225 deletions

View File

@ -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 * as configFunctions from "../../helpers/functions.config.js";
import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js"; import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js";
export const handler = (request, response) => { export const handler = (request, response) => {
@ -9,14 +9,18 @@ export const handler = (request, response) => {
} }
if (workOrder.workOrderCloseDate) { if (workOrder.workOrderCloseDate) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") + return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") +
"/workOrders/" + workOrder.workOrderId.toString() + "/?error=workOrderIsClosed"); "/workOrders/" +
workOrder.workOrderId.toString() +
"/?error=workOrderIsClosed");
} }
const workOrderTypes = getWorkOrderTypes(); const workOrderTypes = getWorkOrderTypes();
const lotStatuses = getLotStatuses();
response.render("workOrder-edit", { response.render("workOrder-edit", {
headTitle: "Work Order #" + workOrder.workOrderNumber, headTitle: "Work Order #" + workOrder.workOrderNumber,
workOrder, workOrder,
isCreate: false, isCreate: false,
workOrderTypes workOrderTypes,
lotStatuses
}); });
}; };
export default handler; export default handler;

View File

@ -1,5 +1,9 @@
import type { RequestHandler } from "express"; 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"; import * as configFunctions from "../../helpers/functions.config.js";
@ -18,17 +22,22 @@ export const handler: RequestHandler = (request, response) => {
if (workOrder.workOrderCloseDate) { if (workOrder.workOrderCloseDate) {
return response.redirect( return response.redirect(
configFunctions.getProperty("reverseProxy.urlPrefix") + configFunctions.getProperty("reverseProxy.urlPrefix") +
"/workOrders/" + workOrder.workOrderId.toString() + "/?error=workOrderIsClosed" "/workOrders/" +
workOrder.workOrderId.toString() +
"/?error=workOrderIsClosed"
); );
} }
const workOrderTypes = getWorkOrderTypes(); const workOrderTypes = getWorkOrderTypes();
const lotStatuses = getLotStatuses();
response.render("workOrder-edit", { response.render("workOrder-edit", {
headTitle: "Work Order #" + workOrder.workOrderNumber, headTitle: "Work Order #" + workOrder.workOrderNumber,
workOrder, workOrder,
isCreate: false, isCreate: false,
workOrderTypes workOrderTypes,
lotStatuses
}); });
}; };

View File

@ -0,0 +1,3 @@
import type { RequestHandler } from "express";
export declare const handler: RequestHandler;
export default handler;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,3 @@
import type { RequestHandler } from "express";
export declare const handler: RequestHandler;
export default handler;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,3 @@
import type { RequestHandler } from "express";
export declare const handler: RequestHandler;
export default handler;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,3 @@
import type { RequestHandler } from "express";
export declare const handler: RequestHandler;
export default handler;

View File

@ -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;

View File

@ -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;

View File

@ -3,14 +3,37 @@ import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const addWorkOrderLotOccupancy = (workOrderLotOccupancyForm, requestSession) => { export const addWorkOrderLotOccupancy = (workOrderLotOccupancyForm, requestSession) => {
const database = sqlite(databasePath); const database = sqlite(databasePath);
const rightNowMillis = Date.now(); const rightNowMillis = Date.now();
const result = database 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 (" + .prepare("insert into WorkOrderLotOccupancies (" +
"workOrderId, lotOccupancyId," + "workOrderId, lotOccupancyId," +
" recordCreate_userName, recordCreate_timeMillis," + " recordCreate_userName, recordCreate_timeMillis," +
" recordUpdate_userName, recordUpdate_timeMillis)" + " recordUpdate_userName, recordUpdate_timeMillis)" +
" values (?, ?, ?, ?, ?, ?)") " values (?, ?, ?, ?, ?, ?)")
.run(workOrderLotOccupancyForm.workOrderId, workOrderLotOccupancyForm.lotOccupancyId, requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis); .run(workOrderLotOccupancyForm.workOrderId, workOrderLotOccupancyForm.lotOccupancyId, requestSession.user.userName, rightNowMillis, requestSession.user.userName, rightNowMillis);
}
database.close(); database.close();
return result.changes > 0; return true;
}; };
export default addWorkOrderLotOccupancy; export default addWorkOrderLotOccupancy;

View File

@ -17,7 +17,43 @@ export const addWorkOrderLotOccupancy = (
const rightNowMillis = Date.now(); const rightNowMillis = Date.now();
const result = database const row: { recordDelete_timeMillis?: number } = 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( .prepare(
"insert into WorkOrderLotOccupancies (" + "insert into WorkOrderLotOccupancies (" +
"workOrderId, lotOccupancyId," + "workOrderId, lotOccupancyId," +
@ -33,10 +69,11 @@ export const addWorkOrderLotOccupancy = (
requestSession.user.userName, requestSession.user.userName,
rightNowMillis rightNowMillis
); );
}
database.close(); database.close();
return result.changes > 0; return true;
}; };
export default addWorkOrderLotOccupancy; export default addWorkOrderLotOccupancy;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -4,12 +4,14 @@ interface GetLotOccupanciesFilters {
lotId?: number | string; lotId?: number | string;
occupancyTime?: "" | "past" | "current" | "future"; occupancyTime?: "" | "past" | "current" | "future";
occupancyStartDateString?: string; occupancyStartDateString?: string;
occupancyEffectiveDateString?: string;
occupantName?: string; occupantName?: string;
occupancyTypeId?: number | string; occupancyTypeId?: number | string;
mapId?: number | string; mapId?: number | string;
lotName?: string; lotName?: string;
lotTypeId?: number | string; lotTypeId?: number | string;
workOrderId?: number | string; workOrderId?: number | string;
notWorkOrderId?: number | string;
} }
interface GetLotOccupanciesOptions { interface GetLotOccupanciesOptions {
limit: -1 | number; limit: -1 | number;

View File

@ -57,6 +57,11 @@ export const getLotOccupancies = (filters, options, connectedDatabase) => {
sqlWhereClause += " and o.occupancyStartDate = ?"; sqlWhereClause += " and o.occupancyStartDate = ?";
sqlParameters.push(dateStringToInteger(filters.occupancyStartDateString)); 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) { if (filters.mapId) {
sqlWhereClause += " and l.mapId = ?"; sqlWhereClause += " and l.mapId = ?";
sqlParameters.push(filters.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 = ?)"; " and o.lotOccupancyId in (select lotOccupancyId from WorkOrderLotOccupancies where recordDelete_timeMillis is null and workOrderId = ?)";
sqlParameters.push(filters.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 const count = database
.prepare("select count(*) as recordCount" + .prepare("select count(*) as recordCount" +
" from LotOccupancies o" + " from LotOccupancies o" +

View File

@ -16,12 +16,14 @@ interface GetLotOccupanciesFilters {
lotId?: number | string; lotId?: number | string;
occupancyTime?: "" | "past" | "current" | "future"; occupancyTime?: "" | "past" | "current" | "future";
occupancyStartDateString?: string; occupancyStartDateString?: string;
occupancyEffectiveDateString?: string;
occupantName?: string; occupantName?: string;
occupancyTypeId?: number | string; occupancyTypeId?: number | string;
mapId?: number | string; mapId?: number | string;
lotName?: string; lotName?: string;
lotTypeId?: number | string; lotTypeId?: number | string;
workOrderId?: number | string; workOrderId?: number | string;
notWorkOrderId?: number | string;
} }
interface GetLotOccupanciesOptions { 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) { if (filters.mapId) {
sqlWhereClause += " and l.mapId = ?"; sqlWhereClause += " and l.mapId = ?";
sqlParameters.push(filters.mapId); sqlParameters.push(filters.mapId);
@ -123,6 +134,12 @@ export const getLotOccupancies = (
sqlParameters.push(filters.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: number = database const count: number = database
.prepare( .prepare(
"select count(*) as recordCount" + "select count(*) as recordCount" +

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -1,6 +1,7 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
(() => { (() => {
const los = exports.los;
const urlPrefix = document.querySelector("main").dataset.urlPrefix; const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const workOrderId = document.querySelector("#workOrderEdit--workOrderId").value; const workOrderId = document.querySelector("#workOrderEdit--workOrderId").value;
const isCreate = workOrderId === ""; 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 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(); document.querySelector(".tabs a[href='#relatedTab--lotOccupancies'] .tag").textContent = workOrderLotOccupancies.length.toString();
if (workOrderLotOccupancies.length === 0) { if (workOrderLotOccupancies.length === 0) {
occupanciesContainerElement.innerHTML = occupanciesContainerElement.innerHTML =
@ -114,6 +163,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
lotOccupancy.lotOccupancyId.toString(); lotOccupancy.lotOccupancyId.toString();
const isActive = !(lotOccupancy.occupancyEndDate && const isActive = !(lotOccupancy.occupancyEndDate &&
lotOccupancy.occupancyEndDateString < currentDateString); lotOccupancy.occupancyEndDateString < currentDateString);
const hasLotRecord = lotOccupancy.lotId &&
workOrderLots.some((lot) => {
return lotOccupancy.lotId === lot.lotId;
});
rowElement.innerHTML = rowElement.innerHTML =
'<td class="has-text-centered">' + '<td class="has-text-centered">' +
(isActive (isActive
@ -132,15 +185,36 @@ Object.defineProperty(exports, "__esModule", { value: true });
'">' + '">' +
cityssm.escapeHTML(lotOccupancy.occupancyType) + cityssm.escapeHTML(lotOccupancy.occupancyType) +
"</a>" + "</a>" +
"</td>") + "</td>");
("<td>" + if (lotOccupancy.lotId) {
(lotOccupancy.lotId rowElement.insertAdjacentHTML("beforeend", "<td>" +
? cityssm.escapeHTML(lotOccupancy.lotName) cityssm.escapeHTML(lotOccupancy.lotName) +
: '<span class="has-text-grey">(No ' + (hasLotRecord
? ""
: ' <button class="button is-small is-light is-success button--addLot"' +
' data-lot-id="' +
lotOccupancy.lotId +
'"' +
' data-tooltip="Add ' +
cityssm.escapeHTML(exports.aliases.lot) +
'"' +
' aria-label="Add ' +
cityssm.escapeHTML(exports.aliases.lot) +
'" type="button">' +
'<i class="fas fa-plus" aria-hidden="true"></i>' +
"</button>") +
"</td>");
}
else {
rowElement.insertAdjacentHTML("beforeend", "<td>" +
'<span class="has-text-grey">(No ' +
exports.aliases.lot + exports.aliases.lot +
")</span>") + ")</span>" +
"</td>") + "</td>");
("<td>" + lotOccupancy.occupancyStartDateString + "</td>") + }
rowElement.insertAdjacentHTML("beforeend", "<td>" +
lotOccupancy.occupancyStartDateString +
"</td>" +
("<td>" + ("<td>" +
(lotOccupancy.occupancyEndDate (lotOccupancy.occupancyEndDate
? lotOccupancy.occupancyEndDateString ? lotOccupancy.occupancyEndDateString
@ -155,7 +229,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
.occupantName) + .occupantName) +
(lotOccupancy.lotOccupancyOccupants.length > 1 (lotOccupancy.lotOccupancyOccupants.length > 1
? " plus " + ? " plus " +
(lotOccupancy.lotOccupancyOccupants.length - (lotOccupancy.lotOccupancyOccupants
.length -
1) 1)
: "")) + : "")) +
"</td>") + "</td>") +
@ -163,7 +238,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
'<button class="button is-small is-light is-danger button--deleteLotOccupancy" data-tooltip="Delete Relationship" type="button">' + '<button class="button is-small is-light is-danger button--deleteLotOccupancy" data-tooltip="Delete Relationship" type="button">' +
'<i class="fas fa-trash" aria-hidden="true"></i>' + '<i class="fas fa-trash" aria-hidden="true"></i>' +
"</button>" + "</button>" +
"</td>"); "</td>"));
if (lotOccupancy.lotId && !hasLotRecord) {
rowElement
.querySelector(".button--addLot")
.addEventListener("click", addLotFromLotOccupancy);
}
rowElement rowElement
.querySelector(".button--deleteLotOccupancy") .querySelector(".button--deleteLotOccupancy")
.addEventListener("click", deleteLotOccupancy); .addEventListener("click", deleteLotOccupancy);
@ -180,8 +260,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
lotId lotId
}, (responseJSON) => { }, (responseJSON) => {
if (responseJSON.success) { if (responseJSON.success) {
workOrderLots = workOrderLots = responseJSON.workOrderLots;
responseJSON.workOrderLots;
renderRelatedLotsAndOccupancies(); renderRelatedLotsAndOccupancies();
} }
else { else {
@ -212,7 +291,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
}); });
}; };
const renderRelatedLots = () => { 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(); document.querySelector(".tabs a[href='#relatedTab--lots'] .tag").textContent = workOrderLots.length.toString();
if (workOrderLots.length === 0) { if (workOrderLots.length === 0) {
lotsContainerElement.innerHTML = lotsContainerElement.innerHTML =
@ -269,5 +348,398 @@ Object.defineProperty(exports, "__esModule", { value: true });
renderRelatedLots(); renderRelatedLots();
}; };
renderRelatedLotsAndOccupancies(); 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 =
'<p class="has-text-centered has-text-grey-dark">' +
'<i class="fas fa-5x fa-circle-notch fa-spin" aria-hidden="true"></i><br />' +
"Searching..." +
"</p>";
cityssm.postJSON(urlPrefix + "/lotOccupancies/doSearchLotOccupancies", searchFormElement, (responseJSON) => {
if (responseJSON.lotOccupancies.length === 0) {
searchResultsContainerElement.innerHTML =
'<div class="message is-info">' +
'<p class="message-body">There are no records that meet the search criteria.</p>' +
"</div>";
return;
}
searchResultsContainerElement.innerHTML =
'<table class="table is-fullwidth is-striped is-hoverable">' +
"<thead>" +
"<tr>" +
'<th class="has-width-1"></th>' +
("<th>" +
exports.aliases.occupancy +
" Type</th>") +
("<th>" + exports.aliases.lot + "</th>") +
"<th>Start Date</th>" +
"<th>End Date</th>" +
("<th>" + exports.aliases.occupants + "</th>") +
"</tr>" +
"</thead>" +
"<tbody></tbody>" +
"</table>";
for (const lotOccupancy of responseJSON.lotOccupancies) {
const rowElement = document.createElement("tr");
rowElement.className =
"container--lotOccupancy";
rowElement.dataset.lotOccupancyId =
lotOccupancy.lotOccupancyId.toString();
rowElement.innerHTML =
'<td class="has-text-centered">' +
'<button class="button is-small is-success button--addLotOccupancy" data-tooltip="Add" type="button" aria-label="Add">' +
'<i class="fas fa-plus" aria-hidden="true"></i>' +
"</button>" +
"</td>" +
('<td class="has-text-weight-bold">' +
cityssm.escapeHTML(lotOccupancy.occupancyType) +
"</td>");
if (lotOccupancy.lotId) {
rowElement.insertAdjacentHTML("beforeend", "<td>" +
cityssm.escapeHTML(lotOccupancy.lotName) +
"</td>");
}
else {
rowElement.insertAdjacentHTML("beforeend", "<td>" +
'<span class="has-text-grey">(No ' +
exports.aliases.lot +
")</span>" +
"</td>");
}
rowElement.insertAdjacentHTML("beforeend", "<td>" +
lotOccupancy.occupancyStartDateString +
"</td>" +
("<td>" +
(lotOccupancy.occupancyEndDate
? lotOccupancy.occupancyEndDateString
: '<span class="has-text-grey">(No End Date)</span>') +
"</td>") +
("<td>" +
(lotOccupancy.lotOccupancyOccupants
.length === 0
? '<span class="has-text-grey">(No ' +
cityssm.escapeHTML(exports.aliases.occupants) +
")</span>"
: cityssm.escapeHTML(lotOccupancy
.lotOccupancyOccupants[0]
.occupantName) +
(lotOccupancy
.lotOccupancyOccupants
.length > 1
? " plus " +
(lotOccupancy
.lotOccupancyOccupants
.length -
1)
: "")) +
"</td>"));
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 =
'<p class="has-text-centered has-text-grey-dark">' +
'<i class="fas fa-5x fa-circle-notch fa-spin" aria-hidden="true"></i><br />' +
"Searching..." +
"</p>";
cityssm.postJSON(urlPrefix + "/lots/doSearchLots", searchFormElement, (responseJSON) => {
if (responseJSON.lots.length === 0) {
searchResultsContainerElement.innerHTML =
'<div class="message is-info">' +
'<p class="message-body">There are no records that meet the search criteria.</p>' +
"</div>";
return;
}
searchResultsContainerElement.innerHTML =
'<table class="table is-fullwidth is-striped is-hoverable">' +
"<thead>" +
"<tr>" +
'<th class="has-width-1"></th>' +
("<th>" + exports.aliases.lot + "</th>") +
("<th>" + exports.aliases.map + "</th>") +
("<th>" + exports.aliases.lot + " Type</th>") +
"<th>Status</th>" +
"</tr>" +
"</thead>" +
"<tbody></tbody>" +
"</table>";
for (const lot of responseJSON.lots) {
const rowElement = document.createElement("tr");
rowElement.className = "container--lot";
rowElement.dataset.lotId = lot.lotId.toString();
rowElement.innerHTML =
'<td class="has-text-centered">' +
'<button class="button is-small is-success button--addLot" data-tooltip="Add" type="button" aria-label="Add">' +
'<i class="fas fa-plus" aria-hidden="true"></i>' +
"</button>" +
"</td>" +
('<td class="has-text-weight-bold">' +
cityssm.escapeHTML(lot.lotName) +
"</td>") +
"<td>" +
cityssm.escapeHTML(lot.mapName) +
"</td>" +
("<td>" +
cityssm.escapeHTML(lot.lotType) +
"</td>") +
("<td>" +
cityssm.escapeHTML(lot.lotStatus) +
"</td>");
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 =
'<div class="columns">' +
('<div class="column is-narrow">' +
(milestone.workOrderMilestoneCompletionDate
? '<span class="button is-static" data-tooltip="Completed ' +
milestone.workOrderMilestoneCompletionDateString +
'" aria-label="Completed ' +
milestone.workOrderMilestoneCompletionDateString +
'">' +
'<span class="icon is-small"><i class="fas fa-check" aria-hidden="true"></i></span>' +
"</span>"
: '<button class="button button--completeMilestone" data-tooltip="Incomplete" type="button" aria-label="Incomplete">' +
'<span class="icon is-small"><i class="far fa-square" aria-hidden="true"></i></span>' +
"</button>") +
"</div>") +
('<div class="column">' +
(milestone.workOrderMilestoneTypeId
? "<strong>" +
cityssm.escapeHTML(milestone.workOrderMilestoneType) +
"</strong><br />"
: "") +
milestone.workOrderMilestoneDateString +
"<br />" +
'<span class="is-size-7">' +
cityssm.escapeHTML(milestone.workOrderMilestoneDescription) +
"</span>" +
"</div>") +
('<div class="column is-narrow">' +
'<div class="dropdown is-right">' +
('<div class="dropdown-trigger">' +
'<button class="button is-small" data-tooltip="Options" type="button" aria-label="Options">' +
'<i class="fas fa-ellipsis-v" aria-hidden="true"></i>' +
"</button>" +
"</div>") +
('<div class="dropdown-menu">' +
'<div class="dropdown-content">' +
(milestone.workOrderMilestoneCompletionDate
? '<a class="dropdown-item button--reopenMilestone" href="#">' +
'<span class="icon is-small"><i class="fas fa-times" aria-hidden="true"></i></span>' +
" <span>Reopen Milestone</span>" +
"</a>" +
'<hr class="dropdown-divider" />'
: "") +
'<a class="dropdown-item button--deleteMilestone" href="#">' +
'<span class="icon is-small"><i class="fas fa-trash has-text-danger" aria-hidden="true"></i></span>' +
" <span>Delete Milestone</span>" +
"</a>" +
"</div>" +
"</div>") +
"</div>" +
"</div>") +
"</div>";
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();
} }
})(); })();

View File

@ -2,12 +2,17 @@
import type { cityssmGlobal } from "@cityssm/bulma-webapp-js/src/types"; import type { cityssmGlobal } from "@cityssm/bulma-webapp-js/src/types";
import type { BulmaJS } from "@cityssm/bulma-js/types"; import type { BulmaJS } from "@cityssm/bulma-js/types";
import type * as globalTypes from "../types/globalTypes";
import type * as recordTypes from "../types/recordTypes"; import type * as recordTypes from "../types/recordTypes";
import { response } from "express";
declare const cityssm: cityssmGlobal; declare const cityssm: cityssmGlobal;
declare const bulmaJS: BulmaJS; declare const bulmaJS: BulmaJS;
(() => { (() => {
const los = exports.los as globalTypes.LOS;
const urlPrefix = document.querySelector("main").dataset.urlPrefix; const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const workOrderId = ( 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 renderRelatedOccupancies = () => {
const occupanciesContainerElement = document.querySelector( const occupanciesContainerElement = document.querySelector(
"#relatedTab--lotOccupancies" "#container--lotOccupancies"
) as HTMLElement; ) as HTMLElement;
document.querySelector( document.querySelector(
@ -173,6 +251,12 @@ declare const bulmaJS: BulmaJS;
lotOccupancy.occupancyEndDateString < currentDateString lotOccupancy.occupancyEndDateString < currentDateString
); );
const hasLotRecord =
lotOccupancy.lotId &&
workOrderLots.some((lot) => {
return lotOccupancy.lotId === lot.lotId;
});
rowElement.innerHTML = rowElement.innerHTML =
'<td class="has-text-centered">' + '<td class="has-text-centered">' +
(isActive (isActive
@ -191,15 +275,45 @@ declare const bulmaJS: BulmaJS;
'">' + '">' +
cityssm.escapeHTML(lotOccupancy.occupancyType) + cityssm.escapeHTML(lotOccupancy.occupancyType) +
"</a>" + "</a>" +
"</td>") + "</td>");
("<td>" +
(lotOccupancy.lotId if (lotOccupancy.lotId) {
? cityssm.escapeHTML(lotOccupancy.lotName) rowElement.insertAdjacentHTML(
: '<span class="has-text-grey">(No ' + "beforeend",
"<td>" +
cityssm.escapeHTML(lotOccupancy.lotName) +
(hasLotRecord
? ""
: ' <button class="button is-small is-light is-success button--addLot"' +
' data-lot-id="' +
lotOccupancy.lotId +
'"' +
' data-tooltip="Add ' +
cityssm.escapeHTML(exports.aliases.lot) +
'"' +
' aria-label="Add ' +
cityssm.escapeHTML(exports.aliases.lot) +
'" type="button">' +
'<i class="fas fa-plus" aria-hidden="true"></i>' +
"</button>") +
"</td>"
);
} else {
rowElement.insertAdjacentHTML(
"beforeend",
"<td>" +
'<span class="has-text-grey">(No ' +
exports.aliases.lot + exports.aliases.lot +
")</span>") + ")</span>" +
"</td>") + "</td>"
("<td>" + lotOccupancy.occupancyStartDateString + "</td>") + );
}
rowElement.insertAdjacentHTML(
"beforeend",
"<td>" +
lotOccupancy.occupancyStartDateString +
"</td>" +
("<td>" + ("<td>" +
(lotOccupancy.occupancyEndDate (lotOccupancy.occupancyEndDate
? lotOccupancy.occupancyEndDateString ? lotOccupancy.occupancyEndDateString
@ -208,7 +322,9 @@ declare const bulmaJS: BulmaJS;
("<td>" + ("<td>" +
(lotOccupancy.lotOccupancyOccupants.length === 0 (lotOccupancy.lotOccupancyOccupants.length === 0
? '<span class="has-text-grey">(No ' + ? '<span class="has-text-grey">(No ' +
cityssm.escapeHTML(exports.aliases.occupants) + cityssm.escapeHTML(
exports.aliases.occupants
) +
")</span>" ")</span>"
: cityssm.escapeHTML( : cityssm.escapeHTML(
lotOccupancy.lotOccupancyOccupants[0] lotOccupancy.lotOccupancyOccupants[0]
@ -216,7 +332,8 @@ declare const bulmaJS: BulmaJS;
) + ) +
(lotOccupancy.lotOccupancyOccupants.length > 1 (lotOccupancy.lotOccupancyOccupants.length > 1
? " plus " + ? " plus " +
(lotOccupancy.lotOccupancyOccupants.length - (lotOccupancy.lotOccupancyOccupants
.length -
1) 1)
: "")) + : "")) +
"</td>") + "</td>") +
@ -224,7 +341,14 @@ declare const bulmaJS: BulmaJS;
'<button class="button is-small is-light is-danger button--deleteLotOccupancy" data-tooltip="Delete Relationship" type="button">' + '<button class="button is-small is-light is-danger button--deleteLotOccupancy" data-tooltip="Delete Relationship" type="button">' +
'<i class="fas fa-trash" aria-hidden="true"></i>' + '<i class="fas fa-trash" aria-hidden="true"></i>' +
"</button>" + "</button>" +
"</td>"); "</td>")
);
if (lotOccupancy.lotId && !hasLotRecord) {
rowElement
.querySelector(".button--addLot")
.addEventListener("click", addLotFromLotOccupancy);
}
rowElement rowElement
.querySelector(".button--deleteLotOccupancy") .querySelector(".button--deleteLotOccupancy")
@ -256,8 +380,7 @@ declare const bulmaJS: BulmaJS;
workOrderLots?: recordTypes.Lot[]; workOrderLots?: recordTypes.Lot[];
}) => { }) => {
if (responseJSON.success) { if (responseJSON.success) {
workOrderLots = workOrderLots = responseJSON.workOrderLots;
responseJSON.workOrderLots;
renderRelatedLotsAndOccupancies(); renderRelatedLotsAndOccupancies();
} else { } else {
bulmaJS.alert({ bulmaJS.alert({
@ -293,7 +416,7 @@ declare const bulmaJS: BulmaJS;
const renderRelatedLots = () => { const renderRelatedLots = () => {
const lotsContainerElement = document.querySelector( const lotsContainerElement = document.querySelector(
"#relatedTab--lots" "#container--lots"
) as HTMLElement; ) as HTMLElement;
document.querySelector( document.querySelector(
@ -364,6 +487,348 @@ declare const bulmaJS: BulmaJS;
}; };
renderRelatedLotsAndOccupancies(); 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 =
'<p class="has-text-centered has-text-grey-dark">' +
'<i class="fas fa-5x fa-circle-notch fa-spin" aria-hidden="true"></i><br />' +
"Searching..." +
"</p>";
cityssm.postJSON(
urlPrefix + "/lotOccupancies/doSearchLotOccupancies",
searchFormElement,
(responseJSON: {
lotOccupancies: recordTypes.LotOccupancy[];
}) => {
if (responseJSON.lotOccupancies.length === 0) {
searchResultsContainerElement.innerHTML =
'<div class="message is-info">' +
'<p class="message-body">There are no records that meet the search criteria.</p>' +
"</div>";
return;
}
searchResultsContainerElement.innerHTML =
'<table class="table is-fullwidth is-striped is-hoverable">' +
"<thead>" +
"<tr>" +
'<th class="has-width-1"></th>' +
("<th>" +
exports.aliases.occupancy +
" Type</th>") +
("<th>" + exports.aliases.lot + "</th>") +
"<th>Start Date</th>" +
"<th>End Date</th>" +
("<th>" + exports.aliases.occupants + "</th>") +
"</tr>" +
"</thead>" +
"<tbody></tbody>" +
"</table>";
for (const lotOccupancy of responseJSON.lotOccupancies) {
const rowElement = document.createElement("tr");
rowElement.className =
"container--lotOccupancy";
rowElement.dataset.lotOccupancyId =
lotOccupancy.lotOccupancyId.toString();
rowElement.innerHTML =
'<td class="has-text-centered">' +
'<button class="button is-small is-success button--addLotOccupancy" data-tooltip="Add" type="button" aria-label="Add">' +
'<i class="fas fa-plus" aria-hidden="true"></i>' +
"</button>" +
"</td>" +
('<td class="has-text-weight-bold">' +
cityssm.escapeHTML(
lotOccupancy.occupancyType
) +
"</td>");
if (lotOccupancy.lotId) {
rowElement.insertAdjacentHTML(
"beforeend",
"<td>" +
cityssm.escapeHTML(
lotOccupancy.lotName
) +
"</td>"
);
} else {
rowElement.insertAdjacentHTML(
"beforeend",
"<td>" +
'<span class="has-text-grey">(No ' +
exports.aliases.lot +
")</span>" +
"</td>"
);
}
rowElement.insertAdjacentHTML(
"beforeend",
"<td>" +
lotOccupancy.occupancyStartDateString +
"</td>" +
("<td>" +
(lotOccupancy.occupancyEndDate
? lotOccupancy.occupancyEndDateString
: '<span class="has-text-grey">(No End Date)</span>') +
"</td>") +
("<td>" +
(lotOccupancy.lotOccupancyOccupants
.length === 0
? '<span class="has-text-grey">(No ' +
cityssm.escapeHTML(
exports.aliases.occupants
) +
")</span>"
: cityssm.escapeHTML(
lotOccupancy
.lotOccupancyOccupants[0]
.occupantName
) +
(lotOccupancy
.lotOccupancyOccupants
.length > 1
? " plus " +
(lotOccupancy
.lotOccupancyOccupants
.length -
1)
: "")) +
"</td>")
);
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 =
'<p class="has-text-centered has-text-grey-dark">' +
'<i class="fas fa-5x fa-circle-notch fa-spin" aria-hidden="true"></i><br />' +
"Searching..." +
"</p>";
cityssm.postJSON(
urlPrefix + "/lots/doSearchLots",
searchFormElement,
(responseJSON: { lots: recordTypes.Lot[] }) => {
if (responseJSON.lots.length === 0) {
searchResultsContainerElement.innerHTML =
'<div class="message is-info">' +
'<p class="message-body">There are no records that meet the search criteria.</p>' +
"</div>";
return;
}
searchResultsContainerElement.innerHTML =
'<table class="table is-fullwidth is-striped is-hoverable">' +
"<thead>" +
"<tr>" +
'<th class="has-width-1"></th>' +
("<th>" + exports.aliases.lot + "</th>") +
("<th>" + exports.aliases.map + "</th>") +
("<th>" + exports.aliases.lot + " Type</th>") +
"<th>Status</th>" +
"</tr>" +
"</thead>" +
"<tbody></tbody>" +
"</table>";
for (const lot of responseJSON.lots) {
const rowElement = document.createElement("tr");
rowElement.className = "container--lot";
rowElement.dataset.lotId = lot.lotId.toString();
rowElement.innerHTML =
'<td class="has-text-centered">' +
'<button class="button is-small is-success button--addLot" data-tooltip="Add" type="button" aria-label="Add">' +
'<i class="fas fa-plus" aria-hidden="true"></i>' +
"</button>" +
"</td>" +
('<td class="has-text-weight-bold">' +
cityssm.escapeHTML(lot.lotName) +
"</td>") +
"<td>" +
cityssm.escapeHTML(lot.mapName) +
"</td>" +
("<td>" +
cityssm.escapeHTML(lot.lotType) +
"</td>") +
("<td>" +
cityssm.escapeHTML(lot.lotStatus) +
"</td>");
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 * 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 =
'<div class="columns">' +
('<div class="column is-narrow">' +
(milestone.workOrderMilestoneCompletionDate
? '<span class="button is-static" data-tooltip="Completed ' +
milestone.workOrderMilestoneCompletionDateString +
'" aria-label="Completed ' +
milestone.workOrderMilestoneCompletionDateString +
'">' +
'<span class="icon is-small"><i class="fas fa-check" aria-hidden="true"></i></span>' +
"</span>"
: '<button class="button button--completeMilestone" data-tooltip="Incomplete" type="button" aria-label="Incomplete">' +
'<span class="icon is-small"><i class="far fa-square" aria-hidden="true"></i></span>' +
"</button>") +
"</div>") +
('<div class="column">' +
(milestone.workOrderMilestoneTypeId
? "<strong>" +
cityssm.escapeHTML(
milestone.workOrderMilestoneType
) +
"</strong><br />"
: "") +
milestone.workOrderMilestoneDateString +
"<br />" +
'<span class="is-size-7">' +
cityssm.escapeHTML(
milestone.workOrderMilestoneDescription
) +
"</span>" +
"</div>") +
('<div class="column is-narrow">' +
'<div class="dropdown is-right">' +
('<div class="dropdown-trigger">' +
'<button class="button is-small" data-tooltip="Options" type="button" aria-label="Options">' +
'<i class="fas fa-ellipsis-v" aria-hidden="true"></i>' +
"</button>" +
"</div>") +
('<div class="dropdown-menu">' +
'<div class="dropdown-content">' +
(milestone.workOrderMilestoneCompletionDate
? '<a class="dropdown-item button--reopenMilestone" href="#">' +
'<span class="icon is-small"><i class="fas fa-times" aria-hidden="true"></i></span>' +
" <span>Reopen Milestone</span>" +
"</a>" +
'<hr class="dropdown-divider" />'
: "") +
'<a class="dropdown-item button--deleteMilestone" href="#">' +
'<span class="icon is-small"><i class="fas fa-trash has-text-danger" aria-hidden="true"></i></span>' +
" <span>Delete Milestone</span>" +
"</a>" +
"</div>" +
"</div>") +
"</div>" +
"</div>") +
"</div>";
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();
}
})(); })();

View File

@ -0,0 +1,56 @@
<div class="modal">
<div class="modal-background"></div>
<div class="modal-card" style="width:900px">
<header class="modal-card-head">
<h3 class="modal-card-title">
Add Related <span class="alias" data-alias="Lot"></span> to Work Order
</h3>
<button class="delete is-close-modal-button" aria-label="close" type="button"></button>
</header>
<section class="modal-card-body">
<div class="box">
<form id="form--lotSearch">
<input name="limit" type="hidden" value="100" />
<input name="offset" type="hidden" value="0" />
<input id="lotSearch--notWorkOrderId" name="notWorkOrderId" type="hidden" value="" />
<div class="columns">
<div class="column">
<div class="field">
<label class="label" for="lotSearch--lotName">
<span class="alias" data-alias="Lot"></span>
Name
</label>
<div class="control has-icons-left">
<input class="input" id="lotSearch--lotName" name="lotName" type="text" />
<span class="icon is-small is-left">
<i class="fas fa-search" aria-hidden="true"></i>
</span>
</div>
</div>
</div>
<div class="column">
<div class="field">
<label class="label" for="lotSearch--lotStatusId">Status</label>
<div class="control has-icons-left">
<div class="select is-fullwidth">
<select id="lotSearch--lotStatusId" name="lotStatusId">
<option value="">(All Statuses)</option>
</select>
</div>
<span class="icon is-small is-left">
<i class="fas fa-search" aria-hidden="true"></i>
</span>
</div>
</div>
</div>
</div>
</form>
</div>
<div id="resultsContainer--lotAdd"></div>
</section>
<footer class="modal-card-foot justify-right">
<button class="button is-close-modal-button" type="button">Close</button>
</footer>
</div>
</div>

View File

@ -0,0 +1,55 @@
<div class="modal">
<div class="modal-background"></div>
<div class="modal-card" style="width:900px">
<header class="modal-card-head">
<h3 class="modal-card-title">
Add Related <span class="alias" data-alias="Occupancy"></span> to Work Order
</h3>
<button class="delete is-close-modal-button" aria-label="close" type="button"></button>
</header>
<section class="modal-card-body">
<div class="box">
<form id="form--lotOccupancySearch">
<input name="limit" type="hidden" value="100" />
<input name="offset" type="hidden" value="0" />
<input id="lotOccupancySearch--notWorkOrderId" name="notWorkOrderId" type="hidden" value="" />
<input id="lotOccupancySearch--occupancyEffectiveDateString" name="occupancyEffectiveDateString" type="hidden" value="" />
<div class="columns">
<div class="column">
<div class="field">
<label class="label" for="lotOccupancySearch--occupantName">
<span class="alias" data-alias="Occupant"></span>
Name
</label>
<div class="control has-icons-left">
<input class="input" id="lotOccupancySearch--occupantName" name="occupantName" type="text" />
<span class="icon is-small is-left">
<i class="fas fa-search" aria-hidden="true"></i>
</span>
</div>
</div>
</div>
<div class="column">
<div class="field">
<label class="label" for="lotOccupancySearch--lotName">
<span class="alias" data-alias="Lot"></span>
Name
</label>
<div class="control has-icons-left">
<input class="input" id="lotOccupancySearch--lotName" name="lotName" type="text" />
<span class="icon is-small is-left">
<i class="fas fa-search" aria-hidden="true"></i>
</span>
</div>
</div>
</div>
</div>
</form>
</div>
<div id="resultsContainer--lotOccupancyAdd"></div>
</section>
<footer class="modal-card-foot justify-right">
<button class="button is-close-modal-button" type="button">Close</button>
</footer>
</div>
</div>

File diff suppressed because one or more lines are too long

View File

@ -6,8 +6,12 @@ import handler_view from "../handlers/workOrders-get/view.js";
import handler_doReopenWorkOrder from "../handlers/workOrders-post/doReopenWorkOrder.js"; import handler_doReopenWorkOrder from "../handlers/workOrders-post/doReopenWorkOrder.js";
import handler_edit from "../handlers/workOrders-get/edit.js"; import handler_edit from "../handlers/workOrders-get/edit.js";
import handler_doUpdateWorkOrder from "../handlers/workOrders-post/doUpdateWorkOrder.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_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_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(); export const router = Router();
router.get("/", handler_search); router.get("/", handler_search);
router.post("/doSearchWorkOrders", handler_doSearchWorkOrders); router.post("/doSearchWorkOrders", handler_doSearchWorkOrders);
@ -15,6 +19,10 @@ router.get("/:workOrderId", handler_view);
router.post("/doReopenWorkOrder", permissionHandlers.updatePostHandler, handler_doReopenWorkOrder); router.post("/doReopenWorkOrder", permissionHandlers.updatePostHandler, handler_doReopenWorkOrder);
router.get("/:workOrderId/edit", permissionHandlers.updateGetHandler, handler_edit); router.get("/:workOrderId/edit", permissionHandlers.updateGetHandler, handler_edit);
router.post("/doUpdateWorkOrder", permissionHandlers.updatePostHandler, handler_doUpdateWorkOrder); router.post("/doUpdateWorkOrder", permissionHandlers.updatePostHandler, handler_doUpdateWorkOrder);
router.post("/doAddWorkOrderLotOccupancy", permissionHandlers.updatePostHandler, handler_doAddWorkOrderLotOccupancy);
router.post("/doDeleteWorkOrderLotOccupancy", permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLotOccupancy); router.post("/doDeleteWorkOrderLotOccupancy", permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLotOccupancy);
router.post("/doAddWorkOrderLot", permissionHandlers.updatePostHandler, handler_doAddWorkOrderLot);
router.post("/doDeleteWorkOrderLot", permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLot); router.post("/doDeleteWorkOrderLot", permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLot);
router.post("/doCompleteWorkOrderMilestone", permissionHandlers.updatePostHandler, handler_doCompleteWorkOrderMilestone);
router.post("/doReopenWorkOrderMilestone", permissionHandlers.updatePostHandler, handler_doReopenWorkOrderMilestone);
export default router; export default router;

View File

@ -10,9 +10,16 @@ import handler_doReopenWorkOrder from "../handlers/workOrders-post/doReopenWorkO
import handler_edit from "../handlers/workOrders-get/edit.js"; import handler_edit from "../handlers/workOrders-get/edit.js";
import handler_doUpdateWorkOrder from "../handlers/workOrders-post/doUpdateWorkOrder.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_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_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(); export const router = Router();
router.get("/", handler_search); router.get("/", handler_search);
@ -39,16 +46,40 @@ router.post(
handler_doUpdateWorkOrder handler_doUpdateWorkOrder
); );
router.post(
"/doAddWorkOrderLotOccupancy",
permissionHandlers.updatePostHandler,
handler_doAddWorkOrderLotOccupancy
);
router.post( router.post(
"/doDeleteWorkOrderLotOccupancy", "/doDeleteWorkOrderLotOccupancy",
permissionHandlers.updatePostHandler, permissionHandlers.updatePostHandler,
handler_doDeleteWorkOrderLotOccupancy handler_doDeleteWorkOrderLotOccupancy
); );
router.post(
"/doAddWorkOrderLot",
permissionHandlers.updatePostHandler,
handler_doAddWorkOrderLot
);
router.post( router.post(
"/doDeleteWorkOrderLot", "/doDeleteWorkOrderLot",
permissionHandlers.updatePostHandler, permissionHandlers.updatePostHandler,
handler_doDeleteWorkOrderLot handler_doDeleteWorkOrderLot
); );
router.post(
"/doCompleteWorkOrderMilestone",
permissionHandlers.updatePostHandler,
handler_doCompleteWorkOrderMilestone
);
router.post(
"/doReopenWorkOrderMilestone",
permissionHandlers.updatePostHandler,
handler_doReopenWorkOrderMilestone
);
export default router; export default router;

View File

@ -38,6 +38,8 @@
<% } %> <% } %>
</h1> </h1>
<div class="columns">
<div class="column">
<form id="form--workOrderEdit"> <form id="form--workOrderEdit">
<input id="workOrderEdit--workOrderId" name="workOrderId" type="hidden" value="<%= workOrder.workOrderId %>" /> <input id="workOrderEdit--workOrderId" name="workOrderId" type="hidden" value="<%= workOrder.workOrderId %>" />
<div class="panel"> <div class="panel">
@ -126,25 +128,11 @@
<% } %> <% } %>
</div> </div>
</div> </div>
</form>
<% if (!isCreate) { %> <% if (!isCreate) { %>
<div class="panel"> <div class="panel mt-4">
<div class="panel-heading"> <h2 class="panel-heading">Related <%= configFunctions.getProperty("aliases.lots") %></h2>
<div class="level">
<div class="level-left">
<div class="level-item">
<h2 class="title has-text-weight-bold is-5">Related <%= configFunctions.getProperty("aliases.lots") %></h2>
</div>
</div>
<div class="level-right">
<div class="level-item">
<button class="button is-small is-success" id="button--addWorkOrderLot" type="button">
<span class="icon is-small"><i class="fas fa-plus" aria-hidden="true"></i></span>
<span>Add <%= configFunctions.getProperty("aliases.lots") %></span>
</button>
</div>
</div>
</div>
</div>
<div class="panel-block is-block"> <div class="panel-block is-block">
<% <%
const tabToSelect = (workOrder.workOrderLotOccupancies.length > 0 || workOrder.workOrderLots.length === 0 ? "lotOccupancies" : "lots"); const tabToSelect = (workOrder.workOrderLotOccupancies.length > 0 || workOrder.workOrderLots.length === 0 ? "lotOccupancies" : "lots");
@ -167,14 +155,53 @@
</div> </div>
<div class="tab-container"> <div class="tab-container">
<div class="<%= (tabToSelect === "lotOccupancies" ? "" : "is-hidden") %>" id="relatedTab--lotOccupancies"> <div class="<%= (tabToSelect === "lotOccupancies" ? "" : "is-hidden") %>" id="relatedTab--lotOccupancies">
<div class="box has-background-light has-text-right p-3">
<button class="button is-success" id="button--addLotOccupancy" type="button">
<span class="icon is-small"><i class="fas fa-plus" aria-hidden="true"></i></span>
<span>Add Related <%= configFunctions.getProperty("aliases.occupancy") %></span>
</button>
</div>
<div id="container--lotOccupancies"></div>
</div> </div>
<div class="<%= (tabToSelect === "lots" ? "" : "is-hidden") %>" id="relatedTab--lots"> <div class="<%= (tabToSelect === "lots" ? "" : "is-hidden") %>" id="relatedTab--lots">
<div class="box has-background-light has-text-right p-3">
<button class="button is-success" id="button--addLot" type="button">
<span class="icon is-small"><i class="fas fa-plus" aria-hidden="true"></i></span>
<span>Add Related <%= configFunctions.getProperty("aliases.lot") %></span>
</button>
</div>
<div id="container--lots"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<% } %> <% } %>
</form> </div>
<% if (!isCreate) { %>
<div class="column is-4">
<div class="panel" id="panel--milestones">
<div class="panel-heading">
<div class="level">
<div class="level-left">
<div class="level-item">
<h2 class="title has-text-weight-bold is-5">Milestones</h2>
</div>
</div>
<div class="level-right">
<div class="level-item">
<button class="button is-small is-success" id="button--addMilestone" type="button">
<span class="icon is-small"><i class="fas fa-plus" aria-hidden="true"></i></span>
<span>Add Milestone</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<% } %>
</div>
<%- include('_footerA'); -%> <%- include('_footerA'); -%>
@ -183,6 +210,8 @@
exports.workOrderLots = <%- JSON.stringify(workOrder.workOrderLots) %>; exports.workOrderLots = <%- JSON.stringify(workOrder.workOrderLots) %>;
exports.workOrderLotOccupancies = <%- JSON.stringify(workOrder.workOrderLotOccupancies) %>; exports.workOrderLotOccupancies = <%- JSON.stringify(workOrder.workOrderLotOccupancies) %>;
exports.workOrderMilestones = <%- JSON.stringify(workOrder.workOrderMilestones) %>; exports.workOrderMilestones = <%- JSON.stringify(workOrder.workOrderMilestones) %>;
exports.lotStatuses = <%- JSON.stringify(lotStatuses) %>;
</script> </script>
<script src="<% urlPrefix %>/javascripts/workOrderEdit.min.js"></script> <script src="<% urlPrefix %>/javascripts/workOrderEdit.min.js"></script>