IO-3020 IO-3036 Update job actions menu & improve feature wrapper/blur wrapper trace
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
<babeledit_project be_version="2.7.1" version="1.2">
|
<babeledit_project version="1.2" be_version="2.7.1">
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
BabelEdit project file
|
BabelEdit project file
|
||||||
@@ -3834,27 +3834,6 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
|
||||||
<name>nobilllines</name>
|
|
||||||
<definition_loaded>false</definition_loaded>
|
|
||||||
<description></description>
|
|
||||||
<comment></comment>
|
|
||||||
<default_text></default_text>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>es-MX</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-CA</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>noneselected</name>
|
<name>noneselected</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -24909,6 +24888,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>deliver_quick</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>dms</name>
|
<name>dms</name>
|
||||||
<children>
|
<children>
|
||||||
@@ -25276,6 +25276,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>intake_quick</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>manualnew</name>
|
<name>manualnew</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const blurringProps = {
|
const blurringProps = {
|
||||||
filter: "blur(6px)"
|
filter: "blur(4px)",
|
||||||
|
webkitUserSelect: "none",
|
||||||
|
msUserSelect: "none",
|
||||||
|
mozUserSelect: "none",
|
||||||
|
userSelect: "none"
|
||||||
};
|
};
|
||||||
|
|
||||||
export function BlurWrapper({
|
export function BlurWrapper({
|
||||||
@@ -19,8 +23,18 @@ export function BlurWrapper({
|
|||||||
valueProp = "value",
|
valueProp = "value",
|
||||||
overrideValue = true,
|
overrideValue = true,
|
||||||
overrideValueFunction,
|
overrideValueFunction,
|
||||||
children
|
children,
|
||||||
|
bypass
|
||||||
}) {
|
}) {
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
if (!ValidateFeatureName(featureName)) console.trace("*** INVALID FEATURE NAME", featureName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bypass) {
|
||||||
|
console.trace("*** BYPASS USED", featureName);
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
if (!HasFeatureAccess({ featureName, bodyshop })) {
|
if (!HasFeatureAccess({ featureName, bodyshop })) {
|
||||||
const childrenWithBlurProps = React.Children.map(children, (child) => {
|
const childrenWithBlurProps = React.Children.map(children, (child) => {
|
||||||
if (React.isValidElement(child)) {
|
if (React.isValidElement(child)) {
|
||||||
@@ -48,6 +62,7 @@ export function BlurWrapper({
|
|||||||
|
|
||||||
return childrenWithBlurProps;
|
return childrenWithBlurProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
export default connect(mapStateToProps, null)(BlurWrapper);
|
export default connect(mapStateToProps, null)(BlurWrapper);
|
||||||
@@ -55,3 +70,25 @@ export default connect(mapStateToProps, null)(BlurWrapper);
|
|||||||
function RandomDinero() {
|
function RandomDinero() {
|
||||||
return Dinero({ amount: Math.round(Math.exp(Math.random() * 100, 2)) }).toFormat();
|
return Dinero({ amount: Math.round(Math.exp(Math.random() * 100, 2)) }).toFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const featureNameList = [
|
||||||
|
"mobile",
|
||||||
|
"allAccess",
|
||||||
|
"timetickets",
|
||||||
|
"payments",
|
||||||
|
"partsorders",
|
||||||
|
"bills",
|
||||||
|
"export",
|
||||||
|
"csi",
|
||||||
|
"courtesycars",
|
||||||
|
"media",
|
||||||
|
"visualboard",
|
||||||
|
"scoreboard",
|
||||||
|
"checklist",
|
||||||
|
"smartscheduling",
|
||||||
|
"roguard"
|
||||||
|
];
|
||||||
|
|
||||||
|
function ValidateFeatureName(featureName) {
|
||||||
|
return featureNameList.includes(featureName);
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,17 +43,11 @@ function FeatureWrapper({ bodyshop, featureName, noauth, blurContent = false, ch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function HasFeatureAccess({ featureName, bodyshop }) {
|
export function HasFeatureAccess({ featureName, bodyshop, debug = false }) {
|
||||||
|
if (debug) {
|
||||||
|
console.trace(`*** HasFeatureAccessFunction called with feature << ${featureName} >>`);
|
||||||
|
}
|
||||||
return bodyshop?.features?.allAccess || dayjs(bodyshop?.features[featureName]).isAfter(dayjs());
|
return bodyshop?.features?.allAccess || dayjs(bodyshop?.features[featureName]).isAfter(dayjs());
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(FeatureWrapper);
|
export default connect(mapStateToProps, null)(FeatureWrapper);
|
||||||
|
|
||||||
/*
|
|
||||||
dashboard
|
|
||||||
production-board
|
|
||||||
scoreboard
|
|
||||||
csi
|
|
||||||
tech-console
|
|
||||||
mobile-imaging
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import Icon, {
|
|||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Layout, Menu, Space } from "antd";
|
import { Layout, Menu, Space } from "antd";
|
||||||
import React from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { BsKanban } from "react-icons/bs";
|
import { BsKanban } from "react-icons/bs";
|
||||||
import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar, FaTasks } from "react-icons/fa";
|
import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar, FaTasks } from "react-icons/fa";
|
||||||
@@ -44,7 +43,7 @@ import { signOutStart } from "../../redux/user/user.actions";
|
|||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
import LockWrapper from "../lock-wrapper/locker-wrapper.component";
|
import LockWrapper from "../lock-wrapper/lock-wrapper.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
@@ -673,25 +672,19 @@ function Header({
|
|||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
|
||||||
...(InstanceRenderManager({
|
{
|
||||||
imex: HasFeatureAccess({ featureName: "timetickets", bodyshop }),
|
key: "shiftclock",
|
||||||
rome: "USE_IMEX"
|
id: "header-shiftclock",
|
||||||
})
|
icon: <Icon component={GiPlayerTime} />,
|
||||||
? [
|
label: (
|
||||||
{
|
<Link to="/manage/shiftclock">
|
||||||
key: "shiftclock",
|
<LockWrapper featureName="export" bodyshop={bodyshop}>
|
||||||
id: "header-shiftclock",
|
{t("menus.header.shiftclock")}
|
||||||
icon: <Icon component={GiPlayerTime} />,
|
</LockWrapper>
|
||||||
label: (
|
</Link>
|
||||||
<Link to="/manage/shiftclock">
|
)
|
||||||
<LockWrapper featureName="export" bodyshop={bodyshop}>
|
},
|
||||||
{t("menus.header.shiftclock")}
|
|
||||||
</LockWrapper>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
{
|
{
|
||||||
key: "profile",
|
key: "profile",
|
||||||
id: "header-profile",
|
id: "header-profile",
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import BillDetailEditcontainer from "../bill-detail-edit/bill-detail-edit.container.jsx";
|
import BillDetailEditcontainer from "../bill-detail-edit/bill-detail-edit.container";
|
||||||
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component.jsx";
|
|
||||||
import TaskListContainer from "../task-list/task-list.container.jsx";
|
import BlurWrapper from "../feature-wrapper/blur-wrapper.component";
|
||||||
|
import TaskListContainer from "../task-list/task-list.container";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -117,57 +118,61 @@ export function JobLinesExpander({ jobline, jobid, bodyshop, technician }) {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<FeatureWrapper featureName="bills" noauth={() => null}>
|
|
||||||
<Col md={24} lg={8}>
|
<Col md={24} lg={8}>
|
||||||
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
||||||
<BillDetailEditcontainer />
|
<BillDetailEditcontainer />
|
||||||
<Timeline
|
<Timeline
|
||||||
items={
|
items={
|
||||||
data.billlines.length > 0
|
data.billlines.length > 0
|
||||||
? data.billlines.map((line) => ({
|
? data.billlines.map((line) => ({
|
||||||
key: line.id,
|
key: line.id,
|
||||||
|
children: (
|
||||||
|
<Row wrap>
|
||||||
|
<Col span={4}>
|
||||||
|
{!technician ? (
|
||||||
|
<>
|
||||||
|
<Link to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}>
|
||||||
|
{line.bill.invoice_number}
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
`${line.bill.invoice_number}`
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<span>
|
||||||
|
{`${t("billlines.fields.actual_price")}: `}
|
||||||
|
<CurrencyFormatter>{line.actual_price}</CurrencyFormatter>
|
||||||
|
</span>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<span>
|
||||||
|
{`${t("billlines.fields.actual_cost")}: `}
|
||||||
|
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
|
||||||
|
</span>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<DateFormatter>{line.bill.date}</DateFormatter>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}> {line.bill.vendor.name}</Col>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
key: "no-orders",
|
||||||
children: (
|
children: (
|
||||||
<Row wrap>
|
<BlurWrapper featureName="bills">
|
||||||
<Col span={4}>
|
<span>{t("bills.labels.nobilllines")}</span>
|
||||||
{!technician ? (
|
</BlurWrapper>
|
||||||
<>
|
|
||||||
<Link to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}>
|
|
||||||
{line.bill.invoice_number}
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
`${line.bill.invoice_number}`
|
|
||||||
)}
|
|
||||||
</Col>
|
|
||||||
<Col span={4}>
|
|
||||||
<span>
|
|
||||||
{`${t("billlines.fields.actual_price")}: `}
|
|
||||||
<CurrencyFormatter>{line.actual_price}</CurrencyFormatter>
|
|
||||||
</span>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}>
|
|
||||||
<span>
|
|
||||||
{`${t("billlines.fields.actual_cost")}: `}
|
|
||||||
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
|
|
||||||
</span>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}>
|
|
||||||
<DateFormatter>{line.bill.date}</DateFormatter>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}> {line.bill.vendor.name}</Col>
|
|
||||||
</Row>
|
|
||||||
)
|
)
|
||||||
}))
|
}
|
||||||
: [
|
]
|
||||||
{
|
}
|
||||||
key: "no-orders",
|
/>
|
||||||
children: t("bills.labels.nobilllines")
|
</Col>
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</FeatureWrapper>
|
|
||||||
<Col md={24} lg={24}>
|
<Col md={24} lg={24}>
|
||||||
<TaskListContainer
|
<TaskListContainer
|
||||||
parentJobId={jobid}
|
parentJobId={jobid}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
|||||||
import { Button, Card, Dropdown, Form, Input, Modal, notification, Popconfirm, Popover, Select, Space } from "antd";
|
import { Button, Card, Dropdown, Form, Input, Modal, notification, Popconfirm, Popover, Select, Space } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import parsePhoneNumber from "libphonenumber-js";
|
import parsePhoneNumber from "libphonenumber-js";
|
||||||
import React, { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
@@ -23,9 +23,9 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|||||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
||||||
|
import LockerWrapperComponent from "../lock-wrapper/lock-wrapper.component";
|
||||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
||||||
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
||||||
@@ -196,6 +196,7 @@ export function JobsDetailHeaderActions({
|
|||||||
message: t("appointments.successes.created")
|
message: t("appointments.successes.created")
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
notification.open({ type: "error", message: t("appointments.errors.saving", { error: error.message }) });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setVisibility(false);
|
setVisibility(false);
|
||||||
@@ -206,7 +207,7 @@ export function JobsDetailHeaderActions({
|
|||||||
//delete the job.
|
//delete the job.
|
||||||
const result = await deleteJob({ variables: { id: job.id } });
|
const result = await deleteJob({ variables: { id: job.id } });
|
||||||
|
|
||||||
if (!!!result.errors) {
|
if (!result.errors) {
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("jobs.successes.delete")
|
message: t("jobs.successes.delete")
|
||||||
});
|
});
|
||||||
@@ -264,7 +265,7 @@ export function JobsDetailHeaderActions({
|
|||||||
awaitRefetchQueries: true
|
awaitRefetchQueries: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!!!result.errors) {
|
if (!result.errors) {
|
||||||
notification["success"]({ message: t("csi.successes.created") });
|
notification["success"]({ message: t("csi.successes.created") });
|
||||||
} else {
|
} else {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
@@ -385,7 +386,7 @@ export function JobsDetailHeaderActions({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!!!result.errors) {
|
if (!result.errors) {
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("jobs.successes.voided")
|
message: t("jobs.successes.voided")
|
||||||
});
|
});
|
||||||
@@ -405,7 +406,7 @@ export function JobsDetailHeaderActions({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExportCustData = async (e) => {
|
const handleExportCustData = async () => {
|
||||||
logImEXEvent("job_export_cust_data");
|
logImEXEvent("job_export_cust_data");
|
||||||
let PartnerResponse;
|
let PartnerResponse;
|
||||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||||
@@ -482,7 +483,7 @@ export function JobsDetailHeaderActions({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAlertToggle = (e) => {
|
const handleAlertToggle = () => {
|
||||||
logImEXEvent("production_toggle_alert");
|
logImEXEvent("production_toggle_alert");
|
||||||
//e.stopPropagation();
|
//e.stopPropagation();
|
||||||
updateJob({
|
updateJob({
|
||||||
@@ -505,7 +506,7 @@ export function JobsDetailHeaderActions({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSuspend = (e) => {
|
const handleSuspend = () => {
|
||||||
logImEXEvent("production_toggle_alert");
|
logImEXEvent("production_toggle_alert");
|
||||||
//e.stopPropagation();
|
//e.stopPropagation();
|
||||||
updateJob({
|
updateJob({
|
||||||
@@ -518,7 +519,7 @@ export function JobsDetailHeaderActions({
|
|||||||
});
|
});
|
||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
operation: AuditTrailMapping.jobsuspend(!!job.suspended ? !job.suspended : true),
|
operation: AuditTrailMapping.jobsuspend(job.suspended ? !job.suspended : true),
|
||||||
type: "jobsuspend"
|
type: "jobsuspend"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -599,7 +600,7 @@ export function JobsDetailHeaderActions({
|
|||||||
required: true
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
({ getFieldValue }) => ({
|
() => ({
|
||||||
async validator(rule, value) {
|
async validator(rule, value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
const { start } = form.getFieldsValue();
|
const { start } = form.getFieldsValue();
|
||||||
@@ -668,75 +669,73 @@ export function JobsDetailHeaderActions({
|
|||||||
disabled: job.status !== bodyshop.md_ro_statuses.default_scheduled,
|
disabled: job.status !== bodyshop.md_ro_statuses.default_scheduled,
|
||||||
label: t("menus.jobsactions.cancelallappointments")
|
label: t("menus.jobsactions.cancelallappointments")
|
||||||
},
|
},
|
||||||
...InstanceRenderManager({
|
{
|
||||||
imex: [
|
key: "intake",
|
||||||
...(HasFeatureAccess({ featureName: "checklist", bodyshop })
|
id: "job-actions-intake",
|
||||||
? [
|
disabled: !!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO,
|
||||||
{
|
label:
|
||||||
key: "intake",
|
!!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO ? (
|
||||||
id: "job-actions-intake",
|
<LockerWrapperComponent featureName="checklist">{t("jobs.actions.intake")}</LockerWrapperComponent>
|
||||||
disabled: !!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO,
|
) : (
|
||||||
label:
|
<Link to={`/manage/jobs/${job.id}/intake`}>
|
||||||
!!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO ? (
|
<LockerWrapperComponent featureName="checklist">{t("jobs.actions.intake")}</LockerWrapperComponent>
|
||||||
t("jobs.actions.intake")
|
</Link>
|
||||||
) : (
|
)
|
||||||
<Link to={`/manage/jobs/${job.id}/intake`}>{t("jobs.actions.intake")}</Link>
|
},
|
||||||
)
|
{
|
||||||
},
|
key: "deliver",
|
||||||
{
|
id: "job-actions-deliver",
|
||||||
key: "deliver",
|
disabled: !jobInProduction || jobRO,
|
||||||
id: "job-actions-deliver",
|
label: !jobInProduction ? (
|
||||||
disabled: !jobInProduction || jobRO,
|
<LockerWrapperComponent disabled featureName="checklist">
|
||||||
label: !jobInProduction ? (
|
{t("jobs.actions.deliver")}
|
||||||
t("jobs.actions.deliver")
|
</LockerWrapperComponent>
|
||||||
) : (
|
) : (
|
||||||
<Link to={`/manage/jobs/${job.id}/deliver`}>{t("jobs.actions.deliver")}</Link>
|
<Link to={`/manage/jobs/${job.id}/deliver`}>
|
||||||
)
|
<LockerWrapperComponent disabled featureName="checklist">
|
||||||
},
|
{t("jobs.actions.deliver")}{" "}
|
||||||
{
|
</LockerWrapperComponent>
|
||||||
key: "checklist",
|
</Link>
|
||||||
id: "job-actions-checklist",
|
)
|
||||||
disabled: !job.converted,
|
},
|
||||||
label: <Link to={`/manage/jobs/${job.id}/checklist`}>{t("jobs.actions.viewchecklist")}</Link>
|
{
|
||||||
}
|
key: "checklist",
|
||||||
]
|
id: "job-actions-checklist",
|
||||||
: [
|
disabled: !job.converted,
|
||||||
{
|
label: (
|
||||||
key: "toggleproduction",
|
<Link to={`/manage/jobs/${job.id}/checklist`}>
|
||||||
id: "job-actions-toggleproduction",
|
<LockerWrapperComponent featureName="checklist">{t("jobs.actions.viewchecklist")}</LockerWrapperComponent>
|
||||||
disabled: !job.converted || jobRO,
|
</Link>
|
||||||
label: <JobsDetailHeaderActionsToggleProduction job={job} refetch={refetch} />
|
)
|
||||||
}
|
},
|
||||||
])
|
{
|
||||||
],
|
key: "toggleproduction",
|
||||||
rome: "USE_IMEX"
|
id: "job-actions-toggleproduction",
|
||||||
}),
|
disabled: !job.converted || jobRO,
|
||||||
...(InstanceRenderManager({
|
label: <JobsDetailHeaderActionsToggleProduction job={job} refetch={refetch} />
|
||||||
imex: HasFeatureAccess({ featureName: "timetickets", bodyshop }),
|
},
|
||||||
rome: "USE_IMEX"
|
|
||||||
})
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
key: "entertimetickets",
|
|
||||||
id: "job-actions-entertimetickets",
|
|
||||||
disabled: !job.converted || (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
|
|
||||||
label: t("timetickets.actions.enter"),
|
|
||||||
onClick: () => {
|
|
||||||
logImEXEvent("job_header_enter_time_ticekts");
|
|
||||||
|
|
||||||
setTimeTicketContext({
|
{
|
||||||
actions: {},
|
key: "entertimetickets",
|
||||||
context: {
|
id: "job-actions-entertimetickets",
|
||||||
jobId: job.id,
|
disabled: !job.converted || (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
|
||||||
created_by: currentUser.displayName
|
label: (
|
||||||
? currentUser.email.concat(" | ", currentUser.displayName)
|
<LockerWrapperComponent featureName="timetickets">{t("timetickets.actions.enter")}</LockerWrapperComponent>
|
||||||
: currentUser.email
|
),
|
||||||
}
|
onClick: () => {
|
||||||
});
|
logImEXEvent("job_header_enter_time_ticekts");
|
||||||
}
|
|
||||||
|
setTimeTicketContext({
|
||||||
|
actions: {},
|
||||||
|
context: {
|
||||||
|
jobId: job.id,
|
||||||
|
created_by: currentUser.displayName
|
||||||
|
? currentUser.email.concat(" | ", currentUser.displayName)
|
||||||
|
: currentUser.email
|
||||||
}
|
}
|
||||||
]
|
});
|
||||||
: [])
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
if (bodyshop.md_tasks_presets.enable_tasks) {
|
if (bodyshop.md_tasks_presets.enable_tasks) {
|
||||||
@@ -758,7 +757,7 @@ export function JobsDetailHeaderActions({
|
|||||||
key: "enterpayments",
|
key: "enterpayments",
|
||||||
id: "job-actions-enterpayments",
|
id: "job-actions-enterpayments",
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
label: t("menus.header.enterpayment"),
|
label: <LockerWrapperComponent featureName="payments">{t("menus.header.enterpayment")}</LockerWrapperComponent>,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
logImEXEvent("job_header_enter_payment");
|
logImEXEvent("job_header_enter_payment");
|
||||||
|
|
||||||
@@ -786,18 +785,18 @@ export function JobsDetailHeaderActions({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasFeatureAccess({ featureName: "courtesycars", bodyshop })) {
|
menuItems.push({
|
||||||
menuItems.push({
|
key: "cccontract",
|
||||||
key: "cccontract",
|
id: "job-actions-cccontract",
|
||||||
id: "job-actions-cccontract",
|
disabled: jobRO || !job.converted,
|
||||||
disabled: jobRO || !job.converted,
|
label: (
|
||||||
label: (
|
<Link state={{ jobId: job.id }} to="/manage/courtesycars/contracts/new">
|
||||||
<Link state={{ jobId: job.id }} to="/manage/courtesycars/contracts/new">
|
<LockerWrapperComponent featureName="courtesycars">
|
||||||
{t("menus.jobsactions.newcccontract")}
|
{t("menus.jobsactions.newcccontract")}
|
||||||
</Link>
|
</LockerWrapperComponent>
|
||||||
)
|
</Link>
|
||||||
});
|
)
|
||||||
}
|
});
|
||||||
|
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "createtask",
|
key: "createtask",
|
||||||
@@ -882,29 +881,23 @@ export function JobsDetailHeaderActions({
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
...(InstanceRenderManager({
|
|
||||||
imex: HasFeatureAccess({ featureName: "bills", bodyshop }),
|
|
||||||
rome: "USE_IMEX"
|
|
||||||
})
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
key: "postbills",
|
|
||||||
id: "job-actions-postbills",
|
|
||||||
disabled: !job.converted,
|
|
||||||
label: t("jobs.actions.postbills"),
|
|
||||||
onClick: () => {
|
|
||||||
logImEXEvent("job_header_enter_bills");
|
|
||||||
|
|
||||||
setBillEnterContext({
|
{
|
||||||
actions: { refetch: refetch },
|
key: "postbills",
|
||||||
context: {
|
id: "job-actions-postbills",
|
||||||
job: job
|
disabled: !job.converted,
|
||||||
}
|
label: <LockerWrapperComponent featureName="bill">{t("jobs.actions.postbills")}</LockerWrapperComponent>,
|
||||||
});
|
onClick: () => {
|
||||||
}
|
logImEXEvent("job_header_enter_bills");
|
||||||
|
|
||||||
|
setBillEnterContext({
|
||||||
|
actions: { refetch: refetch },
|
||||||
|
context: {
|
||||||
|
job: job
|
||||||
}
|
}
|
||||||
]
|
});
|
||||||
: []),
|
}
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: "addtopartsqueue",
|
key: "addtopartsqueue",
|
||||||
@@ -919,7 +912,7 @@ export function JobsDetailHeaderActions({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!!!result.errors) {
|
if (!result.errors) {
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("jobs.successes.partsqueue")
|
message: t("jobs.successes.partsqueue")
|
||||||
});
|
});
|
||||||
@@ -963,79 +956,70 @@ export function JobsDetailHeaderActions({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
menuItems.push({
|
||||||
InstanceRenderManager({
|
key: "exportcustdata",
|
||||||
imex: HasFeatureAccess({ featureName: "export", bodyshop }),
|
id: "job-actions-exportcustdata",
|
||||||
rome: "USE_IMEX"
|
disabled: !(job.converted && HasFeatureAccess({ bodyshop, featureName: "export", debug: true })),
|
||||||
})
|
label: <LockerWrapperComponent featureName="export">{t("jobs.actions.exportcustdata")}</LockerWrapperComponent>,
|
||||||
) {
|
onClick: handleExportCustData
|
||||||
menuItems.push({
|
});
|
||||||
key: "exportcustdata",
|
|
||||||
id: "job-actions-exportcustdata",
|
|
||||||
disabled: !job.converted,
|
|
||||||
label: t("jobs.actions.exportcustdata"),
|
|
||||||
onClick: handleExportCustData
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasFeatureAccess({ featureName: "csi", bodyshop })) {
|
const children = [
|
||||||
const children = [
|
{
|
||||||
{
|
key: "email",
|
||||||
key: "email",
|
id: "job-actions-email",
|
||||||
id: "job-actions-email",
|
disabled: !(job.ownr_ea && HasFeatureAccess({ bodyshop, featureName: "csi", debug: true })),
|
||||||
disabled: !!!job.ownr_ea,
|
label: <LockerWrapperComponent featureName="checklist">{t("general.labels.email")}</LockerWrapperComponent>,
|
||||||
label: t("general.labels.email"),
|
onClick: handleCreateCsi
|
||||||
onClick: handleCreateCsi
|
},
|
||||||
},
|
{
|
||||||
{
|
key: "text",
|
||||||
key: "text",
|
id: "job-actions-text",
|
||||||
id: "job-actions-text",
|
disabled: !(job.ownr_ph1 && HasFeatureAccess({ bodyshop, featureName: "csi", debug: true })),
|
||||||
disabled: !!!job.ownr_ph1,
|
label: <LockerWrapperComponent featureName="checklist">{t("general.labels.text")}</LockerWrapperComponent>,
|
||||||
label: t("general.labels.text"),
|
onClick: handleCreateCsi
|
||||||
onClick: handleCreateCsi
|
},
|
||||||
},
|
{
|
||||||
{
|
key: "generate",
|
||||||
key: "generate",
|
id: "job-actions-generate",
|
||||||
id: "job-actions-generate",
|
disabled: job.csiinvites?.length > 0 || !HasFeatureAccess({ bodyshop, featureName: "csi", debug: true }),
|
||||||
disabled: job.csiinvites && job.csiinvites.length > 0,
|
label: <LockerWrapperComponent featureName="checklist">{t("jobs.actions.generatecsi")}</LockerWrapperComponent>,
|
||||||
label: t("jobs.actions.generatecsi"),
|
onClick: handleCreateCsi
|
||||||
onClick: handleCreateCsi
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
if (job?.csiinvites?.length) {
|
|
||||||
children.push(
|
|
||||||
{
|
|
||||||
type: "divider"
|
|
||||||
},
|
|
||||||
...job.csiinvites.map((item, idx) => {
|
|
||||||
return item.completedon
|
|
||||||
? {
|
|
||||||
key: idx,
|
|
||||||
label: (
|
|
||||||
<Link to={`/manage/shop/csi?responseid=${item.id}`}>
|
|
||||||
<DateTimeFormatter>{item.completedon}</DateTimeFormatter>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
key: idx,
|
|
||||||
onClick: () => {
|
|
||||||
navigator.clipboard.writeText(`${window.location.protocol}//${window.location.host}/csi/${item.id}`);
|
|
||||||
},
|
|
||||||
label: t("general.actions.copylink")
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
menuItems.push({
|
];
|
||||||
key: "sendcsi",
|
|
||||||
id: "job-actions-sendcsi",
|
if (job?.csiinvites?.length) {
|
||||||
label: t("jobs.actions.sendcsi"),
|
children.push(
|
||||||
disabled: !job.converted,
|
{
|
||||||
children
|
type: "divider"
|
||||||
});
|
},
|
||||||
|
...job.csiinvites.map((item, idx) => {
|
||||||
|
return item.completedon
|
||||||
|
? {
|
||||||
|
key: idx,
|
||||||
|
label: (
|
||||||
|
<Link to={`/manage/shop/csi?responseid=${item.id}`}>
|
||||||
|
<DateTimeFormatter>{item.completedon}</DateTimeFormatter>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
key: idx,
|
||||||
|
onClick: () => {
|
||||||
|
navigator.clipboard.writeText(`${window.location.protocol}//${window.location.host}/csi/${item.id}`);
|
||||||
|
},
|
||||||
|
label: t("general.actions.copylink")
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
menuItems.push({
|
||||||
|
key: "sendcsi",
|
||||||
|
id: "job-actions-sendcsi",
|
||||||
|
label: <LockerWrapperComponent featureName="checklist">{t("jobs.actions.sendcsi")}</LockerWrapperComponent>,
|
||||||
|
disabled: !job.converted,
|
||||||
|
children
|
||||||
|
});
|
||||||
|
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "jobcosting",
|
key: "jobcosting",
|
||||||
@@ -1075,7 +1059,7 @@ export function JobsDetailHeaderActions({
|
|||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "manualevent",
|
key: "manualevent",
|
||||||
id: "job-actions-manualevent",
|
id: "job-actions-manualevent",
|
||||||
onClick: (e) => {
|
onClick: () => {
|
||||||
setVisibility(true);
|
setVisibility(true);
|
||||||
},
|
},
|
||||||
label: t("appointments.labels.manualevent")
|
label: t("appointments.labels.manualevent")
|
||||||
|
|||||||
@@ -168,8 +168,8 @@ export function JobsDetailHeaderActionsToggleProduction({ bodyshop, job, jobRO,
|
|||||||
getPopupContainer={(trigger) => trigger.parentNode}
|
getPopupContainer={(trigger) => trigger.parentNode}
|
||||||
trigger="click"
|
trigger="click"
|
||||||
>
|
>
|
||||||
{scenario === "pre" && t("jobs.actions.intake")}
|
{scenario === "pre" && t("jobs.actions.intake_quick")}
|
||||||
{scenario === "prod" && t("jobs.actions.deliver")}
|
{scenario === "prod" && t("jobs.actions.deliver_quick")}
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,7 +230,7 @@
|
|||||||
"markexported": "Mark Exported",
|
"markexported": "Mark Exported",
|
||||||
"markforreexport": "Mark for Re-export",
|
"markforreexport": "Mark for Re-export",
|
||||||
"new": "New Bill",
|
"new": "New Bill",
|
||||||
"nobilllines": "",
|
"nobilllines": "No bills have been posted yet.",
|
||||||
"noneselected": "No bill selected.",
|
"noneselected": "No bill selected.",
|
||||||
"onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
|
"onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
|
||||||
"printlabels": "Print Labels",
|
"printlabels": "Print Labels",
|
||||||
@@ -1508,7 +1508,7 @@
|
|||||||
"addDocuments": "Add Job Documents",
|
"addDocuments": "Add Job Documents",
|
||||||
"addNote": "Add Note",
|
"addNote": "Add Note",
|
||||||
"addtopartsqueue": "Add to Parts Queue",
|
"addtopartsqueue": "Add to Parts Queue",
|
||||||
"addtoproduction": "Add to Production",
|
"addtoproduction": "Add In Production Flag",
|
||||||
"addtoscoreboard": "Add to Scoreboard",
|
"addtoscoreboard": "Add to Scoreboard",
|
||||||
"allocate": "Allocate",
|
"allocate": "Allocate",
|
||||||
"autoallocate": "Auto Allocate",
|
"autoallocate": "Auto Allocate",
|
||||||
@@ -1519,6 +1519,7 @@
|
|||||||
"convert": "Convert",
|
"convert": "Convert",
|
||||||
"createiou": "Create IOU",
|
"createiou": "Create IOU",
|
||||||
"deliver": "Deliver",
|
"deliver": "Deliver",
|
||||||
|
"deliver_quick": "Quick Deliver",
|
||||||
"dms": {
|
"dms": {
|
||||||
"addpayer": "Add Payer",
|
"addpayer": "Add Payer",
|
||||||
"createnewcustomer": "Create New Customer",
|
"createnewcustomer": "Create New Customer",
|
||||||
@@ -1540,6 +1541,7 @@
|
|||||||
"generatecsi": "Generate CSI & Copy Link",
|
"generatecsi": "Generate CSI & Copy Link",
|
||||||
"gotojob": "Go to Job",
|
"gotojob": "Go to Job",
|
||||||
"intake": "Intake",
|
"intake": "Intake",
|
||||||
|
"intake_quick": "Quick Intake",
|
||||||
"manualnew": "Create New Job Manually",
|
"manualnew": "Create New Job Manually",
|
||||||
"mark": "Mark",
|
"mark": "Mark",
|
||||||
"markasexported": "Mark as Exported",
|
"markasexported": "Mark as Exported",
|
||||||
@@ -1549,7 +1551,7 @@
|
|||||||
"printCenter": "Print Center",
|
"printCenter": "Print Center",
|
||||||
"recalculate": "Recalculate",
|
"recalculate": "Recalculate",
|
||||||
"reconcile": "Reconcile",
|
"reconcile": "Reconcile",
|
||||||
"removefromproduction": "Remove from Production",
|
"removefromproduction": "Remove In Production Flag",
|
||||||
"schedule": "Schedule",
|
"schedule": "Schedule",
|
||||||
"sendcsi": "Send CSI",
|
"sendcsi": "Send CSI",
|
||||||
"sendpartspricechange": "Send Parts Price Change",
|
"sendpartspricechange": "Send Parts Price Change",
|
||||||
|
|||||||
@@ -1519,6 +1519,7 @@
|
|||||||
"convert": "Convertir",
|
"convert": "Convertir",
|
||||||
"createiou": "",
|
"createiou": "",
|
||||||
"deliver": "",
|
"deliver": "",
|
||||||
|
"deliver_quick": "",
|
||||||
"dms": {
|
"dms": {
|
||||||
"addpayer": "",
|
"addpayer": "",
|
||||||
"createnewcustomer": "",
|
"createnewcustomer": "",
|
||||||
@@ -1540,6 +1541,7 @@
|
|||||||
"generatecsi": "",
|
"generatecsi": "",
|
||||||
"gotojob": "",
|
"gotojob": "",
|
||||||
"intake": "",
|
"intake": "",
|
||||||
|
"intake_quick": "",
|
||||||
"manualnew": "",
|
"manualnew": "",
|
||||||
"mark": "",
|
"mark": "",
|
||||||
"markasexported": "",
|
"markasexported": "",
|
||||||
|
|||||||
@@ -1519,6 +1519,7 @@
|
|||||||
"convert": "Convertir",
|
"convert": "Convertir",
|
||||||
"createiou": "",
|
"createiou": "",
|
||||||
"deliver": "",
|
"deliver": "",
|
||||||
|
"deliver_quick": "",
|
||||||
"dms": {
|
"dms": {
|
||||||
"addpayer": "",
|
"addpayer": "",
|
||||||
"createnewcustomer": "",
|
"createnewcustomer": "",
|
||||||
@@ -1540,6 +1541,7 @@
|
|||||||
"generatecsi": "",
|
"generatecsi": "",
|
||||||
"gotojob": "",
|
"gotojob": "",
|
||||||
"intake": "",
|
"intake": "",
|
||||||
|
"intake_quick": "",
|
||||||
"manualnew": "",
|
"manualnew": "",
|
||||||
"mark": "",
|
"mark": "",
|
||||||
"markasexported": "",
|
"markasexported": "",
|
||||||
|
|||||||
Reference in New Issue
Block a user