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
@@ -3834,27 +3834,6 @@
</translation>
</translations>
</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>
<name>noneselected</name>
<definition_loaded>false</definition_loaded>
@@ -24909,6 +24888,27 @@
</translation>
</translations>
</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>
<name>dms</name>
<children>
@@ -25276,6 +25276,27 @@
</translation>
</translations>
</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>
<name>manualnew</name>
<definition_loaded>false</definition_loaded>

View File

@@ -9,7 +9,11 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const blurringProps = {
filter: "blur(6px)"
filter: "blur(4px)",
webkitUserSelect: "none",
msUserSelect: "none",
mozUserSelect: "none",
userSelect: "none"
};
export function BlurWrapper({
@@ -19,8 +23,18 @@ export function BlurWrapper({
valueProp = "value",
overrideValue = true,
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 })) {
const childrenWithBlurProps = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
@@ -48,6 +62,7 @@ export function BlurWrapper({
return childrenWithBlurProps;
}
return children;
}
export default connect(mapStateToProps, null)(BlurWrapper);
@@ -55,3 +70,25 @@ export default connect(mapStateToProps, null)(BlurWrapper);
function RandomDinero() {
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());
}
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";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Layout, Menu, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { BsKanban } from "react-icons/bs";
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 InstanceRenderManager from "../../utils/instanceRenderMgr";
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({
currentUser: selectCurrentUser,
@@ -673,25 +672,19 @@ function Header({
]
: []),
...(InstanceRenderManager({
imex: HasFeatureAccess({ featureName: "timetickets", bodyshop }),
rome: "USE_IMEX"
})
? [
{
key: "shiftclock",
id: "header-shiftclock",
icon: <Icon component={GiPlayerTime} />,
label: (
<Link to="/manage/shiftclock">
<LockWrapper featureName="export" bodyshop={bodyshop}>
{t("menus.header.shiftclock")}
</LockWrapper>
</Link>
)
}
]
: []),
{
key: "shiftclock",
id: "header-shiftclock",
icon: <Icon component={GiPlayerTime} />,
label: (
<Link to="/manage/shiftclock">
<LockWrapper featureName="export" bodyshop={bodyshop}>
{t("menus.header.shiftclock")}
</LockWrapper>
</Link>
)
},
{
key: "profile",
id: "header-profile",

View File

@@ -12,9 +12,10 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import AlertComponent from "../alert/alert.component";
import BillDetailEditcontainer from "../bill-detail-edit/bill-detail-edit.container.jsx";
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component.jsx";
import TaskListContainer from "../task-list/task-list.container.jsx";
import BillDetailEditcontainer from "../bill-detail-edit/bill-detail-edit.container";
import BlurWrapper from "../feature-wrapper/blur-wrapper.component";
import TaskListContainer from "../task-list/task-list.container";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -117,57 +118,61 @@ export function JobLinesExpander({ jobline, jobid, bodyshop, technician }) {
}
/>
</Col>
<FeatureWrapper featureName="bills" noauth={() => null}>
<Col md={24} lg={8}>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<BillDetailEditcontainer />
<Timeline
items={
data.billlines.length > 0
? data.billlines.map((line) => ({
key: line.id,
<Col md={24} lg={8}>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<BillDetailEditcontainer />
<Timeline
items={
data.billlines.length > 0
? data.billlines.map((line) => ({
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: (
<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>
<BlurWrapper featureName="bills">
<span>{t("bills.labels.nobilllines")}</span>
</BlurWrapper>
)
}))
: [
{
key: "no-orders",
children: t("bills.labels.nobilllines")
}
]
}
/>
</Col>
</FeatureWrapper>
}
]
}
/>
</Col>
<Col md={24} lg={24}>
<TaskListContainer
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 axios from "axios";
import parsePhoneNumber from "libphonenumber-js";
import React, { useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useNavigate } from "react-router-dom";
@@ -23,9 +23,9 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { TemplateList } from "../../utils/TemplateConstants";
import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.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 AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
@@ -196,6 +196,7 @@ export function JobsDetailHeaderActions({
message: t("appointments.successes.created")
});
} catch (error) {
notification.open({ type: "error", message: t("appointments.errors.saving", { error: error.message }) });
} finally {
setLoading(false);
setVisibility(false);
@@ -206,7 +207,7 @@ export function JobsDetailHeaderActions({
//delete the job.
const result = await deleteJob({ variables: { id: job.id } });
if (!!!result.errors) {
if (!result.errors) {
notification["success"]({
message: t("jobs.successes.delete")
});
@@ -264,7 +265,7 @@ export function JobsDetailHeaderActions({
awaitRefetchQueries: true
});
if (!!!result.errors) {
if (!result.errors) {
notification["success"]({ message: t("csi.successes.created") });
} else {
notification["error"]({
@@ -385,7 +386,7 @@ export function JobsDetailHeaderActions({
}
});
if (!!!result.errors) {
if (!result.errors) {
notification["success"]({
message: t("jobs.successes.voided")
});
@@ -405,7 +406,7 @@ export function JobsDetailHeaderActions({
}
};
const handleExportCustData = async (e) => {
const handleExportCustData = async () => {
logImEXEvent("job_export_cust_data");
let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
@@ -482,7 +483,7 @@ export function JobsDetailHeaderActions({
}
};
const handleAlertToggle = (e) => {
const handleAlertToggle = () => {
logImEXEvent("production_toggle_alert");
//e.stopPropagation();
updateJob({
@@ -505,7 +506,7 @@ export function JobsDetailHeaderActions({
});
};
const handleSuspend = (e) => {
const handleSuspend = () => {
logImEXEvent("production_toggle_alert");
//e.stopPropagation();
updateJob({
@@ -518,7 +519,7 @@ export function JobsDetailHeaderActions({
});
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobsuspend(!!job.suspended ? !job.suspended : true),
operation: AuditTrailMapping.jobsuspend(job.suspended ? !job.suspended : true),
type: "jobsuspend"
});
};
@@ -599,7 +600,7 @@ export function JobsDetailHeaderActions({
required: true
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
() => ({
async validator(rule, value) {
if (value) {
const { start } = form.getFieldsValue();
@@ -668,75 +669,73 @@ export function JobsDetailHeaderActions({
disabled: job.status !== bodyshop.md_ro_statuses.default_scheduled,
label: t("menus.jobsactions.cancelallappointments")
},
...InstanceRenderManager({
imex: [
...(HasFeatureAccess({ featureName: "checklist", bodyshop })
? [
{
key: "intake",
id: "job-actions-intake",
disabled: !!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO,
label:
!!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO ? (
t("jobs.actions.intake")
) : (
<Link to={`/manage/jobs/${job.id}/intake`}>{t("jobs.actions.intake")}</Link>
)
},
{
key: "deliver",
id: "job-actions-deliver",
disabled: !jobInProduction || jobRO,
label: !jobInProduction ? (
t("jobs.actions.deliver")
) : (
<Link to={`/manage/jobs/${job.id}/deliver`}>{t("jobs.actions.deliver")}</Link>
)
},
{
key: "checklist",
id: "job-actions-checklist",
disabled: !job.converted,
label: <Link to={`/manage/jobs/${job.id}/checklist`}>{t("jobs.actions.viewchecklist")}</Link>
}
]
: [
{
key: "toggleproduction",
id: "job-actions-toggleproduction",
disabled: !job.converted || jobRO,
label: <JobsDetailHeaderActionsToggleProduction job={job} refetch={refetch} />
}
])
],
rome: "USE_IMEX"
}),
...(InstanceRenderManager({
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");
{
key: "intake",
id: "job-actions-intake",
disabled: !!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO,
label:
!!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO ? (
<LockerWrapperComponent featureName="checklist">{t("jobs.actions.intake")}</LockerWrapperComponent>
) : (
<Link to={`/manage/jobs/${job.id}/intake`}>
<LockerWrapperComponent featureName="checklist">{t("jobs.actions.intake")}</LockerWrapperComponent>
</Link>
)
},
{
key: "deliver",
id: "job-actions-deliver",
disabled: !jobInProduction || jobRO,
label: !jobInProduction ? (
<LockerWrapperComponent disabled featureName="checklist">
{t("jobs.actions.deliver")}
</LockerWrapperComponent>
) : (
<Link to={`/manage/jobs/${job.id}/deliver`}>
<LockerWrapperComponent disabled featureName="checklist">
{t("jobs.actions.deliver")}{" "}
</LockerWrapperComponent>
</Link>
)
},
{
key: "checklist",
id: "job-actions-checklist",
disabled: !job.converted,
label: (
<Link to={`/manage/jobs/${job.id}/checklist`}>
<LockerWrapperComponent featureName="checklist">{t("jobs.actions.viewchecklist")}</LockerWrapperComponent>
</Link>
)
},
{
key: "toggleproduction",
id: "job-actions-toggleproduction",
disabled: !job.converted || jobRO,
label: <JobsDetailHeaderActionsToggleProduction job={job} refetch={refetch} />
},
setTimeTicketContext({
actions: {},
context: {
jobId: job.id,
created_by: currentUser.displayName
? currentUser.email.concat(" | ", currentUser.displayName)
: currentUser.email
}
});
}
{
key: "entertimetickets",
id: "job-actions-entertimetickets",
disabled: !job.converted || (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
label: (
<LockerWrapperComponent featureName="timetickets">{t("timetickets.actions.enter")}</LockerWrapperComponent>
),
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) {
@@ -758,7 +757,7 @@ export function JobsDetailHeaderActions({
key: "enterpayments",
id: "job-actions-enterpayments",
disabled: !job.converted,
label: t("menus.header.enterpayment"),
label: <LockerWrapperComponent featureName="payments">{t("menus.header.enterpayment")}</LockerWrapperComponent>,
onClick: () => {
logImEXEvent("job_header_enter_payment");
@@ -786,18 +785,18 @@ export function JobsDetailHeaderActions({
});
}
if (HasFeatureAccess({ featureName: "courtesycars", bodyshop })) {
menuItems.push({
key: "cccontract",
id: "job-actions-cccontract",
disabled: jobRO || !job.converted,
label: (
<Link state={{ jobId: job.id }} to="/manage/courtesycars/contracts/new">
menuItems.push({
key: "cccontract",
id: "job-actions-cccontract",
disabled: jobRO || !job.converted,
label: (
<Link state={{ jobId: job.id }} to="/manage/courtesycars/contracts/new">
<LockerWrapperComponent featureName="courtesycars">
{t("menus.jobsactions.newcccontract")}
</Link>
)
});
}
</LockerWrapperComponent>
</Link>
)
});
menuItems.push({
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 },
context: {
job: job
}
});
}
{
key: "postbills",
id: "job-actions-postbills",
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",
@@ -919,7 +912,7 @@ export function JobsDetailHeaderActions({
}
});
if (!!!result.errors) {
if (!result.errors) {
notification["success"]({
message: t("jobs.successes.partsqueue")
});
@@ -963,79 +956,70 @@ export function JobsDetailHeaderActions({
}
);
if (
InstanceRenderManager({
imex: HasFeatureAccess({ featureName: "export", bodyshop }),
rome: "USE_IMEX"
})
) {
menuItems.push({
key: "exportcustdata",
id: "job-actions-exportcustdata",
disabled: !job.converted,
label: t("jobs.actions.exportcustdata"),
onClick: handleExportCustData
});
}
menuItems.push({
key: "exportcustdata",
id: "job-actions-exportcustdata",
disabled: !(job.converted && HasFeatureAccess({ bodyshop, featureName: "export", debug: true })),
label: <LockerWrapperComponent featureName="export">{t("jobs.actions.exportcustdata")}</LockerWrapperComponent>,
onClick: handleExportCustData
});
if (HasFeatureAccess({ featureName: "csi", bodyshop })) {
const children = [
{
key: "email",
id: "job-actions-email",
disabled: !!!job.ownr_ea,
label: t("general.labels.email"),
onClick: handleCreateCsi
},
{
key: "text",
id: "job-actions-text",
disabled: !!!job.ownr_ph1,
label: t("general.labels.text"),
onClick: handleCreateCsi
},
{
key: "generate",
id: "job-actions-generate",
disabled: job.csiinvites && job.csiinvites.length > 0,
label: t("jobs.actions.generatecsi"),
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")
};
})
);
const children = [
{
key: "email",
id: "job-actions-email",
disabled: !(job.ownr_ea && HasFeatureAccess({ bodyshop, featureName: "csi", debug: true })),
label: <LockerWrapperComponent featureName="checklist">{t("general.labels.email")}</LockerWrapperComponent>,
onClick: handleCreateCsi
},
{
key: "text",
id: "job-actions-text",
disabled: !(job.ownr_ph1 && HasFeatureAccess({ bodyshop, featureName: "csi", debug: true })),
label: <LockerWrapperComponent featureName="checklist">{t("general.labels.text")}</LockerWrapperComponent>,
onClick: handleCreateCsi
},
{
key: "generate",
id: "job-actions-generate",
disabled: job.csiinvites?.length > 0 || !HasFeatureAccess({ bodyshop, featureName: "csi", debug: true }),
label: <LockerWrapperComponent featureName="checklist">{t("jobs.actions.generatecsi")}</LockerWrapperComponent>,
onClick: handleCreateCsi
}
menuItems.push({
key: "sendcsi",
id: "job-actions-sendcsi",
label: t("jobs.actions.sendcsi"),
disabled: !job.converted,
children
});
];
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",
label: <LockerWrapperComponent featureName="checklist">{t("jobs.actions.sendcsi")}</LockerWrapperComponent>,
disabled: !job.converted,
children
});
menuItems.push({
key: "jobcosting",
@@ -1075,7 +1059,7 @@ export function JobsDetailHeaderActions({
menuItems.push({
key: "manualevent",
id: "job-actions-manualevent",
onClick: (e) => {
onClick: () => {
setVisibility(true);
},
label: t("appointments.labels.manualevent")

View File

@@ -168,8 +168,8 @@ export function JobsDetailHeaderActionsToggleProduction({ bodyshop, job, jobRO,
getPopupContainer={(trigger) => trigger.parentNode}
trigger="click"
>
{scenario === "pre" && t("jobs.actions.intake")}
{scenario === "prod" && t("jobs.actions.deliver")}
{scenario === "pre" && t("jobs.actions.intake_quick")}
{scenario === "prod" && t("jobs.actions.deliver_quick")}
</Popover>
);
}

View File

@@ -230,7 +230,7 @@
"markexported": "Mark Exported",
"markforreexport": "Mark for Re-export",
"new": "New Bill",
"nobilllines": "",
"nobilllines": "No bills have been posted yet.",
"noneselected": "No bill selected.",
"onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
"printlabels": "Print Labels",
@@ -1508,7 +1508,7 @@
"addDocuments": "Add Job Documents",
"addNote": "Add Note",
"addtopartsqueue": "Add to Parts Queue",
"addtoproduction": "Add to Production",
"addtoproduction": "Add In Production Flag",
"addtoscoreboard": "Add to Scoreboard",
"allocate": "Allocate",
"autoallocate": "Auto Allocate",
@@ -1519,6 +1519,7 @@
"convert": "Convert",
"createiou": "Create IOU",
"deliver": "Deliver",
"deliver_quick": "Quick Deliver",
"dms": {
"addpayer": "Add Payer",
"createnewcustomer": "Create New Customer",
@@ -1540,6 +1541,7 @@
"generatecsi": "Generate CSI & Copy Link",
"gotojob": "Go to Job",
"intake": "Intake",
"intake_quick": "Quick Intake",
"manualnew": "Create New Job Manually",
"mark": "Mark",
"markasexported": "Mark as Exported",
@@ -1549,7 +1551,7 @@
"printCenter": "Print Center",
"recalculate": "Recalculate",
"reconcile": "Reconcile",
"removefromproduction": "Remove from Production",
"removefromproduction": "Remove In Production Flag",
"schedule": "Schedule",
"sendcsi": "Send CSI",
"sendpartspricechange": "Send Parts Price Change",

View File

@@ -1519,6 +1519,7 @@
"convert": "Convertir",
"createiou": "",
"deliver": "",
"deliver_quick": "",
"dms": {
"addpayer": "",
"createnewcustomer": "",
@@ -1540,6 +1541,7 @@
"generatecsi": "",
"gotojob": "",
"intake": "",
"intake_quick": "",
"manualnew": "",
"mark": "",
"markasexported": "",

View File

@@ -1519,6 +1519,7 @@
"convert": "Convertir",
"createiou": "",
"deliver": "",
"deliver_quick": "",
"dms": {
"addpayer": "",
"createnewcustomer": "",
@@ -1540,6 +1541,7 @@
"generatecsi": "",
"gotojob": "",
"intake": "",
"intake_quick": "",
"manualnew": "",
"mark": "",
"markasexported": "",