/* eslint-disable unicorn/filename-case */
import ical, { ICalEventData, ICalEventStatus } from "ical-generator";
import {
getWorkOrderMilestones,
WorkOrderMilestoneFilters
} from "../../helpers/lotOccupancyDB/getWorkOrderMilestones.js";
import type { RequestHandler, Request } from "express";
import * as configFunctions from "../../helpers/functions.config.js";
import { getPrintConfig } from "../../helpers/functions.print.js";
import type * as recordTypes from "../../types/recordTypes";
const calendarCompany = "cityssm.github.io";
const calendarProduct = configFunctions.getProperty("application.applicationName");
const timeStringSplitRegex = /[ :-]/;
function escapeHTML(stringToEscape: string) {
return stringToEscape.replace(/[^\d A-Za-z]/g, (c) => "" + c.codePointAt(0) + ";");
}
function getUrlRoot(request: Request): string {
return (
"http://" +
request.hostname +
(configFunctions.getProperty("application.httpPort") === 80
? ""
: ":" + configFunctions.getProperty("application.httpPort")) +
configFunctions.getProperty("reverseProxy.urlPrefix")
);
}
function getWorkOrderUrl(request: Request, milestone: recordTypes.WorkOrderMilestone) {
return getUrlRoot(request) + "/workOrders/" + milestone.workOrderId;
}
function buildEventSummary(milestone: recordTypes.WorkOrderMilestone): string {
let summary =
(milestone.workOrderMilestoneCompletionDate ? "✔ " : "") +
(milestone.workOrderMilestoneTypeId
? milestone.workOrderMilestoneType
: milestone.workOrderMilestoneDescription
).trim();
if (milestone.workOrderLotOccupancies.length > 0) {
let occupantCount = 0;
for (const lotOccupancy of milestone.workOrderLotOccupancies) {
for (const occupant of lotOccupancy.lotOccupancyOccupants) {
occupantCount += 1;
if (occupantCount === 1) {
if (summary !== "") {
summary += ": ";
}
summary += occupant.occupantName;
}
}
}
if (occupantCount > 1) {
summary += " plus " + (occupantCount - 1);
}
}
return summary;
}
function buildEventDescriptionHTML(
request: Request,
milestone: recordTypes.WorkOrderMilestone
): string {
const urlRoot = getUrlRoot(request);
const workOrderUrl = getWorkOrderUrl(request, milestone);
let descriptionHTML =
"
Milestone Description
" +
"" +
escapeHTML(milestone.workOrderMilestoneDescription) +
"
" +
"Work Order #" +
milestone.workOrderNumber +
"
" +
("" + escapeHTML(milestone.workOrderDescription) + "
") +
("" + workOrderUrl + "
");
if (milestone.workOrderLotOccupancies.length > 0) {
descriptionHTML +=
"Related " +
escapeHTML(configFunctions.getProperty("aliases.occupancies")) +
"
" +
'' +
("| " + escapeHTML(configFunctions.getProperty("aliases.occupancy")) + " Type | ") +
("" + escapeHTML(configFunctions.getProperty("aliases.lot")) + " | ") +
"Start Date | " +
"End Date | " +
("" + escapeHTML(configFunctions.getProperty("aliases.occupants")) + " | ") +
"
" +
"";
for (const occupancy of milestone.workOrderLotOccupancies) {
descriptionHTML +=
"" +
("| " +
'' +
escapeHTML(occupancy.occupancyType) +
" | ") +
("" +
(occupancy.lotName ? escapeHTML(occupancy.lotName) : "(Not Set)") +
" | ") +
("" + occupancy.occupancyStartDateString + " | ") +
"" +
(occupancy.occupancyEndDate ? occupancy.occupancyEndDateString : "(No End Date)") +
" | " +
"";
for (const occupant of occupancy.lotOccupancyOccupants) {
descriptionHTML +=
escapeHTML(occupant.lotOccupantType) +
": " +
escapeHTML(occupant.occupantName) +
" ";
}
descriptionHTML += " | " + "
";
}
descriptionHTML += "
";
}
if (milestone.workOrderLots.length > 0) {
descriptionHTML +=
"Related " +
escapeHTML(configFunctions.getProperty("aliases.lots")) +
"
" +
'' +
("| " + escapeHTML(configFunctions.getProperty("aliases.lot")) + " Type | ") +
("" + escapeHTML(configFunctions.getProperty("aliases.map")) + " | ") +
("" + escapeHTML(configFunctions.getProperty("aliases.lot")) + " Type" + " | ") +
"Status | " +
"
" +
"";
for (const lot of milestone.workOrderLots) {
descriptionHTML +=
"" +
("| " +
'' +
escapeHTML(lot.lotName) +
" | ") +
("" + escapeHTML(lot.mapName) + " | ") +
("" + escapeHTML(lot.lotType) + " | ") +
("" + escapeHTML(lot.lotStatus) + " | ") +
"
";
}
descriptionHTML += "
";
}
const prints = configFunctions.getProperty("settings.workOrders.prints");
if (prints.length > 0) {
descriptionHTML += "Prints
";
for (const printName of prints) {
const printConfig = getPrintConfig(printName);
if (printConfig) {
descriptionHTML +=
"" +
escapeHTML(printConfig.title) +
"
" +
(urlRoot + "/print/" + printName + "/?workOrderId=" + milestone.workOrderId) +
"
";
}
}
}
return descriptionHTML;
}
export const handler: RequestHandler = (request, response) => {
const urlRoot = getUrlRoot(request);
/*
* Get work order milestones
*/
const workOrderMilestoneFilters: WorkOrderMilestoneFilters = {
workOrderTypeIds: request.query.workOrderTypeIds as string,
workOrderMilestoneTypeIds: request.query.workOrderMilestoneTypeIds as string
};
if (request.query.workOrderId) {
workOrderMilestoneFilters.workOrderId = request.query.workOrderId as string;
} else {
workOrderMilestoneFilters.workOrderMilestoneDateFilter = "recent";
}
const workOrderMilestones = getWorkOrderMilestones(workOrderMilestoneFilters, {
includeWorkOrders: true,
orderBy: "date"
});
/*
* Create calendar object
*/
const calendar = ical({
name: "Work Order Milestone Calendar",
url: urlRoot + "/workOrders"
});
if (request.query.workOrderId && workOrderMilestones.length > 0) {
calendar.name("Work Order #" + workOrderMilestones[0].workOrderNumber);
calendar.url(urlRoot + "/workOrders/" + workOrderMilestones[0].workOrderId);
}
calendar.prodId({
company: calendarCompany,
product: calendarProduct
});
/*
* Loop through milestones
*/
for (const milestone of workOrderMilestones) {
const milestoneTimePieces = (
milestone.workOrderMilestoneDateString +
" " +
milestone.workOrderMilestoneTimeString
).split(timeStringSplitRegex);
const milestoneDate = new Date(
Number.parseInt(milestoneTimePieces[0], 10),
Number.parseInt(milestoneTimePieces[1], 10) - 1,
Number.parseInt(milestoneTimePieces[2], 10),
Number.parseInt(milestoneTimePieces[3], 10),
Number.parseInt(milestoneTimePieces[4], 10)
);
const milestoneEndDate = new Date(milestoneDate.getTime());
milestoneEndDate.setHours(milestoneEndDate.getHours() + 1);
// Build summary (title in Outlook)
const summary = buildEventSummary(milestone);
// Build URL
const workOrderUrl = getWorkOrderUrl(request, milestone);
// Create event
const eventData: ICalEventData = {
start: milestoneDate,
created: new Date(milestone.recordCreate_timeMillis),
stamp: new Date(milestone.recordCreate_timeMillis),
lastModified: new Date(
Math.max(
milestone.recordUpdate_timeMillis,
milestone.workOrderRecordUpdate_timeMillis
)
),
allDay: !milestone.workOrderMilestoneTime,
summary,
url: workOrderUrl
};
if (!eventData.allDay) {
eventData.end = milestoneEndDate;
}
const calendarEvent = calendar.createEvent(eventData);
// Build description
const descriptionHTML = buildEventDescriptionHTML(request, milestone);
calendarEvent.description({
plain: workOrderUrl,
html: descriptionHTML
});
// Set status
if (milestone.workOrderMilestoneCompletionDate) {
calendarEvent.status(ICalEventStatus.CONFIRMED);
}
// Add categories
if (milestone.workOrderMilestoneTypeId) {
calendarEvent.createCategory({
name: milestone.workOrderMilestoneType
});
calendarEvent.createCategory({
name: milestone.workOrderType
});
}
if (milestone.workOrderMilestoneCompletionDate) {
calendarEvent.createCategory({
name: "Completed"
});
}
// Set location
if (milestone.workOrderLots.length > 0) {
const lotNames = [];
for (const lot of milestone.workOrderLots) {
lotNames.push(lot.mapName + ": " + lot.lotName);
}
calendarEvent.location(lotNames.join(", "));
}
// Set organizer / attendees
if (milestone.workOrderLotOccupancies.length > 0) {
let organizerSet = false;
for (const lotOccupancy of milestone.workOrderLotOccupancies) {
for (const occupant of lotOccupancy.lotOccupancyOccupants) {
if (organizerSet) {
calendarEvent.createAttendee({
name: occupant.occupantName,
email: configFunctions.getProperty(
"settings.workOrders.calendarEmailAddress"
)
});
} else {
calendarEvent.organizer({
name: occupant.occupantName,
email: configFunctions.getProperty(
"settings.workOrders.calendarEmailAddress"
)
});
organizerSet = true;
}
}
}
} else {
calendarEvent.organizer({
name: milestone.recordCreate_userName,
email: configFunctions.getProperty("settings.workOrders.calendarEmailAddress")
});
}
}
calendar.serve(response);
};
export default handler;