diff --git a/client/.env.production b/client/.env.production
index 7a58e4591..a4cdecee5 100644
--- a/client/.env.production
+++ b/client/.env.production
@@ -1,3 +1,4 @@
+GENERATE_SOURCEMAP=false
REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql
REACT_APP_GA_CODE=231103507
diff --git a/client/src/components/production-board-kanban-card/production-board-kanban-card-color-legend.component.jsx b/client/src/components/production-board-kanban-card/production-board-kanban-card-color-legend.component.jsx
new file mode 100644
index 000000000..e9cc0f4e0
--- /dev/null
+++ b/client/src/components/production-board-kanban-card/production-board-kanban-card-color-legend.component.jsx
@@ -0,0 +1,39 @@
+import { Col, List, Space, Typography } from "antd";
+import React from "react";
+import { useTranslation } from "react-i18next";
+
+const CardColorLegend = ({ bodyshop, cardSettings }) => {
+ const { t } = useTranslation();
+ const data = bodyshop.ssbuckets.map((size) => ({
+ label: size.label,
+ color: size.color?.hex ?? "white",
+ }));
+
+ return (
+
+ {t("production.labels.legend")}
+ (
+
+
+
+ {item.label}
+
+
+ )}
+ />
+
+ );
+};
+
+export default CardColorLegend;
diff --git a/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx b/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx
index 750204fb5..cb955a9c6 100644
--- a/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx
+++ b/client/src/components/production-board-kanban-card/production-board-kanban-card.component.jsx
@@ -18,6 +18,28 @@ import moment from "moment";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
+const cardColor = (ssbuckets, totalHrs) => {
+ const bucket = ssbuckets.filter(
+ (bucket) =>
+ bucket.gte <= totalHrs && (!!bucket.lt ? bucket.lt > totalHrs : true)
+ )[0];
+
+ if (bucket.color) {
+ return bucket.color.hex;
+ }
+
+ return "";
+};
+
+function getContrastYIQ(hexColor) {
+ const r = parseInt(hexColor.substr(1, 2), 16);
+ const g = parseInt(hexColor.substr(3, 2), 16);
+ const b = parseInt(hexColor.substr(5, 2), 16);
+ const yiq = (r * 299 + g * 587 + b * 114) / 1000;
+
+ return yiq >= 128 ? "black" : "white";
+}
+
export default function ProductionBoardCard(
technician,
card,
@@ -54,10 +76,19 @@ export default function ProductionBoardCard(
.isSame(moment(card.scheduled_completion), "day") &&
"production-completion-soon"));
+ const totalHrs =
+ card.labhrs.aggregate.sum.mod_lb_hrs + card.larhrs.aggregate.sum.mod_lb_hrs;
+ const bgColor = cardColor(bodyshop.ssbuckets, totalHrs);
+
return (
diff --git a/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx b/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx
index 25c77e307..bc7f83494 100644
--- a/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx
+++ b/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx
@@ -104,6 +104,13 @@ export default function ProductionBoardKanbanCardSettings({
>
+
+
+
}
/>
+
+ {cardSettings.cardcolor && (
+
+ )}
+
+
+
+
+
+
{
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index f52178267..1819fadbe 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -508,7 +508,8 @@
"id": "ID",
"label": "Label",
"lt": "Less than (hrs)",
- "target": "Target (count)"
+ "target": "Target (count)",
+ "color": "Job Color"
},
"state": "Province/State",
"state_tax_id": "Provincial/State Tax ID (PST, QST)",
@@ -2385,7 +2386,9 @@
"sublets": "Sublets",
"totalhours": "Total Hrs ",
"touchtime": "T/T",
- "viewname": "View Name"
+ "viewname": "View Name",
+ "legend": "Legend:",
+ "cardcolor": "Card Colors"
},
"successes": {
"removed": "Job removed from production."
diff --git a/client/src/utils/RenderTemplate.js b/client/src/utils/RenderTemplate.js
index acdfe1bf2..21ad1e30b 100644
--- a/client/src/utils/RenderTemplate.js
+++ b/client/src/utils/RenderTemplate.js
@@ -1,15 +1,15 @@
import { gql } from "@apollo/client";
import { notification } from "antd";
-import axios from "axios";
import jsreport from "@jsreport/browser-client";
import _ from "lodash";
import moment from "moment";
-import { auth } from "../firebase/firebase.utils";
+//import { auth } from "../firebase/firebase.utils";
import { setEmailOptions } from "../redux/email/email.actions";
import { store } from "../redux/store";
import client from "../utils/GraphQLClient";
import { TemplateList } from "./TemplateConstants";
-
+import cleanAxios from "./CleanAxios";
+import axios from "axios";
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
jsreport.serverUrl = server;
@@ -26,10 +26,14 @@ export default async function RenderTemplate(
if (window.jsr3) {
jsreport.serverUrl = "https://reports3.test.imex.online/";
}
+ const jsrAuth = (await axios.post("/utils/jsr")).data;
+ console.log("🚀 ~ file: RenderTemplate.js:30 ~ jsrAuth:", jsrAuth);
+ jsreport.headers["Authorization"] = jsrAuth;
//Query assets that match the template name. Must be in format <>.query
let { contextData, useShopSpecificTemplate } = await fetchContextData(
- templateObject
+ templateObject,
+ jsrAuth
);
const { ignoreCustomMargins } = Templates[templateObject.name];
@@ -137,11 +141,15 @@ export async function RenderTemplates(
//Query assets that match the template name. Must be in format <>.query
let unsortedTemplatesAndData = [];
let proms = [];
+ const jsrAuth = (await axios.post("/utils/jsr")).data;
+ jsreport.headers["Authorization"] = jsrAuth;
+
templateObjects.forEach((template) => {
proms.push(
(async () => {
let { contextData, useShopSpecificTemplate } = await fetchContextData(
- template
+ template,
+ jsrAuth
);
unsortedTemplatesAndData.push({
templateObject: template,
@@ -298,19 +306,22 @@ export const GenerateDocuments = async (templates) => {
await RenderTemplates(templates, bodyshop);
};
-const fetchContextData = async (templateObject) => {
+const fetchContextData = async (templateObject, jsrAuth) => {
const bodyshop = store.getState().user.bodyshop;
- jsreport.headers["Authorization"] =
- "Bearer " + (await auth.currentUser.getIdToken());
+ // jsreport.headers["Authorization"] =
+ // "Bearer " + (await auth.currentUser.getIdToken());
- const folders = await axios.get(`${server}/odata/folders`);
+ const folders = await cleanAxios.get(`${server}/odata/folders`, {
+ headers: { Authorization: jsrAuth },
+ });
const shopSpecificFolder = folders.data.value.find(
(f) => f.name === bodyshop.imexshopid
);
- const jsReportQueries = await axios.get(
- `${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`
+ const jsReportQueries = await cleanAxios.get(
+ `${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`,
+ { headers: { Authorization: jsrAuth } }
);
let templateQueryToExecute;
diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml
index 2d36a544e..23aefaa8e 100644
--- a/hasura/metadata/tables.yaml
+++ b/hasura/metadata/tables.yaml
@@ -694,6 +694,9 @@
num_retries: 3
timeout_sec: 60
webhook_from_env: HASURA_API_URL
+ headers:
+ - name: event-secret
+ value_from_env: EVENT_SECRET
request_transform:
method: POST
query_params: {}
@@ -4112,6 +4115,9 @@
num_retries: 3
timeout_sec: 60
webhook_from_env: HASURA_API_URL
+ headers:
+ - name: event-secret
+ value_from_env: EVENT_SECRET
request_transform:
method: POST
query_params: {}
@@ -4562,6 +4568,9 @@
num_retries: 3
timeout_sec: 60
webhook_from_env: HASURA_API_URL
+ headers:
+ - name: event-secret
+ value_from_env: EVENT_SECRET
request_transform:
method: POST
query_params: {}
@@ -5015,6 +5024,9 @@
num_retries: 3
timeout_sec: 60
webhook_from_env: HASURA_API_URL
+ headers:
+ - name: event-secret
+ value_from_env: EVENT_SECRET
request_transform:
method: POST
query_params: {}
@@ -5957,6 +5969,9 @@
num_retries: 3
timeout_sec: 60
webhook_from_env: HASURA_API_URL
+ headers:
+ - name: event-secret
+ value_from_env: EVENT_SECRET
request_transform:
method: POST
query_params: {}
diff --git a/server.js b/server.js
index 273676bb9..6a0c9e20a 100644
--- a/server.js
+++ b/server.js
@@ -123,7 +123,11 @@ app.post(
twilio.webhook({ validate: process.env.NODE_ENV === "PRODUCTION" }),
smsStatus.status
);
-app.post("/sms/markConversationRead", smsStatus.markConversationRead);
+app.post(
+ "/sms/markConversationRead",
+ fb.validateFirebaseIdToken,
+ smsStatus.markConversationRead
+);
var job = require("./server/job/job");
app.post("/job/totals", fb.validateFirebaseIdToken, job.totals);
@@ -147,11 +151,11 @@ app.post("/scheduling/job", fb.validateFirebaseIdToken, scheduling.job);
var inlineCss = require("./server/render/inlinecss");
app.post("/render/inlinecss", fb.validateFirebaseIdToken, inlineCss.inlinecss);
-app.post(
- "/notifications/send",
+// app.post(
+// "/notifications/send",
- fb.sendNotification
-);
+// fb.sendNotification
+// );
app.post("/notifications/subscribe", fb.validateFirebaseIdToken, fb.subscribe);
app.post(
"/notifications/unsubscribe",
@@ -188,13 +192,13 @@ app.post(
);
//Stripe Processing
-var stripe = require("./server/stripe/payment");
-app.post("/stripe/payment", fb.validateFirebaseIdToken, stripe.payment);
-app.post(
- "/stripe/mobilepayment",
- fb.validateFirebaseIdToken,
- stripe.mobile_payment
-);
+// var stripe = require("./server/stripe/payment");
+// app.post("/stripe/payment", fb.validateFirebaseIdToken, stripe.payment);
+// app.post(
+// "/stripe/mobilepayment",
+// fb.validateFirebaseIdToken,
+// stripe.mobile_payment
+// );
//Tech Console
var tech = require("./server/tech/tech");
@@ -202,7 +206,7 @@ app.post("/tech/login", fb.validateFirebaseIdToken, tech.techLogin);
var utils = require("./server/utils/utils");
app.post("/utils/time", utils.servertime);
-
+app.post("/utils/jsr", fb.validateFirebaseIdToken, utils.jsrAuth);
var qbo = require("./server/accounting/qbo/qbo");
app.post("/qbo/authorize", fb.validateFirebaseIdToken, qbo.authorize);
app.get("/qbo/callback", qbo.callback);
@@ -215,7 +219,7 @@ app.post("/data/ah", data.autohouse);
app.post("/record-handler/arms", data.arms);
var taskHandler = require("./server/tasks/tasks");
-app.post("/taskHandler", taskHandler.taskHandler);
+app.post("/taskHandler", fb.validateFirebaseIdToken, taskHandler.taskHandler);
var mixdataUpload = require("./server/mixdata/mixdata");
@@ -228,10 +232,10 @@ app.post(
var ioevent = require("./server/ioevent/ioevent");
app.post("/ioevent", ioevent.default);
-app.post("/newlog", (req, res) => {
- const { message, type, user, record, object } = req.body;
- logger.log(message, type, user, record, object);
-});
+// app.post("/newlog", (req, res) => {
+// const { message, type, user, record, object } = req.body;
+// logger.log(message, type, user, record, object);
+// });
var os = require("./server/opensearch/os-handler");
app.post(
@@ -243,9 +247,9 @@ app.post("/search", fb.validateFirebaseIdToken, os.search);
var cdkGetMake = require("./server/cdk/cdk-get-makes");
app.post("/cdk/getvehicles", fb.validateFirebaseIdToken, cdkGetMake.default);
-app.get("/", async function (req, res) {
- res.status(200).send("Access Forbidden.");
-});
+// app.get("/", async function (req, res) {
+// res.status(200).send("Access Forbidden.");
+// });
server.listen(port, (error) => {
if (error) throw error;
diff --git a/server/data/arms.js b/server/data/arms.js
index ec5f337f8..382f2437b 100644
--- a/server/data/arms.js
+++ b/server/data/arms.js
@@ -50,7 +50,7 @@ async function getEntegralShopData() {
}
exports.default = async (req, res) => {
- res.sendStatus(200);
+ res.sendStatus(401);
return;
//Query for the List of Bodyshop Clients.
const job = req.body.event.data.new;
diff --git a/server/data/autohouse.js b/server/data/autohouse.js
index 5e32c81de..bce69ec0f 100644
--- a/server/data/autohouse.js
+++ b/server/data/autohouse.js
@@ -40,6 +40,14 @@ exports.default = async (req, res) => {
const specificShopIds = req.body.bodyshopIds; // ['uuid]
const { start, end, skipUpload } = req.body; //YYYY-MM-DD
+ if (
+ !start ||
+ !moment(start).isValid ||
+ req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN
+ ) {
+ res.sendStatus(401);
+ return;
+ }
const allxmlsToUpload = [];
const allErrors = [];
try {
@@ -772,7 +780,9 @@ const CreateCosts = (job) => {
billTotalsByCostCenters[
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
] = Dinero({
- amount: Math.round((job.mixdata[0] && job.mixdata[0].totalliquidcost || 0) * 100)
+ amount: Math.round(
+ ((job.mixdata[0] && job.mixdata[0].totalliquidcost) || 0) * 100
+ ),
});
} else {
billTotalsByCostCenters[
diff --git a/server/job/job-status-transition.js b/server/job/job-status-transition.js
index 4b6a7db76..fd74a2ec3 100644
--- a/server/job/job-status-transition.js
+++ b/server/job/job-status-transition.js
@@ -17,7 +17,7 @@ require("dotenv").config({
});
async function StatusTransition(req, res) {
if (req.headers["event-secret"] !== process.env.EVENT_SECRET) {
- res.status(403).send("Unauthorized");
+ res.status(401).send("Unauthorized");
return;
}
res.sendStatus(200);
diff --git a/server/opensearch/os-handler.js b/server/opensearch/os-handler.js
index 6bb2adcdc..016e9530e 100644
--- a/server/opensearch/os-handler.js
+++ b/server/opensearch/os-handler.js
@@ -45,6 +45,10 @@ const getClient = async () => {
};
async function OpenSearchUpdateHandler(req, res) {
+ if (req.headers["event-secret"] !== process.env.EVENT_SECRET) {
+ res.status(401).send("Unauthorized");
+ return;
+ }
try {
var osClient = await getClient();
// const osClient = new Client({
diff --git a/server/utils/utils.js b/server/utils/utils.js
index 4587106be..ad3a06d24 100644
--- a/server/utils/utils.js
+++ b/server/utils/utils.js
@@ -1,3 +1,12 @@
exports.servertime = (req, res) => {
res.status(200).send(new Date());
};
+
+exports.jsrAuth = async (req, res) => {
+ res.send(
+ "Basic " +
+ Buffer.from(
+ `${process.env.JSR_USER}:${process.env.JSR_PASSWORD}`
+ ).toString("base64")
+ );
+};