@plusscommunities/pluss-maintenance-aws-forms 2.1.7-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@plusscommunities/pluss-maintenance-aws-forms",
3
+ "version": "2.1.7-beta.0",
4
+ "description": "Extension package to enable maintenance on Pluss Communities Platform",
5
+ "scripts": {
6
+ "gc": "node ../../tools/gc ./",
7
+ "gs": "node ../../tools/gs ./ ../../activity/serverless.yml",
8
+ "betapatch": "npm version prepatch --preid=beta",
9
+ "patch": "npm version patch",
10
+ "deploy": "npm run gc && npm run gs && serverless deploy",
11
+ "betaupload": "rm -rf .serverless && npm i && npm publish --access public --tag beta",
12
+ "betaupload:p": "npm run betapatch && npm run betaupload",
13
+ "upload": "rm -rf .serverless && npm i && npm publish --access public",
14
+ "upload:p": "npm run patch && npm run upload",
15
+ "copy:add": "run(){ ext=${1:-default}; test -f values.config.$ext.js || cp values.config.default.js values.config.$ext.js; }; run",
16
+ "copy:get": "echo $npm_package_name",
17
+ "copy:set": "run(){ target='\\@plusscommunities\\/pluss-maintenance-aws'; ext=${1:-default}; [ $ext == 'default' ] && replace=$target || replace=$target'-'$ext; echo 'Setting target to '$replace; test -f values.config.$ext.js && cp -f values.config.$ext.js values.config.js; sed -i '' -e 's/'$target'.*\"/'$replace'\"/g' package.json; }; run",
18
+ "copy:deploy": "for file in `ls ./values.config.*.js`; do dup=`echo $file | sed 's/.*values\\.config\\.\\(.*\\)\\.js/\\1/'`; npm run copy:set $dup; npm run deploy; done; npm run copy:set; npm run gs;",
19
+ "copy:betaupload": "npm run betapatch; for file in `ls ./values.config.*.js`; do dup=`echo $file | sed 's/.*values\\.config\\.\\(.*\\)\\.js/\\1/'`; npm run copy:set $dup; npm run betaupload; done; npm run copy:set;",
20
+ "copy:upload": "npm run patch; for file in `ls ./values.config.*.js`; do dup=`echo $file | sed 's/.*values\\.config\\.\\(.*\\)\\.js/\\1/'`; npm run copy:set $dup; npm run upload; done; npm run copy:set;",
21
+ "test": "jest tests -i"
22
+ },
23
+ "author": "Thorbjorn Kappel Davis",
24
+ "license": "ISC",
25
+ "dependencies": {
26
+ "@aws/dynamodb-auto-marshaller": "^0.7.1",
27
+ "@plusscommunities/pluss-core-aws": "2.0.16-beta.0",
28
+ "amazon-cognito-identity-js": "^2.0.19",
29
+ "aws-sdk": "^2.1591.0",
30
+ "axios": "^1.6.8",
31
+ "base64-arraybuffer": "^1.0.2",
32
+ "expo-server-sdk": "^3.0.1",
33
+ "https": "^1.0.0",
34
+ "lodash": "^4.17.10",
35
+ "moment": "^2.30.1",
36
+ "node-fetch": "^2.2.0",
37
+ "node-jose": "^1.0.0",
38
+ "nodemailer": "^6.9.12",
39
+ "twilio": "^3.18.0",
40
+ "uuid": "^2.0.3"
41
+ },
42
+ "devDependencies": {
43
+ "@types/jest": "^26.0.23",
44
+ "eslint-config-rallycoding": "^3.2.0",
45
+ "jest": "^29.0.0",
46
+ "jest-aws-sdk-mock": "^1.0.2",
47
+ "serverless-domain-manager": "^3.3.1",
48
+ "serverless-prune-plugin": "^1.4.1"
49
+ },
50
+ "files": [
51
+ "db/*",
52
+ "integration/*",
53
+ "requests/*",
54
+ "ticketing/*",
55
+ "*.js",
56
+ "package*.json"
57
+ ],
58
+ "jest": {
59
+ "rootDir": "./",
60
+ "moduleNameMapper": {
61
+ "^./testing/(.*)$": "<rootDir>/testing/$1"
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,106 @@
1
+ const moment = require("moment");
2
+ const editRef = require("@plusscommunities/pluss-core-aws/db/common/editRef");
3
+ const validateMasterAuth = require("@plusscommunities/pluss-core-aws/helper/auth/validateMasterAuth");
4
+ const getUserPreviewFromReq = require("@plusscommunities/pluss-core-aws/helper/getUserPreviewFromReq");
5
+ const getRef = require("@plusscommunities/pluss-core-aws/db/common/getRef");
6
+ const isValidAssignee = require("./helper/isValidAssignee");
7
+ const getUserPreview = require("@plusscommunities/pluss-core-aws/helper/getUserPreview");
8
+ const { log } = require("@plusscommunities/pluss-core-aws/helper");
9
+ const publishNotifications = require("@plusscommunities/pluss-core-aws/db/notifications/publishNotifications");
10
+ const { values } = require("../values.config");
11
+
12
+ module.exports = async (event, data) => {
13
+ const logId = log("assignRequest", "data", data);
14
+ // check required data
15
+ const requiredKeys = ["id", "userId"];
16
+ if (!data || requiredKeys.some((prop) => !data.hasOwnProperty(prop))) {
17
+ log("assignRequest", "InsufficientInput", 422, logId);
18
+ return { status: 422, data: { error: "Insufficient input" } };
19
+ }
20
+
21
+ const request = await getRef(values.tableNameMaintenance, "id", data.id);
22
+ log("assignRequest", "request", request, logId);
23
+ log("assignRequest", "site", request.site, logId);
24
+
25
+ // validate authorisation
26
+ const valid = await validateMasterAuth(
27
+ event,
28
+ values.permissionMaintenanceTracking,
29
+ request.site
30
+ );
31
+ log("assignRequest", "valid", valid, logId);
32
+ if (!valid) {
33
+ const validAssignee = await isValidAssignee(
34
+ event,
35
+ request.site,
36
+ request.AssigneeId
37
+ );
38
+ log("assignRequest", "validAssignee", validAssignee, logId);
39
+ if (!validAssignee) {
40
+ log("assignRequest", "NotAuthorised", 403, logId);
41
+ return { status: 403, data: { error: "Not authorised" } };
42
+ }
43
+ }
44
+
45
+ const user = await getUserPreviewFromReq(event);
46
+ log("assignRequest", "user", user, logId);
47
+ const assignedUser = await getUserPreview(data.userId);
48
+ log("assignRequest", "assignedUser", assignedUser, logId);
49
+
50
+ const changes = {
51
+ history: request.history || [],
52
+ };
53
+
54
+ // Update history
55
+ changes.history.push({
56
+ timestamp: moment.utc().valueOf(),
57
+ EntryType: "assignment",
58
+ user,
59
+ assignedUser,
60
+ });
61
+
62
+ changes.AssigneeId = data.userId;
63
+ changes.Assignee = assignedUser;
64
+
65
+ log("assignRequest", "changes", changes, logId);
66
+
67
+ const result = await editRef(
68
+ values.tableNameMaintenance,
69
+ "id",
70
+ data.id,
71
+ changes
72
+ );
73
+
74
+ log("assignRequest", "result", result, logId);
75
+
76
+ // send notification to assigned user
77
+ if (user.id !== assignedUser.id) {
78
+ await publishNotifications(
79
+ [assignedUser.id],
80
+ values.notificationMaintenanceJobAssigned,
81
+ result.site,
82
+ result.id,
83
+ result,
84
+ true
85
+ );
86
+ }
87
+
88
+ // send notification to previous assignee
89
+ if (request.AssigneeId && result.AssigneeId !== request.AssigneeId) {
90
+ await publishNotifications(
91
+ [request.AssigneeId],
92
+ values.notificationMaintenanceJobUnassigned,
93
+ result.site,
94
+ result.id,
95
+ result,
96
+ true
97
+ );
98
+ }
99
+
100
+ return {
101
+ status: 200,
102
+ data: {
103
+ job: result,
104
+ },
105
+ };
106
+ };
@@ -0,0 +1,40 @@
1
+ const { log } = require("@plusscommunities/pluss-core-aws/helper");
2
+ const getUsersByPermission = require("@plusscommunities/pluss-core-aws/helper/users/getUsersByPermission");
3
+ const validateMasterAuth = require("@plusscommunities/pluss-core-aws/helper/auth/validateMasterAuth");
4
+ const { values } = require("../values.config");
5
+
6
+ // Define the new function to get assignees
7
+ module.exports = async (event) => {
8
+ const qParams = event.queryStringParameters || {};
9
+ const logId = log("getAssignees", "Input", qParams);
10
+
11
+ const isAdmin = await validateMasterAuth(
12
+ event,
13
+ values.permissionMaintenanceTracking,
14
+ qParams.site
15
+ );
16
+ const isAssignee = await validateMasterAuth(
17
+ event,
18
+ values.permissionMaintenanceAssignment,
19
+ qParams.site
20
+ );
21
+
22
+ const valid = isAdmin || isAssignee;
23
+ log("getRequests", "valid", valid, logId);
24
+ if (!valid) {
25
+ return { status: 403, data: { error: "Not authorised" } };
26
+ }
27
+
28
+ // get assignees
29
+ const assignees = await getUsersByPermission(qParams.site, [
30
+ values.permissionMaintenanceTracking,
31
+ values.permissionMaintenanceAssignment,
32
+ ]);
33
+
34
+ log("getAssignees", "AssigneesLength", assignees.length, logId);
35
+
36
+ // compile results
37
+ const results = { Users: assignees };
38
+ log("getAssignees", "Done", true, logId);
39
+ return { status: 200, data: results };
40
+ };
@@ -0,0 +1,89 @@
1
+ const { log, getRowId } = require("@plusscommunities/pluss-core-aws/helper");
2
+ const getRef = require("@plusscommunities/pluss-core-aws/db/common/getRef");
3
+ const indexQuery = require("@plusscommunities/pluss-core-aws/db/common/indexQuery");
4
+ const validateMasterAuth = require("@plusscommunities/pluss-core-aws/helper/auth/validateMasterAuth");
5
+ const isValidAssignee = require("./helper/isValidAssignee");
6
+ const getSessionUserFromReqAuthKey = require("@plusscommunities/pluss-core-aws/helper/auth/getSessionUserFromReqAuthKey");
7
+ const { values } = require("../values.config");
8
+
9
+ module.exports = async (event, params) => {
10
+ const data = params || event.queryStringParameters;
11
+ const logId = log("getRequest", "Params", data);
12
+ if (!data.id && (!data.site || !data.jobId)) {
13
+ return {
14
+ status: 422,
15
+ data: {
16
+ error: "Insufficient input",
17
+ },
18
+ };
19
+ }
20
+
21
+ try {
22
+ let result = null;
23
+ if (data.jobId) {
24
+ const query = {
25
+ IndexName: "MaintenanceSiteJobIdIndex",
26
+ KeyConditionExpression: "site = :site and jobId = :jobId",
27
+ ExpressionAttributeValues: {
28
+ ":site": data.site,
29
+ ":jobId": data.jobId,
30
+ },
31
+ };
32
+ const { Items } = await indexQuery(values.tableNameMaintenance, query);
33
+ result = Items[0];
34
+ }
35
+ if (!result) {
36
+ result = await getRef(
37
+ values.tableNameMaintenance,
38
+ "id",
39
+ data.id || data.jobId
40
+ );
41
+ }
42
+
43
+ const authorised = await validateMasterAuth(
44
+ event,
45
+ values.permissionMaintenanceTracking,
46
+ result.site
47
+ );
48
+ const assignAuthorised = await isValidAssignee(
49
+ event,
50
+ result.site,
51
+ result.AssigneeId
52
+ );
53
+ if (!authorised && !assignAuthorised) {
54
+ // Check if the job belongs to the user
55
+ const userId = await getSessionUserFromReqAuthKey(event);
56
+ if (userId !== result.userID) {
57
+ return {
58
+ status: 403,
59
+ data: {
60
+ error: "Not authorised",
61
+ },
62
+ };
63
+ }
64
+ }
65
+ if (data.includeComments) {
66
+ const commentsQuery = {
67
+ IndexName: "CommentsEntityIdIndex",
68
+ KeyConditionExpression: "EntityId = :groupId",
69
+ ExpressionAttributeValues: {
70
+ ":groupId": getRowId(result.id, values.entityKey),
71
+ },
72
+ };
73
+ const commentsData = await indexQuery("comments", commentsQuery);
74
+ result.Comments = commentsData.Items;
75
+ }
76
+ return {
77
+ status: 200,
78
+ data: result,
79
+ };
80
+ } catch (error) {
81
+ log("getRequest", "Error", error.toString(), logId);
82
+ }
83
+ return {
84
+ status: 500,
85
+ data: {
86
+ error: "Internal error",
87
+ },
88
+ };
89
+ };
@@ -0,0 +1,100 @@
1
+ const { log } = require("@plusscommunities/pluss-core-aws/helper");
2
+ const getSessionUserFromReqAuthKey = require("@plusscommunities/pluss-core-aws/helper/auth/getSessionUserFromReqAuthKey");
3
+ const validateMasterAuth = require("@plusscommunities/pluss-core-aws/helper/auth/validateMasterAuth");
4
+ const validateSiteAccess = require("@plusscommunities/pluss-core-aws/helper/auth/validateSiteAccess");
5
+ const indexQuery = require("@plusscommunities/pluss-core-aws/db/common/indexQuery");
6
+ const { values } = require("../values.config");
7
+
8
+ module.exports = async (event) => {
9
+ const qParams = event.queryStringParameters;
10
+ const logId = log("getRequests", "Params", qParams);
11
+
12
+ // insufficient input
13
+ if (!qParams.site) {
14
+ return { status: 422, data: { error: "Insufficient input" } };
15
+ }
16
+ log("getRequests", "SufficientInput", true, logId);
17
+
18
+ // no access to site
19
+ const valid = await validateSiteAccess(event, qParams.site);
20
+ log("getRequests", "valid", valid, logId);
21
+ if (!valid) {
22
+ return { status: 403, data: { error: "Not authorised" } };
23
+ }
24
+
25
+ // check auth level to determine whether to fetch all requests or only matching requests
26
+ const authorised = await validateMasterAuth(
27
+ event,
28
+ values.permissionMaintenanceTracking,
29
+ qParams.site
30
+ );
31
+ let assigneeTracking = false;
32
+ if (!authorised) {
33
+ assigneeTracking = await validateMasterAuth(
34
+ event,
35
+ values.permissionMaintenanceAssignment,
36
+ qParams.site
37
+ );
38
+ }
39
+
40
+ log("getRequests", "authorised", authorised, logId);
41
+ const userId = authorised ? null : await getSessionUserFromReqAuthKey(event);
42
+
43
+ log("getRequests", "userId", userId, logId);
44
+
45
+ const query =
46
+ authorised || assigneeTracking
47
+ ? {
48
+ IndexName: "MaintenanceSiteIndex",
49
+ KeyConditionExpression: "site = :site",
50
+ ExpressionAttributeValues: {
51
+ ":site": qParams.site,
52
+ },
53
+ }
54
+ : {
55
+ IndexName: "MaintenanceSiteUserIdIndex",
56
+ KeyConditionExpression: "site = :site AND userID = :userId",
57
+ ExpressionAttributeValues: {
58
+ ":site": qParams.site,
59
+ ":userId": userId,
60
+ },
61
+ };
62
+ log("getRequests", "query", query, logId);
63
+
64
+ // check whether pagination is applied
65
+ if (qParams.lastKey) {
66
+ try {
67
+ query.ExclusiveStartKey = JSON.parse(qParams.lastKey);
68
+ } catch (e) {}
69
+ }
70
+
71
+ // get jobs
72
+ const result = await indexQuery(values.tableNameMaintenance, query);
73
+ let jobs = result.Items;
74
+
75
+ log("getRequests", "LastEvaluatedKey", result.LastEvaluatedKey, logId);
76
+ log("getRequests", "JobsLength", jobs.length, logId);
77
+
78
+ // filter on status
79
+ if (qParams.status) {
80
+ jobs = jobs.filter((j) => qParams.status.includes(j.status));
81
+ log("getRequests", "FilteredOnStatus", jobs.length, logId);
82
+ }
83
+
84
+ // filter on type
85
+ if (qParams.type) {
86
+ jobs = jobs.filter((j) => qParams.type.includes(j.type));
87
+ log("getRequests", "FilteredOnType", jobs.length, logId);
88
+ }
89
+
90
+ // filter to assigned jobs
91
+ if (!authorised && assigneeTracking) {
92
+ jobs = jobs.filter((j) => j.AssigneeId === userId || j.userID === userId);
93
+ log("getRequests", "FilteredOnAssignee", jobs.length, logId);
94
+ }
95
+
96
+ // compile results
97
+ const results = { Items: jobs, LastKey: result.LastEvaluatedKey };
98
+ log("getRequests", "Done", true, logId);
99
+ return { status: 200, data: results };
100
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Checks if the request has permission.
3
+ *
4
+ * @param {Object} event - The api request object.
5
+ * @param {Object} job - The job object.
6
+ * @returns {Promise<boolean>} - A promise that resolves to a boolean indicating if the request has permission.
7
+ */
8
+ const validateMasterAuth = require("@plusscommunities/pluss-core-aws/helper/auth/validateMasterAuth");
9
+ const isValidAssignee = require("./isValidAssignee");
10
+ const { values } = require("../../values.config");
11
+
12
+ module.exports = async (event, job) => {
13
+ const valid = await validateMasterAuth(
14
+ event,
15
+ values.permissionMaintenanceTracking,
16
+ job.site
17
+ );
18
+ if (valid) {
19
+ return true;
20
+ }
21
+ if (!job.AssigneeId) {
22
+ return false;
23
+ }
24
+ return await isValidAssignee(event, job.site, job.AssigneeId);
25
+ };
@@ -0,0 +1,23 @@
1
+ const getSessionUserFromReqAuthKey = require("@plusscommunities/pluss-core-aws/helper/auth/getSessionUserFromReqAuthKey");
2
+ const validateMasterAuth = require("@plusscommunities/pluss-core-aws/helper/auth/validateMasterAuth");
3
+ const { values } = require("../../values.config");
4
+
5
+ /**
6
+ * Checks whether the logged in user is the assignee as has the assignment permission for that site
7
+ * @param {*} event A request including headers
8
+ * @param {String} site A site id
9
+ * @param {String} userId A user id to check for
10
+ * @returns {Boolean} true if valid
11
+ */
12
+ module.exports = async (event, site, userId) => {
13
+ const valid = await validateMasterAuth(
14
+ event,
15
+ values.permissionMaintenanceAssignment,
16
+ site
17
+ );
18
+ if (!valid) {
19
+ return false;
20
+ }
21
+ const loggedInUser = await getSessionUserFromReqAuthKey(event);
22
+ return loggedInUser === userId;
23
+ };
@@ -0,0 +1,81 @@
1
+ const indexQuery = require("@plusscommunities/pluss-core-aws/db/common/indexQuery");
2
+ const editRef = require("@plusscommunities/pluss-core-aws/db/common/editRef");
3
+ const moment = require("moment");
4
+ const { getStrategy } = require("./integration");
5
+ const { log } = require("@plusscommunities/pluss-core-aws/helper");
6
+
7
+ const processSingle = async (strategy, item) => {
8
+ // use IDs to refresh request data
9
+ const logId = log("processSingle", "RefreshFromSource", item.ExternalId);
10
+
11
+ // refresh from external source
12
+ await strategy.refreshFromSource(
13
+ item.InternalId,
14
+ item.ExternalId,
15
+ item.TrackedData
16
+ );
17
+
18
+ // save updated timestamp
19
+ await editRef("externalentities", "RowId", item.RowId, {
20
+ LastUpdated: moment().valueOf(),
21
+ });
22
+
23
+ log("processBatch", "Refreshed", item.ExternalId, logId);
24
+ return true;
25
+ };
26
+
27
+ const processBatch = async (strategy, startTime) => {
28
+ const logId = log("processBatch", "StartTime", moment().valueOf());
29
+
30
+ // use index query to fetch IDs of requests
31
+ const query = {
32
+ IndexName: "EntityTypeIndex",
33
+ KeyConditionExpression:
34
+ "EntityType = :entityType AND LastUpdated < :lastUpdate", // only fetch items that haven't been updated in this scan
35
+ ExpressionAttributeValues: {
36
+ ":entityType": strategy.getEntityType(),
37
+ ":lastUpdate": startTime - strategy.getRefreshInterval(),
38
+ },
39
+ Limit: 10,
40
+ ScanIndexForward: false,
41
+ };
42
+ const { Items } = await indexQuery("externalentities", query);
43
+
44
+ const promises = [];
45
+
46
+ Items.forEach((item) => {
47
+ log("processBatch", "ProcessSingle", JSON.stringify(item), logId);
48
+ promises.push(processSingle(strategy, JSON.parse(JSON.stringify(item))));
49
+ });
50
+
51
+ await Promise.all(promises);
52
+ log("processBatch", "EndOfBatch", Items.length, logId);
53
+ return Items.length;
54
+ };
55
+
56
+ module.exports.scheduleJobImport = async (event, context, callback) => {
57
+ const logId = log("scheduleJobImport", "Start", true);
58
+ // get active integration
59
+ const integrationStrategy = await getStrategy("plussSpace");
60
+ if (!integrationStrategy.isValidIntegration()) {
61
+ log("scheduleJobImport", "Exit", "No valid integration", logId);
62
+ return;
63
+ }
64
+
65
+ const startTime = moment().valueOf();
66
+ const timeout = 3 * 60 * 1000; // 3 minutes
67
+ let noneToProcess = false;
68
+ log("scheduleJobImport", "StartTime", startTime, logId);
69
+
70
+ while (!noneToProcess && moment().valueOf() < startTime + timeout) {
71
+ const logId = log("scheduleJobImport", "StartLoop", moment().valueOf());
72
+ const count = await processBatch(integrationStrategy, startTime);
73
+ log("scheduleJobImport", "Count", count, logId);
74
+ if (!count) {
75
+ log("scheduleJobImport", "noneToProcess", true, logId);
76
+ noneToProcess = true;
77
+ }
78
+ log("scheduleJobImport", "EndLoop", moment().valueOf(), logId);
79
+ }
80
+ log("scheduleJobImport", "End", moment().valueOf(), logId);
81
+ };
@@ -0,0 +1,153 @@
1
+ const _ = require("lodash");
2
+ const moment = require("moment");
3
+ const getJobEmail = require("./db/maintenance/getJobEmail");
4
+ const sendEmail = require("@plusscommunities/pluss-core-aws/helper/sendEmail");
5
+ const getRef = require("@plusscommunities/pluss-core-aws/db/common/getRef");
6
+ const { values } = require("./values.config");
7
+
8
+ module.exports = function (job, updated) {
9
+ const whenEvs = job.isHome
10
+ ? "Yes - I would like to be present"
11
+ : "No - Work may be done while I am not home.";
12
+
13
+ getRef("sites", "Id", job.site || job.location)
14
+ .then((site) => {
15
+ const siteToUse = site ? site.siteName : job.site || job.location;
16
+ const { customFields } = job;
17
+ const hasCustomFields = customFields && customFields.length > 0;
18
+
19
+ const renderCommon = () => `
20
+ <p>
21
+ <b>${values.textJobEmailTitle} ${job.id != null ? `#${job.id}` : ""}${
22
+ updated ? "-DETAILS EDITED- " : " "
23
+ }(${job.type}):</b> ${job.room}
24
+ </p>
25
+ <div>
26
+ <b>Title: </b>${job.title}
27
+ </div>
28
+ <p>Client Details:</p>
29
+ <div>
30
+ <b>Name: </b>${job.userName}
31
+ </div>
32
+ <div>
33
+ <b>Phone: </b>${job.phone || ""}
34
+ </div>
35
+ <div>
36
+ <b>Location: </b>${siteToUse} <br /><br />
37
+ </div>
38
+ <div>
39
+ <b>Address: </b>${job.room} <br /><br />
40
+ </div>
41
+ `;
42
+ const renderImages = () => {
43
+ let inner = "";
44
+ if (!_.isNil(job.images)) {
45
+ inner = job.images.map(
46
+ (image) => `<img style="max-height: 400px" src="${image}" />`
47
+ );
48
+ } else if (!_.isNil(job.image)) {
49
+ inner = `<img style="max-height: 400px" src="${job.image}" />`;
50
+ }
51
+ return _.isEmpty(inner) ? "" : `<div>${inner}</div>`;
52
+ };
53
+ const renderNonCustom = () => `
54
+ <div>
55
+ <b>Person Present During Service: </b>${whenEvs}
56
+ <div>
57
+ ${job.isHome ? job.homeText : ""}
58
+ <br /><br />
59
+ </div>
60
+ </div>
61
+ <div>
62
+ <b>${job.type} Description: </b>
63
+ </div>
64
+ <div>
65
+ ${job.description || ""}
66
+ </div>
67
+ ${renderImages()}
68
+ `;
69
+ const renderCustom = () => {
70
+ const renderAnswer = (field) => {
71
+ switch (field.type) {
72
+ case "date":
73
+ return field.answer
74
+ ? moment(field.answer, "YYYY-MM-DD").format("DD MMM YYYY")
75
+ : "";
76
+ case "time":
77
+ return field.answer
78
+ ? moment(field.answer, "HH:mm").format("h:mm a")
79
+ : "";
80
+ case "yn":
81
+ return field.answer ? "Yes" : "No";
82
+ case "checkbox":
83
+ return field.answer && Array.isArray(field.answer)
84
+ ? field.answer.join(", ")
85
+ : "";
86
+ case "image":
87
+ return field.answer && Array.isArray(field.answer)
88
+ ? `<br>${field.answer.map(
89
+ (image) =>
90
+ `<img style="max-height: 400px" src="${image}" />`
91
+ )}`
92
+ : "";
93
+ default:
94
+ return field.answer;
95
+ }
96
+ };
97
+
98
+ return customFields
99
+ .filter((f) => !["staticTitle", "staticText"].includes(f.type))
100
+ .map((field) => {
101
+ return `
102
+ <div>
103
+ <b>${field.label}: </b>${renderAnswer(field)}
104
+ </div>
105
+ `;
106
+ })
107
+ .join("\n");
108
+ };
109
+
110
+ const htmlString = `
111
+ <div>
112
+ ${renderCommon()}
113
+ ${hasCustomFields ? renderCustom() : renderNonCustom()}
114
+ </div>
115
+ `;
116
+
117
+ getJobEmail(job.site || job.location, job.type)
118
+ .then((obj) => {
119
+ const email = obj.email;
120
+ if (_.isEmpty(email)) {
121
+ return;
122
+ }
123
+
124
+ sendEmail(
125
+ email,
126
+ `${values.textJobEmailTitle} #${job.id}${
127
+ updated ? " -DETAILS EDITED" : ""
128
+ }- (${job.type}) - ${job.room}`,
129
+ htmlString
130
+ )
131
+ .then((result) => {
132
+ console.log(
133
+ `SUCCESS - Maintenance email --> ${job.type}, ${job.userName}`
134
+ );
135
+ })
136
+ .catch((error) => {
137
+ console.log(
138
+ `FAIL - Maintenance email --> ${job.type}, ${job.userName}`
139
+ );
140
+ });
141
+ })
142
+ .catch(() => {
143
+ console.log(
144
+ `FAIL - Maintenance email - fetch email address --> ${job.type}, ${job.userName}`
145
+ );
146
+ });
147
+ })
148
+ .catch(() => {
149
+ console.log(
150
+ `FAIL - Maintenance email - fetch site --> ${job.site || job.location}`
151
+ );
152
+ });
153
+ };