diff --git a/client/src/components/dms-log-events/dms-log-events.component.jsx b/client/src/components/dms-log-events/dms-log-events.component.jsx index 9391d8edb..b73e5dbe7 100644 --- a/client/src/components/dms-log-events/dms-log-events.component.jsx +++ b/client/src/components/dms-log-events/dms-log-events.component.jsx @@ -44,7 +44,7 @@ function LogLevelHierarchy(level) { return "orange"; case "INFO": return "blue"; - case "WARNING": + case "WARN": return "yellow"; case "ERROR": return "red"; diff --git a/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx b/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx index 0ef0faf80..c9bbc0dd2 100644 --- a/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx +++ b/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx @@ -29,7 +29,7 @@ const DateTimePicker = ({ const handleChange = useCallback( (newDate) => { 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); }, diff --git a/client/src/components/task-list/task-list.component.jsx b/client/src/components/task-list/task-list.component.jsx index 0c01ef6a8..ba7d71b17 100644 --- a/client/src/components/task-list/task-list.component.jsx +++ b/client/src/components/task-list/task-list.component.jsx @@ -144,7 +144,7 @@ function TaskListComponent({ title: t("tasks.fields.created_by"), dataIndex: "created_by", key: "created_by", - width: "10%", + width: "8%", defaultSortOrder: "descend", sorter: true, sortOrder: sortcolumn === "created_by" && sortorder, @@ -166,65 +166,70 @@ function TaskListComponent({ }); } - if (showRo) { - columns.push({ - title: t("tasks.fields.job.ro_number"), - dataIndex: ["job", "ro_number"], - key: "job.ro_number", - width: "8%", - render: (text, record) => - record.job ? ( - {record.job.ro_number || t("general.labels.na")} - ) : ( - t("general.labels.na") - ) - }); - } + columns.push({ + title: t("tasks.fields.related_items"), + key: "related_items", + width: "12%", + render: (text, record) => { + const items = []; + + // Job + if (showRo && record.job) { + items.push( + + {t("tasks.fields.job.ro_number")}: {record.job.ro_number} + + ); + } + if (showRo && !record.job) { + items.push(`${t("tasks.fields.job.ro_number")}: ${t("general.labels.na")}`); + } + + // Jobline + if (record.jobline?.line_desc) { + items.push( + + {t("tasks.fields.jobline")}: {record.jobline.line_desc} + + ); + } + + // 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( + + {t("tasks.fields.parts_order")}: {partsOrderText} + + ); + } + + // 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( + + {t("tasks.fields.bill")}: {billText} + + ); + } + + return items.length > 0 ? {items} : null; + } + }); 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 ? ( - - {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")} - - ) : ( - "" - ) - }, - { - title: t("tasks.fields.bill"), - dataIndex: ["bill", "id"], - key: "bill.id", - width: "10%", - render: (text, record) => - record.bill ? ( - - {record.bill.invoice_number && record.bill.vendor && record.bill.vendor.name - ? `${record.bill.invoice_number} - ${record.bill.vendor.name}` - : t("general.labels.na")} - - ) : ( - "" - ) - }, { title: t("tasks.fields.title"), dataIndex: "title", key: "title", + minWidth: "20%", sorter: true, sortOrder: sortcolumn === "title" && sortorder }, @@ -258,7 +263,7 @@ function TaskListComponent({ { title: t("tasks.fields.actions"), key: "toggleCompleted", - width: "5%", + width: "8%", render: (text, record) => ( diff --git a/client/src/pages/dms/dms.container.jsx b/client/src/pages/dms/dms.container.jsx index 3f7569cbd..3a8ac77b0 100644 --- a/client/src/pages/dms/dms.container.jsx +++ b/client/src/pages/dms/dms.container.jsx @@ -90,7 +90,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse ...logs, { timestamp: new Date(), - level: "WARNING", + level: "warn", message: "Reconnected to CDK Export Service" } ]; @@ -175,7 +175,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse > DEBUG INFO - WARNING + WARN ERROR diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 18b7b4e32..38f13e032 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -3204,6 +3204,7 @@ "medium": "Medium" }, "priority": "Priority", + "related_items": "Related Items", "remind_at": "Remind At", "title": "Title" }, diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 51b4bd8c0..ed7760e9d 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -3204,6 +3204,7 @@ "medium": "" }, "priority": "", + "related_items": "", "remind_at": "", "title": "" }, diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 0560af00a..4a64bcc55 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -3204,6 +3204,7 @@ "medium": "" }, "priority": "", + "related_items": "", "remind_at": "", "title": "" }, diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 70be46ad0..240b218a4 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -69,7 +69,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: jobline: job: @@ -180,7 +179,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -387,7 +385,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -504,7 +501,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bill: job: @@ -671,7 +667,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: _and: - job: @@ -1285,7 +1280,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: courtesycar: bodyshop: @@ -1526,7 +1520,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -1786,7 +1779,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -1920,7 +1912,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: _or: - job: @@ -2105,7 +2096,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: employee_team: bodyshop: @@ -2268,7 +2258,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: employee: bodyshop: @@ -2449,7 +2438,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -2696,7 +2684,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -2808,7 +2795,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: conversation: bodyshop: @@ -3123,7 +3109,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: job: bodyshop: @@ -4232,7 +4217,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -4248,41 +4232,41 @@ enable_manual: false update: columns: - - clm_no - - v_make_desc - - date_next_contact - - status - - employee_csr - employee_prep - clm_total - suspended - employee_body - ro_number - - actual_in - ownr_co_nm - - v_model_yr - - comment - - job_totals - v_vin - - ownr_fn - scheduled_completion - special_coverage_policy - - v_color - - ca_gst_registrant - scheduled_delivery - actual_delivery - actual_completion - kanbanparent - 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 - ownr_ph1 - date_last_contacted - - alt_transport - inproduction - est_ct_ln - production_vars - category - - v_model_desc - date_invoiced - est_co_nm - ownr_ln @@ -4295,6 +4279,12 @@ - name: event-secret value_from_env: EVENT_SECRET request_transform: + body: + action: transform + template: |- + { + "data": {{$body?.event?.data?.new}} + } method: POST query_params: {} template_engine: Kriti @@ -4496,7 +4486,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: conversation: bodyshop: @@ -4670,7 +4659,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: job: bodyshop: @@ -4805,7 +4793,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -5110,7 +5097,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: parts_order: job: @@ -5243,7 +5229,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: job: bodyshop: @@ -5419,7 +5404,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: job: bodyshop: @@ -5559,7 +5543,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -5670,7 +5653,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: _or: - parentjob_rel: @@ -5760,7 +5742,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: job: bodyshop: @@ -6045,7 +6026,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -6541,7 +6521,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: @@ -6698,7 +6677,6 @@ delete_permissions: - role: user permission: - backend_only: false filter: bodyshop: associations: diff --git a/server/accounting/pbs/pbs-job-export.js b/server/accounting/pbs/pbs-job-export.js index c2f6b96b7..c38560293 100644 --- a/server/accounting/pbs/pbs-job-export.js +++ b/server/accounting/pbs/pbs-job-export.js @@ -611,7 +611,7 @@ async function InsertFailedExportLog(socket, error) { bodyshopid: socket.JobData.bodyshop.id, jobid: socket.JobData.id, successful: false, - message: [error], + message: JSON.stringify(error), useremail: socket.user.email } }); diff --git a/server/cdk/cdk-job-export.js b/server/cdk/cdk-job-export.js index 7fedbc608..90001fa28 100644 --- a/server/cdk/cdk-job-export.js +++ b/server/cdk/cdk-job-export.js @@ -995,7 +995,7 @@ async function InsertFailedExportLog(socket, error) { bodyshopid: socket.JobData.bodyshop.id, jobid: socket.JobData.id, successful: false, - message: [error], + message: JSON.stringify(error), useremail: socket.user.email } }); diff --git a/server/cdk/cdk-wsdl.js b/server/cdk/cdk-wsdl.js index 032d780c2..8926e98ae 100644 --- a/server/cdk/cdk-wsdl.js +++ b/server/cdk/cdk-wsdl.js @@ -20,7 +20,7 @@ function CheckCdkResponseForError(socket, soapResponse) { //The response was null, this might be ok, it might not. CdkBase.createLogEvent( socket, - "WARNING", + "warn", `Warning detected in CDK Response - it appears to be null. Stack: ${new Error().stack}` ); return; diff --git a/server/data/chatter.js b/server/data/chatter.js index 24173a872..e610f9791 100644 --- a/server/data/chatter.js +++ b/server/data/chatter.js @@ -160,7 +160,8 @@ async function getPrivateKey() { try { const { SecretString, SecretBinary } = await client.send(command); 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) { logger.log("chatter-get-private-key", "ERROR", "api", null, error); throw err; diff --git a/server/job/job-updated.js b/server/job/job-updated.js index 606e6682c..89637a6f8 100644 --- a/server/job/job-updated.js +++ b/server/job/job-updated.js @@ -2,8 +2,16 @@ const { isObject } = require("lodash"); const jobUpdated = async (req, res) => { 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, { message: `Malformed Job Update request sent from Hasura`, body: req?.body @@ -15,12 +23,14 @@ const jobUpdated = async (req, res) => { }); } - logger.log("job-update", "DEBUG", req.user?.email, null, { - message: `Job updated event received from Hasura`, - jobid: req?.body?.event?.data?.new?.id - }); + // Uncomment for further testing + // You can also test this using SocketIOAdmin + // 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; // Emit the job-updated event only to the room corresponding to the bodyshop diff --git a/server/mixdata/mixdata.js b/server/mixdata/mixdata.js index d61989103..919726efd 100644 --- a/server/mixdata/mixdata.js +++ b/server/mixdata/mixdata.js @@ -59,7 +59,7 @@ exports.mixdataUpload = async (req, res) => { res.status(500).json(error); logger.log("job-mixdata-upload-error", "ERROR", null, null, { error: error.message, - ...error + stack: error.stack }); } }; diff --git a/server/utils/logger.js b/server/utils/logger.js index 26dd72150..bfb0c0cda 100644 --- a/server/utils/logger.js +++ b/server/utils/logger.js @@ -10,6 +10,18 @@ const WinstonCloudWatch = require("winston-cloudwatch"); const { isString, isEmpty } = require("lodash"); 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 = () => { try { 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) => { winstonLogger.log({ - level: type.toLowerCase(), + level: normalizeLevel(type), message, user, record, @@ -131,7 +143,8 @@ const createLogger = () => { console.error("Error setting up enhanced Logger, defaulting to console.: " + e?.message || ""); return { log: console.log, - logger: console.log + logger: console.log, + LOG_LEVELS }; } }; diff --git a/server/web-sockets/web-socket.js b/server/web-sockets/web-socket.js index a56f6cff6..c5e5012c8 100644 --- a/server/web-sockets/web-socket.js +++ b/server/web-sockets/web-socket.js @@ -212,7 +212,7 @@ function LogLevelHierarchy(level) { return 4; case "INFO": return 3; - case "WARNING": + case "WARN": return 2; case "ERROR": return 1;