IO-3020 IO-3036 Update job actions menu & improve feature wrapper/blur wrapper trace

This commit is contained in:
Patrick Fic
2024-11-29 15:55:20 -08:00
parent 801cd724ac
commit c85a5eb208
11 changed files with 334 additions and 294 deletions

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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
*/

View File

@@ -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",

View File

@@ -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}

View File

@@ -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")

View File

@@ -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>
); );
} }

View File

@@ -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",

View File

@@ -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": "",

View File

@@ -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": "",