Merged in release/2024-11-01 (pull request #1884)

Release/2024-11-01 into master-AIO - IO-2921, IO-3006, IO-3008, IO-3009, IO-3010
This commit is contained in:
Dave Richer
2024-11-02 15:14:06 +00:00
18 changed files with 133 additions and 123 deletions

View File

@@ -44,7 +44,7 @@ function LogLevelHierarchy(level) {
return "orange"; return "orange";
case "INFO": case "INFO":
return "blue"; return "blue";
case "WARNING": case "WARN":
return "yellow"; return "yellow";
case "ERROR": case "ERROR":
return "red"; return "red";

View File

@@ -29,7 +29,7 @@ const DateTimePicker = ({
const handleChange = useCallback( const handleChange = useCallback(
(newDate) => { (newDate) => {
if (onChange) { if (onChange) {
onChange(bodyshop?.timezone ? dayjs(newDate).tz(bodyshop.timezone, true) : newDate); onChange(bodyshop?.timezone && newDate ? dayjs(newDate).tz(bodyshop.timezone, true) : newDate);
} }
setIsManualInput(false); setIsManualInput(false);
}, },

View File

@@ -144,7 +144,7 @@ function TaskListComponent({
title: t("tasks.fields.created_by"), title: t("tasks.fields.created_by"),
dataIndex: "created_by", dataIndex: "created_by",
key: "created_by", key: "created_by",
width: "10%", width: "8%",
defaultSortOrder: "descend", defaultSortOrder: "descend",
sorter: true, sorter: true,
sortOrder: sortcolumn === "created_by" && sortorder, sortOrder: sortcolumn === "created_by" && sortorder,
@@ -166,65 +166,70 @@ function TaskListComponent({
}); });
} }
if (showRo) { columns.push({
columns.push({ title: t("tasks.fields.related_items"),
title: t("tasks.fields.job.ro_number"), key: "related_items",
dataIndex: ["job", "ro_number"], width: "12%",
key: "job.ro_number", render: (text, record) => {
width: "8%", const items = [];
render: (text, record) =>
record.job ? ( // Job
<Link to={`/manage/jobs/${record.job.id}?tab=tasks`}>{record.job.ro_number || t("general.labels.na")}</Link> if (showRo && record.job) {
) : ( items.push(
t("general.labels.na") <Link key="job" to={`/manage/jobs/${record.job.id}?tab=tasks`}>
) {t("tasks.fields.job.ro_number")}: {record.job.ro_number}
}); </Link>
} );
}
if (showRo && !record.job) {
items.push(`${t("tasks.fields.job.ro_number")}: ${t("general.labels.na")}`);
}
// Jobline
if (record.jobline?.line_desc) {
items.push(
<span key="jobline">
{t("tasks.fields.jobline")}: {record.jobline.line_desc}
</span>
);
}
// Parts Order
if (record.parts_order) {
const { order_number, vendor } = record.parts_order;
const partsOrderText =
order_number && vendor?.name ? `${order_number} - ${vendor.name}` : t("general.labels.na");
items.push(
<Link
key="parts_order"
to={`/manage/jobs/${record.job.id}?partsorderid=${record.parts_order.id}&tab=partssublet`}
>
{t("tasks.fields.parts_order")}: {partsOrderText}
</Link>
);
}
// Bill
if (record.bill) {
const { invoice_number, vendor } = record.bill;
const billText = invoice_number && vendor?.name ? `${invoice_number} - ${vendor.name}` : t("general.labels.na");
items.push(
<Link key="bill" to={`/manage/jobs/${record.job.id}?billid=${record.bill.id}&tab=partssublet`}>
{t("tasks.fields.bill")}: {billText}
</Link>
);
}
return items.length > 0 ? <Space direction="vertical">{items}</Space> : null;
}
});
columns.push( columns.push(
{
title: t("tasks.fields.jobline"),
dataIndex: ["jobline", "id"],
key: "jobline.id",
width: "8%",
render: (text, record) => record?.jobline?.line_desc || ""
},
{
title: t("tasks.fields.parts_order"),
dataIndex: ["parts_order", "id"],
key: "part_order.id",
width: "8%",
render: (text, record) =>
record.parts_order ? (
<Link to={`/manage/jobs/${record.job.id}?partsorderid=${record.parts_order.id}&tab=partssublet`}>
{record.parts_order.order_number && record.parts_order.vendor && record.parts_order.vendor.name
? `${record.parts_order.order_number} - ${record.parts_order.vendor.name}`
: t("general.labels.na")}
</Link>
) : (
""
)
},
{
title: t("tasks.fields.bill"),
dataIndex: ["bill", "id"],
key: "bill.id",
width: "10%",
render: (text, record) =>
record.bill ? (
<Link to={`/manage/jobs/${record.job.id}?billid=${record.bill.id}&tab=partssublet`}>
{record.bill.invoice_number && record.bill.vendor && record.bill.vendor.name
? `${record.bill.invoice_number} - ${record.bill.vendor.name}`
: t("general.labels.na")}
</Link>
) : (
""
)
},
{ {
title: t("tasks.fields.title"), title: t("tasks.fields.title"),
dataIndex: "title", dataIndex: "title",
key: "title", key: "title",
minWidth: "20%",
sorter: true, sorter: true,
sortOrder: sortcolumn === "title" && sortorder sortOrder: sortcolumn === "title" && sortorder
}, },
@@ -258,7 +263,7 @@ function TaskListComponent({
{ {
title: t("tasks.fields.actions"), title: t("tasks.fields.actions"),
key: "toggleCompleted", key: "toggleCompleted",
width: "5%", width: "8%",
render: (text, record) => ( render: (text, record) => (
<Space direction="horizontal"> <Space direction="horizontal">
<Button <Button

View File

@@ -1,7 +1,7 @@
import { useMutation, useQuery } from "@apollo/client";
import { Button, Form, Modal, notification, Space } from "antd";
import { PageHeader } from "@ant-design/pro-layout"; import { PageHeader } from "@ant-design/pro-layout";
import dayjs from "../../utils/day"; import { useMutation, useQuery } from "@apollo/client";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Button, Form, Modal, notification, Space } from "antd";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -11,9 +11,9 @@ import { INSERT_NEW_TIME_TICKET, UPDATE_TIME_TICKET } from "../../graphql/timeti
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectTimeTicket } from "../../redux/modals/modals.selectors"; import { selectTimeTicket } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import TimeTicketModalComponent from "./time-ticket-modal.component"; import dayjs from "../../utils/day";
import TimeTicketsCommitToggleComponent from "../time-tickets-commit-toggle/time-tickets-commit-toggle.component"; import TimeTicketsCommitToggleComponent from "../time-tickets-commit-toggle/time-tickets-commit-toggle.component";
import { useSplitTreatments } from "@splitsoftware/splitio-react"; import TimeTicketModalComponent from "./time-ticket-modal.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
timeTicketModal: selectTimeTicket, timeTicketModal: selectTimeTicket,
@@ -87,7 +87,7 @@ export function TimeTicketModalContainer({ timeTicketModal, toggleModalVisible,
if (enterAgain) { if (enterAgain) {
//Capture the existing information and repopulate it. //Capture the existing information and repopulate it.
const prev = form.getFieldsValue(["date", "employeeid"]); const prev = form.getFieldsValue(["date", "employeeid", "flat_rate"]);
form.resetFields(); form.resetFields();

View File

@@ -71,7 +71,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
...logs, ...logs,
{ {
timestamp: new Date(), timestamp: new Date(),
level: "WARNING", level: "WARN",
message: "Reconnected to CDK Export Service" message: "Reconnected to CDK Export Service"
} }
]; ];
@@ -125,7 +125,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
> >
<Select.Option key="DEBUG">DEBUG</Select.Option> <Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option> <Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARNING">WARNING</Select.Option> <Select.Option key="WARN">WARN</Select.Option>
<Select.Option key="ERROR">ERROR</Select.Option> <Select.Option key="ERROR">ERROR</Select.Option>
</Select> </Select>
<Button onClick={() => setLogs([])}>Clear Logs</Button> <Button onClick={() => setLogs([])}>Clear Logs</Button>

View File

@@ -90,7 +90,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
...logs, ...logs,
{ {
timestamp: new Date(), timestamp: new Date(),
level: "WARNING", level: "warn",
message: "Reconnected to CDK Export Service" message: "Reconnected to CDK Export Service"
} }
]; ];
@@ -175,7 +175,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
> >
<Select.Option key="DEBUG">DEBUG</Select.Option> <Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option> <Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARNING">WARNING</Select.Option> <Select.Option key="WARN">WARN</Select.Option>
<Select.Option key="ERROR">ERROR</Select.Option> <Select.Option key="ERROR">ERROR</Select.Option>
</Select> </Select>
<Button onClick={() => setLogs([])}>Clear Logs</Button> <Button onClick={() => setLogs([])}>Clear Logs</Button>

View File

@@ -3204,6 +3204,7 @@
"medium": "Medium" "medium": "Medium"
}, },
"priority": "Priority", "priority": "Priority",
"related_items": "Related Items",
"remind_at": "Remind At", "remind_at": "Remind At",
"title": "Title" "title": "Title"
}, },

View File

@@ -3204,6 +3204,7 @@
"medium": "" "medium": ""
}, },
"priority": "", "priority": "",
"related_items": "",
"remind_at": "", "remind_at": "",
"title": "" "title": ""
}, },

View File

@@ -3204,6 +3204,7 @@
"medium": "" "medium": ""
}, },
"priority": "", "priority": "",
"related_items": "",
"remind_at": "", "remind_at": "",
"title": "" "title": ""
}, },

View File

@@ -69,7 +69,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
jobline: jobline:
job: job:
@@ -180,7 +179,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -387,7 +385,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -504,7 +501,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bill: bill:
job: job:
@@ -671,7 +667,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
_and: _and:
- job: - job:
@@ -1285,7 +1280,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
courtesycar: courtesycar:
bodyshop: bodyshop:
@@ -1526,7 +1520,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -1786,7 +1779,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -1920,7 +1912,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
_or: _or:
- job: - job:
@@ -2105,7 +2096,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
employee_team: employee_team:
bodyshop: bodyshop:
@@ -2268,7 +2258,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
employee: employee:
bodyshop: bodyshop:
@@ -2449,7 +2438,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -2696,7 +2684,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -2808,7 +2795,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
conversation: conversation:
bodyshop: bodyshop:
@@ -3123,7 +3109,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
job: job:
bodyshop: bodyshop:
@@ -4232,7 +4217,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -4248,41 +4232,41 @@
enable_manual: false enable_manual: false
update: update:
columns: columns:
- clm_no
- v_make_desc
- date_next_contact
- status
- employee_csr
- employee_prep - employee_prep
- clm_total - clm_total
- suspended - suspended
- employee_body - employee_body
- ro_number - ro_number
- actual_in
- ownr_co_nm - ownr_co_nm
- v_model_yr
- comment
- job_totals
- v_vin - v_vin
- ownr_fn
- scheduled_completion - scheduled_completion
- special_coverage_policy - special_coverage_policy
- v_color
- ca_gst_registrant
- scheduled_delivery - scheduled_delivery
- actual_delivery - actual_delivery
- actual_completion - actual_completion
- kanbanparent - kanbanparent
- est_ct_fn - est_ct_fn
- alt_transport
- v_model_desc
- clm_no
- v_make_desc
- date_next_contact
- status
- employee_csr
- actual_in
- v_model_yr
- comment
- job_totals
- ownr_fn
- v_color
- ca_gst_registrant
- employee_refinish - employee_refinish
- ownr_ph1 - ownr_ph1
- date_last_contacted - date_last_contacted
- alt_transport
- inproduction - inproduction
- est_ct_ln - est_ct_ln
- production_vars - production_vars
- category - category
- v_model_desc
- date_invoiced - date_invoiced
- est_co_nm - est_co_nm
- ownr_ln - ownr_ln
@@ -4295,6 +4279,12 @@
- name: event-secret - name: event-secret
value_from_env: EVENT_SECRET value_from_env: EVENT_SECRET
request_transform: request_transform:
body:
action: transform
template: |-
{
"data": {{$body?.event?.data?.new}}
}
method: POST method: POST
query_params: {} query_params: {}
template_engine: Kriti template_engine: Kriti
@@ -4496,7 +4486,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
conversation: conversation:
bodyshop: bodyshop:
@@ -4670,7 +4659,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
job: job:
bodyshop: bodyshop:
@@ -4805,7 +4793,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -5110,7 +5097,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
parts_order: parts_order:
job: job:
@@ -5243,7 +5229,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
job: job:
bodyshop: bodyshop:
@@ -5419,7 +5404,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
job: job:
bodyshop: bodyshop:
@@ -5559,7 +5543,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -5670,7 +5653,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
_or: _or:
- parentjob_rel: - parentjob_rel:
@@ -5760,7 +5742,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
job: job:
bodyshop: bodyshop:
@@ -6045,7 +6026,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -6541,7 +6521,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -6698,7 +6677,6 @@
delete_permissions: delete_permissions:
- role: user - role: user
permission: permission:
backend_only: false
filter: filter:
bodyshop: bodyshop:
associations: associations:

View File

@@ -611,7 +611,7 @@ async function InsertFailedExportLog(socket, error) {
bodyshopid: socket.JobData.bodyshop.id, bodyshopid: socket.JobData.bodyshop.id,
jobid: socket.JobData.id, jobid: socket.JobData.id,
successful: false, successful: false,
message: [error], message: JSON.stringify(error),
useremail: socket.user.email useremail: socket.user.email
} }
}); });

View File

@@ -995,7 +995,7 @@ async function InsertFailedExportLog(socket, error) {
bodyshopid: socket.JobData.bodyshop.id, bodyshopid: socket.JobData.bodyshop.id,
jobid: socket.JobData.id, jobid: socket.JobData.id,
successful: false, successful: false,
message: [error], message: JSON.stringify(error),
useremail: socket.user.email useremail: socket.user.email
} }
}); });

View File

@@ -20,7 +20,7 @@ function CheckCdkResponseForError(socket, soapResponse) {
//The response was null, this might be ok, it might not. //The response was null, this might be ok, it might not.
CdkBase.createLogEvent( CdkBase.createLogEvent(
socket, socket,
"WARNING", "warn",
`Warning detected in CDK Response - it appears to be null. Stack: ${new Error().stack}` `Warning detected in CDK Response - it appears to be null. Stack: ${new Error().stack}`
); );
return; return;

View File

@@ -160,7 +160,8 @@ async function getPrivateKey() {
try { try {
const { SecretString, SecretBinary } = await client.send(command); const { SecretString, SecretBinary } = await client.send(command);
if (SecretString || SecretBinary) logger.log("chatter-retrieved-private-key", "DEBUG", "api", null, null); if (SecretString || SecretBinary) logger.log("chatter-retrieved-private-key", "DEBUG", "api", null, null);
return SecretString || Buffer.from(SecretBinary, "base64").toString("ascii"); const chatterPrivateKey = SecretString ? JSON.parse(SecretString) : JSON.parse(Buffer.from(SecretBinary, "base64").toString("ascii"));
return chatterPrivateKey.private_key;
} catch (error) { } catch (error) {
logger.log("chatter-get-private-key", "ERROR", "api", null, error); logger.log("chatter-get-private-key", "ERROR", "api", null, error);
throw err; throw err;

View File

@@ -2,8 +2,16 @@ const { isObject } = require("lodash");
const jobUpdated = async (req, res) => { const jobUpdated = async (req, res) => {
const { ioRedis, logger, ioHelpers } = req; const { ioRedis, logger, ioHelpers } = req;
// Old Way
if (req?.body?.event?.data?.new || isObject(req?.body?.event?.data?.new)) {
const updatedJob = req.body.event.data.new;
const bodyshopID = updatedJob.shopid;
ioRedis.to(ioHelpers.getBodyshopRoom(bodyshopID)).emit("production-job-updated", updatedJob);
return res.json({ message: "Job updated and event emitted" });
}
if (!req?.body?.event?.data?.new || !isObject(req?.body?.event?.data?.new)) { // New way
if (!req?.body?.data || !isObject(req.body.data)) {
logger.log("job-update-error", "ERROR", req.user?.email, null, { logger.log("job-update-error", "ERROR", req.user?.email, null, {
message: `Malformed Job Update request sent from Hasura`, message: `Malformed Job Update request sent from Hasura`,
body: req?.body body: req?.body
@@ -15,12 +23,14 @@ const jobUpdated = async (req, res) => {
}); });
} }
logger.log("job-update", "DEBUG", req.user?.email, null, { // Uncomment for further testing
message: `Job updated event received from Hasura`, // You can also test this using SocketIOAdmin
jobid: req?.body?.event?.data?.new?.id // logger.log("job-update", "DEBUG", req.user?.email, null, {
}); // message: `Job updated event received from Hasura`,
// jobid: req?.body?.event?.data?.new?.id
// });
const updatedJob = req.body.event.data.new; const updatedJob = req.body.data;
const bodyshopID = updatedJob.shopid; const bodyshopID = updatedJob.shopid;
// Emit the job-updated event only to the room corresponding to the bodyshop // Emit the job-updated event only to the room corresponding to the bodyshop

View File

@@ -59,7 +59,7 @@ exports.mixdataUpload = async (req, res) => {
res.status(500).json(error); res.status(500).json(error);
logger.log("job-mixdata-upload-error", "ERROR", null, null, { logger.log("job-mixdata-upload-error", "ERROR", null, null, {
error: error.message, error: error.message,
...error stack: error.stack
}); });
} }
}; };

View File

@@ -10,6 +10,18 @@ const WinstonCloudWatch = require("winston-cloudwatch");
const { isString, isEmpty } = require("lodash"); const { isString, isEmpty } = require("lodash");
const { networkInterfaces, hostname } = require("node:os"); const { networkInterfaces, hostname } = require("node:os");
const LOG_LEVELS = {
error: { level: 0, name: "error" },
warn: { level: 1, name: "warn" },
info: { level: 2, name: "info" },
http: { level: 3, name: "http" },
verbose: { level: 4, name: "verbose" },
debug: { level: 5, name: "debug" },
silly: { level: 6, name: "silly" }
};
const normalizeLevel = (level) => (level ? level.toLowerCase() : LOG_LEVELS.debug.name);
const createLogger = () => { const createLogger = () => {
try { try {
const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME); const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME);
@@ -114,7 +126,7 @@ const createLogger = () => {
const log = (message, type, user, record, meta) => { const log = (message, type, user, record, meta) => {
winstonLogger.log({ winstonLogger.log({
level: type.toLowerCase(), level: normalizeLevel(type),
message, message,
user, user,
record, record,
@@ -131,7 +143,8 @@ const createLogger = () => {
console.error("Error setting up enhanced Logger, defaulting to console.: " + e?.message || ""); console.error("Error setting up enhanced Logger, defaulting to console.: " + e?.message || "");
return { return {
log: console.log, log: console.log,
logger: console.log logger: console.log,
LOG_LEVELS
}; };
} }
}; };

View File

@@ -212,7 +212,7 @@ function LogLevelHierarchy(level) {
return 4; return 4;
case "INFO": case "INFO":
return 3; return 3;
case "WARNING": case "WARN":
return 2; return 2;
case "ERROR": case "ERROR":
return 1; return 1;