diff --git a/client/src/App/App.styles.scss b/client/src/App/App.styles.scss index 2ed11cbc6..b57e94677 100644 --- a/client/src/App/App.styles.scss +++ b/client/src/App/App.styles.scss @@ -156,3 +156,11 @@ td.ant-table-column-sort { background-color: transparent; } + +.ant-table-tbody > tr.ant-table-row:nth-child(2n) > td { + background-color: #f4f4f4; +} + +.rowWithColor > td { + background-color: var(--bgColor) !important; +} diff --git a/client/src/components/email-overlay/email-overlay.component.jsx b/client/src/components/email-overlay/email-overlay.component.jsx index 7db03471d..362d973b4 100644 --- a/client/src/components/email-overlay/email-overlay.component.jsx +++ b/client/src/components/email-overlay/email-overlay.component.jsx @@ -1,28 +1,28 @@ import { UploadOutlined, UserAddOutlined } from "@ant-design/icons"; import { + Button, Divider, + Dropdown, Form, Input, + Menu, Select, + Space, Tabs, Upload, - Space, - Menu, - Dropdown, - Button, } from "antd"; +import _ from "lodash"; import React from "react"; import { useTranslation } from "react-i18next"; -import EmailDocumentsComponent from "../email-documents/email-documents.component"; -import _ from "lodash"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import { selectEmailConfig } from "../../redux/email/email.selectors"; import { selectBodyshop, selectCurrentUser, } from "../../redux/user/user.selectors"; import { CreateExplorerLinkForJob } from "../../utils/localmedia"; -import { selectEmailConfig } from "../../redux/email/email.selectors"; +import EmailDocumentsComponent from "../email-documents/email-documents.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -54,6 +54,15 @@ export function EmailOverlayComponent({ ]), }); }; + const handle_CC_Click = ({ item, key, keyPath }) => { + const email = item.props.value; + form.setFieldsValue({ + cc: _.uniq([ + ...(form.getFieldValue("cc") || ""), + ...(typeof email === "string" ? [email] : email), + ]), + }); + }; const menu = (
@@ -74,6 +83,25 @@ export function EmailOverlayComponent({
); + const menuCC = ( +
+ + {bodyshop.employees + .filter((e) => e.user_email) + .map((e, idx) => ( + + {`${e.first_name} ${e.last_name}`} + + ))} + {bodyshop.md_to_emails.map((e, idx) => ( + + {e.label} + + ))} + +
+ ); + return (
{!jobRO && job.converted && ( - - e.stopPropagation()} - onConfirm={async () => { - //delete the job. - const result = await voidJob({ - variables: { - jobId: job.id, - job: { - status: bodyshop.md_ro_statuses.default_void, - voided: true, - scheduled_in: null, - scheduled_completion: null, - inproduction: false, - }, - note: [ - { - jobid: job.id, - created_by: currentUser.email, - audit: true, - text: t("jobs.labels.voidnote"), + + + e.stopPropagation()} + onConfirm={async () => { + //delete the job. + const result = await voidJob({ + variables: { + jobId: job.id, + job: { + status: bodyshop.md_ro_statuses.default_void, + voided: true, + scheduled_in: null, + scheduled_completion: null, + inproduction: false, }, - ], - }, - }); + note: [ + { + jobid: job.id, + created_by: currentUser.email, + audit: true, + text: t("jobs.labels.voidnote"), + }, + ], + }, + }); - if (!!!result.errors) { - notification["success"]({ - message: t("jobs.successes.voided"), - }); - //go back to jobs list. - history.push(`/manage/`); - } else { - notification["error"]({ - message: t("jobs.errors.voiding", { - error: JSON.stringify(result.errors), - }), - }); - } - }} - getPopupContainer={(trigger) => trigger.parentNode} - > - {t("menus.jobsactions.void")} - - + if (!!!result.errors) { + notification["success"]({ + message: t("jobs.successes.voided"), + }); + //go back to jobs list. + history.push(`/manage/`); + } else { + notification["error"]({ + message: t("jobs.errors.voiding", { + error: JSON.stringify(result.errors), + }), + }); + } + }} + getPopupContainer={(trigger) => trigger.parentNode} + > + {t("menus.jobsactions.void")} + + + )} ); diff --git a/client/src/components/jobs-list/jobs-list.component.jsx b/client/src/components/jobs-list/jobs-list.component.jsx index 189014d37..e28e21901 100644 --- a/client/src/components/jobs-list/jobs-list.component.jsx +++ b/client/src/components/jobs-list/jobs-list.component.jsx @@ -112,7 +112,9 @@ export function JobsList({ bodyshop }) { title: t("jobs.fields.ro_number"), dataIndex: "ro_number", key: "ro_number", - sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), + sorter: (a, b) => + parseInt((a.ro_number || "0").replace(/\D/g, "")) - + parseInt((b.ro_number || "0").replace(/\D/g, "")), sortOrder: state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, diff --git a/client/src/components/print-center-jobs/print-center-jobs.component.jsx b/client/src/components/print-center-jobs/print-center-jobs.component.jsx index c96e3d3a5..ddb9a247d 100644 --- a/client/src/components/print-center-jobs/print-center-jobs.component.jsx +++ b/client/src/components/print-center-jobs/print-center-jobs.component.jsx @@ -23,17 +23,34 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) { const { id: jobId, job } = printCenterModal.context; const tempList = TemplateList("job", {}); const { t } = useTranslation(); - const JobsReportsList = Object.keys(tempList) - .map((key) => { - return tempList[key]; - }) - .filter( - (temp) => - !temp.regions || - (temp.regions && temp.regions[bodyshop.region_config]) || - (temp.regions && - bodyshop.region_config.includes(Object.keys(temp.regions)) === true) - ); + + const JobsReportsList = + bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null + ? Object.keys(tempList) + .map((key) => { + return tempList[key]; + }) + .filter( + (temp) => + (!temp.regions || + (temp.regions && temp.regions[bodyshop.region_config]) || + (temp.regions && + bodyshop.region_config.includes(Object.keys(temp.regions)) === + true)) && + (!temp.dms || temp.dms === false) + ) + : Object.keys(tempList) + .map((key) => { + return tempList[key]; + }) + .filter( + (temp) => + !temp.regions || + (temp.regions && temp.regions[bodyshop.region_config]) || + (temp.regions && + bodyshop.region_config.includes(Object.keys(temp.regions)) === + true) + ); const filteredJobsReportsList = search !== "" diff --git a/client/src/components/production-list-table/production-list-table.component.jsx b/client/src/components/production-list-table/production-list-table.component.jsx index 746d956ab..1585c6480 100644 --- a/client/src/components/production-list-table/production-list-table.component.jsx +++ b/client/src/components/production-list-table/production-list-table.component.jsx @@ -246,11 +246,21 @@ export function ProductionListTable({ (x) => x.status === record.status ); - if (!color) return null; + if (!color) { + if (index % 2 === 0) + return { + style: { + backgroundColor: `rgb(236, 236, 236)`, + }, + }; + + return null; + } return { + className: "rowWithColor", style: { - backgroundColor: `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`, + "--bgColor": `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`, }, }; }, diff --git a/client/src/components/rbac-wrapper/rbac-defaults.js b/client/src/components/rbac-wrapper/rbac-defaults.js index 1c2a779c4..24a564872 100644 --- a/client/src/components/rbac-wrapper/rbac-defaults.js +++ b/client/src/components/rbac-wrapper/rbac-defaults.js @@ -26,6 +26,8 @@ const ret = { "jobs:partsqueue": 4, "jobs:checklist-view": 2, "jobs:list-ready": 1, + "jobs:void": 5, + "bills:enter": 2, "bills:view": 2, "bills:list": 2, diff --git a/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx b/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx index 837f5cbbe..7cd90bd14 100644 --- a/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx +++ b/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx @@ -12,7 +12,8 @@ import "./schedule-calendar.styles.scss"; import JobDetailCards from "../job-detail-cards/job-detail-cards.component"; import { selectProblemJobs } from "../../redux/application/application.selectors"; import { Alert, Collapse } from "antd"; -import { useTranslation } from "react-i18next"; +import { useTranslation, Trans } from "react-i18next"; +import { Link } from "react-router-dom"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -66,10 +67,21 @@ export function ScheduleCalendarWrapperComponent({ , + ]} + values={{ + ro_number: problem.ro_number, + code: problem.code, + }} + /> + } /> ))} @@ -79,10 +91,18 @@ export function ScheduleCalendarWrapperComponent({ , + ]} + values={{ + ro_number: problem.ro_number, + code: problem.code, + }} + /> + } /> )) )} diff --git a/client/src/components/shop-info/shop-info.rbac.component.jsx b/client/src/components/shop-info/shop-info.rbac.component.jsx index 291369255..fe4f80f31 100644 --- a/client/src/components/shop-info/shop-info.rbac.component.jsx +++ b/client/src/components/shop-info/shop-info.rbac.component.jsx @@ -1,12 +1,12 @@ +import { useTreatments } from "@splitsoftware/splitio-react"; import { Form, InputNumber } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; -import LayoutFormRow from "../layout-form-row/layout-form-row.component"; -import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component"; -import { useTreatments } from "@splitsoftware/splitio-react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; +import LayoutFormRow from "../layout-form-row/layout-form-row.component"; +import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, }); @@ -316,6 +316,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) { > + + + diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 21983e70f..b93d0c0a4 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -49,7 +49,7 @@ "blocked": "Blocked", "cancelledappointment": "Canceled appointment for: ", "completingjobs": "Completing Jobs", - "dataconsistency": "{{ro_number}} has a data consistency issue. It may have been excluded for scheduling purposes. CODE: {{code}}.", + "dataconsistency": "<0>{{ro_number}} has a data consistency issue. It may have been excluded for scheduling purposes. CODE: {{code}}.", "expectedjobs": "Expected Jobs in Production: ", "expectedprodhrs": "Expected Production Hours:", "history": "History", @@ -405,7 +405,8 @@ "list-active": "Jobs -> List Active", "list-all": "Jobs -> List All", "list-ready": "Jobs -> List Ready", - "partsqueue": "Jobs -> Parts Queue" + "partsqueue": "Jobs -> Parts Queue", + "void": "Jobs -> Void" }, "owners": { "detail": "Owners -> Detail", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 0ead918b2..ff3efbd30 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -405,7 +405,8 @@ "list-active": "", "list-all": "", "list-ready": "", - "partsqueue": "" + "partsqueue": "", + "void": "" }, "owners": { "detail": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 040080a88..c8fc9ee4a 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -405,7 +405,8 @@ "list-active": "", "list-all": "", "list-ready": "", - "partsqueue": "" + "partsqueue": "", + "void": "" }, "owners": { "detail": "", diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js index 78f2f12f2..debbfc57e 100644 --- a/client/src/utils/TemplateConstants.js +++ b/client/src/utils/TemplateConstants.js @@ -512,6 +512,7 @@ export const TemplateList = (type, context) => { key: "dms_posting_sheet", disabled: false, group: "financial", + dms: true, }, } : {}), diff --git a/server/cdk/cdk-job-export.js b/server/cdk/cdk-job-export.js index 37265a0a7..c20a899a1 100644 --- a/server/cdk/cdk-job-export.js +++ b/server/cdk/cdk-job-export.js @@ -729,9 +729,15 @@ async function InsertDmsVehicle(socket) { deliveryDate: moment() // .tz(socket.JobData.bodyshop.timezone) .format("YYYYMMDD"), - licensePlateNo: String(socket.JobData.plate_no) - .replace(/([^\w]|_)/g, "") - .toUpperCase(), + licensePlateNo: + socket.JobData.plate_no === null + ? null + : String(socket.JobData.plate_no).replace(/([^\w]|_)/g, "") + .length === 0 + ? null + : String(socket.JobData.plate_no) + .replace(/([^\w]|_)/g, "") + .toUpperCase(), make: socket.txEnvelope.dms_make, modelAbrev: socket.txEnvelope.dms_model, modelYear: socket.JobData.v_model_yr,