@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = (
|
||||
<div>
|
||||
@@ -74,6 +83,25 @@ export function EmailOverlayComponent({
|
||||
</div>
|
||||
);
|
||||
|
||||
const menuCC = (
|
||||
<div>
|
||||
<Menu onClick={handle_CC_Click}>
|
||||
{bodyshop.employees
|
||||
.filter((e) => e.user_email)
|
||||
.map((e, idx) => (
|
||||
<Menu.Item value={e.user_email} key={idx}>
|
||||
{`${e.first_name} ${e.last_name}`}
|
||||
</Menu.Item>
|
||||
))}
|
||||
{bodyshop.md_to_emails.map((e, idx) => (
|
||||
<Menu.Item value={e.emails} key={idx + "group"}>
|
||||
{e.label}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
@@ -122,7 +150,23 @@ export function EmailOverlayComponent({
|
||||
>
|
||||
<Select mode="tags" tokenSeparators={[",", ";"]} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("emails.fields.cc")} name="cc">
|
||||
<Form.Item
|
||||
label={
|
||||
<Space>
|
||||
{t("emails.fields.cc")}
|
||||
<Dropdown overlay={menuCC}>
|
||||
<a
|
||||
className="ant-dropdown-link"
|
||||
href=" #"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
>
|
||||
<UserAddOutlined />
|
||||
</a>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
}
|
||||
name="cc"
|
||||
>
|
||||
<Select mode="tags" tokenSeparators={[",", ";"]} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
|
||||
@@ -29,6 +29,7 @@ import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
||||
import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component";
|
||||
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
||||
import JobsDetailHeaderActionsExportcustdataComponent from "./jobs-detail-header-actions.exportcustdata.component";
|
||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -461,54 +462,56 @@ export function JobsDetailHeaderActions({
|
||||
)}
|
||||
<JobsDetailHeaderActionsAddevent jobid={job.id} />
|
||||
{!jobRO && job.converted && (
|
||||
<Menu.Item>
|
||||
<Popconfirm
|
||||
title={t("jobs.labels.voidjob")}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onClick={(e) => 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"),
|
||||
<RbacWrapper action="jobs:void" noauth>
|
||||
<Menu.Item>
|
||||
<Popconfirm
|
||||
title={t("jobs.labels.voidjob")}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onClick={(e) => 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")}
|
||||
</Popconfirm>
|
||||
</Menu.Item>
|
||||
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")}
|
||||
</Popconfirm>
|
||||
</Menu.Item>
|
||||
</RbacWrapper>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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 !== ""
|
||||
|
||||
@@ -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})`,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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({
|
||||
<Alert
|
||||
key={problem.id}
|
||||
type="error"
|
||||
message={t("appointments.labels.dataconsistency", {
|
||||
ro_number: problem.ro_number,
|
||||
code: problem.code,
|
||||
})}
|
||||
message={
|
||||
<Trans
|
||||
i18nKey="appointments.labels.dataconsistency"
|
||||
components={[
|
||||
<Link
|
||||
to={`/manage/jobs/${problem.id}`}
|
||||
target="_blank"
|
||||
/>,
|
||||
]}
|
||||
values={{
|
||||
ro_number: problem.ro_number,
|
||||
code: problem.code,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Collapse.Panel>
|
||||
@@ -79,10 +91,18 @@ export function ScheduleCalendarWrapperComponent({
|
||||
<Alert
|
||||
key={problem.id}
|
||||
type="error"
|
||||
message={t("appointments.labels.dataconsistency", {
|
||||
ro_number: problem.ro_number,
|
||||
code: problem.code,
|
||||
})}
|
||||
message={
|
||||
<Trans
|
||||
i18nKey="appointments.labels.dataconsistency"
|
||||
components={[
|
||||
<Link to={`/manage/jobs/${problem.id}`} target="_blank" />,
|
||||
]}
|
||||
values={{
|
||||
ro_number: problem.ro_number,
|
||||
code: problem.code,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
|
||||
@@ -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 }) {
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.rbac.jobs.void")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["md_rbac", "jobs:void"]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.rbac.bills.enter")}
|
||||
rules={[
|
||||
|
||||
@@ -33,6 +33,7 @@ export function TechClockInComponent({ form, bodyshop, technician }) {
|
||||
<JobSearchSelect
|
||||
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
|
||||
notExported={!bodyshop.tt_allow_post_to_invoiced}
|
||||
notInvoiced={!bodyshop.tt_allow_post_to_invoiced}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
|
||||
@@ -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}}</0> 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",
|
||||
|
||||
@@ -405,7 +405,8 @@
|
||||
"list-active": "",
|
||||
"list-all": "",
|
||||
"list-ready": "",
|
||||
"partsqueue": ""
|
||||
"partsqueue": "",
|
||||
"void": ""
|
||||
},
|
||||
"owners": {
|
||||
"detail": "",
|
||||
|
||||
@@ -405,7 +405,8 @@
|
||||
"list-active": "",
|
||||
"list-all": "",
|
||||
"list-ready": "",
|
||||
"partsqueue": ""
|
||||
"partsqueue": "",
|
||||
"void": ""
|
||||
},
|
||||
"owners": {
|
||||
"detail": "",
|
||||
|
||||
@@ -512,6 +512,7 @@ export const TemplateList = (type, context) => {
|
||||
key: "dms_posting_sheet",
|
||||
disabled: false,
|
||||
group: "financial",
|
||||
dms: true,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user