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

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