development

- close work order
- delete work order
- milestone calendar
deepsource-autofix-76c6eb20
Dan Gowans 2022-09-14 16:08:18 -04:00
parent 754cf80a36
commit f4a6e14dba
68 changed files with 1167 additions and 164 deletions

1
app.js
View File

@ -60,6 +60,7 @@ app.use(urlPrefix + "/lib/fa", express.static(path.join("node_modules", "@fortaw
app.use(urlPrefix + "/lib/cityssm-bulma-webapp-js", express.static(path.join("node_modules", "@cityssm", "bulma-webapp-js")));
app.use(urlPrefix + "/lib/cityssm-bulma-js", express.static(path.join("node_modules", "@cityssm", "bulma-js", "dist")));
app.use(urlPrefix + "/lib/leaflet", express.static(path.join("node_modules", "leaflet", "dist")));
app.use(urlPrefix + "/lib/randomcolor", express.static(path.join("node_modules", "randomcolor")));
const sessionCookieName = configFunctions.getProperty("session.cookieName");
const FileStoreSession = FileStore(session);
app.use(session({

5
app.ts
View File

@ -122,6 +122,11 @@ app.use(
express.static(path.join("node_modules", "leaflet", "dist"))
);
app.use(
urlPrefix + "/lib/randomcolor",
express.static(path.join("node_modules", "randomcolor"))
);
/*
* SESSION MANAGEMENT
*/

View File

@ -25,7 +25,9 @@ config.settings.lot = {
config.settings.lotOccupancy.occupantCityDefault = "Sault Ste. Marie";
config.settings.map.mapCityDefault = "Sault Ste. Marie";
config.settings.workOrders = {
workOrderNumberLength: 6
workOrderNumberLength: 6,
workOrderMilestoneDateRecentBeforeDays: 7,
workOrderMilestoneDateRecentAfterDays: 30
};
config.aliases.externalReceiptNumber = "GP Receipt Number";
export default config;

View File

@ -39,7 +39,9 @@ config.settings.lotOccupancy.occupantCityDefault = "Sault Ste. Marie";
config.settings.map.mapCityDefault = "Sault Ste. Marie";
config.settings.workOrders = {
workOrderNumberLength: 6
workOrderNumberLength: 6,
workOrderMilestoneDateRecentBeforeDays: 7,
workOrderMilestoneDateRecentAfterDays: 30
};
config.aliases.externalReceiptNumber = "GP Receipt Number";

View File

@ -2,7 +2,11 @@ import { getLotStatuses, getWorkOrderMilestoneTypes, getWorkOrderTypes } from ".
import * as configFunctions from "../../helpers/functions.config.js";
import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js";
export const handler = (request, response) => {
const workOrder = getWorkOrder(request.params.workOrderId);
const workOrder = getWorkOrder(request.params.workOrderId, {
includeLotsAndLotOccupancies: true,
includeComments: true,
includeMilestones: true
});
if (!workOrder) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") +
"/workOrders/?error=workOrderIdNotFound");

View File

@ -11,7 +11,11 @@ import * as configFunctions from "../../helpers/functions.config.js";
import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js";
export const handler: RequestHandler = (request, response) => {
const workOrder = getWorkOrder(request.params.workOrderId);
const workOrder = getWorkOrder(request.params.workOrderId, {
includeLotsAndLotOccupancies: true,
includeComments: true,
includeMilestones: true
});
if (!workOrder) {
return response.redirect(

View File

@ -1,7 +1,11 @@
import * as configFunctions from "../../helpers/functions.config.js";
import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js";
export const handler = (request, response) => {
const workOrder = getWorkOrder(request.params.workOrderId);
const workOrder = getWorkOrder(request.params.workOrderId, {
includeLotsAndLotOccupancies: true,
includeComments: true,
includeMilestones: true
});
if (!workOrder) {
return response.redirect(configFunctions.getProperty("reverseProxy.urlPrefix") +
"/workOrders/?error=workOrderIdNotFound");

View File

@ -5,7 +5,11 @@ import * as configFunctions from "../../helpers/functions.config.js";
import { getWorkOrder } from "../../helpers/lotOccupancyDB/getWorkOrder.js";
export const handler: RequestHandler = (request, response) => {
const workOrder = getWorkOrder(request.params.workOrderId);
const workOrder = getWorkOrder(request.params.workOrderId, {
includeLotsAndLotOccupancies: true,
includeComments: true,
includeMilestones: true
});
if (!workOrder) {
return response.redirect(

View File

@ -4,6 +4,8 @@ export const handler = async (request, response) => {
const success = addWorkOrderMilestone(request.body, request.session);
const workOrderMilestones = getWorkOrderMilestones({
workOrderId: request.body.workOrderId
}, {
orderBy: "completion"
});
response.json({
success,

View File

@ -6,9 +6,14 @@ import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrde
export const handler: RequestHandler = async (request, response) => {
const success = addWorkOrderMilestone(request.body, request.session);
const workOrderMilestones = getWorkOrderMilestones({
const workOrderMilestones = getWorkOrderMilestones(
{
workOrderId: request.body.workOrderId
});
},
{
orderBy: "completion"
}
);
response.json({
success,

View File

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

View File

@ -0,0 +1,8 @@
import { closeWorkOrder } from "../../helpers/lotOccupancyDB/closeWorkOrder.js";
export const handler = async (request, response) => {
const success = closeWorkOrder(request.body, request.session);
response.json({
success
});
};
export default handler;

View File

@ -0,0 +1,16 @@
import type { RequestHandler } from "express";
import { closeWorkOrder } from "../../helpers/lotOccupancyDB/closeWorkOrder.js";
export const handler: RequestHandler = async (request, response) => {
const success = closeWorkOrder(
request.body,
request.session
);
response.json({
success
});
};
export default handler;

View File

@ -6,6 +6,8 @@ export const handler = async (request, response) => {
}, request.session);
const workOrderMilestones = getWorkOrderMilestones({
workOrderId: request.body.workOrderId
}, {
orderBy: "completion"
});
response.json({
success,

View File

@ -12,9 +12,14 @@ export const handler: RequestHandler = async (request, response) => {
request.session
);
const workOrderMilestones = getWorkOrderMilestones({
const workOrderMilestones = getWorkOrderMilestones(
{
workOrderId: request.body.workOrderId
});
},
{
orderBy: "completion"
}
);
response.json({
success,

View File

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

View File

@ -0,0 +1,8 @@
import { deleteWorkOrder } from "../../helpers/lotOccupancyDB/deleteWorkOrder.js";
export const handler = async (request, response) => {
const success = deleteWorkOrder(request.body.workOrderId, request.session);
response.json({
success
});
};
export default handler;

View File

@ -0,0 +1,13 @@
import type { RequestHandler } from "express";
import { deleteWorkOrder } from "../../helpers/lotOccupancyDB/deleteWorkOrder.js";
export const handler: RequestHandler = async (request, response) => {
const success = deleteWorkOrder(request.body.workOrderId, request.session);
response.json({
success
});
};
export default handler;

View File

@ -4,6 +4,8 @@ export const handler = async (request, response) => {
const success = deleteWorkOrderMilestone(request.body.workOrderMilestoneId, request.session);
const workOrderMilestones = getWorkOrderMilestones({
workOrderId: request.body.workOrderId
}, {
orderBy: "completion"
});
response.json({
success,

View File

@ -10,9 +10,14 @@ export const handler: RequestHandler = async (request, response) => {
request.session
);
const workOrderMilestones = getWorkOrderMilestones({
const workOrderMilestones = getWorkOrderMilestones(
{
workOrderId: request.body.workOrderId
});
},
{
orderBy: "completion"
}
);
response.json({
success,

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 { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrderMilestones.js";
export const handler = async (request, response) => {
const workOrderMilestones = getWorkOrderMilestones(request.body, {
includeWorkOrders: true,
orderBy: "date"
});
response.json({
workOrderMilestones
});
};
export default handler;

View File

@ -0,0 +1,16 @@
import type { RequestHandler } from "express";
import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrderMilestones.js";
export const handler: RequestHandler = async (request, response) => {
const workOrderMilestones = getWorkOrderMilestones(request.body, {
includeWorkOrders: true,
orderBy: "date"
});
response.json({
workOrderMilestones
});
};
export default handler;

View File

@ -4,6 +4,8 @@ export const handler = async (request, response) => {
const success = reopenWorkOrderMilestone(request.body.workOrderMilestoneId, request.session);
const workOrderMilestones = getWorkOrderMilestones({
workOrderId: request.body.workOrderId
}, {
orderBy: "completion"
});
response.json({
success,

View File

@ -10,9 +10,14 @@ export const handler: RequestHandler = async (request, response) => {
request.session
);
const workOrderMilestones = getWorkOrderMilestones({
const workOrderMilestones = getWorkOrderMilestones(
{
workOrderId: request.body.workOrderId
});
},
{
orderBy: "completion"
}
);
response.json({
success,

View File

@ -4,6 +4,8 @@ export const handler = async (request, response) => {
const success = updateWorkOrderMilestone(request.body, request.session);
const workOrderMilestones = getWorkOrderMilestones({
workOrderId: request.body.workOrderId
}, {
orderBy: "completion"
});
response.json({
success,

View File

@ -6,9 +6,14 @@ import { getWorkOrderMilestones } from "../../helpers/lotOccupancyDB/getWorkOrde
export const handler: RequestHandler = async (request, response) => {
const success = updateWorkOrderMilestone(request.body, request.session);
const workOrderMilestones = getWorkOrderMilestones({
const workOrderMilestones = getWorkOrderMilestones(
{
workOrderId: request.body.workOrderId
});
},
{
orderBy: "completion"
}
);
response.json({
success,

View File

@ -33,4 +33,6 @@ export declare function getProperty(propertyName: "settings.lotOccupancy.occupan
export declare function getProperty(propertyName: "settings.lotOccupancy.occupantProvinceDefault"): string;
export declare function getProperty(propertyName: "settings.fees.taxPercentageDefault"): number;
export declare function getProperty(propertyName: "settings.workOrders.workOrderNumberLength"): number;
export declare function getProperty(propertyName: "settings.workOrders.workOrderMilestoneDateRecentBeforeDays"): number;
export declare function getProperty(propertyName: "settings.workOrders.workOrderMilestoneDateRecentAfterDays"): number;
export declare const keepAliveMillis: number;

View File

@ -32,6 +32,8 @@ configFallbackValues.set("settings.lotOccupancy.occupantCityDefault", "");
configFallbackValues.set("settings.lotOccupancy.occupantProvinceDefault", "");
configFallbackValues.set("settings.fees.taxPercentageDefault", 0);
configFallbackValues.set("settings.workOrders.workOrderNumberLength", 6);
configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentBeforeDays", 5);
configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentAfterDays", 60);
export function getProperty(propertyName) {
const propertyNameSplit = propertyName.split(".");
let currentObject = config;

View File

@ -63,6 +63,14 @@ configFallbackValues.set("settings.lotOccupancy.occupantProvinceDefault", "");
configFallbackValues.set("settings.fees.taxPercentageDefault", 0);
configFallbackValues.set("settings.workOrders.workOrderNumberLength", 6);
configFallbackValues.set(
"settings.workOrders.workOrderMilestoneDateRecentBeforeDays",
5
);
configFallbackValues.set(
"settings.workOrders.workOrderMilestoneDateRecentAfterDays",
60
);
/*
* Set up function overloads
@ -148,6 +156,14 @@ export function getProperty(
propertyName: "settings.workOrders.workOrderNumberLength"
): number;
export function getProperty(
propertyName: "settings.workOrders.workOrderMilestoneDateRecentBeforeDays"
): number;
export function getProperty(
propertyName: "settings.workOrders.workOrderMilestoneDateRecentAfterDays"
): number;
export function getProperty(propertyName: string): unknown {
const propertyNameSplit = propertyName.split(".");

View File

@ -3,5 +3,5 @@ interface AddWorkOrderForm {
workOrderId: number | string;
workOrderCloseDateString?: string;
}
export declare const closeWorkOrder: (workOrderForm: AddWorkOrderForm, requestSession: recordTypes.PartialSession) => number;
export declare const closeWorkOrder: (workOrderForm: AddWorkOrderForm, requestSession: recordTypes.PartialSession) => boolean;
export default closeWorkOrder;

View File

@ -14,6 +14,6 @@ export const closeWorkOrder = (workOrderForm, requestSession) => {
? dateStringToInteger(workOrderForm.workOrderCloseDateString)
: dateToInteger(new Date()), requestSession.user.userName, rightNow.getTime(), workOrderForm.workOrderId);
database.close();
return result.lastInsertRowid;
return result.changes > 0;
};
export default closeWorkOrder;

View File

@ -17,7 +17,7 @@ interface AddWorkOrderForm {
export const closeWorkOrder = (
workOrderForm: AddWorkOrderForm,
requestSession: recordTypes.PartialSession
): number => {
): boolean => {
const database = sqlite(databasePath);
const rightNow = new Date();
@ -41,7 +41,7 @@ export const closeWorkOrder = (
database.close();
return result.lastInsertRowid as number;
return result.changes > 0;
};
export default closeWorkOrder;

View File

@ -0,0 +1,3 @@
import type * as recordTypes from "../../types/recordTypes";
export declare const deleteWorkOrder: (workOrderId: number | string, requestSession: recordTypes.PartialSession) => boolean;
export default deleteWorkOrder;

View File

@ -0,0 +1,15 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
export const deleteWorkOrder = (workOrderId, requestSession) => {
const database = sqlite(databasePath);
const rightNowMillis = Date.now();
const result = database
.prepare("update WorkOrders" +
" set recordDelete_userName = ?," +
" recordDelete_timeMillis = ?" +
" where workOrderId = ?")
.run(requestSession.user.userName, rightNowMillis, workOrderId);
database.close();
return result.changes > 0;
};
export default deleteWorkOrder;

View File

@ -0,0 +1,29 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
import type * as recordTypes from "../../types/recordTypes";
export const deleteWorkOrder = (
workOrderId: number | string,
requestSession: recordTypes.PartialSession
): boolean => {
const database = sqlite(databasePath);
const rightNowMillis = Date.now();
const result = database
.prepare(
"update WorkOrders" +
" set recordDelete_userName = ?," +
" recordDelete_timeMillis = ?" +
" where workOrderId = ?"
)
.run(requestSession.user.userName, rightNowMillis, workOrderId);
database.close();
return result.changes > 0;
};
export default deleteWorkOrder;

View File

@ -1,4 +1,10 @@
import sqlite from "better-sqlite3";
import type * as recordTypes from "../../types/recordTypes";
interface WorkOrderOptions {
includeLotsAndLotOccupancies: boolean;
includeComments: boolean;
includeMilestones: boolean;
}
export declare const getWorkOrderByWorkOrderNumber: (workOrderNumber: string) => recordTypes.WorkOrder;
export declare const getWorkOrder: (workOrderId: number | string) => recordTypes.WorkOrder;
export declare const getWorkOrder: (workOrderId: number | string, options: WorkOrderOptions, connectedDatabase?: sqlite.Database) => recordTypes.WorkOrder;
export default getWorkOrder;

View File

@ -13,8 +13,9 @@ const baseSQL = "select w.workOrderId," +
" from WorkOrders w" +
" left join WorkOrderTypes t on w.workOrderTypeId = t.workOrderTypeId" +
" where w.recordDelete_timeMillis is null";
const _getWorkOrder = (sql, workOrderId_or_workOrderNumber) => {
const database = sqlite(databasePath, {
const _getWorkOrder = (sql, workOrderId_or_workOrderNumber, options, connectedDatabase) => {
const database = connectedDatabase ||
sqlite(databasePath, {
readonly: true
});
database.function("userFn_dateIntegerToString", dateIntegerToString);
@ -22,6 +23,7 @@ const _getWorkOrder = (sql, workOrderId_or_workOrderNumber) => {
.prepare(sql)
.get(workOrderId_or_workOrderNumber);
if (workOrder) {
if (options.includeLotsAndLotOccupancies) {
workOrder.workOrderLots = getLots({
workOrderId: workOrder.workOrderId
}, {
@ -35,18 +37,32 @@ const _getWorkOrder = (sql, workOrderId_or_workOrderNumber) => {
offset: 0,
includeOccupants: true
}, database).lotOccupancies;
}
if (options.includeComments) {
workOrder.workOrderComments = getWorkOrderComments(workOrder.workOrderId, database);
}
if (options.includeMilestones) {
workOrder.workOrderMilestones = getWorkOrderMilestones({
workOrderId: workOrder.workOrderId
}, {
includeWorkOrders: false,
orderBy: "completion"
}, database);
}
}
if (!connectedDatabase) {
database.close();
}
return workOrder;
};
export const getWorkOrderByWorkOrderNumber = (workOrderNumber) => {
return _getWorkOrder(baseSQL + " and w.workOrderNumber = ?", workOrderNumber);
return _getWorkOrder(baseSQL + " and w.workOrderNumber = ?", workOrderNumber, {
includeLotsAndLotOccupancies: true,
includeComments: true,
includeMilestones: true
});
};
export const getWorkOrder = (workOrderId) => {
return _getWorkOrder(baseSQL + " and w.workOrderId = ?", workOrderId);
export const getWorkOrder = (workOrderId, options, connectedDatabase) => {
return _getWorkOrder(baseSQL + " and w.workOrderId = ?", workOrderId, options, connectedDatabase);
};
export default getWorkOrder;

View File

@ -14,6 +14,12 @@ import { getWorkOrderMilestones } from "./getWorkOrderMilestones.js";
import type * as recordTypes from "../../types/recordTypes";
interface WorkOrderOptions {
includeLotsAndLotOccupancies: boolean;
includeComments: boolean;
includeMilestones: boolean;
}
const baseSQL =
"select w.workOrderId," +
" w.workOrderTypeId, t.workOrderType," +
@ -26,9 +32,13 @@ const baseSQL =
const _getWorkOrder = (
sql: string,
workOrderId_or_workOrderNumber: number | string
workOrderId_or_workOrderNumber: number | string,
options: WorkOrderOptions,
connectedDatabase?: sqlite.Database
): recordTypes.WorkOrder => {
const database = sqlite(databasePath, {
const database =
connectedDatabase ||
sqlite(databasePath, {
readonly: true
});
@ -39,6 +49,7 @@ const _getWorkOrder = (
.get(workOrderId_or_workOrderNumber);
if (workOrder) {
if (options.includeLotsAndLotOccupancies) {
workOrder.workOrderLots = getLots(
{
workOrderId: workOrder.workOrderId
@ -61,21 +72,32 @@ const _getWorkOrder = (
},
database
).lotOccupancies;
}
if (options.includeComments) {
workOrder.workOrderComments = getWorkOrderComments(
workOrder.workOrderId,
database
);
}
if (options.includeMilestones) {
workOrder.workOrderMilestones = getWorkOrderMilestones(
{
workOrderId: workOrder.workOrderId
},
{
includeWorkOrders: false,
orderBy: "completion"
},
database
);
}
}
if (!connectedDatabase) {
database.close();
}
return workOrder;
};
@ -85,14 +107,26 @@ export const getWorkOrderByWorkOrderNumber = (
): recordTypes.WorkOrder => {
return _getWorkOrder(
baseSQL + " and w.workOrderNumber = ?",
workOrderNumber
workOrderNumber,
{
includeLotsAndLotOccupancies: true,
includeComments: true,
includeMilestones: true
}
);
};
export const getWorkOrder = (
workOrderId: number | string
workOrderId: number | string,
options: WorkOrderOptions,
connectedDatabase?: sqlite.Database
): recordTypes.WorkOrder => {
return _getWorkOrder(baseSQL + " and w.workOrderId = ?", workOrderId);
return _getWorkOrder(
baseSQL + " and w.workOrderId = ?",
workOrderId,
options,
connectedDatabase
);
};
export default getWorkOrder;

View File

@ -2,6 +2,12 @@ import sqlite from "better-sqlite3";
import type * as recordTypes from "../../types/recordTypes";
interface WorkOrderMilestoneFilters {
workOrderId?: number | string;
workOrderMilestoneDateFilter?: "upcomingMissed" | "recent" | "date";
workOrderMilestoneDateString?: string;
}
export declare const getWorkOrderMilestones: (filters: WorkOrderMilestoneFilters, connectedDatabase?: sqlite.Database) => recordTypes.WorkOrderMilestone[];
interface WorkOrderMilestoneOptions {
includeWorkOrders?: boolean;
orderBy: "completion" | "date";
}
export declare const getWorkOrderMilestones: (filters: WorkOrderMilestoneFilters, options: WorkOrderMilestoneOptions, connectedDatabase?: sqlite.Database) => recordTypes.WorkOrderMilestone[];
export default getWorkOrderMilestones;

View File

@ -1,7 +1,9 @@
import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
import { dateIntegerToString, timeIntegerToString } from "@cityssm/expressjs-server-js/dateTimeFns.js";
export const getWorkOrderMilestones = (filters, connectedDatabase) => {
import { getWorkOrder } from "./getWorkOrder.js";
import { dateIntegerToString, dateStringToInteger, dateToInteger, timeIntegerToString } from "@cityssm/expressjs-server-js/dateTimeFns.js";
import * as configFunctions from "../functions.config.js";
export const getWorkOrderMilestones = (filters, options, connectedDatabase) => {
const database = connectedDatabase ||
sqlite(databasePath, {
readonly: true
@ -14,9 +16,48 @@ export const getWorkOrderMilestones = (filters, connectedDatabase) => {
sqlWhereClause += " and m.workOrderId = ?";
sqlParameters.push(filters.workOrderId);
}
const date = new Date();
const currentDateNumber = dateToInteger(date);
date.setDate(date.getDate() -
configFunctions.getProperty("settings.workOrders.workOrderMilestoneDateRecentBeforeDays"));
const recentBeforeDateNumber = dateToInteger(date);
date.setDate(date.getDate() +
configFunctions.getProperty("settings.workOrders.workOrderMilestoneDateRecentBeforeDays") +
configFunctions.getProperty("settings.workOrders.workOrderMilestoneDateRecentAfterDays"));
const recentAfterDateNumber = dateToInteger(date);
switch (filters.workOrderMilestoneDateFilter) {
case "upcomingMissed":
sqlWhereClause +=
" and (m.workOrderMilestoneCompletionDate is null or m.workOrderMilestoneDate >= ?)";
sqlParameters.push(currentDateNumber);
break;
case "recent":
sqlWhereClause +=
" and m.workOrderMilestoneDate >= ? and m.workOrderMilestoneDate <= ?";
sqlParameters.push(recentBeforeDateNumber, recentAfterDateNumber);
break;
}
if (filters.workOrderMilestoneDateString) {
sqlWhereClause += " and m.workOrderMilestoneDate = ?";
sqlParameters.push(dateStringToInteger(filters.workOrderMilestoneDateString));
}
let orderByClause = "";
switch (options.orderBy) {
case "completion":
orderByClause =
" order by" +
" m.workOrderMilestoneCompletionDate, m.workOrderMilestoneCompletionTime," +
" m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
" t.orderNumber, m.workOrderMilestoneId";
break;
case "date":
orderByClause =
" order by m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
" t.orderNumber, m.workOrderId, m.workOrderMilestoneId";
}
const workOrderMilestones = database
.prepare("select m.workOrderMilestoneId," +
" m.workOrderMilestoneTypeId, t.workORderMilestoneType," +
.prepare("select m.workOrderId, m.workOrderMilestoneId," +
" m.workOrderMilestoneTypeId, t.workOrderMilestoneType," +
" m.workOrderMilestoneDate, userFn_dateIntegerToString(m.workOrderMilestoneDate) as workOrderMilestoneDateString," +
" m.workOrderMilestoneTime, userFn_timeIntegerToString(m.workOrderMilestoneTime) as workOrderMilestoneTimeString," +
" m.workOrderMilestoneDescription," +
@ -26,11 +67,17 @@ export const getWorkOrderMilestones = (filters, connectedDatabase) => {
" from WorkOrderMilestones m" +
" left join WorkOrderMilestoneTypes t on m.workOrderMilestoneTypeId = t.workOrderMilestoneTypeId" +
sqlWhereClause +
" order by" +
" m.workOrderMilestoneCompletionDate, m.workOrderMilestoneCompletionTime," +
" m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
" t.orderNumber, m.workOrderMilestoneId")
orderByClause)
.all(sqlParameters);
if (options.includeWorkOrders) {
for (const workOrderMilestone of workOrderMilestones) {
workOrderMilestone.workOrder = getWorkOrder(workOrderMilestone.workOrderId, {
includeLotsAndLotOccupancies: true,
includeComments: false,
includeMilestones: false
}, database);
}
}
if (!connectedDatabase) {
database.close();
}

View File

@ -2,19 +2,33 @@ import sqlite from "better-sqlite3";
import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js";
import { getWorkOrder } from "./getWorkOrder.js";
import {
dateIntegerToString,
dateStringToInteger,
dateToInteger,
timeIntegerToString
} from "@cityssm/expressjs-server-js/dateTimeFns.js";
import * as configFunctions from "../functions.config.js";
import type * as recordTypes from "../../types/recordTypes";
interface WorkOrderMilestoneFilters {
workOrderId?: number | string;
workOrderMilestoneDateFilter?: "upcomingMissed" | "recent" | "date";
workOrderMilestoneDateString?: string;
}
interface WorkOrderMilestoneOptions {
includeWorkOrders?: boolean;
orderBy: "completion" | "date";
}
export const getWorkOrderMilestones = (
filters: WorkOrderMilestoneFilters,
options: WorkOrderMilestoneOptions,
connectedDatabase?: sqlite.Database
): recordTypes.WorkOrderMilestone[] => {
const database =
@ -26,6 +40,8 @@ export const getWorkOrderMilestones = (
database.function("userFn_dateIntegerToString", dateIntegerToString);
database.function("userFn_timeIntegerToString", timeIntegerToString);
// Filters
let sqlWhereClause = " where m.recordDelete_timeMillis is null";
const sqlParameters = [];
@ -34,10 +50,76 @@ export const getWorkOrderMilestones = (
sqlParameters.push(filters.workOrderId);
}
const workOrderMilestones = database
const date = new Date();
const currentDateNumber = dateToInteger(date);
date.setDate(
date.getDate() -
configFunctions.getProperty(
"settings.workOrders.workOrderMilestoneDateRecentBeforeDays"
)
);
const recentBeforeDateNumber = dateToInteger(date);
date.setDate(
date.getDate() +
configFunctions.getProperty(
"settings.workOrders.workOrderMilestoneDateRecentBeforeDays"
) +
configFunctions.getProperty(
"settings.workOrders.workOrderMilestoneDateRecentAfterDays"
)
);
const recentAfterDateNumber = dateToInteger(date);
switch (filters.workOrderMilestoneDateFilter) {
case "upcomingMissed":
sqlWhereClause +=
" and (m.workOrderMilestoneCompletionDate is null or m.workOrderMilestoneDate >= ?)";
sqlParameters.push(currentDateNumber);
break;
case "recent":
sqlWhereClause +=
" and m.workOrderMilestoneDate >= ? and m.workOrderMilestoneDate <= ?";
sqlParameters.push(recentBeforeDateNumber, recentAfterDateNumber);
break;
}
if (filters.workOrderMilestoneDateString) {
sqlWhereClause += " and m.workOrderMilestoneDate = ?";
sqlParameters.push(
dateStringToInteger(filters.workOrderMilestoneDateString)
);
}
// Order By
let orderByClause = "";
switch (options.orderBy) {
case "completion":
orderByClause =
" order by" +
" m.workOrderMilestoneCompletionDate, m.workOrderMilestoneCompletionTime," +
" m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
" t.orderNumber, m.workOrderMilestoneId";
break;
case "date":
orderByClause =
" order by m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
" t.orderNumber, m.workOrderId, m.workOrderMilestoneId";
}
// Query
const workOrderMilestones: recordTypes.WorkOrderMilestone[] = database
.prepare(
"select m.workOrderMilestoneId," +
" m.workOrderMilestoneTypeId, t.workORderMilestoneType," +
"select m.workOrderId, m.workOrderMilestoneId," +
" m.workOrderMilestoneTypeId, t.workOrderMilestoneType," +
" m.workOrderMilestoneDate, userFn_dateIntegerToString(m.workOrderMilestoneDate) as workOrderMilestoneDateString," +
" m.workOrderMilestoneTime, userFn_timeIntegerToString(m.workOrderMilestoneTime) as workOrderMilestoneTimeString," +
" m.workOrderMilestoneDescription," +
@ -47,13 +129,24 @@ export const getWorkOrderMilestones = (
" from WorkOrderMilestones m" +
" left join WorkOrderMilestoneTypes t on m.workOrderMilestoneTypeId = t.workOrderMilestoneTypeId" +
sqlWhereClause +
" order by" +
" m.workOrderMilestoneCompletionDate, m.workOrderMilestoneCompletionTime," +
" m.workOrderMilestoneDate, case when m.workOrderMilestoneTime = 0 then 9999 else m.workOrderMilestoneTime end," +
" t.orderNumber, m.workOrderMilestoneId"
orderByClause
)
.all(sqlParameters);
if (options.includeWorkOrders) {
for (const workOrderMilestone of workOrderMilestones) {
workOrderMilestone.workOrder = getWorkOrder(
workOrderMilestone.workOrderId,
{
includeLotsAndLotOccupancies: true,
includeComments: false,
includeMilestones: false
},
database
);
}
}
if (!connectedDatabase) {
database.close();
}

22
package-lock.json generated
View File

@ -14,6 +14,7 @@
"@cityssm/date-diff": "^2.2.3",
"@cityssm/expressjs-server-js": "^2.3.2",
"@fortawesome/fontawesome-free": "^5.15.4",
"@types/randomcolor": "^0.5.6",
"activedirectory2": "^2.1.0",
"better-sqlite3": "^7.6.2",
"camelcase": "^7.0.0",
@ -30,6 +31,7 @@
"http-errors": "^2.0.0",
"leaflet": "^1.8.0",
"papaparse": "^5.3.2",
"randomcolor": "^0.6.2",
"session-file-store": "^1.5.0"
},
"devDependencies": {
@ -1165,6 +1167,11 @@
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
"dev": true
},
"node_modules/@types/randomcolor": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@types/randomcolor/-/randomcolor-0.5.6.tgz",
"integrity": "sha512-lKkW8DGUQpZldTrwa+HM5rY+7eTyaHOMTsnj9ewt7AAwXuMPwBmkLlfh8+SXdgOhBW9iNI4x4zRjQ/TQZkdycQ=="
},
"node_modules/@types/range-parser": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
@ -9141,6 +9148,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/randomcolor": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/randomcolor/-/randomcolor-0.6.2.tgz",
"integrity": "sha512-Mn6TbyYpFgwFuQ8KJKqf3bqqY9O1y37/0jgSK/61PUxV4QfIMv0+K2ioq8DfOjkBslcjwSzRfIDEXfzA9aCx7A=="
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -12856,6 +12868,11 @@
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
"dev": true
},
"@types/randomcolor": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@types/randomcolor/-/randomcolor-0.5.6.tgz",
"integrity": "sha512-lKkW8DGUQpZldTrwa+HM5rY+7eTyaHOMTsnj9ewt7AAwXuMPwBmkLlfh8+SXdgOhBW9iNI4x4zRjQ/TQZkdycQ=="
},
"@types/range-parser": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
@ -18967,6 +18984,11 @@
"resolved": "https://registry.npmjs.org/random-item/-/random-item-4.0.1.tgz",
"integrity": "sha512-52SyTkFhFm6YP6MN9U5+txr8lBN5/fE2+xjzp1snaDzDNHN8a6Lu/G9fSc3gvD1+bmT+kIS7A0EP9QJQgRBfsg=="
},
"randomcolor": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/randomcolor/-/randomcolor-0.6.2.tgz",
"integrity": "sha512-Mn6TbyYpFgwFuQ8KJKqf3bqqY9O1y37/0jgSK/61PUxV4QfIMv0+K2ioq8DfOjkBslcjwSzRfIDEXfzA9aCx7A=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",

View File

@ -38,6 +38,7 @@
"@cityssm/date-diff": "^2.2.3",
"@cityssm/expressjs-server-js": "^2.3.2",
"@fortawesome/fontawesome-free": "^5.15.4",
"@types/randomcolor": "^0.5.6",
"activedirectory2": "^2.1.0",
"better-sqlite3": "^7.6.2",
"camelcase": "^7.0.0",
@ -54,6 +55,7 @@
"http-errors": "^2.0.0",
"leaflet": "^1.8.0",
"papaparse": "^5.3.2",
"randomcolor": "^0.6.2",
"session-file-store": "^1.5.0"
},
"devDependencies": {

View File

@ -72,10 +72,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
}
}
};
const hues = ["red", "green", "orange", "blue", "pink", "yellow", "purple"];
const luminosity = ["bright", "light", "dark"];
const getRandomColor = (seedString) => {
let actualSeedString = seedString;
if (actualSeedString.length < 2) {
actualSeedString = actualSeedString + "a1";
}
return exports.randomColor({
seed: actualSeedString + actualSeedString,
hue: hues[actualSeedString.codePointAt(actualSeedString.length - 1) % hues.length],
luminosity: luminosity[actualSeedString.codePointAt(actualSeedString.length - 2) % luminosity.length]
});
};
const los = {
highlightMap,
initializeUnlockFieldButtons,
populateAliases
populateAliases,
getRandomColor
};
exports.los = los;
})();

View File

@ -118,10 +118,29 @@ import type * as globalTypes from "../types/globalTypes";
}
};
const hues = ["red", "green", "orange", "blue", "pink", "yellow", "purple"];
const luminosity = ["bright", "light", "dark"];
const getRandomColor = (seedString: string) => {
let actualSeedString = seedString;
if (actualSeedString.length < 2) {
actualSeedString = actualSeedString + "a1";
}
return exports.randomColor({
seed: actualSeedString + actualSeedString,
hue: hues[actualSeedString.codePointAt(actualSeedString.length - 1) % hues.length],
luminosity: luminosity[actualSeedString.codePointAt(actualSeedString.length - 2) % luminosity.length]
});
};
const los: globalTypes.LOS = {
highlightMap,
initializeUnlockFieldButtons,
populateAliases
populateAliases,
getRandomColor
};
exports.los = los;

View File

@ -37,6 +37,85 @@ Object.defineProperty(exports, "__esModule", { value: true });
}
});
});
let workOrderMilestones;
if (!isCreate) {
const doClose = () => {
cityssm.postJSON(urlPrefix + "/workOrders/doCloseWorkOrder", {
workOrderId
}, (responseJSON) => {
if (responseJSON.success) {
window.location.href =
urlPrefix + "/workOrders/" + workOrderId;
}
else {
bulmaJS.alert({
title: "Error Closing Work Order",
message: responseJSON.errorMessage,
contextualColorName: "danger"
});
}
});
};
document
.querySelector("#button--closeWorkOrder")
.addEventListener("click", () => {
const hasOpenMilestones = workOrderMilestones.some((milestone) => {
return !milestone.workOrderMilestoneCompletionDate;
});
if (hasOpenMilestones) {
bulmaJS.confirm({
title: "Close Work Order with Outstanding Milestones",
message: "Are you sure you want to close this work order with outstanding milestones?",
contextualColorName: "danger",
okButton: {
text: "Yes, Close Work Order",
callbackFunction: doClose
}
});
}
else {
bulmaJS.confirm({
title: "Close Work Order",
message: "Are you sure you want to close this work order?",
contextualColorName: "info",
okButton: {
text: "Yes, Close Work Order",
callbackFunction: doClose
}
});
}
});
const doDelete = () => {
cityssm.postJSON(urlPrefix + "/workOrders/doDeleteWorkOrder", {
workOrderId
}, (responseJSON) => {
if (responseJSON.success) {
window.location.href = urlPrefix + "/workOrders";
}
else {
bulmaJS.alert({
title: "Error Deleting Work Order",
message: responseJSON.errorMessage,
contextualColorName: "danger"
});
}
});
};
document
.querySelector("#button--deleteWorkOrder")
.addEventListener("click", (clickEvent) => {
clickEvent.preventDefault();
bulmaJS.confirm({
title: "Delete Work Order",
message: "Are you sure you want to delete this work order?",
contextualColorName: "warning",
okButton: {
text: "Yes, Delete Work Order",
callbackFunction: doDelete
}
});
});
}
if (!isCreate) {
let workOrderLots = exports.workOrderLots;
delete exports.workOrderLots;
@ -593,7 +672,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
});
}
if (!isCreate) {
let workOrderMilestones = exports.workOrderMilestones;
workOrderMilestones =
exports.workOrderMilestones;
delete exports.workOrderMilestones;
const processMilestoneResponse = (responseJSON) => {
if (responseJSON.success) {
@ -723,7 +803,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
milestoneTypeElement.append(optionElement);
}
modalElement.querySelector("#milestoneEdit--workOrderMilestoneDateString").value = workOrderMilestone.workOrderMilestoneDateString;
modalElement.querySelector("#milestoneEdit--workOrderMilestoneTimeString").value = workOrderMilestone.workOrderMilestoneTimeString;
if (workOrderMilestone.workOrderMilestoneTime) {
modalElement.querySelector("#milestoneEdit--workOrderMilestoneTimeString").value =
workOrderMilestone.workOrderMilestoneTimeString;
}
modalElement.querySelector("#milestoneEdit--workOrderMilestoneDescription").value = workOrderMilestone.workOrderMilestoneDescription;
},
onshown: (modalElement, closeModalFunction) => {
@ -832,9 +915,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
document
.querySelector("#button--addMilestone")
.addEventListener("click", () => {
let addModalElement;
let addCloseModalFunction;
const doAdd = (submitEvent) => {
submitEvent.preventDefault();
const currentDateString = cityssm.dateToString(new Date());
const _doAdd = () => {
cityssm.postJSON(urlPrefix + "/workOrders/doAddWorkOrderMilestone", submitEvent.currentTarget, (responseJSON) => {
processMilestoneResponse(responseJSON);
if (responseJSON.success) {
@ -842,6 +928,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
}
});
};
if (addModalElement.querySelector("#milestoneAdd--workOrderMilestoneDateString").value < currentDateString) {
bulmaJS.confirm({
title: "Milestone Date in the Past",
message: "Are you sure you want to create a milestone with a date in the past?",
contextualColorName: "warning",
okButton: {
text: "Yes, Create a Past Milestone",
callbackFunction: _doAdd
}
});
}
else {
_doAdd();
}
};
cityssm.openHtmlModal("workOrder-addMilestone", {
onshow: (modalElement) => {
modalElement.querySelector("#milestoneAdd--workOrderId").value = workOrderId;
@ -857,6 +958,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
modalElement.querySelector("#milestoneAdd--workOrderMilestoneDateString").valueAsDate = new Date();
},
onshown: (modalElement, closeModalFunction) => {
addModalElement = modalElement;
addCloseModalFunction = closeModalFunction;
bulmaJS.toggleHtmlClipped();
modalElement

View File

@ -65,6 +65,105 @@ declare const bulmaJS: BulmaJS;
);
});
/*
* Work Order Options
*/
let workOrderMilestones: recordTypes.WorkOrderMilestone[];
if (!isCreate) {
const doClose = () => {
cityssm.postJSON(
urlPrefix + "/workOrders/doCloseWorkOrder",
{
workOrderId
},
(responseJSON: { success: boolean; errorMessage?: string }) => {
if (responseJSON.success) {
window.location.href =
urlPrefix + "/workOrders/" + workOrderId;
} else {
bulmaJS.alert({
title: "Error Closing Work Order",
message: responseJSON.errorMessage,
contextualColorName: "danger"
});
}
}
);
};
document
.querySelector("#button--closeWorkOrder")
.addEventListener("click", () => {
const hasOpenMilestones = workOrderMilestones.some(
(milestone) => {
return !milestone.workOrderMilestoneCompletionDate;
}
);
if (hasOpenMilestones) {
bulmaJS.confirm({
title: "Close Work Order with Outstanding Milestones",
message:
"Are you sure you want to close this work order with outstanding milestones?",
contextualColorName: "danger",
okButton: {
text: "Yes, Close Work Order",
callbackFunction: doClose
}
});
} else {
bulmaJS.confirm({
title: "Close Work Order",
message:
"Are you sure you want to close this work order?",
contextualColorName: "info",
okButton: {
text: "Yes, Close Work Order",
callbackFunction: doClose
}
});
}
});
const doDelete = () => {
cityssm.postJSON(
urlPrefix + "/workOrders/doDeleteWorkOrder",
{
workOrderId
},
(responseJSON: { success: boolean; errorMessage?: string }) => {
if (responseJSON.success) {
window.location.href = urlPrefix + "/workOrders";
} else {
bulmaJS.alert({
title: "Error Deleting Work Order",
message: responseJSON.errorMessage,
contextualColorName: "danger"
});
}
}
);
};
document
.querySelector("#button--deleteWorkOrder")
.addEventListener("click", (clickEvent: Event) => {
clickEvent.preventDefault();
bulmaJS.confirm({
title: "Delete Work Order",
message: "Are you sure you want to delete this work order?",
contextualColorName: "warning",
okButton: {
text: "Yes, Delete Work Order",
callbackFunction: doDelete
}
});
});
}
/*
* Related Lots
*/
@ -843,7 +942,7 @@ declare const bulmaJS: BulmaJS;
*/
if (!isCreate) {
let workOrderMilestones =
workOrderMilestones =
exports.workOrderMilestones as recordTypes.WorkOrderMilestone[];
delete exports.workOrderMilestones;
@ -1079,11 +1178,15 @@ declare const bulmaJS: BulmaJS;
) as HTMLInputElement
).value = workOrderMilestone.workOrderMilestoneDateString;
if (workOrderMilestone.workOrderMilestoneTime) {
(
modalElement.querySelector(
"#milestoneEdit--workOrderMilestoneTimeString"
) as HTMLInputElement
).value = workOrderMilestone.workOrderMilestoneTimeString;
).value =
workOrderMilestone.workOrderMilestoneTimeString;
}
(
modalElement.querySelector(
"#milestoneEdit--workOrderMilestoneDescription"
@ -1219,11 +1322,15 @@ declare const bulmaJS: BulmaJS;
document
.querySelector("#button--addMilestone")
.addEventListener("click", () => {
let addModalElement: HTMLElement;
let addCloseModalFunction: () => void;
const doAdd = (submitEvent: SubmitEvent) => {
submitEvent.preventDefault();
const currentDateString = cityssm.dateToString(new Date());
const _doAdd = () => {
cityssm.postJSON(
urlPrefix + "/workOrders/doAddWorkOrderMilestone",
submitEvent.currentTarget,
@ -1241,6 +1348,28 @@ declare const bulmaJS: BulmaJS;
);
};
if (
(
addModalElement.querySelector(
"#milestoneAdd--workOrderMilestoneDateString"
) as HTMLInputElement
).value < currentDateString
) {
bulmaJS.confirm({
title: "Milestone Date in the Past",
message:
"Are you sure you want to create a milestone with a date in the past?",
contextualColorName: "warning",
okButton: {
text: "Yes, Create a Past Milestone",
callbackFunction: _doAdd
}
});
} else {
_doAdd();
}
};
cityssm.openHtmlModal("workOrder-addMilestone", {
onshow: (modalElement) => {
(
@ -1272,8 +1401,11 @@ declare const bulmaJS: BulmaJS;
).valueAsDate = new Date();
},
onshown: (modalElement, closeModalFunction) => {
addModalElement = modalElement;
addCloseModalFunction = closeModalFunction;
bulmaJS.toggleHtmlClipped();
modalElement
.querySelector("form")
.addEventListener("submit", doAdd);

View File

@ -1,22 +1,121 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
(() => {
const los = exports.los;
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const workOrderSearchFiltersFormElement = document.querySelector("#form--searchFilters");
const workOrderMilestoneDateFilterElement = workOrderSearchFiltersFormElement.querySelector("#searchFilter--workOrderMilestoneDateFilter");
const workOrderMilestoneDateStringElement = workOrderSearchFiltersFormElement.querySelector("#searchFilter--workOrderMilestoneDateString");
const milestoneCalendarContainerElement = document.querySelector("#container--milestoneCalendar");
const renderMilestones = (workOrderMilestones) => {
if (workOrderMilestones.length === 0) {
milestoneCalendarContainerElement.innerHTML =
'<div class="message is-info">' +
'<p class="message-body">There are no milestones that meet the search criteria.</p>' +
"</div>";
return;
}
milestoneCalendarContainerElement.innerHTML = "";
let currentDate = cityssm.dateToString(new Date());
let currentPanelElement;
let currentPanelDateString = "";
for (const milestone of workOrderMilestones) {
if (currentPanelDateString !== milestone.workOrderMilestoneDateString) {
if (currentPanelElement) {
milestoneCalendarContainerElement.append(currentPanelElement);
}
currentPanelElement = document.createElement("div");
currentPanelElement.className = "panel";
currentPanelElement.innerHTML =
'<h2 class="panel-heading">' +
milestone.workOrderMilestoneDateString +
"</h2>";
currentPanelDateString = milestone.workOrderMilestoneDateString;
}
const panelBlockElement = document.createElement("div");
panelBlockElement.className = "panel-block is-block";
if (!milestone.workOrderMilestoneCompletionDate && milestone.workOrderMilestoneDateString < currentDate) {
panelBlockElement.classList.add("has-background-warning-light");
}
let lotOccupancyHTML = "";
for (const lot of milestone.workOrder.workOrderLots) {
lotOccupancyHTML +=
'<i class="fas fa-vector-square" aria-label="' +
cityssm.escapeHTML(exports.aliases.lot) +
'"></i> ' +
cityssm.escapeHTML(lot.lotName) +
"<br />";
}
for (const lotOccupancy of milestone.workOrder
.workOrderLotOccupancies) {
if (lotOccupancy.lotOccupancyOccupants.length > 0) {
lotOccupancyHTML +=
'<i class="fas fa-user" aria-label="' +
cityssm.escapeHTML(exports.aliases.lotOccupancy) +
'"></i> ' +
cityssm.escapeHTML(lotOccupancy.lotOccupancyOccupants[0].occupantName) +
"<br />";
}
}
panelBlockElement.innerHTML =
'<div class="columns">' +
('<div class="column is-narrow">' +
'<span class="icon is-small">' +
(milestone.workOrderMilestoneCompletionDate
? '<i class="fas fa-check" aria-label="Completed"></i>'
: '<i class="far fa-square has-text-grey" aria-label="Incomplete"></i>') +
"</span>" +
"</div>") +
('<div class="column">' +
(milestone.workOrderMilestoneTime === 0
? ""
: milestone.workOrderMilestoneTimeString + "<br />") +
(milestone.workOrderMilestoneTypeId
? "<strong>" +
cityssm.escapeHTML(milestone.workOrderMilestoneType) +
"</strong><br />"
: "") +
'<span class="is-size-7">' +
cityssm.escapeHTML(milestone.workOrderMilestoneDescription) +
"</span>" +
"</div>") +
('<div class="column">' +
"<i class=\"fas fa-circle\" style=\"color:" + los.getRandomColor(milestone.workOrder.workOrderNumber) + "\" aria-hidden=\"true\"></i>" +
' <a class="has-text-weight-bold" href="' +
urlPrefix +
"/workOrders/" +
milestone.workOrderId +
'">' +
cityssm.escapeHTML(milestone.workOrder.workOrderNumber) +
"</a><br />" +
'<span class="is-size-7">' +
cityssm.escapeHTML(milestone.workOrder.workOrderDescription) +
"</span>" +
"</div>") +
('<div class="column is-size-7">' +
lotOccupancyHTML +
"</div>") +
"</div>";
currentPanelElement.append(panelBlockElement);
}
milestoneCalendarContainerElement.append(currentPanelElement);
};
const getMilestones = (event) => {
if (event) {
event.preventDefault();
}
milestoneCalendarContainerElement.innerHTML =
'<div class="has-text-grey has-text-centered">' +
'<i class="fas fa-5x fa-circle-notch fa-spin" aria-hidden="true"></i><br />' +
"Loading Milestones..." +
"</div>";
cityssm.postJSON(urlPrefix + "/workOrders/doGetWorkOrderMilestones", workOrderSearchFiltersFormElement, (responseJSON) => {
renderMilestones(responseJSON.workOrderMilestones);
});
};
workOrderMilestoneDateFilterElement.addEventListener("change", () => {
workOrderMilestoneDateStringElement.disabled = (workOrderMilestoneDateFilterElement.value !== "date");
workOrderMilestoneDateStringElement.disabled =
workOrderMilestoneDateFilterElement.value !== "date";
getMilestones();
});
workOrderMilestoneDateStringElement.addEventListener("change", getMilestones);

View File

@ -1,21 +1,152 @@
/* eslint-disable unicorn/prefer-module */
import type * as recordTypes from "../types/recordTypes";
import type * as globalTypes from "../types/globalTypes";
import type { cityssmGlobal } from "@cityssm/bulma-webapp-js/src/types";
declare const cityssm: cityssmGlobal;
(() => {
const los = exports.los as globalTypes.LOS;
const urlPrefix = document.querySelector("main").dataset.urlPrefix;
const workOrderSearchFiltersFormElement = document.querySelector("#form--searchFilters") as HTMLFormElement;
const workOrderSearchFiltersFormElement = document.querySelector(
"#form--searchFilters"
) as HTMLFormElement;
const workOrderMilestoneDateFilterElement = workOrderSearchFiltersFormElement.querySelector("#searchFilter--workOrderMilestoneDateFilter") as HTMLSelectElement;
const workOrderMilestoneDateStringElement = workOrderSearchFiltersFormElement.querySelector("#searchFilter--workOrderMilestoneDateString") as HTMLInputElement;
const workOrderMilestoneDateFilterElement =
workOrderSearchFiltersFormElement.querySelector(
"#searchFilter--workOrderMilestoneDateFilter"
) as HTMLSelectElement;
const renderMilestones = (workOrderMilestones: recordTypes.WorkOrderMilestone[]) => {
const workOrderMilestoneDateStringElement =
workOrderSearchFiltersFormElement.querySelector(
"#searchFilter--workOrderMilestoneDateString"
) as HTMLInputElement;
const milestoneCalendarContainerElement = document.querySelector(
"#container--milestoneCalendar"
) as HTMLElement;
const renderMilestones = (
workOrderMilestones: recordTypes.WorkOrderMilestone[]
) => {
if (workOrderMilestones.length === 0) {
milestoneCalendarContainerElement.innerHTML =
'<div class="message is-info">' +
'<p class="message-body">There are no milestones that meet the search criteria.</p>' +
"</div>";
return;
}
milestoneCalendarContainerElement.innerHTML = "";
let currentDate = cityssm.dateToString(new Date());
let currentPanelElement: HTMLElement;
let currentPanelDateString = "";
for (const milestone of workOrderMilestones) {
if (currentPanelDateString !== milestone.workOrderMilestoneDateString) {
if (currentPanelElement) {
milestoneCalendarContainerElement.append(
currentPanelElement
);
}
currentPanelElement = document.createElement("div");
currentPanelElement.className = "panel";
currentPanelElement.innerHTML =
'<h2 class="panel-heading">' +
milestone.workOrderMilestoneDateString +
"</h2>";
currentPanelDateString = milestone.workOrderMilestoneDateString;
}
const panelBlockElement = document.createElement("div");
panelBlockElement.className = "panel-block is-block";
if (!milestone.workOrderMilestoneCompletionDate && milestone.workOrderMilestoneDateString < currentDate) {
panelBlockElement.classList.add("has-background-warning-light");
}
let lotOccupancyHTML = "";
for (const lot of milestone.workOrder.workOrderLots) {
lotOccupancyHTML +=
'<i class="fas fa-vector-square" aria-label="' +
cityssm.escapeHTML(exports.aliases.lot) +
'"></i> ' +
cityssm.escapeHTML(lot.lotName) +
"<br />";
}
for (const lotOccupancy of milestone.workOrder
.workOrderLotOccupancies) {
if (lotOccupancy.lotOccupancyOccupants.length > 0) {
lotOccupancyHTML +=
'<i class="fas fa-user" aria-label="' +
cityssm.escapeHTML(exports.aliases.lotOccupancy) +
'"></i> ' +
cityssm.escapeHTML(
lotOccupancy.lotOccupancyOccupants[0].occupantName
) +
"<br />";
}
}
panelBlockElement.innerHTML =
'<div class="columns">' +
('<div class="column is-narrow">' +
'<span class="icon is-small">' +
(milestone.workOrderMilestoneCompletionDate
? '<i class="fas fa-check" aria-label="Completed"></i>'
: '<i class="far fa-square has-text-grey" aria-label="Incomplete"></i>') +
"</span>" +
"</div>") +
('<div class="column">' +
(milestone.workOrderMilestoneTime === 0
? ""
: milestone.workOrderMilestoneTimeString + "<br />") +
(milestone.workOrderMilestoneTypeId
? "<strong>" +
cityssm.escapeHTML(milestone.workOrderMilestoneType) +
"</strong><br />"
: "") +
'<span class="is-size-7">' +
cityssm.escapeHTML(
milestone.workOrderMilestoneDescription
) +
"</span>" +
"</div>") +
('<div class="column">' +
"<i class=\"fas fa-circle\" style=\"color:" + los.getRandomColor(milestone.workOrder.workOrderNumber) + "\" aria-hidden=\"true\"></i>" +
' <a class="has-text-weight-bold" href="' +
urlPrefix +
"/workOrders/" +
milestone.workOrderId +
'">' +
cityssm.escapeHTML(milestone.workOrder.workOrderNumber) +
"</a><br />" +
'<span class="is-size-7">' +
cityssm.escapeHTML(
milestone.workOrder.workOrderDescription
) +
"</span>" +
"</div>") +
('<div class="column is-size-7">' +
lotOccupancyHTML +
"</div>") +
"</div>";
currentPanelElement.append(panelBlockElement);
}
milestoneCalendarContainerElement.append(currentPanelElement);
};
const getMilestones = (event?: Event) => {
@ -23,19 +154,33 @@ declare const cityssm: cityssmGlobal;
event.preventDefault();
}
cityssm.postJSON(urlPrefix + "/workOrders/doGetWorkOrderMilestones",
milestoneCalendarContainerElement.innerHTML =
'<div class="has-text-grey has-text-centered">' +
'<i class="fas fa-5x fa-circle-notch fa-spin" aria-hidden="true"></i><br />' +
"Loading Milestones..." +
"</div>";
cityssm.postJSON(
urlPrefix + "/workOrders/doGetWorkOrderMilestones",
workOrderSearchFiltersFormElement,
(responseJSON: {workOrderMilestones: recordTypes.WorkOrderMilestone[]}) => {
(responseJSON: {
workOrderMilestones: recordTypes.WorkOrderMilestone[];
}) => {
renderMilestones(responseJSON.workOrderMilestones);
})
} ;
}
);
};
workOrderMilestoneDateFilterElement.addEventListener("change", () => {
workOrderMilestoneDateStringElement.disabled = (workOrderMilestoneDateFilterElement.value !== "date");
workOrderMilestoneDateStringElement.disabled =
workOrderMilestoneDateFilterElement.value !== "date";
getMilestones();
});
workOrderMilestoneDateStringElement.addEventListener("change", getMilestones);
workOrderMilestoneDateStringElement.addEventListener(
"change",
getMilestones
);
workOrderSearchFiltersFormElement.addEventListener("submit", getMilestones);
getMilestones();

View File

@ -1 +1 @@
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=e=>{const t=e.currentTarget.closest(".field").querySelector("input, select");if("INPUT"===t.tagName)t.readOnly=!1;else{const e=t.querySelectorAll("option");for(const t of e)t.disabled=!1}t.focus()},t={highlightMap:(e,t,s)=>{let o,a=t;for(;!(o=e.querySelector("#"+a))&&a.includes("-");)a=a.slice(0,Math.max(0,a.lastIndexOf("-")));if(o){o.style.fill=null,o.classList.add("highlight","is-"+s);const e=o.querySelectorAll("path");for(const t of e)t.style.fill=null}},initializeUnlockFieldButtons:t=>{const s=t.querySelectorAll(".is-unlock-field-button");for(const t of s)t.addEventListener("click",e)},populateAliases:e=>{const t=e.querySelectorAll(".alias");for(const e of t)switch(e.dataset.alias){case"Lot":e.textContent=exports.aliases.lot;break;case"lot":e.textContent=exports.aliases.lot.toLowerCase();break;case"Occupancy":e.textContent=exports.aliases.occupancy;break;case"occupancy":e.textContent=exports.aliases.occupancy.toLowerCase();break;case"Occupant":e.textContent=exports.aliases.occupant;break;case"occupant":e.textContent=exports.aliases.occupant.toLowerCase();break;case"ExternalReceiptNumber":e.textContent=exports.aliases.externalReceiptNumber}}};exports.los=t})();
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=e=>{const t=e.currentTarget.closest(".field").querySelector("input, select");if("INPUT"===t.tagName)t.readOnly=!1;else{const e=t.querySelectorAll("option");for(const t of e)t.disabled=!1}t.focus()},t=["red","green","orange","blue","pink","yellow","purple"],o=["bright","light","dark"],l={highlightMap:(e,t,o)=>{let l,s=t;for(;!(l=e.querySelector("#"+s))&&s.includes("-");)s=s.slice(0,Math.max(0,s.lastIndexOf("-")));if(l){l.style.fill=null,l.classList.add("highlight","is-"+o);const e=l.querySelectorAll("path");for(const t of e)t.style.fill=null}},initializeUnlockFieldButtons:t=>{const o=t.querySelectorAll(".is-unlock-field-button");for(const t of o)t.addEventListener("click",e)},populateAliases:e=>{const t=e.querySelectorAll(".alias");for(const e of t)switch(e.dataset.alias){case"Lot":e.textContent=exports.aliases.lot;break;case"lot":e.textContent=exports.aliases.lot.toLowerCase();break;case"Occupancy":e.textContent=exports.aliases.occupancy;break;case"occupancy":e.textContent=exports.aliases.occupancy.toLowerCase();break;case"Occupant":e.textContent=exports.aliases.occupant;break;case"occupant":e.textContent=exports.aliases.occupant.toLowerCase();break;case"ExternalReceiptNumber":e.textContent=exports.aliases.externalReceiptNumber}},getRandomColor:e=>{let l=e;return l.length<2&&(l+="a1"),exports.randomColor({seed:l+l,hue:t[l.codePointAt(l.length-1)%t.length],luminosity:o[l.codePointAt(l.length-2)%o.length]})}};exports.los=l})();

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=document.querySelector("main").dataset.urlPrefix,r=document.querySelector("#form--searchFilters"),t=r.querySelector("#searchFilter--workOrderMilestoneDateFilter"),s=r.querySelector("#searchFilter--workOrderMilestoneDateString"),o=t=>{t&&t.preventDefault(),cityssm.postJSON(e+"/workOrders/doGetWorkOrderMilestones",r,e=>{e.workOrderMilestones})};t.addEventListener("change",()=>{s.disabled="date"!==t.value,o()}),s.addEventListener("change",o),r.addEventListener("submit",o),o()})();
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=exports.los,r=document.querySelector("main").dataset.urlPrefix,s=document.querySelector("#form--searchFilters"),a=s.querySelector("#searchFilter--workOrderMilestoneDateFilter"),t=s.querySelector("#searchFilter--workOrderMilestoneDateString"),i=document.querySelector("#container--milestoneCalendar"),o=a=>{a&&a.preventDefault(),i.innerHTML='<div class="has-text-grey has-text-centered"><i class="fas fa-5x fa-circle-notch fa-spin" aria-hidden="true"></i><br />Loading Milestones...</div>',cityssm.postJSON(r+"/workOrders/doGetWorkOrderMilestones",s,s=>{(s=>{if(0===s.length)return void(i.innerHTML='<div class="message is-info"><p class="message-body">There are no milestones that meet the search criteria.</p></div>');i.innerHTML="";let a,t=cityssm.dateToString(new Date),o="";for(const n of s){o!==n.workOrderMilestoneDateString&&(a&&i.append(a),(a=document.createElement("div")).className="panel",a.innerHTML='<h2 class="panel-heading">'+n.workOrderMilestoneDateString+"</h2>",o=n.workOrderMilestoneDateString);const s=document.createElement("div");s.className="panel-block is-block",!n.workOrderMilestoneCompletionDate&&n.workOrderMilestoneDateString<t&&s.classList.add("has-background-warning-light");let c="";for(const e of n.workOrder.workOrderLots)c+='<i class="fas fa-vector-square" aria-label="'+cityssm.escapeHTML(exports.aliases.lot)+'"></i> '+cityssm.escapeHTML(e.lotName)+"<br />";for(const e of n.workOrder.workOrderLotOccupancies)e.lotOccupancyOccupants.length>0&&(c+='<i class="fas fa-user" aria-label="'+cityssm.escapeHTML(exports.aliases.lotOccupancy)+'"></i> '+cityssm.escapeHTML(e.lotOccupancyOccupants[0].occupantName)+"<br />");s.innerHTML='<div class="columns"><div class="column is-narrow"><span class="icon is-small">'+(n.workOrderMilestoneCompletionDate?'<i class="fas fa-check" aria-label="Completed"></i>':'<i class="far fa-square has-text-grey" aria-label="Incomplete"></i>')+'</span></div><div class="column">'+(0===n.workOrderMilestoneTime?"":n.workOrderMilestoneTimeString+"<br />")+(n.workOrderMilestoneTypeId?"<strong>"+cityssm.escapeHTML(n.workOrderMilestoneType)+"</strong><br />":"")+'<span class="is-size-7">'+cityssm.escapeHTML(n.workOrderMilestoneDescription)+'</span></div><div class="column"><i class="fas fa-circle" style="color:'+e.getRandomColor(n.workOrder.workOrderNumber)+'" aria-hidden="true"></i> <a class="has-text-weight-bold" href="'+r+"/workOrders/"+n.workOrderId+'">'+cityssm.escapeHTML(n.workOrder.workOrderNumber)+'</a><br /><span class="is-size-7">'+cityssm.escapeHTML(n.workOrder.workOrderDescription)+'</span></div><div class="column is-size-7">'+c+"</div></div>",a.append(s)}i.append(a)})(s.workOrderMilestones)})};a.addEventListener("change",()=>{t.disabled="date"!==a.value,o()}),t.addEventListener("change",o),s.addEventListener("submit",o),o()})();

View File

@ -3,12 +3,15 @@ import * as permissionHandlers from "../handlers/permissions.js";
import handler_search from "../handlers/workOrders-get/search.js";
import handler_doSearchWorkOrders from "../handlers/workOrders-post/doSearchWorkOrders.js";
import handler_milestoneCalendar from "../handlers/workOrders-get/milestoneCalendar.js";
import handler_doGetWorkOrderMilestones from "../handlers/workOrders-post/doGetWorkOrderMilestones.js";
import handler_view from "../handlers/workOrders-get/view.js";
import handler_doReopenWorkOrder from "../handlers/workOrders-post/doReopenWorkOrder.js";
import handler_new from "../handlers/workOrders-get/new.js";
import handler_doCreateWorkOrder from "../handlers/workOrders-post/doCreateWorkOrder.js";
import handler_edit from "../handlers/workOrders-get/edit.js";
import handler_doUpdateWorkOrder from "../handlers/workOrders-post/doUpdateWorkOrder.js";
import handler_doCloseWorkOrder from "../handlers/workOrders-post/doCloseWorkOrder.js";
import handler_doDeleteWorkOrder from "../handlers/workOrders-post/doDeleteWorkOrder.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";
@ -22,12 +25,15 @@ export const router = Router();
router.get("/", handler_search);
router.post("/doSearchWorkOrders", handler_doSearchWorkOrders);
router.get("/milestoneCalendar", handler_milestoneCalendar);
router.post("/doGetWorkOrderMilestones", handler_doGetWorkOrderMilestones);
router.get("/new", permissionHandlers.adminGetHandler, handler_new);
router.post("/doCreateWorkOrder", permissionHandlers.updatePostHandler, handler_doCreateWorkOrder);
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("/doCloseWorkOrder", permissionHandlers.updatePostHandler, handler_doCloseWorkOrder);
router.post("/doDeleteWorkOrder", permissionHandlers.updatePostHandler, handler_doDeleteWorkOrder);
router.post("/doAddWorkOrderLotOccupancy", permissionHandlers.updatePostHandler, handler_doAddWorkOrderLotOccupancy);
router.post("/doDeleteWorkOrderLotOccupancy", permissionHandlers.updatePostHandler, handler_doDeleteWorkOrderLotOccupancy);
router.post("/doAddWorkOrderLot", permissionHandlers.updatePostHandler, handler_doAddWorkOrderLot);

View File

@ -6,6 +6,7 @@ import handler_search from "../handlers/workOrders-get/search.js";
import handler_doSearchWorkOrders from "../handlers/workOrders-post/doSearchWorkOrders.js";
import handler_milestoneCalendar from "../handlers/workOrders-get/milestoneCalendar.js";
import handler_doGetWorkOrderMilestones from "../handlers/workOrders-post/doGetWorkOrderMilestones.js";
import handler_view from "../handlers/workOrders-get/view.js";
import handler_doReopenWorkOrder from "../handlers/workOrders-post/doReopenWorkOrder.js";
@ -15,6 +16,8 @@ import handler_doCreateWorkOrder from "../handlers/workOrders-post/doCreateWorkO
import handler_edit from "../handlers/workOrders-get/edit.js";
import handler_doUpdateWorkOrder from "../handlers/workOrders-post/doUpdateWorkOrder.js";
import handler_doCloseWorkOrder from "../handlers/workOrders-post/doCloseWorkOrder.js";
import handler_doDeleteWorkOrder from "../handlers/workOrders-post/doDeleteWorkOrder.js";
import handler_doAddWorkOrderLotOccupancy from "../handlers/workOrders-post/doAddWorkOrderLotOccupancy.js";
import handler_doDeleteWorkOrderLotOccupancy from "../handlers/workOrders-post/doDeleteWorkOrderLotOccupancy.js";
@ -40,6 +43,8 @@ router.post("/doSearchWorkOrders", handler_doSearchWorkOrders);
router.get("/milestoneCalendar", handler_milestoneCalendar);
router.post("/doGetWorkOrderMilestones", handler_doGetWorkOrderMilestones);
// New
router.get("/new", permissionHandlers.adminGetHandler, handler_new);
@ -74,6 +79,20 @@ router.post(
handler_doUpdateWorkOrder
);
router.post(
"/doCloseWorkOrder",
permissionHandlers.updatePostHandler,
handler_doCloseWorkOrder
);
router.post(
"/doDeleteWorkOrder",
permissionHandlers.updatePostHandler,
handler_doDeleteWorkOrder
);
// Lot Occupancy
router.post(
"/doAddWorkOrderLotOccupancy",
permissionHandlers.updatePostHandler,

View File

@ -728,7 +728,11 @@ function importFromWorkOrderCSV() {
workOrderRow.WO_REMARK3).trim(),
workOrderOpenDateString
}, user);
workOrder = getWorkOrder(workOrderId);
workOrder = getWorkOrder(workOrderId, {
includeLotsAndLotOccupancies: true,
includeComments: true,
includeMilestones: true
});
}
let lot;
if (workOrderRow.WO_CEMETERY !== "00") {
@ -912,7 +916,11 @@ function importFromWorkOrderCSV() {
}
if (workOrderRow.WO_FUNERAL_YR) {
const workOrderMilestoneDateString = formatDateString(workOrderRow.WO_FUNERAL_YR, workOrderRow.WO_FUNERAL_MON, workOrderRow.WO_FUNERAL_DAY);
const workOrderMilestoneTimeString = formatTimeString(workOrderRow.WO_FUNERAL_HR, workOrderRow.WO_FUNERAL_MIN);
let funeralHour = Number.parseInt(workOrderRow.WO_FUNERAL_HR, 10);
if (funeralHour <= 6) {
funeralHour += 12;
}
const workOrderMilestoneTimeString = formatTimeString(funeralHour.toString(), workOrderRow.WO_FUNERAL_MIN);
addWorkOrderMilestone({
workOrderId: workOrder.workOrderId,
workOrderMilestoneTypeId: funeralWorkOrderMilestoneType.workOrderMilestoneTypeId,

View File

@ -1295,7 +1295,11 @@ function importFromWorkOrderCSV() {
user
);
workOrder = getWorkOrder(workOrderId);
workOrder = getWorkOrder(workOrderId, {
includeLotsAndLotOccupancies: true,
includeComments: true,
includeMilestones: true
});
}
let lot: recordTypes.Lot;
@ -1628,8 +1632,13 @@ function importFromWorkOrderCSV() {
workOrderRow.WO_FUNERAL_DAY
);
let funeralHour = Number.parseInt(workOrderRow.WO_FUNERAL_HR, 10);
if (funeralHour <= 6) {
funeralHour += 12;
}
const workOrderMilestoneTimeString = formatTimeString(
workOrderRow.WO_FUNERAL_HR,
funeralHour.toString(),
workOrderRow.WO_FUNERAL_MIN
);

View File

@ -44,6 +44,8 @@ export interface Config {
};
workOrders?: {
workOrderNumberLength?: number;
workOrderMilestoneDateRecentBeforeDays?: number;
workOrderMilestoneDateRecentAfterDays?: number;
};
};
}

View File

@ -44,6 +44,8 @@ export interface Config {
};
workOrders?: {
workOrderNumberLength?: number;
workOrderMilestoneDateRecentBeforeDays?: number;
workOrderMilestoneDateRecentAfterDays?: number;
};
};
}

View File

@ -2,4 +2,5 @@ export interface LOS {
highlightMap: (mapContainerElement: HTMLElement, mapKey: string, contextualClass: "success" | "danger") => void;
initializeUnlockFieldButtons: (containerElement: HTMLElement) => void;
populateAliases: (containerElement: HTMLElement) => void;
getRandomColor: (seedString: string) => string;
}

View File

@ -1 +1,2 @@
export {};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -6,4 +6,5 @@ export interface LOS {
) => void;
initializeUnlockFieldButtons: (containerElement: HTMLElement) => void;
populateAliases: (containerElement: HTMLElement) => void;
getRandomColor: (seedString: string) => string;
}

View File

@ -209,6 +209,7 @@ export interface WorkOrderComment extends Record {
export interface WorkOrderMilestone extends Record {
workOrderMilestoneId?: number;
workOrderId?: number;
workOrder?: WorkOrder;
workOrderMilestoneTypeId?: number;
workOrderMilestoneType?: string;
workOrderMilestoneDate?: number;

View File

@ -279,7 +279,9 @@ export interface WorkOrderComment extends Record {
export interface WorkOrderMilestone extends Record {
workOrderMilestoneId?: number;
workOrderId?: number;
workOrder?: WorkOrder;
workOrderMilestoneTypeId?: number;
workOrderMilestoneType?: string;

View File

@ -28,6 +28,7 @@
<script src="<%= urlPrefix %>/lib/cityssm-bulma-js/bulma-js.js"></script>
<script src="<%= urlPrefix %>/lib/cityssm-bulma-webapp-js/dist/cityssm.min.js"></script>
<script src="<%= urlPrefix %>/lib/leaflet/leaflet.js"></script>
<script src="<%= urlPrefix %>/lib/randomcolor/randomcolor.js"></script>
<script>
cityssm.htmlModalFolder ="<%= urlPrefix %>/html/";
bulmaJS.init();

View File

@ -115,6 +115,12 @@
</span>
</button>
<% if (!isCreate) { %>
<button class="button is-primary ml-2" id="button--closeWorkOrder" type="button">
<span class="icon is-small"><i class="fas fa-stop-circle" aria-hidden="true"></i></span>
<span>
Close Work Order
</span>
</button>
<div class="dropdown is-right ml-2">
<div class="dropdown-trigger">
<button class="button" type="button">
@ -124,7 +130,7 @@
</div>
<div class="dropdown-menu">
<div class="dropdown-content">
<a class="dropdown-item" href="#">
<a class="dropdown-item" id="button--deleteWorkOrder" href="#">
<span class="icon is-small">
<i class="fas fa-trash has-text-danger" aria-hidden="true"></i>
</span>

View File

@ -1,7 +1,5 @@
<%- include('_header'); -%>
<%- include('_header'); -%>
<div class="columns">
<div class="column is-3 is-hidden-mobile">
<%- include('_menu-workOrders'); -%>
@ -36,7 +34,7 @@
<label class="label" for="searchFilter--workOrderMilestoneDateFilter">Miestone Date Filter Type</label>
<div class="control">
<div class="select is-fullwidth">
<select id="searchFilter--workOrderMilestoneDateFilter">
<select id="searchFilter--workOrderMilestoneDateFilter" name="workOrderMilestoneDateFilter">
<option value="upcomingMissed" selected>Upcoming and Missed</option>
<option value="recent">Recent</option>
<option value="date">Specific Date</option>
@ -56,6 +54,8 @@
</div>
</form>
</div>
<div id="container--milestoneCalendar"></div>
</div>
</div>

View File

@ -1,7 +1,5 @@
<%- include('_header'); -%>
<%- include('_header'); -%>
<div class="columns">
<div class="column is-3 is-hidden-mobile">
<%- include('_menu-workOrders'); -%>