Antd 4.24.x compatibility updates => Menu fixes.

This commit is contained in:
Patrick Fic
2023-01-17 19:14:31 -08:00
parent 713b3f4f6d
commit ef290e79b1
24 changed files with 1118 additions and 1008 deletions

View File

@@ -12,7 +12,7 @@
"@sentry/tracing": "^7.28.1", "@sentry/tracing": "^7.28.1",
"@splitsoftware/splitio-react": "^1.6.0", "@splitsoftware/splitio-react": "^1.6.0",
"@tanem/react-nprogress": "^5.0.8", "@tanem/react-nprogress": "^5.0.8",
"antd": "^4.22.3", "antd": "4.24.5",
"apollo-link-logger": "^2.0.0", "apollo-link-logger": "^2.0.0",
"axios": "^1.2.2", "axios": "^1.2.2",
"craco-less": "^2.0.0", "craco-less": "^2.0.0",

View File

@@ -16,19 +16,15 @@ const mapDispatchToProps = (dispatch) => ({
}); });
export function ChatPresetsComponent({ bodyshop, setMessage, className }) { export function ChatPresetsComponent({ bodyshop, setMessage, className }) {
const menu = ( const menu = bodyshop.md_messaging_presets.map((i, idx) => ({
<Menu> label: i.label,
{bodyshop.md_messaging_presets.map((i, idx) => ( key: idx,
<Menu.Item onClick={() => setMessage(i.text)} key={idx}> onClick: () => setMessage(i.text),
{i.label} }));
</Menu.Item>
))}
</Menu>
);
return ( return (
<div className={className}> <div className={className}>
<Dropdown trigger={["click"]} overlay={menu}> <Dropdown trigger={["click"]} menu={{ items: menu }} placement="top">
<PlusCircleOutlined /> <PlusCircleOutlined />
</Dropdown> </Dropdown>
</div> </div>

View File

@@ -13,25 +13,14 @@ const mapStateToProps = createStructuredSelector({
export function ContractsRatesChangeButton({ disabled, form, bodyshop }) { export function ContractsRatesChangeButton({ disabled, form, bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const handleClick = ({ item, key, keyPath }) => { const menu = bodyshop.md_ccc_rates.map((rate, idx) => ({
const { label, ...rate } = item.props.value; onClick: () => form.setFieldsValue(rate),
form.setFieldsValue(rate); key: idx,
}; label: rate.label,
}));
const menu = (
<div>
<Menu onClick={handleClick}>
{bodyshop.md_ccc_rates.map((rate, idx) => (
<Menu.Item value={rate} key={idx}>
{rate.label}
</Menu.Item>
))}
</Menu>
</div>
);
return ( return (
<Dropdown overlay={menu} disabled={disabled}> <Dropdown menu={{ items: menu }} disabled={disabled}>
<a <a
className="ant-dropdown-link" className="ant-dropdown-link"
href=" #" href=" #"

View File

@@ -179,10 +179,14 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
</Button> </Button>
<Dropdown <Dropdown
trigger="click" trigger="click"
overlay={ menu={{
<Menu> items: [
<Menu.Item {
onClick={() => label: t(
"printcenter.courtesycarcontract.courtesy_car_inventory"
),
key: "cc_inv",
onClick: () =>
GenerateDocument( GenerateDocument(
{ {
name: TemplateList("courtesycar").courtesy_car_inventory name: TemplateList("courtesycar").courtesy_car_inventory
@@ -193,13 +197,10 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
}, },
{}, {},
"p" "p"
) ),
} },
> ],
{t("printcenter.courtesycarcontract.courtesy_car_inventory")} }}
</Menu.Item>
</Menu>
}
> >
<Button>{t("general.labels.print")}</Button> <Button>{t("general.labels.print")}</Button>
</Dropdown> </Dropdown>

View File

@@ -117,17 +117,15 @@ export function DashboardGridComponent({ currentUser, bodyshop }) {
); );
const existingLayoutKeys = state.items.map((i) => i.i); const existingLayoutKeys = state.items.map((i) => i.i);
const addComponentOverlay = ( const addComponentOverlay = (
<Menu onClick={handleAddComponent}> <Menu
{Object.keys(componentList).map((key) => ( onClick={handleAddComponent}
<Menu.Item items={Object.keys(componentList).map((key) => ({
key={key} key: key,
value={key} value: key,
disabled={existingLayoutKeys.includes(key)} disabled: existingLayoutKeys.includes(key),
> label: componentList[key].label,
{componentList[key].label} }))}
</Menu.Item> ></Menu>
))}
</Menu>
); );
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
@@ -283,8 +281,12 @@ const createDashboardQuery = (state) => {
monthly_sales: jobs(where: {_and: [ monthly_sales: jobs(where: {_and: [
{ voided: {_eq: false}}, { voided: {_eq: false}},
{date_invoiced: {_gte: "${moment() {date_invoiced: {_gte: "${moment()
.startOf("month").startOf('day').toISOString()}"}}, {date_invoiced: {_lte: "${moment() .startOf("month")
.endOf("month").endOf('day').toISOString()}"}}]}) { .startOf("day")
.toISOString()}"}}, {date_invoiced: {_lte: "${moment()
.endOf("month")
.endOf("day")
.toISOString()}"}}]}) {
id id
ro_number ro_number
date_invoiced date_invoiced

View File

@@ -277,14 +277,14 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
<div> <div>
{t("jobs.fields.dms.payer.controlnumber")}{" "} {t("jobs.fields.dms.payer.controlnumber")}{" "}
<Dropdown <Dropdown
overlay={ menu={{
<Menu> items:
{bodyshop.cdk_configuration.controllist && bodyshop.cdk_configuration.controllist &&
bodyshop.cdk_configuration.controllist.map( bodyshop.cdk_configuration.controllist.map(
(key, idx) => ( (key, idx) => ({
<Menu.Item key: idx,
key={idx} label: key.name,
onClick={() => { onClick: () => {
form.setFieldsValue({ form.setFieldsValue({
payers: form payers: form
.getFieldValue("payers") .getFieldValue("payers")
@@ -299,14 +299,10 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
}; };
}), }),
}); });
},
})
),
}} }}
>
{key.name}
</Menu.Item>
)
)}
</Menu>
}
> >
<a href=" #" onClick={(e) => e.preventDefault()}> <a href=" #" onClick={(e) => e.preventDefault()}>
<DownOutlined /> <DownOutlined />

View File

@@ -57,20 +57,23 @@ export function EmailOverlayComponent({
const menu = ( const menu = (
<div> <div>
<Menu onClick={handleClick}> <Menu
{bodyshop.employees onClick={handleClick}
items={[
...bodyshop.employees
.filter((e) => e.user_email) .filter((e) => e.user_email)
.map((e, idx) => ( .map((e, idx) => ({
<Menu.Item value={e.user_email} key={idx}> value: e.user_email,
{`${e.first_name} ${e.last_name}`} key: idx,
</Menu.Item> label: `${e.first_name} ${e.last_name}`,
))} })),
{bodyshop.md_to_emails.map((e, idx) => ( ...bodyshop.md_to_emails.map((e, idx) => ({
<Menu.Item value={e.emails} key={idx + "group"}> value: e.emails,
{e.label} key: idx + "group",
</Menu.Item> label: e.label,
))} })),
</Menu> ]}
/>
</div> </div>
); );

View File

@@ -106,304 +106,428 @@ function Header({
selectedKeys={[selectedHeader]} selectedKeys={[selectedHeader]}
onClick={handleMenuClick} onClick={handleMenuClick}
subMenuCloseDelay={0.3} subMenuCloseDelay={0.3}
> items={[
<Menu.Item key="home" icon={<HomeFilled />}> {
<Link to="/manage">{t("menus.header.home")}</Link> key: "home",
</Menu.Item> icon: <HomeFilled />,
<Menu.Item key="schedule" icon={<Icon component={FaCalendarAlt} />}> label: <Link to="/manage">{t("menus.header.home")}</Link>,
},
{
key: "schedule",
icon: <Icon component={FaCalendarAlt} />,
label: (
<Link to="/manage/schedule">{t("menus.header.schedule")}</Link> <Link to="/manage/schedule">{t("menus.header.schedule")}</Link>
</Menu.Item> ),
<Menu.SubMenu },
key="jobssubmenu" {
icon={<Icon component={FaCarCrash} />} key: "jobssubmenu",
title={t("menus.header.jobs")} icon: <Icon component={FaCarCrash} />,
> label: t("menus.header.jobs"),
<Menu.Item key="activejobs" icon={<FileFilled />}> children: [
{
key: "activejobs",
icon: <FileFilled />,
label: (
<Link to="/manage/jobs">{t("menus.header.activejobs")}</Link> <Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
</Menu.Item> ),
<Menu.Item key="readyjobs" icon={<CheckCircleOutlined />}> },
<Link to="/manage/jobs/ready">{t("menus.header.readyjobs")}</Link> {
</Menu.Item> key: "readyjobs",
<Menu.Item key="parts-queue" icon={<ToolFilled />}> icon: <CheckCircleOutlined />,
<Link to="/manage/partsqueue">{t("menus.header.parts-queue")}</Link> label: (
</Menu.Item> <Link to="/manage/jobs/ready">
<Menu.Item key="availablejobs" icon={<ImportOutlined />}> {t("menus.header.readyjobs")}
</Link>
),
},
{
key: "parts-queue",
icon: <ToolFilled />,
label: (
<Link to="/manage/partsqueue">
{t("menus.header.parts-queue")}
</Link>
),
},
{
key: "availablejobs",
icon: <ImportOutlined />,
label: (
<Link to="/manage/available"> <Link to="/manage/available">
{t("menus.header.availablejobs")} {t("menus.header.availablejobs")}
</Link> </Link>
</Menu.Item> ),
<Menu.Item key="newjob" icon={<FileAddOutlined />}> },
{
key: "newjob",
icon: <FileAddOutlined />,
label: (
<Link to="/manage/jobs/new">{t("menus.header.newjob")}</Link> <Link to="/manage/jobs/new">{t("menus.header.newjob")}</Link>
</Menu.Item> ),
<Menu.Divider key="div1" /> },
<Menu.Item key="alljobs" icon={<UnorderedListOutlined />}> { type: "divider" },
{
key: "alljobs",
icon: <UnorderedListOutlined />,
label: (
<Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link> <Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
</Menu.Item> ),
<Menu.Divider key="div2" /> },
<Menu.Item key="productionlist" icon={<ScheduleOutlined />}> { type: "divider" },
{
key: "productionlist",
icon: <ScheduleOutlined />,
label: (
<Link to="/manage/production/list"> <Link to="/manage/production/list">
{t("menus.header.productionlist")} {t("menus.header.productionlist")}
</Link> </Link>
</Menu.Item> ),
<Menu.Item key="productionboard" icon={<Icon component={BsKanban} />}> },
{
key: "productionboard",
icon: <Icon component={BsKanban} />,
label: (
<Link to="/manage/production/board"> <Link to="/manage/production/board">
{t("menus.header.productionboard")} {t("menus.header.productionboard")}
</Link> </Link>
</Menu.Item> ),
<Menu.Divider key="div3" /> },
<Menu.Item key="scoreboard" icon={<LineChartOutlined />}> {
<Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link> type: "divider",
</Menu.Item> },
</Menu.SubMenu> {
<Menu.SubMenu key: "scoreboard",
key="customers" icon: <LineChartOutlined />,
icon={<UserOutlined />} label: (
title={t("menus.header.customers")} <Link to="/manage/scoreboard">
> {t("menus.header.scoreboard")}
<Menu.Item key="owners" icon={<TeamOutlined />}> </Link>
),
},
],
},
{
key: "customers",
icon: <UserOutlined />,
label: t("menus.header.customers"),
children: [
{
key: "owners",
icon: <TeamOutlined />,
label: (
<Link to="/manage/owners">{t("menus.header.owners")}</Link> <Link to="/manage/owners">{t("menus.header.owners")}</Link>
</Menu.Item> ),
<Menu.Item key="vehicles" icon={<CarFilled />}> },
<Link to="/manage/vehicles">{t("menus.header.vehicles")}</Link> {
</Menu.Item> key: "vehicles",
</Menu.SubMenu> icon: <CarFilled />,
<Menu.SubMenu label: (
key="ccs" <Link to="/manage/vehicles">
icon={<CarFilled />} {t("menus.header.vehicles")}
title={t("menus.header.courtesycars")} </Link>
> ),
<Menu.Item key="courtesycarsall" icon={<CarFilled />}> },
],
},
{
key: "ccs",
icon: <CarFilled />,
label: t("menus.header.courtesycars"),
children: [
{
key: "courtesycarsall",
icon: <CarFilled />,
label: (
<Link to="/manage/courtesycars"> <Link to="/manage/courtesycars">
{t("menus.header.courtesycars-all")} {t("menus.header.courtesycars-all")}
</Link> </Link>
</Menu.Item> ),
<Menu.Item key="contracts" icon={<FileFilled />}> },
{
key: "contracts",
icon: <FileFilled />,
label: (
<Link to="/manage/courtesycars/contracts"> <Link to="/manage/courtesycars/contracts">
{t("menus.header.courtesycars-contracts")} {t("menus.header.courtesycars-contracts")}
</Link> </Link>
</Menu.Item> ),
<Menu.Item key="newcontract" icon={<FileAddFilled />}> },
{
key: "newcontract",
icon: <FileAddFilled />,
label: (
<Link to="/manage/courtesycars/contracts/new"> <Link to="/manage/courtesycars/contracts/new">
{t("menus.header.courtesycars-newcontract")} {t("menus.header.courtesycars-newcontract")}
</Link> </Link>
</Menu.Item> ),
</Menu.SubMenu> },
<Menu.SubMenu ],
key="accounting" },
icon={<DollarCircleFilled />} {
title={t("menus.header.accounting")} key: "accounting",
> icon: <DollarCircleFilled />,
<Menu.Item label: t("menus.header.accounting"),
key="bills" children: [
icon={<Icon component={FaFileInvoiceDollar} />} {
> key: "bills",
icon: <Icon component={FaFileInvoiceDollar} />,
label: (
<Link to="/manage/bills">{t("menus.header.bills")}</Link> <Link to="/manage/bills">{t("menus.header.bills")}</Link>
</Menu.Item> ),
<Menu.Item },
key="enterbills" {
icon={<Icon component={GiPayMoney} />} key: "enterbills",
onClick={() => { icon: <Icon component={GiPayMoney} />,
onClick: () => {
setBillEnterContext({ setBillEnterContext({
actions: {}, actions: {},
context: {}, context: {},
}); });
}} },
> label: t("menus.header.enterbills"),
{t("menus.header.enterbills")} },
</Menu.Item> ...(Simple_Inventory.treatment === "on"
{Simple_Inventory.treatment === "on" && ( ? [
<> {
<Menu.Divider key="div4" /> type: "divider",
<Menu.Item },
key="inventory" {
icon={<Icon component={FaFileInvoiceDollar} />} key: "inventory",
> icon: <Icon component={FaFileInvoiceDollar} />,
label: (
<Link to="/manage/inventory"> <Link to="/manage/inventory">
{t("menus.header.inventory")} {t("menus.header.inventory")}
</Link> </Link>
</Menu.Item> ),
</> },
)} ]
<Menu.Divider key="div7" /> : []),
<Menu.Item key="allpayments" icon={<BankFilled />}> { type: "divider" },
<Link to="/manage/payments">{t("menus.header.allpayments")}</Link> {
</Menu.Item> key: "allpayments",
<Menu.Item icon: <BankFilled />,
key="enterpayments" label: (
onClick={() => { <Link to="/manage/payments">
{t("menus.header.allpayments")}
</Link>
),
},
{
key: "enterpayments",
onClick: () => {
setPaymentContext({ setPaymentContext({
actions: {}, actions: {},
context: null, context: null,
}); });
}} },
icon={<Icon component={FaCreditCard} />} icon: <Icon component={FaCreditCard} />,
> label: t("menus.header.enterpayment"),
{t("menus.header.enterpayment")} },
</Menu.Item> { type: "divider" },
<Menu.Divider key="div5" /> {
<Menu.Item key="timetickets" icon={<FieldTimeOutlined />}> key: "timetickets",
icon: <FieldTimeOutlined />,
label: (
<Link to="/manage/timetickets"> <Link to="/manage/timetickets">
{t("menus.header.timetickets")} {t("menus.header.timetickets")}
</Link> </Link>
</Menu.Item> ),
<Menu.Item },
key="entertimetickets" {
icon={<Icon component={GiPlayerTime} />} key: "entertimetickets",
onClick={() => { icon: <Icon component={GiPlayerTime} />,
onClick: () => {
setTimeTicketContext({ setTimeTicketContext({
actions: {}, actions: {},
context: {}, context: {},
}); });
}} },
> label: t("menus.header.entertimeticket"),
{t("menus.header.entertimeticket")} },
</Menu.Item> { type: "divider" },
<Menu.Divider key="div6" /> {
<Menu.SubMenu key: "accountingexport",
key="accountingexport" icon: <ExportOutlined />,
title={t("menus.header.export")}
icon={<ExportOutlined />} label: t("menus.header.export"),
> children: [
<Menu.Item key="receivables"> {
key: "receivables",
label: (
<Link to="/manage/accounting/receivables"> <Link to="/manage/accounting/receivables">
{t("menus.header.accounting-receivables")} {t("menus.header.accounting-receivables")}
</Link> </Link>
</Menu.Item> ),
{(!( },
...(!(
(bodyshop && bodyshop.cdk_dealerid) || (bodyshop && bodyshop.cdk_dealerid) ||
(bodyshop && bodyshop.pbs_serialnumber) (bodyshop && bodyshop.pbs_serialnumber)
) || ) || DmsAp.treatment === "on"
DmsAp.treatment === "on") && ( ? [
<Menu.Item key="payables"> {
key: "payables",
label: (
<Link to="/manage/accounting/payables"> <Link to="/manage/accounting/payables">
{t("menus.header.accounting-payables")} {t("menus.header.accounting-payables")}
</Link> </Link>
</Menu.Item> ),
)} },
{!( ]
: []),
...(!(
(bodyshop && bodyshop.cdk_dealerid) || (bodyshop && bodyshop.cdk_dealerid) ||
(bodyshop && bodyshop.pbs_serialnumber) (bodyshop && bodyshop.pbs_serialnumber)
) && ( )
<Menu.Item key="payments"> ? [
{
key: "payments",
label: (
<Link to="/manage/accounting/payments"> <Link to="/manage/accounting/payments">
{t("menus.header.accounting-payments")} {t("menus.header.accounting-payments")}
</Link> </Link>
</Menu.Item> ),
)} },
<Menu.Item key="export-logs"> ]
: []),
{
key: "export-logs",
label: (
<Link to="/manage/accounting/exportlogs"> <Link to="/manage/accounting/exportlogs">
{t("menus.header.export-logs")} {t("menus.header.export-logs")}
</Link> </Link>
</Menu.Item> ),
</Menu.SubMenu> },
</Menu.SubMenu> ],
<Menu.Item key="phonebook" icon={<PhoneOutlined />}> },
],
},
{
key: "phonebook",
icon: <PhoneOutlined />,
label: (
<Link to="/manage/phonebook">{t("menus.header.phonebook")}</Link> <Link to="/manage/phonebook">{t("menus.header.phonebook")}</Link>
</Menu.Item> ),
<Menu.Item key="temporarydocs" icon={<PaperClipOutlined />}> },
{
key: "temporarydocs",
icon: <PaperClipOutlined />,
label: (
<Link to="/manage/temporarydocs"> <Link to="/manage/temporarydocs">
{t("menus.header.temporarydocs")} {t("menus.header.temporarydocs")}
</Link> </Link>
</Menu.Item> ),
<Menu.SubMenu },
key="shopsubmenu"
title={t("menus.header.shop")} {
icon={<SettingOutlined />} key: "shopsubmenu",
> icon: <SettingOutlined />,
<Menu.Item key="shop" icon={<Icon component={GiSettingsKnobs} />}> label: t("menus.header.shop"),
children: [
{
key: "shop",
icon: <Icon component={GiSettingsKnobs} />,
label: (
<Link to="/manage/shop">{t("menus.header.shop_config")}</Link> <Link to="/manage/shop">{t("menus.header.shop_config")}</Link>
</Menu.Item> ),
<Menu.Item key="dashboard" icon={<DashboardFilled />}> },
<Link to="/manage/dashboard">{t("menus.header.dashboard")}</Link> {
</Menu.Item> key: "dashboard",
<Menu.Item icon: <DashboardFilled />,
key="reportcenter" label: (
icon={<BarChartOutlined />} <Link to="/manage/dashboard">
onClick={() => { {t("menus.header.dashboard")}
</Link>
),
},
{
key: "reportcenter",
icon: <BarChartOutlined />,
onClick: () => {
setReportCenterContext({ setReportCenterContext({
actions: {}, actions: {},
context: {}, context: {},
}); });
}} },
> label: t("menus.header.reportcenter"),
{t("menus.header.reportcenter")} },
</Menu.Item> {
<Menu.Item key: "shop-vendors",
key="shop-vendors" icon: <Icon component={IoBusinessOutline} />,
icon={<Icon component={IoBusinessOutline} />} label: (
>
<Link to="/manage/shop/vendors"> <Link to="/manage/shop/vendors">
{t("menus.header.shop_vendors")} {t("menus.header.shop_vendors")}
</Link> </Link>
</Menu.Item> ),
<Menu.Item key="shop-csi" icon={<Icon component={RiSurveyLine} />}> },
<Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link> {
</Menu.Item> key: "shop-csi",
</Menu.SubMenu> icon: <Icon component={RiSurveyLine} />,
<Menu.SubMenu label: (
key="user" <Link to="/manage/shop/csi">
title={ {t("menus.header.shop_csi")}
</Link>
),
},
],
},
{
key: "user",
label:
currentUser.displayName || currentUser.displayName ||
currentUser.email || currentUser.email ||
t("general.labels.unknown") t("general.labels.unknown"),
} children: [
>
<Menu.Item key="signout" danger onClick={() => signOutStart()}>
{t("user.actions.signout")}
</Menu.Item>
<Menu.Item
key="help"
onClick={() => {
window.open("https://help.imex.online/", "_blank");
}}
icon={<Icon component={QuestionCircleFilled} />}
>
{t("menus.header.help")}
</Menu.Item>
<Menu.Item
key="rescue"
onClick={() => {
window.open("https://imexrescue.com/", "_blank");
}}
>
{t("menus.header.rescueme")}
</Menu.Item>
<Menu.Item key="shiftclock">
<Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
</Menu.Item>
<Menu.Item key="profile">
<Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
</Menu.Item>
{ {
// <Menu.SubMenu key: "signout",
// key="langselecter" danger: true,
// title={ onClick: () => signOutStart(),
// <span> label: t("user.actions.signout"),
// <GlobalOutlined /> },
// <span>{t("menus.currentuser.languageselector")}</span> {
// </span> key: "help",
// } icon: <Icon component={QuestionCircleFilled} />,
// > onClick: () => {
// <Menu.Item actiontype="lang-select" key="en-US"> window.open("https://help.imex.online/", "_blank");
// {t("general.languages.english")} },
// </Menu.Item> label: t("menus.header.help"),
// <Menu.Item actiontype="lang-select" key="fr-CA"> },
// {t("general.languages.french")} {
// </Menu.Item> key: "rescue",
// <Menu.Item actiontype="lang-select" key="es-MX"> onClick: () => {
// {t("general.languages.spanish")} window.open("https://imexrescue.com/", "_blank");
// </Menu.Item> },
// </Menu.SubMenu> label: t("menus.header.rescueme"),
} },
</Menu.SubMenu> {
<Menu.SubMenu key="recent" title={<ClockCircleFilled />}> key: "shiftclock",
{recentItems.map((i, idx) => ( label: (
<Menu.Item key={idx}> <Link to="/manage/shiftclock">
<Link to={i.url}>{i.label}</Link> {t("menus.header.shiftclock")}
</Menu.Item> </Link>
))} ),
</Menu.SubMenu> },
</Menu> {
key: "profile",
label: (
<Link to="/manage/profile">
{t("menus.currentuser.profile")}
</Link>
),
},
],
},
{
key: "recent",
label: <ClockCircleFilled />,
children: recentItems.map((i, idx) => ({
key: idx,
label: <Link to={i.url}>{i.label}</Link>,
})),
},
]}
></Menu>
</Layout.Header> </Layout.Header>
); );
} }

View File

@@ -37,18 +37,19 @@ export function JobAltTransportChange({ bodyshop, job }) {
}); });
} }
}; };
const menu = ( const menu = [
<Menu selectedKeys={[job && job.alt_transport]} onClick={onClick}> ...(bodyshop.appt_alt_transport &&
{bodyshop.appt_alt_transport && bodyshop.appt_alt_transport.map((alt) => ({
bodyshop.appt_alt_transport.map((alt) => ( key: alt,
<Menu.Item key={alt}>{alt}</Menu.Item> label: alt,
))} value: alt,
<Menu.Divider /> onClick,
<Menu.Item key={"null"}>{t("general.actions.clear")}</Menu.Item> }))),
</Menu> { type: "divider" },
); { key: "null", label: t("general.actions.clear"), onClick },
];
return ( return (
<Dropdown overlay={menu}> <Dropdown menu={{ items: menu }}>
<a href=" #" onClick={(e) => e.preventDefault()}> <a href=" #" onClick={(e) => e.preventDefault()}>
<DownOutlined /> <DownOutlined />
</a> </a>

View File

@@ -44,21 +44,23 @@ export function ScheduleEventColor({ bodyshop, event }) {
bodyshop.appt_colors.filter((color) => color.color.hex === event.color)[0] bodyshop.appt_colors.filter((color) => color.color.hex === event.color)[0]
?.label; ?.label;
const menu = ( const menu = {
<Menu selectedKeys={[event.color]} onClick={onClick}> items: [
{bodyshop.appt_colors && ...(bodyshop.appt_colors &&
bodyshop.appt_colors.map((color) => ( bodyshop.appt_colors.map((color) => ({
<Menu.Item style={{ color: color.color.hex }} key={color.color.hex}> style: { color: color.color.hex },
{color.label} key: color.color.hex,
</Menu.Item> label: color.label,
))} }))),
<Menu.Divider /> { type: "divider" },
<Menu.Item key={"null"}>{t("general.actions.clear")}</Menu.Item> { key: "null", value: t("general.actions.clear") },
</Menu> ],
); selectedKeys: [event.color],
onClick: onClick,
};
return ( return (
<Dropdown overlay={menu}> <Dropdown menu={menu}>
<a href=" #" onClick={(e) => e.preventDefault()}> <a href=" #" onClick={(e) => e.preventDefault()}>
{selectedColor} {selectedColor}
<DownOutlined /> <DownOutlined />

View File

@@ -149,10 +149,12 @@ export function ScheduleEventComponent({
) : null} ) : null}
{event.job ? ( {event.job ? (
<Dropdown <Dropdown
overlay={ menu={{
<Menu> items: [
<Menu.Item {
onClick={() => { disabled: event.arrived,
label: t("general.labels.email"),
onClick: () => {
const Template = TemplateList("job").appointment_reminder; const Template = TemplateList("job").appointment_reminder;
GenerateDocument( GenerateDocument(
{ {
@@ -166,13 +168,12 @@ export function ScheduleEventComponent({
"e", "e",
event.job && event.job.id event.job && event.job.id
); );
}} },
disabled={event.arrived} },
> {
{t("general.labels.email")} label: t("general.labels.sms"),
</Menu.Item> disabled: event.arrived || !bodyshop.messagingservicesid,
<Menu.Item onClick: () => {
onClick={() => {
const p = parsePhoneNumber(event.job.ownr_ph1, "CA"); const p = parsePhoneNumber(event.job.ownr_ph1, "CA");
if (p && p.isValid()) { if (p && p.isValid()) {
openChatByPhone({ openChatByPhone({
@@ -192,13 +193,10 @@ export function ScheduleEventComponent({
message: t("messaging.error.invalidphone"), message: t("messaging.error.invalidphone"),
}); });
} }
},
},
],
}} }}
disabled={event.arrived || !bodyshop.messagingservicesid}
>
{t("general.labels.sms")}
</Menu.Item>
</Menu>
}
> >
<Button>{t("appointments.actions.sendreminder")}</Button> <Button>{t("appointments.actions.sendreminder")}</Button>
</Dropdown> </Dropdown>
@@ -249,7 +247,7 @@ export function ScheduleEventComponent({
const RegularEvent = event.isintake ? ( const RegularEvent = event.isintake ? (
<Space <Space
wrap wrap
size='small' size="small"
style={{ style={{
backgroundColor: backgroundColor:
event.color && event.color.hex ? event.color.hex : event.color, event.color && event.color.hex ? event.color.hex : event.color,

View File

@@ -103,7 +103,9 @@ export function JobLinesComponent({
fixed: "left", fixed: "left",
key: "line_desc", key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc), sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
onCell: (record) => ({ className: record.manual_line && "job-line-manual" }), onCell: (record) => ({
className: record.manual_line && "job-line-manual",
}),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order, state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
ellipsis: true, ellipsis: true,
@@ -405,16 +407,17 @@ export function JobLinesComponent({
} }
}; };
const markMenu = ( const markMenu = {
<Menu onClick={handleMark}> onClick: handleMark,
<Menu.Item key="PAA">{t("joblines.fields.part_types.PAA")}</Menu.Item> items: [
<Menu.Item key="PAN">{t("joblines.fields.part_types.PAN")}</Menu.Item> { key: "PAA", label: t("joblines.fields.part_types.PAA") },
<Menu.Item key="PAL">{t("joblines.fields.part_types.PAL")}</Menu.Item> { key: "PAN", label: t("joblines.fields.part_types.PAN") },
<Menu.Item key="PAS">{t("joblines.fields.part_types.PAS")}</Menu.Item> { key: "PAL", label: t("joblines.fields.part_types.PAL") },
<Menu.Divider /> { key: "PAS", label: t("joblines.fields.part_types.PAS") },
<Menu.Item key="clear">{t("general.labels.clear")}</Menu.Item> { type: "divider" },
</Menu> { key: "clear", label: t("general.labels.clear") },
); ],
};
return ( return (
<div> <div>
@@ -544,7 +547,7 @@ export function JobLinesComponent({
> >
<FilterFilled /> {t("jobs.actions.filterpartsonly")} <FilterFilled /> {t("jobs.actions.filterpartsonly")}
</Button> </Button>
<Dropdown overlay={markMenu} trigger={["click"]}> <Dropdown menu={markMenu} trigger={["click"]}>
<Button>{t("jobs.actions.mark")}</Button> <Button>{t("jobs.actions.mark")}</Button>
</Dropdown> </Dropdown>
<Button <Button

View File

@@ -21,19 +21,17 @@ export function JoblinePresetButton({ bodyshop, form }) {
form.setFieldsValue(item); form.setFieldsValue(item);
}; };
const menu = ( const menu = {
<Menu> items: bodyshop.md_jobline_presets.map((i, idx) => ({
{bodyshop.md_jobline_presets.map((i, idx) => ( onClick: () => handleSelect(i),
<Menu.Item onClick={() => handleSelect(i)} key={idx}> key: idx,
{i.label} label: i.label,
</Menu.Item> })),
))} };
</Menu>
);
return ( return (
<div> <div>
<Dropdown trigger={["click"]} overlay={menu}> <Dropdown trigger={["click"]} menu={menu}>
<a <a
className="ant-dropdown-link" className="ant-dropdown-link"
href="# " href="# "

View File

@@ -40,20 +40,18 @@ export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) {
}); });
}; };
const statusmenu = ( const statusmenu = {
<Menu onClick: (e) => {
onClick={(e) => {
updateJobStatus(e.key); updateJobStatus(e.key);
}} },
> items: bodyshop.md_ro_statuses.statuses.map((item) => ({
{bodyshop.md_ro_statuses.statuses.map((item) => ( key: item,
<Menu.Item key={item}>{item}</Menu.Item> label: item,
))} })),
</Menu> };
);
return ( return (
<Dropdown overlay={statusmenu} trigger={["click"]} key="changestatus"> <Dropdown menu={statusmenu} trigger={["click"]} key="changestatus">
<Button shape="round"> <Button shape="round">
<span>{job.status}</span> <span>{job.status}</span>

View File

@@ -81,29 +81,30 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
} }
}, [job, setAvailableStatuses, bodyshop]); }, [job, setAvailableStatuses, bodyshop]);
const statusmenu = ( const statusmenu = {
<Menu onClick: (e) => {
onClick={(e) => {
updateJobStatus(e.key); updateJobStatus(e.key);
}} },
> items: [
{availableStatuses.map((item) => ( ...availableStatuses.map((item) => ({
<Menu.Item key={item}>{item}</Menu.Item> key: item,
))} label: item,
{job.converted && ( })),
<> ...(job.converted && [
<Menu.Divider /> {
{otherStages.map((item, idx) => ( type: "divider",
<Menu.Item key={item}>{item}</Menu.Item> },
))} ...otherStages.map((item, idx) => ({
</> key: item,
)} label: item,
</Menu> })),
); ]),
],
};
return ( return (
<Dropdown <Dropdown
overlay={statusmenu} menu={statusmenu}
trigger={["click"]} trigger={["click"]}
key="changestatus" key="changestatus"
disabled={jobRO || !job.converted} disabled={jobRO || !job.converted}

View File

@@ -62,18 +62,17 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
); );
}; };
const overlay = (bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && ( const overlay = {
<Menu onClick={handleMenuClick}> onClick: handleMenuClick,
{bodyshop.md_responsibility_centers.dms_defaults.map((mapping) => ( items: bodyshop.md_responsibility_centers.dms_defaults.map((mapping) => ({
<Menu.Item disabled={disabled} key={mapping.name}> label: mapping.name,
{mapping.name} key: mapping.name,
</Menu.Item> disabled: disabled,
))} })),
</Menu> };
);
return bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber ? ( return bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber ? (
<Dropdown overlay={overlay}> <Dropdown menu={overlay}>
<Button disabled={disabled}>{t("jobs.actions.dmsautoallocate")}</Button> <Button disabled={disabled}>{t("jobs.actions.dmsautoallocate")}</Button>
</Dropdown> </Dropdown>
) : ( ) : (

View File

@@ -15,20 +15,17 @@ export function JobsDetailChangeEstimator({ disabled, form, bodyshop }) {
form.setFieldsValue(est); form.setFieldsValue(est);
}; };
const menu = ( const menu = {
<div> onClick: handleClick,
<Menu onClick={handleClick}> items: bodyshop.md_estimators.map((est, idx) => ({
{bodyshop.md_estimators.map((est, idx) => ( value: est,
<Menu.Item value={est} key={idx}> key: idx,
{`${est.est_ct_fn} ${est.est_ct_ln}`} label: `${est.est_ct_fn || ""} ${est.est_ct_ln || ""}`.trim(),
</Menu.Item> })),
))} };
</Menu>
</div>
);
return ( return (
<Dropdown overlay={menu} disabled={disabled}> <Dropdown menu={menu} disabled={disabled}>
<a <a
className="ant-dropdown-link" className="ant-dropdown-link"
href=" #" href=" #"

View File

@@ -15,23 +15,21 @@ export function JobsDetailChangeFilehandler({ disabled, form, bodyshop }) {
form.setFieldsValue(est); form.setFieldsValue(est);
}; };
const menu = ( const menu = {
<Menu onClick: handleClick,
onClick={handleClick} style: {
style={{
columnCount: Math.floor(bodyshop.md_filehandlers.length / 10) + 1, columnCount: Math.floor(bodyshop.md_filehandlers.length / 10) + 1,
}} },
> items: bodyshop.md_filehandlers.map((est, idx) => ({
{bodyshop.md_filehandlers.map((est, idx) => ( value: est,
<Menu.Item value={est} key={idx} style={{ breakInside: "avoid" }}> key: idx,
{`${est.ins_ct_fn} ${est.ins_ct_ln}`} style: { breakInside: "avoid" },
</Menu.Item> label: `${est.ins_ct_fn || ""} ${est.ins_ct_ln || ""}`.trim(),
))} })),
</Menu> };
);
return ( return (
<Dropdown overlay={menu} disabled={disabled}> <Dropdown menu={menu} disabled={disabled}>
<a <a
className="ant-dropdown-link" className="ant-dropdown-link"
href=" #" href=" #"

View File

@@ -4,7 +4,6 @@ import {
Card, Card,
Form, Form,
Input, Input,
Menu,
notification, notification,
Popover, Popover,
Select, Select,
@@ -13,25 +12,11 @@ import {
import moment from "moment"; import moment from "moment";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_MANUAL_APPT } from "../../graphql/appointments.queries"; import { INSERT_MANUAL_APPT } from "../../graphql/appointments.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component"; import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
const mapStateToProps = createStructuredSelector({ export default function JobsDetailHeaderAddEvent({ bodyshop, jobid }) {
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDetailHeaderAddEvent);
export function JobsDetailHeaderAddEvent({ bodyshop, jobid, ...props }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [insertAppointment] = useMutation(INSERT_MANUAL_APPT); const [insertAppointment] = useMutation(INSERT_MANUAL_APPT);
@@ -153,11 +138,12 @@ export function JobsDetailHeaderAddEvent({ bodyshop, jobid, ...props }) {
setVisibility(true); setVisibility(true);
}; };
return ( return {
<Popover content={overlay} visible={visibility}> key: "addmanualevent",
<Menu.Item {...props} onClick={handleClick}> label: (
<Popover content={overlay} open={visibility} onClick={handleClick}>
{t("appointments.labels.manualevent")} {t("appointments.labels.manualevent")}
</Menu.Item>
</Popover> </Popover>
); ),
};
} }

View File

@@ -105,13 +105,14 @@ export function JobsDetailHeaderActions({
}); });
}; };
const statusmenu = ( const statusmenu = {
<Menu key="popovermenu"> items: [
<Menu.Item {
disabled={!jobInPreProduction || !job.converted || jobRO} key: "schedule",
onClick={() => { disabled: !jobInPreProduction || !job.converted || jobRO,
label: t("jobs.actions.schedule"),
onClick: () => {
logImEXEvent("job_header_schedule"); logImEXEvent("job_header_schedule");
setScheduleContext({ setScheduleContext({
actions: { refetch: refetch }, actions: { refetch: refetch },
context: { context: {
@@ -120,13 +121,12 @@ export function JobsDetailHeaderActions({
alt_transport: job.alt_transport, alt_transport: job.alt_transport,
}, },
}); });
}} },
> },
{t("jobs.actions.schedule")} {
</Menu.Item> key: "cancelappts",
<Menu.Item disabled: job.status !== bodyshop.md_ro_statuses.default_scheduled,
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled} label: (
>
<Popconfirm <Popconfirm
title={t("general.labels.areyousure")} title={t("general.labels.areyousure")}
okText="Yes" okText="Yes"
@@ -156,16 +156,17 @@ export function JobsDetailHeaderActions({
> >
{t("menus.jobsactions.cancelallappointments")} {t("menus.jobsactions.cancelallappointments")}
</Popconfirm> </Popconfirm>
</Menu.Item> ),
<Menu.Item },
disabled={ {
key: "intake",
disabled:
!!job.intakechecklist || !!job.intakechecklist ||
!jobInPreProduction || !jobInPreProduction ||
!job.converted || !job.converted ||
jobRO jobRO,
} label:
> !!job.intakechecklist ||
{!!job.intakechecklist ||
!jobInPreProduction || !jobInPreProduction ||
!job.converted || !job.converted ||
jobRO ? ( jobRO ? (
@@ -174,54 +175,60 @@ export function JobsDetailHeaderActions({
<Link to={`/manage/jobs/${job.id}/intake`}> <Link to={`/manage/jobs/${job.id}/intake`}>
{t("jobs.actions.intake")} {t("jobs.actions.intake")}
</Link> </Link>
)} ),
</Menu.Item> },
<Menu.Item disabled={!jobInProduction || jobRO}> {
{!jobInProduction ? ( disabled: !jobInProduction || jobRO,
key: "deliver",
label: !jobInProduction ? (
t("jobs.actions.deliver") t("jobs.actions.deliver")
) : ( ) : (
<Link to={`/manage/jobs/${job.id}/deliver`}> <Link to={`/manage/jobs/${job.id}/deliver`}>
{t("jobs.actions.deliver")} {t("jobs.actions.deliver")}
</Link> </Link>
)} ),
</Menu.Item> },
<Menu.Item disabled={!job.converted}> {
key: "viewchecklist",
disabled: !job.converted,
label: (
<Link to={`/manage/jobs/${job.id}/checklist`}> <Link to={`/manage/jobs/${job.id}/checklist`}>
{t("jobs.actions.viewchecklist")} {t("jobs.actions.viewchecklist")}
</Link> </Link>
</Menu.Item> ),
<Menu.Item },
key="entertimetickets" {
disabled={ key: "entertimetickets",
disabled:
!job.converted || !job.converted ||
(!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced) (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
} onClick: () => {
onClick={() => {
logImEXEvent("job_header_enter_time_ticekts"); logImEXEvent("job_header_enter_time_ticekts");
setTimeTicketContext({ setTimeTicketContext({
actions: {}, actions: {},
context: { jobId: job.id }, context: { jobId: job.id },
}); });
}} },
> label: t("timetickets.actions.enter"),
{t("timetickets.actions.enter")} },
</Menu.Item> {
<Menu.Item key: "enterpayments",
key="enterpayments" disabled: !job.converted,
disabled={!job.converted} label: t("menus.header.enterpayment"),
onClick={() => { onClick: () => {
logImEXEvent("job_header_enter_payment"); logImEXEvent("job_header_enter_payment");
setPaymentContext({ setPaymentContext({
actions: {}, actions: {},
context: { jobid: job.id }, context: { jobid: job.id },
}); });
}} },
> },
{t("menus.header.enterpayment")} {
</Menu.Item> key: "cccontract",
<Menu.Item key="cccontract" disabled={jobRO || !job.converted}> disabled: jobRO || !job.converted,
label: (
<Link <Link
to={{ to={{
pathname: "/manage/courtesycars/contracts/new", pathname: "/manage/courtesycars/contracts/new",
@@ -230,36 +237,47 @@ export function JobsDetailHeaderActions({
> >
{t("menus.jobsactions.newcccontract")} {t("menus.jobsactions.newcccontract")}
</Link> </Link>
</Menu.Item> ),
{job.inproduction ? ( },
<Menu.Item ...(job.inproduction
key="addtoproduction" ? [
disabled={!job.converted} {
onClick={() => AddToProduction(client, job.id, refetch, true)} key: "removetoproduction",
> disabled: !job.converted,
{t("jobs.actions.removefromproduction")} onClick: () => AddToProduction(client, job.id, refetch, true),
</Menu.Item> label: t("jobs.actions.removefromproduction"),
) : ( },
<Menu.Item ]
key="addtoproduction" : [
disabled={!job.converted} {
onClick={() => AddToProduction(client, job.id, refetch)} key: "addtoproduction",
> disabled: !job.converted,
{t("jobs.actions.addtoproduction")} onClick: () => AddToProduction(client, job.id, refetch),
</Menu.Item> label: t("jobs.actions.addtoproduction"),
)} },
<Menu.Item key="togglesuspend" onClick={handleSuspend}> ]),
{job.suspended {
key: "togglesuspend",
onClick: handleSuspend,
label: job.suspended
? t("production.actions.unsuspend") ? t("production.actions.unsuspend")
: t("production.actions.suspend")} : t("production.actions.suspend"),
</Menu.Item> },
<Menu.Item key="toggleAlert" onClick={handleAlertToggle}> {
{job.production_vars && job.production_vars.alert key: "toggleAlert",
onClick: handleAlertToggle,
label:
job.production_vars && job.production_vars.alert
? t("production.labels.alertoff") ? t("production.labels.alertoff")
: t("production.labels.alerton")} : t("production.labels.alerton"),
</Menu.Item> },
<Menu.SubMenu key="dupe" title={t("menus.jobsactions.duplicate")}> {
<Menu.Item> key: "dupe",
label: t("menus.jobsactions.duplicate"),
children: [
{
key: "dupewithlines",
label: (
<Popconfirm <Popconfirm
title={t("jobs.labels.duplicateconfirm")} title={t("jobs.labels.duplicateconfirm")}
okText="Yes" okText="Yes"
@@ -269,7 +287,10 @@ export function JobsDetailHeaderActions({
DuplicateJob( DuplicateJob(
client, client,
job.id, job.id,
{ defaultOpenStatus: bodyshop.md_ro_statuses.default_imported }, {
defaultOpenStatus:
bodyshop.md_ro_statuses.default_imported,
},
(newJobId) => { (newJobId) => {
history.push(`/manage/jobs/${newJobId}`); history.push(`/manage/jobs/${newJobId}`);
notification["success"]({ notification["success"]({
@@ -283,8 +304,11 @@ export function JobsDetailHeaderActions({
> >
{t("menus.jobsactions.duplicate")} {t("menus.jobsactions.duplicate")}
</Popconfirm> </Popconfirm>
</Menu.Item> ),
<Menu.Item> },
{
key: "dupewithoutlines",
label: (
<Popconfirm <Popconfirm
title={t("jobs.labels.duplicateconfirm")} title={t("jobs.labels.duplicateconfirm")}
okText="Yes" okText="Yes"
@@ -294,7 +318,10 @@ export function JobsDetailHeaderActions({
DuplicateJob( DuplicateJob(
client, client,
job.id, job.id,
{ defaultOpenStatus: bodyshop.md_ro_statuses.default_imported }, {
defaultOpenStatus:
bodyshop.md_ro_statuses.default_imported,
},
(newJobId) => { (newJobId) => {
history.push(`/manage/jobs/${newJobId}`); history.push(`/manage/jobs/${newJobId}`);
notification["success"]({ notification["success"]({
@@ -307,13 +334,15 @@ export function JobsDetailHeaderActions({
> >
{t("menus.jobsactions.duplicatenolines")} {t("menus.jobsactions.duplicatenolines")}
</Popconfirm> </Popconfirm>
</Menu.Item> ),
</Menu.SubMenu> },
],
<Menu.Item },
key="postbills" {
disabled={!job.converted} key: "postbills",
onClick={() => { disabled: !job.converted,
label: t("jobs.actions.postbills"),
onClick: () => {
logImEXEvent("job_header_enter_bills"); logImEXEvent("job_header_enter_bills");
setBillEnterContext({ setBillEnterContext({
@@ -322,14 +351,13 @@ export function JobsDetailHeaderActions({
job: job, job: job,
}, },
}); });
}} },
> },
{t("jobs.actions.postbills")} {
</Menu.Item> key: "addtopartsqueue",
<Menu.Item disabled: !job.converted || !jobInProduction || jobRO,
key="addtopartsqueue" label: t("jobs.actions.addtopartsqueue"),
disabled={!job.converted || !jobInProduction || jobRO} onClick: async () => {
onClick={async () => {
const result = await updateJob({ const result = await updateJob({
variables: { variables: {
jobId: job.id, jobId: job.id,
@@ -348,12 +376,12 @@ export function JobsDetailHeaderActions({
}), }),
}); });
} }
}} },
> },
{t("jobs.actions.addtopartsqueue")} {
</Menu.Item> disabled: !jobInPostProduction,
<Menu.Item disabled={!jobInPostProduction} key="closejob"> key: "closejob",
{!jobInPostProduction ? ( label: !jobInPostProduction ? (
t("menus.jobsactions.closejob") t("menus.jobsactions.closejob")
) : ( ) : (
<Link <Link
@@ -363,9 +391,11 @@ export function JobsDetailHeaderActions({
> >
{t("menus.jobsactions.closejob")} {t("menus.jobsactions.closejob")}
</Link> </Link>
)} ),
</Menu.Item> },
<Menu.Item key="admin"> {
key: "admin",
label: (
<Link <Link
to={{ to={{
pathname: `/manage/jobs/${job.id}/admin`, pathname: `/manage/jobs/${job.id}/admin`,
@@ -373,13 +403,15 @@ export function JobsDetailHeaderActions({
> >
{t("menus.jobsactions.admin")} {t("menus.jobsactions.admin")}
</Link> </Link>
</Menu.Item> ),
<JobsDetailHeaderActionsExportcustdataComponent job={job} /> },
<JobsDetaiLheaderCsi job={job} /> JobsDetailHeaderActionsExportcustdataComponent({ bodyshop, job }),
<Menu.Item JobsDetaiLheaderCsi({ job, bodyshop }),
key="jobcosting" {
disabled={!job.converted} key: "jobcosting",
onClick={() => { label: t("jobs.labels.jobcosting"),
disabled: !job.converted,
onClick: () => {
logImEXEvent("job_header_job_costing"); logImEXEvent("job_header_job_costing");
setJobCostingContext({ setJobCostingContext({
actions: { refetch: refetch }, actions: { refetch: refetch },
@@ -387,12 +419,13 @@ export function JobsDetailHeaderActions({
jobId: job.id, jobId: job.id,
}, },
}); });
}} },
> },
{t("jobs.labels.jobcosting")} ...(job && !job.converted
</Menu.Item> ? [
{job && !job.converted && ( {
<Menu.Item> key: "deletejob",
label: (
<Popconfirm <Popconfirm
title={t("jobs.labels.deleteconfirm")} title={t("jobs.labels.deleteconfirm")}
okText={t("general.labels.yes")} okText={t("general.labels.yes")}
@@ -400,7 +433,9 @@ export function JobsDetailHeaderActions({
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
onConfirm={async () => { onConfirm={async () => {
//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"]({
@@ -420,11 +455,17 @@ export function JobsDetailHeaderActions({
> >
{t("menus.jobsactions.deletejob")} {t("menus.jobsactions.deletejob")}
</Popconfirm> </Popconfirm>
</Menu.Item> ),
)} },
<JobsDetailHeaderActionsAddevent jobid={job.id} /> ]
{!jobRO && job.converted && ( : []),
<Menu.Item> ///////HEADER ADD EVENT ITEM
JobsDetailHeaderActionsAddevent({ jobid: job.id, bodyshop }),
...(!jobRO && job.converted
? [
{
key: "voidjob",
label: (
<Popconfirm <Popconfirm
title={t("jobs.labels.voidjob")} title={t("jobs.labels.voidjob")}
okText="Yes" okText="Yes"
@@ -471,12 +512,15 @@ export function JobsDetailHeaderActions({
> >
{t("menus.jobsactions.void")} {t("menus.jobsactions.void")}
</Popconfirm> </Popconfirm>
</Menu.Item> ),
)} },
</Menu> ]
); : []),
],
};
return ( return (
<Dropdown overlay={statusmenu} trigger={["click"]} key="changestatus"> <Dropdown menu={statusmenu} trigger={["click"]} key="changestatus">
<Button> <Button>
<span>{t("general.labels.actions")}</span> <span>{t("general.labels.actions")}</span>

View File

@@ -1,11 +1,8 @@
import { useApolloClient, useMutation } from "@apollo/client"; import { useApolloClient, useMutation } from "@apollo/client";
import { Menu, notification } from "antd"; import { notification } from "antd";
import parsePhoneNumber from "libphonenumber-js"; import parsePhoneNumber from "libphonenumber-js";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { import {
GET_CURRENT_QUESTIONSET_ID, GET_CURRENT_QUESTIONSET_ID,
@@ -16,30 +13,14 @@ import {
openChatByPhone, openChatByPhone,
setMessage, setMessage,
} from "../../redux/messaging/messaging.actions"; } from "../../redux/messaging/messaging.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { store } from "../../redux/store";
import i18n from "../../translations/i18n";
import { DateTimeFormatter } from "../../utils/DateFormatter"; import { DateTimeFormatter } from "../../utils/DateFormatter";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
const mapStateToProps = createStructuredSelector({ export default function JobsDetailHeaderCsi({ bodyshop, job }) {
//currentUser: selectCurrentUser' const { t } = i18n;
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
setMessage: (text) => dispatch(setMessage(text)),
});
export function JobsDetailHeaderCsi({
setEmailOptions,
bodyshop,
job,
openChatByPhone,
setMessage,
...props
}) {
const { t } = useTranslation();
const [insertCsi] = useMutation(INSERT_CSI); const [insertCsi] = useMutation(INSERT_CSI);
const client = useApolloClient(); const client = useApolloClient();
@@ -97,6 +78,7 @@ export function JobsDetailHeaderCsi({
return; return;
} }
if (e.key === "email") if (e.key === "email")
store.dispatch(
setEmailOptions({ setEmailOptions({
jobid: job.id, jobid: job.id,
messageOptions: { messageOptions: {
@@ -109,17 +91,22 @@ export function JobsDetailHeaderCsi({
id: result.data.insert_csi.returning[0].id, id: result.data.insert_csi.returning[0].id,
}, },
}, },
}); })
);
if (e.key === "text") { if (e.key === "text") {
const p = parsePhoneNumber(job.ownr_ph1, "CA"); const p = parsePhoneNumber(job.ownr_ph1, "CA");
if (p && p.isValid()) { if (p && p.isValid()) {
store.dispatch(
openChatByPhone({ openChatByPhone({
phone_num: p.formatInternational(), phone_num: p.formatInternational(),
jobid: job.id, jobid: job.id,
}); })
);
store.dispatch(
setMessage( setMessage(
`${window.location.protocol}//${window.location.host}/csi/${result.data.insert_csi.returning[0].id}` `${window.location.protocol}//${window.location.host}/csi/${result.data.insert_csi.returning[0].id}`
)
); );
} else { } else {
notification["error"]({ notification["error"]({
@@ -140,6 +127,7 @@ export function JobsDetailHeaderCsi({
} }
} else { } else {
if (e.key === "email") if (e.key === "email")
store.dispatch(
setEmailOptions({ setEmailOptions({
jobid: job.id, jobid: job.id,
messageOptions: { messageOptions: {
@@ -152,17 +140,22 @@ export function JobsDetailHeaderCsi({
id: job.csiinvites[0].id, id: job.csiinvites[0].id,
}, },
}, },
}); })
);
if (e.key === "text") { if (e.key === "text") {
const p = parsePhoneNumber(job.ownr_ph1, "CA"); const p = parsePhoneNumber(job.ownr_ph1, "CA");
if (p && p.isValid()) { if (p && p.isValid()) {
store.dispatch(
openChatByPhone({ openChatByPhone({
phone_num: p.formatInternational(), phone_num: p.formatInternational(),
jobid: job.id, jobid: job.id,
}); })
);
store.dispatch(
setMessage( setMessage(
`${window.location.protocol}//${window.location.host}/csi/${job.csiinvites[0].id}` `${window.location.protocol}//${window.location.host}/csi/${job.csiinvites[0].id}`
)
); );
} else { } else {
notification["error"]({ notification["error"]({
@@ -180,61 +173,51 @@ export function JobsDetailHeaderCsi({
} }
}; };
if (!HasFeatureAccess({ featureName: "csi", bodyshop })) return <></>; if (!HasFeatureAccess({ featureName: "csi", bodyshop })) return {};
return ( return {
<Menu.SubMenu key: "sendcsi",
key="sendcsi" label: t("jobs.actions.sendcsi"),
title={t("jobs.actions.sendcsi")} children: [
disabled={!job.converted} {
{...props} key: "email",
> onClick: handleCreateCsi,
<Menu.Item disabled: !!!job.ownr_ea,
onClick={handleCreateCsi} label: t("general.labels.email"),
key="email" },
disabled={!!!job.ownr_ea} {
> onClick: handleCreateCsi,
{t("general.labels.email")} key: "text",
</Menu.Item> disabled: !!!job.ownr_ph1,
<Menu.Item label: t("general.labels.text"),
onClick={handleCreateCsi} },
key="text" {
disabled={!!!job.ownr_ph1} key: "generate",
> onClick: handleCreateCsi,
{t("general.labels.text")} disabled: job.csiinvites && job.csiinvites.length > 0,
</Menu.Item> label: t("jobs.actions.generatecsi"),
<Menu.Item },
onClick={handleCreateCsi} { type: "divider" },
key="generate" ...job.csiinvites.map((item, idx) => {
disabled={job.csiinvites && job.csiinvites.length > 0} return item.completedon
> ? {
{t("jobs.actions.generatecsi")} key: idx,
</Menu.Item> label: (
<Menu.Divider />
{job.csiinvites.map((item, idx) => {
return item.completedon ? (
<Menu.Item key={idx}>
<Link to={`/manage/shop/csi?responseid=${item.id}`}> <Link to={`/manage/shop/csi?responseid=${item.id}`}>
<DateTimeFormatter>{item.completedon}</DateTimeFormatter> <DateTimeFormatter>{item.completedon}</DateTimeFormatter>
</Link> </Link>
</Menu.Item> ),
) : ( }
<Menu.Item : {
key={idx} key: idx,
onClick={() => { onClick: () => {
navigator.clipboard.writeText( navigator.clipboard.writeText(
`${window.location.protocol}//${window.location.host}/csi/${item.id}` `${window.location.protocol}//${window.location.host}/csi/${item.id}`
); );
}} },
> label: t("general.actions.copylink"),
{t("general.actions.copylink")} };
</Menu.Item> }),
); ],
})} };
</Menu.SubMenu>
);
} }
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDetailHeaderCsi);

View File

@@ -1,23 +1,13 @@
import { Menu, notification } from "antd"; import { notification } from "antd";
import axios from "axios"; import axios from "axios";
import React from "react"; import i18next from "i18next";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth, logImEXEvent } from "../../firebase/firebase.utils"; import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({ export default function JobsDetailHeaderActionexportCustomerData({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({});
export function JobsDetailHeaderActionexportCustomerData({
bodyshop, bodyshop,
job, job,
...props
}) { }) {
const { t } = useTranslation(); const { t } = i18next;
const handleExportCustData = async (e) => { const handleExportCustData = async (e) => {
logImEXEvent("job_export_cust_data"); logImEXEvent("job_export_cust_data");
@@ -100,18 +90,20 @@ export function JobsDetailHeaderActionexportCustomerData({
} }
}; };
return ( return {
<Menu.Item onClick: handleExportCustData,
{...props} key: "exportcustdata",
onClick={handleExportCustData} disabled: !job.converted,
key="exportcustdata" label: t("jobs.actions.exportcustdata"),
disabled={!job.converted} };
> // (
{t("jobs.actions.exportcustdata")} // <Menu.Item
</Menu.Item> // {...props}
); // onClick={handleExportCustData}
// key="exportcustdata"
// disabled={!job.converted}
// >
// {t("jobs.actions.exportcustdata")}
// </Menu.Item>
// );
} }
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDetailHeaderActionexportCustomerData);

View File

@@ -1,5 +1,5 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import Gallery from "react-grid-gallery"; import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
@@ -52,7 +52,7 @@ function JobDocumentsLocalGalleryExternal({
val.type.mime && val.type.mime &&
val.type.mime.startsWith("image") val.type.mime.startsWith("image")
) { ) {
acc.push(val); acc.push({ ...val, src: val.thumbnail });
} }
return acc; return acc;
}, []) }, [])
@@ -65,7 +65,6 @@ function JobDocumentsLocalGalleryExternal({
<div className="clearfix"> <div className="clearfix">
<Gallery <Gallery
images={galleryImages} images={galleryImages}
backdropClosesModal={true}
onSelectImage={(index, image) => { onSelectImage={(index, image) => {
setgalleryImages( setgalleryImages(
galleryImages.map((g, idx) => galleryImages.map((g, idx) =>

View File

@@ -3412,10 +3412,10 @@ ansi-styles@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
antd@^4.22.3: antd@4.24.5:
version "4.24.7" version "4.24.5"
resolved "https://registry.yarnpkg.com/antd/-/antd-4.24.7.tgz#ad90cc2d6225fe3e0030aeccdc64de6c26edc3e7" resolved "https://registry.yarnpkg.com/antd/-/antd-4.24.5.tgz#e2489fe7929b53044f239f0893f22baf52e43c48"
integrity sha512-Qr3AYkeqpd3i/c6M7pjca7Y6XlaIv/p6gD3aqe7/0o8Ueg50G7Aeh+TOaiUfXLGDhnVoNEdaVdDiv8aIaoWB5A== integrity sha512-1dgDd6OqVP0OGNenNm6bPppYsCO1wkT4SFLcpS/QiiiVZQoDCErQYvEH0ULmPWAEler1Brzal260wwpm0uPeeg==
dependencies: dependencies:
"@ant-design/colors" "^6.0.0" "@ant-design/colors" "^6.0.0"
"@ant-design/icons" "^4.7.0" "@ant-design/icons" "^4.7.0"
@@ -3430,10 +3430,10 @@ antd@^4.22.3:
rc-checkbox "~2.3.0" rc-checkbox "~2.3.0"
rc-collapse "~3.4.2" rc-collapse "~3.4.2"
rc-dialog "~9.0.2" rc-dialog "~9.0.2"
rc-drawer "~6.1.0" rc-drawer "~6.0.0"
rc-dropdown "~4.0.0" rc-dropdown "~4.0.0"
rc-field-form "~1.27.0" rc-field-form "~1.27.0"
rc-image "~5.13.0" rc-image "~5.12.0"
rc-input "~0.1.4" rc-input "~0.1.4"
rc-input-number "~7.3.9" rc-input-number "~7.3.9"
rc-mentions "~1.13.1" rc-mentions "~1.13.1"
@@ -3451,7 +3451,7 @@ antd@^4.22.3:
rc-steps "~5.0.0-alpha.2" rc-steps "~5.0.0-alpha.2"
rc-switch "~3.2.0" rc-switch "~3.2.0"
rc-table "~7.26.0" rc-table "~7.26.0"
rc-tabs "~12.5.0" rc-tabs "~12.4.1"
rc-textarea "~0.4.5" rc-textarea "~0.4.5"
rc-tooltip "~5.2.0" rc-tooltip "~5.2.0"
rc-tree "~5.7.0" rc-tree "~5.7.0"
@@ -9819,10 +9819,10 @@ rc-dialog@~9.0.0, rc-dialog@~9.0.2:
rc-motion "^2.3.0" rc-motion "^2.3.0"
rc-util "^5.21.0" rc-util "^5.21.0"
rc-drawer@~6.1.0: rc-drawer@~6.0.0:
version "6.1.2" version "6.0.3"
resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-6.1.2.tgz#032918a21bfa8a7d9e52ada1e7b8ed08c0ae6346" resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-6.0.3.tgz#09993ecdf88ddd569d5a3341d907e3ab258096bb"
integrity sha512-mYsTVT8Amy0LRrpVEv7gI1hOjtfMSO/qHAaCDzFx9QBLnms3cAQLJkaxRWM+Eq99oyLhU/JkgoqTg13bc4ogOQ== integrity sha512-u4RajgrnREKQH/21gB2JHZiA6ZECo0X0BbmDxAJEhKD9jUhlAbqMN5I9VWa4PSzi9ceLHUShqQcPAh2EJswffw==
dependencies: dependencies:
"@babel/runtime" "^7.10.1" "@babel/runtime" "^7.10.1"
"@rc-component/portal" "^1.0.0-6" "@rc-component/portal" "^1.0.0-6"
@@ -9849,10 +9849,10 @@ rc-field-form@~1.27.0:
async-validator "^4.1.0" async-validator "^4.1.0"
rc-util "^5.8.0" rc-util "^5.8.0"
rc-image@~5.13.0: rc-image@~5.12.0:
version "5.13.0" version "5.12.2"
resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.13.0.tgz#1ed9b852a40b5eff34786ba7d2f0e9d26eeab874" resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.12.2.tgz#ccaab23fc0f0eb2351724dc0247503022c1dda90"
integrity sha512-iZTOmw5eWo2+gcrJMMcnd7SsxVHl3w5xlyCgsULUdJhJbnuI8i/AL0tVOsE7aLn9VfOh1qgDT3mC2G75/c7mqg== integrity sha512-12OCOspbN2AW2L1w+7vnYc+k0RexenqfQZIvq3WyYODp9GnTN4GLV8juekm3Apc/pwdfBSp0The1FZ5KXEozhg==
dependencies: dependencies:
"@babel/runtime" "^7.11.2" "@babel/runtime" "^7.11.2"
"@rc-component/portal" "^1.0.2" "@rc-component/portal" "^1.0.2"
@@ -10065,10 +10065,10 @@ rc-table@~7.26.0:
rc-util "^5.22.5" rc-util "^5.22.5"
shallowequal "^1.1.0" shallowequal "^1.1.0"
rc-tabs@~12.5.0: rc-tabs@~12.4.1:
version "12.5.6" version "12.4.2"
resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-12.5.6.tgz#611728575d5f3ef010a8e5e75d2c1b67bb71e86a" resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-12.4.2.tgz#487a1b3f8d8cf0bfc121224013dab00d4a8e0532"
integrity sha512-aArXHzxK7YICxe+622CZ8FlO5coMi8P7E6tXpseCPKm1gdTjUt0LrQK1/AxcrRXZXG3K4QqhlKmET0+cX5DQaQ== integrity sha512-FFlGwuTjQUznWzJtyhmHc6KAp5lRQFxKUv9Aj1UtsOYe2e7WGmuzcrd+/LQchuPe0VjhaZPdGkmFGcqGqNO6ow==
dependencies: dependencies:
"@babel/runtime" "^7.11.2" "@babel/runtime" "^7.11.2"
classnames "2.x" classnames "2.x"