Revert all package upgrades to unblock testing.

This commit is contained in:
Patrick Fic
2023-01-25 11:20:51 -08:00
parent d8cc25a54f
commit 66a80e439f
131 changed files with 18482 additions and 10892 deletions

View File

@@ -1,5 +1,6 @@
// craco.config.js // craco.config.js
const TerserPlugin = require("terser-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin");
const CracoLessPlugin = require("craco-less");
const SentryWebpackPlugin = require("@sentry/webpack-plugin"); const SentryWebpackPlugin = require("@sentry/webpack-plugin");
module.exports = { module.exports = {
@@ -19,6 +20,37 @@ module.exports = {
ignore: ["node_modules", "webpack.config.js"], ignore: ["node_modules", "webpack.config.js"],
}, },
}, },
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: {
...(process.env.NODE_ENV === "development"
? { "@primary-color": "#B22234" }
: {
//"@primary-color": "#1DA57A"
}),
// "@primary-color": " #1890ff", // primary color for all components
// "@link-color": "#1890ff", // link color
// "@success-color": "#52c41a", // success state color
// "@warning-color": "#faad14", // warning state color
// "@error-color": "#f5222d", // error state color
// "@font-size-base": "14px", // major text font size
// " @heading-color": "rgba(0, 0, 0, 0.85)", // heading text color
// "@text-color": "rgba(0, 0, 0, 0.65)", // major text color
// "@text-color-secondary": "rgba(0, 0, 0, 0.45)", // secondary text color
// "@disabled-color": "rgba(0, 0, 0, 0.25)", // disable state color
// "@border-radius-base": "2px", // major border radius
// "@border-color-base": "#d9d9d9", // major border color
// "@box-shadow-base":
// "0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08),0 9px 28px 8px rgba(0, 0, 0, 0.05); // major shadow for layers }",
},
javascriptEnabled: true,
},
},
},
},
], ],
webpack: { webpack: {
configure: (webpackConfig) => ({ configure: (webpackConfig) => ({

View File

@@ -4,55 +4,59 @@
"private": true, "private": true,
"proxy": "http://localhost:4000", "proxy": "http://localhost:4000",
"dependencies": { "dependencies": {
"@ant-design/pro-layout": "^7.6.1",
"@apollo/client": "^3.6.9", "@apollo/client": "^3.6.9",
"@asseinfo/react-kanban": "^2.2.0", "@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^7.0.0", "@craco/craco": "^6.4.5",
"@fingerprintjs/fingerprintjs": "^3.3.3",
"@jsreport/browser-client": "^3.1.0", "@jsreport/browser-client": "^3.1.0",
"@sentry/react": "^7.28.1", "@sentry/react": "^7.28.1",
"@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": "5.1.5", "antd": "^4.22.3",
"apollo-link-logger": "^2.0.0", "apollo-link-logger": "^2.0.0",
"axios": "^1.2.3", "axios": "^0.27.2",
"craco-less": "^1.20.0",
"dinero.js": "^1.9.1", "dinero.js": "^1.9.1",
"dotenv": "^16.0.1",
"enquire-js": "^0.2.1", "enquire-js": "^0.2.1",
"env-cmd": "^10.1.0", "env-cmd": "^10.1.0",
"exifr": "^7.1.3", "exifr": "^7.1.3",
"firebase": "^9.9.1", "firebase": "^9.9.1",
"graphql": "^16.5.0", "graphql": "^16.5.0",
"i18next": "^22.4.6", "i18next": "^21.8.14",
"i18next-browser-languagedetector": "^7.0.1", "i18next-browser-languagedetector": "^6.1.4",
"jsoneditor": "^9.9.0",
"jsreport-browser-client-dist": "^1.3.0", "jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.10.9", "libphonenumber-js": "^1.10.9",
"logrocket": "^3.0.1", "logrocket": "^3.0.1",
"markerjs2": "^2.22.0", "markerjs2": "^2.22.0",
"moment-business-days": "^1.2.0", "moment-business-days": "^1.2.0",
"moment-timezone": "^0.5.34", "moment-timezone": "^0.5.34",
"normalize-url": "^8.0.0", "normalize-url": "^7.0.3",
"phone": "^3.1.32", "phone": "^3.1.23",
"preval.macro": "^5.0.0", "preval.macro": "^5.0.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"query-string": "^8.1.0", "query-string": "^7.1.1",
"rc-queue-anim": "^2.0.0", "rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6", "rc-scroll-anim": "^2.7.6",
"react": "^18.2.0", "react": "^17.0.2",
"react-big-calendar": "^1.5.2", "react-big-calendar": "^1.5.0",
"react-color": "^2.19.3", "react-color": "^2.19.3",
"react-dom": "^18.2.0", "react-cookie": "^4.1.1",
"react-drag-listview": "^2.0.0", "react-dom": "^17.0.2",
"react-grid-gallery": "^1.0.0", "react-drag-listview": "^0.2.1",
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.3.4", "react-grid-layout": "^1.3.4",
"react-i18next": "^12.1.1", "react-i18next": "^11.18.1",
"react-icons": "^4.7.1", "react-icons": "^4.4.0",
"react-image-lightbox": "^5.1.4", "react-number-format": "^4.9.3",
"react-number-format": "^5.1.2", "react-redux": "^7.2.8",
"react-redux": "^8.0.5",
"react-resizable": "^3.0.4", "react-resizable": "^3.0.4",
"react-router-dom": "^5.3.0", "react-router-dom": "^5.3.0",
"react-scripts": "^5.0.1", "react-scripts": "^4.0.3",
"react-sticky": "^6.0.3", "react-sticky": "^6.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3", "react-virtualized": "^9.22.3",
"recharts": "^2.1.12", "recharts": "^2.1.12",
"redux": "^4.2.0", "redux": "^4.2.0",
@@ -64,7 +68,7 @@
"socket.io-client": "^4.5.1", "socket.io-client": "^4.5.1",
"styled-components": "^5.3.5", "styled-components": "^5.3.5",
"subscriptions-transport-ws": "^0.11.0", "subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^3.1.0", "web-vitals": "^2.1.4",
"workbox-background-sync": "^6.5.3", "workbox-background-sync": "^6.5.3",
"workbox-broadcast-update": "^6.5.3", "workbox-broadcast-update": "^6.5.3",
"workbox-cacheable-response": "^6.5.3", "workbox-cacheable-response": "^6.5.3",
@@ -76,12 +80,12 @@
"workbox-range-requests": "^6.5.3", "workbox-range-requests": "^6.5.3",
"workbox-routing": "^6.5.3", "workbox-routing": "^6.5.3",
"workbox-strategies": "^6.5.3", "workbox-strategies": "^6.5.3",
"workbox-streams": "^6.5.3" "workbox-streams": "^6.5.3",
"yauzl": "^2.10.0"
}, },
"scripts": { "scripts": {
"analyze": "source-map-explorer 'build/static/js/*.js'", "analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "craco start", "start": "craco start",
"startrs": "react-scripts start",
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build", "build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
"build:test": "env-cmd -f .env.test yarn run build", "build:test": "env-cmd -f .env.test yarn run build",
"build-deploy:test": "yarn run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'", "build-deploy:test": "yarn run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
@@ -114,8 +118,8 @@
}, },
"devDependencies": { "devDependencies": {
"@sentry/webpack-plugin": "^1.20.0", "@sentry/webpack-plugin": "^1.20.0",
"@testing-library/cypress": "^9.0.0", "@testing-library/cypress": "^8.0.3",
"cypress": "^12.3.0", "cypress": "^10.3.1",
"eslint-plugin-cypress": "^2.12.1", "eslint-plugin-cypress": "^2.12.1",
"react-error-overlay": "6.0.11", "react-error-overlay": "6.0.11",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",

View File

@@ -61,7 +61,7 @@ export function AllocationsAssignmentComponent({
); );
return ( return (
<Popover content={popContent} open={visibility}> <Popover content={popContent} visible={visibility}>
<Button onClick={() => setVisibility(true)}> <Button onClick={() => setVisibility(true)}>
{t("allocations.actions.assign")} {t("allocations.actions.assign")}
</Button> </Button>

View File

@@ -59,7 +59,7 @@ export default connect(
); );
return ( return (
<Popover content={popContent} open={visibility}> <Popover content={popContent} visible={visibility}>
<Button disabled={disabled} onClick={() => setVisibility(true)}> <Button disabled={disabled} onClick={() => setVisibility(true)}>
{t("allocations.actions.assign")} {t("allocations.actions.assign")}
</Button> </Button>

View File

@@ -1,6 +1,5 @@
import { useMutation, useQuery } from "@apollo/client"; import { useMutation, useQuery } from "@apollo/client";
import { PageHeader } from '@ant-design/pro-layout'; import { Button, Form, PageHeader, Popconfirm, Space } from "antd";
import { Button, Form, Popconfirm, Space } from "antd";
import moment from "moment"; import moment from "moment";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, { useState } from "react";
@@ -179,7 +178,7 @@ export function BillDetailEditcontainer({
<BillDetailEditReturn data={data} /> <BillDetailEditReturn data={data} />
<Popconfirm <Popconfirm
open={visible} visible={visible}
onConfirm={() => form.submit()} onConfirm={() => form.submit()}
onCancel={() => setVisible(false)} onCancel={() => setVisible(false)}
okButtonProps={{ loading: updateLoading }} okButtonProps={{ loading: updateLoading }}

View File

@@ -77,7 +77,7 @@ export function BillDetailEditReturn({
return ( return (
<> <>
<Modal <Modal
open={visible} visible={visible}
onCancel={() => setVisible(false)} onCancel={() => setVisible(false)}
destroyOnClose destroyOnClose
title={t("bills.actions.return")} title={t("bills.actions.return")}

View File

@@ -32,7 +32,7 @@ export default function BillDetailEditcontainer() {
history.push({ search: queryString.stringify(search) }); history.push({ search: queryString.stringify(search) });
}} }}
destroyOnClose destroyOnClose
open={search.billid} visible={search.billid}
> >
<BillDetailEditComponent /> <BillDetailEditComponent />
</Drawer> </Drawer>

View File

@@ -344,7 +344,7 @@ function BillEnterModalContainer({
<Modal <Modal
title={t("bills.labels.new")} title={t("bills.labels.new")}
width={"98%"} width={"98%"}
open={billEnterModal.visible} visible={billEnterModal.visible}
okText={t("general.actions.save")} okText={t("general.actions.save")}
keyboard="false" keyboard="false"
onOk={() => form.submit()} onOk={() => form.submit()}

View File

@@ -70,7 +70,7 @@ export function ContractsFindModalContainer({
return ( return (
<Modal <Modal
open={visible} visible={visible}
width="70%" width="70%"
title={t("payments.labels.findermodal")} title={t("payments.labels.findermodal")}
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}

View File

@@ -35,7 +35,7 @@ export default function CABCpvrtCalculator({ disabled, form }) {
<Popover <Popover
destroyTooltipOnHide destroyTooltipOnHide
content={popContent} content={popContent}
open={visibility} visible={visibility}
disabled={disabled} disabled={disabled}
> >
<Button disabled={disabled} onClick={() => setVisibility(true)}> <Button disabled={disabled} onClick={() => setVisibility(true)}>

View File

@@ -88,8 +88,8 @@ export function ChatMediaSelector({
} }
title={t("messaging.labels.selectmedia")} title={t("messaging.labels.selectmedia")}
trigger="click" trigger="click"
open={visible} visible={visible}
onOpenChange={handleVisibleChange} onVisibleChange={handleVisibleChange}
> >
<Badge count={selectedMedia.filter((s) => s.isSelected).length}> <Badge count={selectedMedia.filter((s) => s.isSelected).length}>
<PictureFilled style={{ margin: "0 .5rem" }} /> <PictureFilled style={{ margin: "0 .5rem" }} />

View File

@@ -1,5 +1,5 @@
import { PlusCircleOutlined } from "@ant-design/icons"; import { PlusCircleOutlined } from "@ant-design/icons";
import { Dropdown } from "antd"; import { Dropdown, Menu } from "antd";
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
@@ -16,15 +16,19 @@ const mapDispatchToProps = (dispatch) => ({
}); });
export function ChatPresetsComponent({ bodyshop, setMessage, className }) { export function ChatPresetsComponent({ bodyshop, setMessage, className }) {
const menu = bodyshop.md_messaging_presets.map((i, idx) => ({ const menu = (
label: i.label, <Menu>
key: idx, {bodyshop.md_messaging_presets.map((i, idx) => (
onClick: () => setMessage(i.text), <Menu.Item onClick={() => setMessage(i.text)} key={idx}>
})); {i.label}
</Menu.Item>
))}
</Menu>
);
return ( return (
<div className={className}> <div className={className}>
<Dropdown trigger={["click"]} menu={{ items: menu }} placement="top"> <Dropdown trigger={["click"]} overlay={menu}>
<PlusCircleOutlined /> <PlusCircleOutlined />
</Dropdown> </Dropdown>
</div> </div>

View File

@@ -390,7 +390,7 @@ export function ContractConvertToRo({
return ( return (
<div> <div>
<Popover content={popContent} open={visible}> <Popover content={popContent} visible={visible}>
<Button <Button
onClick={() => setVisible(true)} onClick={() => setVisible(true)}
loading={loading} loading={loading}

View File

@@ -55,7 +55,7 @@ export default function ContractLicenseDecodeButton({ form }) {
return ( return (
<div> <div>
<Modal <Modal
open={modalVisible} visible={modalVisible}
okText={t("contracts.actions.senddltoform")} okText={t("contracts.actions.senddltoform")}
onOk={handleInsertForm} onOk={handleInsertForm}
okButtonProps={{ disabled: !!!decodedBarcode }} okButtonProps={{ disabled: !!!decodedBarcode }}

View File

@@ -59,7 +59,7 @@ export function ContractsFindModalContainer({
return ( return (
<Modal <Modal
open={visible} visible={visible}
width="70%" width="70%"
title={t("contracts.labels.findermodal")} title={t("contracts.labels.findermodal")}
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}

View File

@@ -1,5 +1,5 @@
import { DownOutlined } from "@ant-design/icons"; import { DownOutlined } from "@ant-design/icons";
import { Dropdown } from "antd"; import { Dropdown, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -13,14 +13,25 @@ const mapStateToProps = createStructuredSelector({
export function ContractsRatesChangeButton({ disabled, form, bodyshop }) { export function ContractsRatesChangeButton({ disabled, form, bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const menu = bodyshop.md_ccc_rates.map((rate, idx) => ({ const handleClick = ({ item, key, keyPath }) => {
onClick: () => form.setFieldsValue(rate), const { label, ...rate } = item.props.value;
key: idx, form.setFieldsValue(rate);
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 menu={{ items: menu }} disabled={disabled}> <Dropdown overlay={menu} disabled={disabled}>
<a <a
className="ant-dropdown-link" className="ant-dropdown-link"
href=" #" href=" #"

View File

@@ -1,7 +1,6 @@
import { WarningFilled } from "@ant-design/icons"; import { WarningFilled } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client"; import { useApolloClient } from "@apollo/client";
import { PageHeader } from '@ant-design/pro-layout'; import { Button, Form, Input, InputNumber, PageHeader, Space } from "antd";
import { Button, Form, Input, InputNumber, Space } from "antd";
import moment from "moment"; import moment from "moment";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View File

@@ -64,7 +64,7 @@ export function CCReturnModalContainer({
return ( return (
<Modal <Modal
title={t("courtesycars.labels.return")} title={t("courtesycars.labels.return")}
open={visible} visible={visible}
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}
width={"90%"} width={"90%"}
okText={t("general.actions.save")} okText={t("general.actions.save")}

View File

@@ -3,19 +3,21 @@ import {
Button, Button,
Card, Card,
Dropdown, Dropdown,
Input, Space, Input,
Menu,
Space,
Table, Table,
Tooltip Tooltip,
} from "antd"; } from "antd";
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 { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { DateTimeFormatter } from "../../utils/DateFormatter"; import { DateTimeFormatter } from "../../utils/DateFormatter";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component"; import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
import moment from "moment";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
export default function CourtesyCarsList({ loading, courtesycars, refetch }) { export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
@@ -177,14 +179,10 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
</Button> </Button>
<Dropdown <Dropdown
trigger="click" trigger="click"
menu={{ overlay={
items: [ <Menu>
{ <Menu.Item
label: t( onClick={() =>
"printcenter.courtesycarcontract.courtesy_car_inventory"
),
key: "cc_inv",
onClick: () =>
GenerateDocument( GenerateDocument(
{ {
name: TemplateList("courtesycar").courtesy_car_inventory name: TemplateList("courtesycar").courtesy_car_inventory
@@ -195,10 +193,13 @@ 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

@@ -1,7 +1,6 @@
import Icon, { SyncOutlined } from "@ant-design/icons"; import Icon, { SyncOutlined } from "@ant-design/icons";
import { gql, useMutation, useQuery } from "@apollo/client"; import { gql, useMutation, useQuery } from "@apollo/client";
import { PageHeader } from '@ant-design/pro-layout'; import { Button, Dropdown, Menu, notification, PageHeader, Space } from "antd";
import { Button, Dropdown, Menu, notification, Space } from "antd";
import i18next from "i18next"; import i18next from "i18next";
import _ from "lodash"; import _ from "lodash";
import moment from "moment"; import moment from "moment";
@@ -118,15 +117,17 @@ 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 <Menu onClick={handleAddComponent}>
onClick={handleAddComponent} {Object.keys(componentList).map((key) => (
items={Object.keys(componentList).map((key) => ({ <Menu.Item
key: key, key={key}
value: key, value={key}
disabled: existingLayoutKeys.includes(key), disabled={existingLayoutKeys.includes(key)}
label: componentList[key].label, >
}))} {componentList[key].label}
></Menu> </Menu.Item>
))}
</Menu>
); );
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
@@ -282,12 +283,8 @@ 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("month").startOf('day').toISOString()}"}}, {date_invoiced: {_lte: "${moment()
.startOf("day") .endOf("month").endOf('day').toISOString()}"}}]}) {
.toISOString()}"}}, {date_invoiced: {_lte: "${moment()
.endOf("month")
.endOf("day")
.toISOString()}"}}]}) {
id id
ro_number ro_number
date_invoiced date_invoiced

View File

@@ -51,7 +51,7 @@ export function DmsCdkVehicles({ bodyshop, form, socket, job }) {
<> <>
<Modal <Modal
width={"90%"} width={"90%"}
open={visible} visible={visible}
onCancel={() => setVisible(false)} onCancel={() => setVisible(false)}
onOk={() => { onOk={() => {
form.setFieldsValue({ form.setFieldsValue({

View File

@@ -6,10 +6,12 @@ import {
Dropdown, Dropdown,
Form, Form,
Input, Input,
InputNumber, Select, InputNumber,
Menu,
Select,
Space, Space,
Statistic, Statistic,
Typography Typography,
} from "antd"; } from "antd";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import moment from "moment"; import moment from "moment";
@@ -275,32 +277,36 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
<div> <div>
{t("jobs.fields.dms.payer.controlnumber")}{" "} {t("jobs.fields.dms.payer.controlnumber")}{" "}
<Dropdown <Dropdown
menu={{ overlay={
items: <Menu>
bodyshop.cdk_configuration.controllist && {bodyshop.cdk_configuration.controllist &&
bodyshop.cdk_configuration.controllist.map( bodyshop.cdk_configuration.controllist.map(
(key, idx) => ({ (key, idx) => (
key: idx, <Menu.Item
label: key.name, key={idx}
onClick: () => { onClick={() => {
form.setFieldsValue({ form.setFieldsValue({
payers: form payers: form
.getFieldValue("payers") .getFieldValue("payers")
.map((row, mapIndex) => { .map((row, mapIndex) => {
if (index !== mapIndex) if (index !== mapIndex)
return row; return row;
return { return {
...row, ...row,
controlnumber: controlnumber:
key.controlnumber, key.controlnumber,
}; };
}), }),
}); });
}, }}
}) >
), {key.name}
}} </Menu.Item>
)
)}
</Menu>
}
> >
<a href=" #" onClick={(e) => e.preventDefault()}> <a href=" #" onClick={(e) => e.preventDefault()}>
<DownOutlined /> <DownOutlined />

View File

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

View File

@@ -174,7 +174,7 @@ export function EmailOverlayContainer({
return ( return (
<Modal <Modal
destroyOnClose={true} destroyOnClose={true}
open={modalVisible} visible={modalVisible}
maskClosable={false} maskClosable={false}
width={"80%"} width={"80%"}
onOk={() => form.submit()} onOk={() => form.submit()}

View File

@@ -94,7 +94,7 @@ const FormInputNUmberCalculator = (
return ( return (
<div> <div>
<Popover content={popContent} open={history.length > 0}> <Popover content={popContent} visible={history.length > 0}>
<InputNumber <InputNumber
ref={ref} ref={ref}
value={value} value={value}

View File

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

@@ -103,7 +103,7 @@ export function InventoryUpsertModalContainer({
? t("inventory.actions.edit") ? t("inventory.actions.edit")
: t("inventory.actions.new") : t("inventory.actions.new")
} }
open={visible} visible={visible}
okText={t("general.actions.save")} okText={t("general.actions.save")}
onOk={() => { onOk={() => {
form.submit(); form.submit();

View File

@@ -96,7 +96,7 @@ export function Jobd3RdPartyModal({ bodyshop, jobId, job }) {
return ( return (
<> <>
<Button onClick={showModal}>{t("printcenter.jobs.3rdpartypayer")}</Button> <Button onClick={showModal}>{t("printcenter.jobs.3rdpartypayer")}</Button>
<Modal open={isModalVisible} onOk={handleOk} onCancel={handleCancel}> <Modal visible={isModalVisible} onOk={handleOk} onCancel={handleCancel}>
<Form <Form
onFinish={handleFinish} onFinish={handleFinish}
autoComplete={"off"} autoComplete={"off"}

View File

@@ -1,11 +1,11 @@
import { DownOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Dropdown, notification } from "antd";
import React from "react"; import React from "react";
import { useMutation } from "@apollo/client";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
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";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { Dropdown, Menu, notification } from "antd";
import { DownOutlined } from "@ant-design/icons";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -37,19 +37,18 @@ export function JobAltTransportChange({ bodyshop, job }) {
}); });
} }
}; };
const menu = [ const menu = (
...(bodyshop.appt_alt_transport && <Menu selectedKeys={[job && job.alt_transport]} onClick={onClick}>
bodyshop.appt_alt_transport.map((alt) => ({ {bodyshop.appt_alt_transport &&
key: alt, bodyshop.appt_alt_transport.map((alt) => (
label: alt, <Menu.Item key={alt}>{alt}</Menu.Item>
value: alt, ))}
onClick, <Menu.Divider />
}))), <Menu.Item key={"null"}>{t("general.actions.clear")}</Menu.Item>
{ type: "divider" }, </Menu>
{ key: "null", label: t("general.actions.clear"), onClick }, );
];
return ( return (
<Dropdown menu={{ items: menu }}> <Dropdown overlay={menu}>
<a href=" #" onClick={(e) => e.preventDefault()}> <a href=" #" onClick={(e) => e.preventDefault()}>
<DownOutlined /> <DownOutlined />
</a> </a>

View File

@@ -1,11 +1,11 @@
import { DownOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Dropdown, notification } from "antd";
import React from "react"; import React from "react";
import { useMutation } from "@apollo/client";
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
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";
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries"; import { Dropdown, Menu, notification } from "antd";
import { DownOutlined } from "@ant-design/icons";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -44,23 +44,21 @@ 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 = (
items: [ <Menu selectedKeys={[event.color]} onClick={onClick}>
...(bodyshop.appt_colors && {bodyshop.appt_colors &&
bodyshop.appt_colors.map((color) => ({ bodyshop.appt_colors.map((color) => (
style: { color: color.color.hex }, <Menu.Item style={{ color: color.color.hex }} key={color.color.hex}>
key: color.color.hex, {color.label}
label: color.label, </Menu.Item>
}))), ))}
{ type: "divider" }, <Menu.Divider />
{ key: "null", value: t("general.actions.clear") }, <Menu.Item key={"null"}>{t("general.actions.clear")}</Menu.Item>
], </Menu>
selectedKeys: [event.color], );
onClick: onClick,
};
return ( return (
<Dropdown menu={menu}> <Dropdown overlay={menu}>
<a href=" #" onClick={(e) => e.preventDefault()}> <a href=" #" onClick={(e) => e.preventDefault()}>
{selectedColor} {selectedColor}
<DownOutlined /> <DownOutlined />

View File

@@ -2,9 +2,11 @@ import { AlertFilled } from "@ant-design/icons";
import { import {
Button, Button,
Divider, Divider,
Dropdown, notification, Dropdown,
Menu,
notification,
Popover, Popover,
Space Space,
} from "antd"; } from "antd";
import parsePhoneNumber from "libphonenumber-js"; import parsePhoneNumber from "libphonenumber-js";
import moment from "moment"; import moment from "moment";
@@ -16,7 +18,7 @@ import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { import {
openChatByPhone, openChatByPhone,
setMessage setMessage,
} from "../../redux/messaging/messaging.actions"; } from "../../redux/messaging/messaging.actions";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
@@ -25,11 +27,11 @@ import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import ChatOpenButton from "../chat-open-button/chat-open-button.component"; import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import DataLabel from "../data-label/data-label.component"; import DataLabel from "../data-label/data-label.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component"; import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
import ScheduleAtChange from "./job-at-change.component"; import ScheduleAtChange from "./job-at-change.component";
import ScheduleEventColor from "./schedule-event.color.component"; import ScheduleEventColor from "./schedule-event.color.component";
import ScheduleEventNote from "./schedule-event.note.component"; import ScheduleEventNote from "./schedule-event.note.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -147,12 +149,10 @@ export function ScheduleEventComponent({
) : null} ) : null}
{event.job ? ( {event.job ? (
<Dropdown <Dropdown
menu={{ overlay={
items: [ <Menu>
{ <Menu.Item
disabled: event.arrived, onClick={() => {
label: t("general.labels.email"),
onClick: () => {
const Template = TemplateList("job").appointment_reminder; const Template = TemplateList("job").appointment_reminder;
GenerateDocument( GenerateDocument(
{ {
@@ -166,12 +166,13 @@ export function ScheduleEventComponent({
"e", "e",
event.job && event.job.id event.job && event.job.id
); );
}, }}
}, disabled={event.arrived}
{ >
label: t("general.labels.sms"), {t("general.labels.email")}
disabled: event.arrived || !bodyshop.messagingservicesid, </Menu.Item>
onClick: () => { <Menu.Item
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({
@@ -191,10 +192,13 @@ 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>
@@ -245,7 +249,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,
@@ -283,8 +287,8 @@ export function ScheduleEventComponent({
return ( return (
<Popover <Popover
open={visible} visible={visible}
onOpenChange={(vis) => !event.vacation && setVisible(vis)} onVisibleChange={(vis) => !event.vacation && setVisible(vis)}
trigger="click" trigger="click"
content={event.block ? blockContent : popoverContent} content={event.block ? blockContent : popoverContent}
style={{ style={{

View File

@@ -39,7 +39,7 @@ export function JobCostingModalContainer({
return ( return (
<Modal <Modal
open={visible} visible={visible}
title={t("jobs.labels.jobcosting")} title={t("jobs.labels.jobcosting")}
onOk={() => { onOk={() => {
toggleModalVisible(); toggleModalVisible();

View File

@@ -76,7 +76,7 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
return ( return (
<Drawer <Drawer
open={!!selected} visible={!!selected}
destroyOnClose destroyOnClose
width={drawerPercentage} width={drawerPercentage}
placement="right" placement="right"

View File

@@ -1,16 +1,24 @@
import { import {
DeleteFilled, DeleteFilled,
EditFilled,
FilterFilled, FilterFilled,
HomeOutlined,
MinusCircleTwoTone,
PlusCircleTwoTone,
SyncOutlined, SyncOutlined,
WarningFilled, WarningFilled,
EditFilled,
PlusCircleTwoTone,
MinusCircleTwoTone,
HomeOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { PageHeader } from '@ant-design/pro-layout'; import {
import { Button, Dropdown, Input, Space, Table, Tag } from "antd"; Button,
Dropdown,
Input,
Menu,
PageHeader,
Space,
Table,
Tag,
} from "antd";
import axios from "axios"; import axios from "axios";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -30,13 +38,13 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
// import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container"; // import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container";
// import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container"; // import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container";
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container"; // import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
import _ from "lodash";
import moment from "moment";
import { selectBodyshop } from "../../redux/user/user.selectors";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container"; import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
import _ from "lodash";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobLinesExpander from "./job-lines-expander.component"; import JobLinesExpander from "./job-lines-expander.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment";
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -95,9 +103,7 @@ 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) => ({ onCell: (record) => ({ className: record.manual_line && "job-line-manual" }),
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,
@@ -399,17 +405,16 @@ export function JobLinesComponent({
} }
}; };
const markMenu = { const markMenu = (
onClick: handleMark, <Menu onClick={handleMark}>
items: [ <Menu.Item key="PAA">{t("joblines.fields.part_types.PAA")}</Menu.Item>
{ key: "PAA", label: t("joblines.fields.part_types.PAA") }, <Menu.Item key="PAN">{t("joblines.fields.part_types.PAN")}</Menu.Item>
{ key: "PAN", label: t("joblines.fields.part_types.PAN") }, <Menu.Item key="PAL">{t("joblines.fields.part_types.PAL")}</Menu.Item>
{ key: "PAL", label: t("joblines.fields.part_types.PAL") }, <Menu.Item key="PAS">{t("joblines.fields.part_types.PAS")}</Menu.Item>
{ key: "PAS", label: t("joblines.fields.part_types.PAS") }, <Menu.Divider />
{ type: "divider" }, <Menu.Item key="clear">{t("general.labels.clear")}</Menu.Item>
{ key: "clear", label: t("general.labels.clear") }, </Menu>
], );
};
return ( return (
<div> <div>
@@ -539,7 +544,7 @@ export function JobLinesComponent({
> >
<FilterFilled /> {t("jobs.actions.filterpartsonly")} <FilterFilled /> {t("jobs.actions.filterpartsonly")}
</Button> </Button>
<Dropdown menu={markMenu} trigger={["click"]}> <Dropdown overlay={markMenu} trigger={["click"]}>
<Button>{t("jobs.actions.mark")}</Button> <Button>{t("jobs.actions.mark")}</Button>
</Dropdown> </Dropdown>
<Button <Button

View File

@@ -87,7 +87,7 @@ export function JobEmployeeAssignments({
); );
return ( return (
<Popover destroyTooltipOnHide content={popContent} open={visibility}> <Popover destroyTooltipOnHide content={popContent} visible={visibility}>
<Spin spinning={loading}> <Spin spinning={loading}>
<DataLabel label={t("jobs.fields.employee_body")}> <DataLabel label={t("jobs.fields.employee_body")}>
{body ? ( {body ? (

View File

@@ -225,7 +225,7 @@ export function JobLineConvertToLabor({
<Popover <Popover
disabled={jobline.convertedtolbr} disabled={jobline.convertedtolbr}
content={overlay} content={overlay}
open={visibility} visible={visibility}
placement="bottom" placement="bottom"
> >
<Tooltip title={t("joblines.actions.converttolabor")}> <Tooltip title={t("joblines.actions.converttolabor")}>

View File

@@ -1,5 +1,5 @@
import { DownOutlined } from "@ant-design/icons"; import { DownOutlined } from "@ant-design/icons";
import { Dropdown } from "antd"; import { Dropdown, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -21,17 +21,19 @@ export function JoblinePresetButton({ bodyshop, form }) {
form.setFieldsValue(item); form.setFieldsValue(item);
}; };
const menu = { const menu = (
items: bodyshop.md_jobline_presets.map((i, idx) => ({ <Menu>
onClick: () => handleSelect(i), {bodyshop.md_jobline_presets.map((i, idx) => (
key: idx, <Menu.Item onClick={() => handleSelect(i)} key={idx}>
label: i.label, {i.label}
})), </Menu.Item>
}; ))}
</Menu>
);
return ( return (
<div> <div>
<Dropdown trigger={["click"]} menu={menu}> <Dropdown trigger={["click"]} overlay={menu}>
<a <a
className="ant-dropdown-link" className="ant-dropdown-link"
href="# " href="# "

View File

@@ -54,7 +54,7 @@ export function JobLinesUpsertModalComponent({
: t("joblines.labels.new") : t("joblines.labels.new")
} }
forceRender forceRender
open={visible} visible={visible}
width="60%" width="60%"
okText={t("general.actions.save")} okText={t("general.actions.save")}
onOk={() => form.submit()} onOk={() => form.submit()}

View File

@@ -42,7 +42,7 @@ function JobReconciliationModalContainer({
<Modal <Modal
title={t("jobs.labels.reconciliationheader")} title={t("jobs.labels.reconciliationheader")}
width={"95%"} width={"95%"}
open={visible} visible={visible}
okText={t("general.actions.close")} okText={t("general.actions.close")}
onOk={handleCancel} onOk={handleCancel}
onCancel={handleCancel} onCancel={handleCancel}

View File

@@ -186,7 +186,7 @@ export default function ScoreboardAddButton({
}; };
return ( return (
<Popover content={overlay} open={visibility} placement="bottom"> <Popover content={overlay} visible={visibility} placement="bottom">
<Button <Button
loading={loading} loading={loading}
disabled={disabled} disabled={disabled}

View File

@@ -1,6 +1,6 @@
import { DownCircleFilled } from "@ant-design/icons"; import { DownCircleFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Button, Dropdown, notification } from "antd"; import { Button, Dropdown, Menu, notification } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -40,18 +40,20 @@ export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) {
}); });
}; };
const statusmenu = { const statusmenu = (
onClick: (e) => { <Menu
updateJobStatus(e.key); onClick={(e) => {
}, updateJobStatus(e.key);
items: bodyshop.md_ro_statuses.statuses.map((item) => ({ }}
key: item, >
label: item, {bodyshop.md_ro_statuses.statuses.map((item) => (
})), <Menu.Item key={item}>{item}</Menu.Item>
}; ))}
</Menu>
);
return ( return (
<Dropdown menu={statusmenu} trigger={["click"]} key="changestatus"> <Dropdown overlay={statusmenu} trigger={["click"]} key="changestatus">
<Button shape="round"> <Button shape="round">
<span>{job.status}</span> <span>{job.status}</span>

View File

@@ -1,6 +1,6 @@
import { DownCircleFilled } from "@ant-design/icons"; import { DownCircleFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Button, Dropdown, notification } from "antd"; import { Button, Dropdown, Menu, notification } from "antd";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -81,32 +81,29 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
} }
}, [job, setAvailableStatuses, bodyshop]); }, [job, setAvailableStatuses, bodyshop]);
const statusmenu = { const statusmenu = (
onClick: (e) => { <Menu
updateJobStatus(e.key); onClick={(e) => {
}, updateJobStatus(e.key);
items: [ }}
...availableStatuses.map((item) => ({ >
key: item, {availableStatuses.map((item) => (
label: item, <Menu.Item key={item}>{item}</Menu.Item>
})), ))}
...(job.converted {job.converted && (
? [ <>
{ <Menu.Divider />
type: "divider", {otherStages.map((item, idx) => (
}, <Menu.Item key={item}>{item}</Menu.Item>
...otherStages.map((item, idx) => ({ ))}
key: item, </>
label: item, )}
})), </Menu>
] );
: []),
],
};
return ( return (
<Dropdown <Dropdown
menu={statusmenu} overlay={statusmenu}
trigger={["click"]} trigger={["click"]}
key="changestatus" key="changestatus"
disabled={jobRO || !job.converted} disabled={jobRO || !job.converted}

View File

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

View File

@@ -226,7 +226,7 @@ export function JobsConvertButton({
if (job.converted) return <></>; if (job.converted) return <></>;
return ( return (
<Popover open={visible} content={popMenu}> <Popover visible={visible} content={popMenu}>
<Button <Button
key="convert" key="convert"
type="danger" type="danger"

View File

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

View File

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

View File

@@ -4,6 +4,7 @@ import {
Card, Card,
Form, Form,
Input, Input,
Menu,
notification, notification,
Popover, Popover,
Select, Select,
@@ -12,11 +13,25 @@ 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";
export default function JobsDetailHeaderAddEvent({ bodyshop, jobid }) { const mapStateToProps = createStructuredSelector({
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);
@@ -138,12 +153,11 @@ export default function JobsDetailHeaderAddEvent({ bodyshop, jobid }) {
setVisibility(true); setVisibility(true);
}; };
return { return (
key: "addmanualevent", <Popover content={overlay} visible={visibility}>
label: ( <Menu.Item {...props} onClick={handleClick}>
<Popover content={overlay} open={visibility} onClick={handleClick}>
{t("appointments.labels.manualevent")} {t("appointments.labels.manualevent")}
</Popover> </Menu.Item>
), </Popover>
}; );
} }

View File

@@ -1,6 +1,6 @@
import { DownCircleFilled } from "@ant-design/icons"; import { DownCircleFilled } from "@ant-design/icons";
import { useApolloClient, useMutation } from "@apollo/client"; import { useApolloClient, useMutation } from "@apollo/client";
import { Button, Dropdown, notification, Popconfirm } from "antd"; import { Button, Dropdown, Menu, notification, Popconfirm } from "antd";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -13,7 +13,7 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { import {
selectBodyshop, selectBodyshop,
selectCurrentUser selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import JobsDetailHeaderActionsAddevent from "./jobs-detail-header-actions.addevent"; import JobsDetailHeaderActionsAddevent from "./jobs-detail-header-actions.addevent";
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util"; import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
@@ -105,14 +105,13 @@ export function JobsDetailHeaderActions({
}); });
}; };
const statusmenu = { const statusmenu = (
items: [ <Menu key="popovermenu">
{ <Menu.Item
key: "schedule", disabled={!jobInPreProduction || !job.converted || jobRO}
disabled: !jobInPreProduction || !job.converted || jobRO, onClick={() => {
label: t("jobs.actions.schedule"),
onClick: () => {
logImEXEvent("job_header_schedule"); logImEXEvent("job_header_schedule");
setScheduleContext({ setScheduleContext({
actions: { refetch: refetch }, actions: { refetch: refetch },
context: { context: {
@@ -121,228 +120,200 @@ export function JobsDetailHeaderActions({
alt_transport: job.alt_transport, alt_transport: job.alt_transport,
}, },
}); });
}, }}
}, >
{ {t("jobs.actions.schedule")}
key: "cancelappts", </Menu.Item>
disabled: job.status !== bodyshop.md_ro_statuses.default_scheduled, <Menu.Item
label: ( disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
<Popconfirm >
title={t("general.labels.areyousure")} <Popconfirm
okText="Yes" title={t("general.labels.areyousure")}
cancelText="No" okText="Yes"
onClick={(e) => e.stopPropagation()} cancelText="No"
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled} onClick={(e) => e.stopPropagation()}
onConfirm={async () => { disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
const jobUpdate = await cancelAllAppointments({ onConfirm={async () => {
variables: { const jobUpdate = await cancelAllAppointments({
jobid: job.id, variables: {
job: { jobid: job.id,
date_scheduled: null, job: {
scheduled_in: null, date_scheduled: null,
scheduled_completion: null, scheduled_in: null,
status: bodyshop.md_ro_statuses.default_imported, scheduled_completion: null,
}, status: bodyshop.md_ro_statuses.default_imported,
}, },
},
});
if (!jobUpdate.errors) {
notification["success"]({
message: t("appointments.successes.canceled"),
}); });
if (!jobUpdate.errors) { return;
notification["success"]({ }
message: t("appointments.successes.canceled"), }}
}); getPopupContainer={(trigger) => trigger.parentNode}
return; >
} {t("menus.jobsactions.cancelallappointments")}
}} </Popconfirm>
getPopupContainer={(trigger) => trigger.parentNode} </Menu.Item>
> <Menu.Item
{t("menus.jobsactions.cancelallappointments")} disabled={
</Popconfirm>
),
},
{
key: "intake",
disabled:
!!job.intakechecklist || !!job.intakechecklist ||
!jobInPreProduction || !jobInPreProduction ||
!job.converted || !job.converted ||
jobRO, jobRO
label: }
!!job.intakechecklist || >
!jobInPreProduction || {!!job.intakechecklist ||
!job.converted || !jobInPreProduction ||
jobRO ? ( !job.converted ||
t("jobs.actions.intake") jobRO ? (
) : ( t("jobs.actions.intake")
<Link to={`/manage/jobs/${job.id}/intake`}> ) : (
{t("jobs.actions.intake")} <Link to={`/manage/jobs/${job.id}/intake`}>
</Link> {t("jobs.actions.intake")}
), </Link>
}, )}
{ </Menu.Item>
disabled: !jobInProduction || jobRO, <Menu.Item disabled={!jobInProduction || jobRO}>
key: "deliver", {!jobInProduction ? (
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", <Link to={`/manage/jobs/${job.id}/checklist`}>
disabled: !job.converted, {t("jobs.actions.viewchecklist")}
label: ( </Link>
<Link to={`/manage/jobs/${job.id}/checklist`}> </Menu.Item>
{t("jobs.actions.viewchecklist")} <Menu.Item
</Link> 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>
key: "enterpayments", <Menu.Item
disabled: !job.converted, key="enterpayments"
label: t("menus.header.enterpayment"), disabled={!job.converted}
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")}
key: "cccontract", </Menu.Item>
disabled: jobRO || !job.converted, <Menu.Item key="cccontract" disabled={jobRO || !job.converted}>
label: ( <Link
<Link to={{
to={{ pathname: "/manage/courtesycars/contracts/new",
pathname: "/manage/courtesycars/contracts/new", state: { jobId: job.id },
state: { jobId: job.id }, }}
}} >
> {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}>
key: "toggleAlert", {job.production_vars && job.production_vars.alert
onClick: handleAlertToggle, ? t("production.labels.alertoff")
label: : t("production.labels.alerton")}
job.production_vars && job.production_vars.alert </Menu.Item>
? t("production.labels.alertoff") <Menu.SubMenu key="dupe" title={t("menus.jobsactions.duplicate")}>
: t("production.labels.alerton"), <Menu.Item>
}, <Popconfirm
{ title={t("jobs.labels.duplicateconfirm")}
key: "dupe", okText="Yes"
label: t("menus.jobsactions.duplicate"), cancelText="No"
children: [ onClick={(e) => e.stopPropagation()}
{ onConfirm={() =>
key: "dupewithlines", DuplicateJob(
label: ( client,
<Popconfirm job.id,
title={t("jobs.labels.duplicateconfirm")} { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported },
okText="Yes" (newJobId) => {
cancelText="No" history.push(`/manage/jobs/${newJobId}`);
onClick={(e) => e.stopPropagation()} notification["success"]({
onConfirm={() => message: t("jobs.successes.duplicated"),
DuplicateJob( });
client, },
job.id, true
{ )
defaultOpenStatus: }
bodyshop.md_ro_statuses.default_imported, getPopupContainer={(trigger) => trigger.parentNode}
}, >
(newJobId) => { {t("menus.jobsactions.duplicate")}
history.push(`/manage/jobs/${newJobId}`); </Popconfirm>
notification["success"]({ </Menu.Item>
message: t("jobs.successes.duplicated"), <Menu.Item>
}); <Popconfirm
}, title={t("jobs.labels.duplicateconfirm")}
true okText="Yes"
) cancelText="No"
onClick={(e) => e.stopPropagation()}
onConfirm={() =>
DuplicateJob(
client,
job.id,
{ defaultOpenStatus: bodyshop.md_ro_statuses.default_imported },
(newJobId) => {
history.push(`/manage/jobs/${newJobId}`);
notification["success"]({
message: t("jobs.successes.duplicated"),
});
} }
getPopupContainer={(trigger) => trigger.parentNode} )
> }
{t("menus.jobsactions.duplicate")} getPopupContainer={(trigger) => trigger.parentNode}
</Popconfirm> >
), {t("menus.jobsactions.duplicatenolines")}
}, </Popconfirm>
{ </Menu.Item>
key: "dupewithoutlines", </Menu.SubMenu>
label: (
<Popconfirm <Menu.Item
title={t("jobs.labels.duplicateconfirm")} key="postbills"
okText="Yes" disabled={!job.converted}
cancelText="No" onClick={() => {
onClick={(e) => e.stopPropagation()}
onConfirm={() =>
DuplicateJob(
client,
job.id,
{
defaultOpenStatus:
bodyshop.md_ro_statuses.default_imported,
},
(newJobId) => {
history.push(`/manage/jobs/${newJobId}`);
notification["success"]({
message: t("jobs.successes.duplicated"),
});
}
)
}
getPopupContainer={(trigger) => trigger.parentNode}
>
{t("menus.jobsactions.duplicatenolines")}
</Popconfirm>
),
},
],
},
{
key: "postbills",
disabled: !job.converted,
label: t("jobs.actions.postbills"),
onClick: () => {
logImEXEvent("job_header_enter_bills"); logImEXEvent("job_header_enter_bills");
setBillEnterContext({ setBillEnterContext({
@@ -351,13 +322,14 @@ export function JobsDetailHeaderActions({
job: job, job: job,
}, },
}); });
}, }}
}, >
{ {t("jobs.actions.postbills")}
key: "addtopartsqueue", </Menu.Item>
disabled: !job.converted || !jobInProduction || jobRO, <Menu.Item
label: t("jobs.actions.addtopartsqueue"), key="addtopartsqueue"
onClick: async () => { disabled={!job.converted || !jobInProduction || jobRO}
onClick={async () => {
const result = await updateJob({ const result = await updateJob({
variables: { variables: {
jobId: job.id, jobId: job.id,
@@ -376,12 +348,12 @@ export function JobsDetailHeaderActions({
}), }),
}); });
} }
}, }}
}, >
{ {t("jobs.actions.addtopartsqueue")}
disabled: !jobInPostProduction, </Menu.Item>
key: "closejob", <Menu.Item disabled={!jobInPostProduction} key="closejob">
label: !jobInPostProduction ? ( {!jobInPostProduction ? (
t("menus.jobsactions.closejob") t("menus.jobsactions.closejob")
) : ( ) : (
<Link <Link
@@ -391,27 +363,23 @@ export function JobsDetailHeaderActions({
> >
{t("menus.jobsactions.closejob")} {t("menus.jobsactions.closejob")}
</Link> </Link>
), )}
}, </Menu.Item>
{ <Menu.Item key="admin">
key: "admin", <Link
label: ( to={{
<Link pathname: `/manage/jobs/${job.id}/admin`,
to={{ }}
pathname: `/manage/jobs/${job.id}/admin`, >
}} {t("menus.jobsactions.admin")}
> </Link>
{t("menus.jobsactions.admin")} </Menu.Item>
</Link> <JobsDetailHeaderActionsExportcustdataComponent job={job} />
), <JobsDetaiLheaderCsi job={job} />
}, <Menu.Item
JobsDetailHeaderActionsExportcustdataComponent({ bodyshop, job }), key="jobcosting"
JobsDetaiLheaderCsi({ job, bodyshop }), disabled={!job.converted}
{ onClick={() => {
key: "jobcosting",
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 },
@@ -419,108 +387,96 @@ export function JobsDetailHeaderActions({
jobId: job.id, jobId: job.id,
}, },
}); });
}, }}
}, >
...(job && !job.converted {t("jobs.labels.jobcosting")}
? [ </Menu.Item>
{ {job && !job.converted && (
key: "deletejob", <Menu.Item>
label: ( <Popconfirm
<Popconfirm title={t("jobs.labels.deleteconfirm")}
title={t("jobs.labels.deleteconfirm")} okText={t("general.labels.yes")}
okText={t("general.labels.yes")} cancelText={t("general.labels.no")}
cancelText={t("general.labels.no")} 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"]({
message: t("jobs.successes.delete"), message: t("jobs.successes.delete"),
}); });
//go back to jobs list. //go back to jobs list.
history.push(`/manage/`); history.push(`/manage/`);
} else { } else {
notification["error"]({ notification["error"]({
message: t("jobs.errors.deleted", { message: t("jobs.errors.deleted", {
error: JSON.stringify(result.errors), error: JSON.stringify(result.errors),
}), }),
}); });
} }
}} }}
getPopupContainer={(trigger) => trigger.parentNode} getPopupContainer={(trigger) => trigger.parentNode}
> >
{t("menus.jobsactions.deletejob")} {t("menus.jobsactions.deletejob")}
</Popconfirm> </Popconfirm>
), </Menu.Item>
}, )}
] <JobsDetailHeaderActionsAddevent jobid={job.id} />
: []), {!jobRO && job.converted && (
///////HEADER ADD EVENT ITEM <Menu.Item>
JobsDetailHeaderActionsAddevent({ jobid: job.id, bodyshop }), <Popconfirm
...(!jobRO && job.converted title={t("jobs.labels.voidjob")}
? [ okText="Yes"
{ cancelText="No"
key: "voidjob", onClick={(e) => e.stopPropagation()}
label: ( onConfirm={async () => {
<Popconfirm //delete the job.
title={t("jobs.labels.voidjob")} const result = await voidJob({
okText="Yes" variables: {
cancelText="No" jobId: job.id,
onClick={(e) => e.stopPropagation()} job: {
onConfirm={async () => { status: bodyshop.md_ro_statuses.default_void,
//delete the job. voided: true,
const result = await voidJob({ scheduled_in: null,
variables: { scheduled_completion: null,
jobId: job.id, inproduction: false,
job: { },
status: bodyshop.md_ro_statuses.default_void, note: [
voided: true, {
scheduled_in: null, jobid: job.id,
scheduled_completion: null, created_by: currentUser.email,
inproduction: false, audit: true,
}, text: t("jobs.labels.voidnote"),
note: [ },
{ ],
jobid: job.id, },
created_by: currentUser.email, });
audit: true,
text: t("jobs.labels.voidnote"),
},
],
},
});
if (!!!result.errors) {
notification["success"]({
message: t("jobs.successes.voided"),
});
//go back to jobs list.
history.push(`/manage/`);
} else {
notification["error"]({
message: t("jobs.errors.voiding", {
error: JSON.stringify(result.errors),
}),
});
}
}}
getPopupContainer={(trigger) => trigger.parentNode}
>
{t("menus.jobsactions.void")}
</Popconfirm>
),
},
]
: []),
],
};
if (!!!result.errors) {
notification["success"]({
message: t("jobs.successes.voided"),
});
//go back to jobs list.
history.push(`/manage/`);
} else {
notification["error"]({
message: t("jobs.errors.voiding", {
error: JSON.stringify(result.errors),
}),
});
}
}}
getPopupContainer={(trigger) => trigger.parentNode}
>
{t("menus.jobsactions.void")}
</Popconfirm>
</Menu.Item>
)}
</Menu>
);
return ( return (
<Dropdown menu={statusmenu} trigger={["click"]} key="changestatus"> <Dropdown overlay={statusmenu} trigger={["click"]} key="changestatus">
<Button> <Button>
<span>{t("general.labels.actions")}</span> <span>{t("general.labels.actions")}</span>

View File

@@ -1,8 +1,11 @@
import { useApolloClient, useMutation } from "@apollo/client"; import { useApolloClient, useMutation } from "@apollo/client";
import { notification } from "antd"; import { Menu, 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,
@@ -13,14 +16,30 @@ import {
openChatByPhone, openChatByPhone,
setMessage, setMessage,
} from "../../redux/messaging/messaging.actions"; } from "../../redux/messaging/messaging.actions";
import { store } from "../../redux/store"; import { selectBodyshop } from "../../redux/user/user.selectors";
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";
export default function JobsDetailHeaderCsi({ bodyshop, job }) { const mapStateToProps = createStructuredSelector({
const { t } = i18n; //currentUser: selectCurrentUser'
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();
@@ -78,35 +97,29 @@ export default function JobsDetailHeaderCsi({ bodyshop, job }) {
return; return;
} }
if (e.key === "email") if (e.key === "email")
store.dispatch( setEmailOptions({
setEmailOptions({ jobid: job.id,
jobid: job.id, messageOptions: {
messageOptions: { to: [job.ownr_ea],
to: [job.ownr_ea], replyTo: bodyshop.email,
replyTo: bodyshop.email, },
template: {
name: TemplateList("job_special").csi_invitation_action.key,
variables: {
id: result.data.insert_csi.returning[0].id,
}, },
template: { },
name: TemplateList("job_special").csi_invitation_action.key, });
variables: {
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, });
}) setMessage(
); `${window.location.protocol}//${window.location.host}/csi/${result.data.insert_csi.returning[0].id}`
store.dispatch(
setMessage(
`${window.location.protocol}//${window.location.host}/csi/${result.data.insert_csi.returning[0].id}`
)
); );
} else { } else {
notification["error"]({ notification["error"]({
@@ -127,35 +140,29 @@ export default function JobsDetailHeaderCsi({ bodyshop, job }) {
} }
} else { } else {
if (e.key === "email") if (e.key === "email")
store.dispatch( setEmailOptions({
setEmailOptions({ jobid: job.id,
jobid: job.id, messageOptions: {
messageOptions: { to: [job.ownr_ea],
to: [job.ownr_ea], replyTo: bodyshop.email,
replyTo: bodyshop.email, },
template: {
name: TemplateList("job_special").csi_invitation_action.key,
variables: {
id: job.csiinvites[0].id,
}, },
template: { },
name: TemplateList("job_special").csi_invitation_action.key, });
variables: {
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, });
}) setMessage(
); `${window.location.protocol}//${window.location.host}/csi/${job.csiinvites[0].id}`
store.dispatch(
setMessage(
`${window.location.protocol}//${window.location.host}/csi/${job.csiinvites[0].id}`
)
); );
} else { } else {
notification["error"]({ notification["error"]({
@@ -173,51 +180,61 @@ export default function JobsDetailHeaderCsi({ bodyshop, job }) {
} }
}; };
if (!HasFeatureAccess({ featureName: "csi", bodyshop })) return {}; if (!HasFeatureAccess({ featureName: "csi", bodyshop })) return <></>;
return { return (
key: "sendcsi", <Menu.SubMenu
label: t("jobs.actions.sendcsi"), key="sendcsi"
children: [ title={t("jobs.actions.sendcsi")}
{ disabled={!job.converted}
key: "email", {...props}
onClick: handleCreateCsi, >
disabled: !!!job.ownr_ea, <Menu.Item
label: t("general.labels.email"), onClick={handleCreateCsi}
}, key="email"
{ disabled={!!!job.ownr_ea}
onClick: handleCreateCsi, >
key: "text", {t("general.labels.email")}
disabled: !!!job.ownr_ph1, </Menu.Item>
label: t("general.labels.text"), <Menu.Item
}, onClick={handleCreateCsi}
{ key="text"
key: "generate", disabled={!!!job.ownr_ph1}
onClick: handleCreateCsi, >
disabled: job.csiinvites && job.csiinvites.length > 0, {t("general.labels.text")}
label: t("jobs.actions.generatecsi"), </Menu.Item>
}, <Menu.Item
{ type: "divider" }, onClick={handleCreateCsi}
...job.csiinvites.map((item, idx) => { key="generate"
return item.completedon disabled={job.csiinvites && job.csiinvites.length > 0}
? { >
key: idx, {t("jobs.actions.generatecsi")}
label: ( </Menu.Item>
<Link to={`/manage/shop/csi?responseid=${item.id}`}> <Menu.Divider />
<DateTimeFormatter>{item.completedon}</DateTimeFormatter> {job.csiinvites.map((item, idx) => {
</Link> return item.completedon ? (
), <Menu.Item key={idx}>
} <Link to={`/manage/shop/csi?responseid=${item.id}`}>
: { <DateTimeFormatter>{item.completedon}</DateTimeFormatter>
key: idx, </Link>
onClick: () => { </Menu.Item>
navigator.clipboard.writeText( ) : (
`${window.location.protocol}//${window.location.host}/csi/${item.id}` <Menu.Item
); key={idx}
}, onClick={() => {
label: t("general.actions.copylink"), navigator.clipboard.writeText(
}; `${window.location.protocol}//${window.location.host}/csi/${item.id}`
}), );
], }}
}; >
{t("general.actions.copylink")}
</Menu.Item>
);
})}
</Menu.SubMenu>
);
} }
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDetailHeaderCsi);

View File

@@ -1,13 +1,23 @@
import { notification } from "antd"; import { Menu, notification } from "antd";
import axios from "axios"; import axios from "axios";
import i18next from "i18next"; import React from "react";
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";
export default function JobsDetailHeaderActionexportCustomerData({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({});
export function JobsDetailHeaderActionexportCustomerData({
bodyshop, bodyshop,
job, job,
...props
}) { }) {
const { t } = i18next; const { t } = useTranslation();
const handleExportCustData = async (e) => { const handleExportCustData = async (e) => {
logImEXEvent("job_export_cust_data"); logImEXEvent("job_export_cust_data");
@@ -90,20 +100,18 @@ export default function JobsDetailHeaderActionexportCustomerData({
} }
}; };
return { return (
onClick: handleExportCustData, <Menu.Item
key: "exportcustdata", {...props}
disabled: !job.converted, onClick={handleExportCustData}
label: t("jobs.actions.exportcustdata"), key="exportcustdata"
}; disabled={!job.converted}
// ( >
// <Menu.Item {t("jobs.actions.exportcustdata")}
// {...props} </Menu.Item>
// 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 { DownOutlined } from "@ant-design/icons"; import { DownOutlined } from "@ant-design/icons";
import { Dropdown } from "antd"; import { Dropdown, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -18,16 +18,20 @@ export function JobsDetailRatesChangeButton({ disabled, form, bodyshop }) {
form.setFieldsValue({ ...rate, labor_rate_desc: rate.rate_label }); form.setFieldsValue({ ...rate, labor_rate_desc: rate.rate_label });
}; };
const menu = { const menu = (
onClick: handleClick, <div>
items: bodyshop.md_labor_rates.map((rate, idx) => ({ <Menu onClick={handleClick}>
key: idx, {bodyshop.md_labor_rates.map((rate, idx) => (
value: rate, <Menu.Item value={rate} key={idx}>
label: rate.rate_label, {rate.rate_label}
})), </Menu.Item>
}; ))}
</Menu>
</div>
);
return ( return (
<Dropdown menu={menu} disabled={disabled}> <Dropdown overlay={menu} disabled={disabled}>
<a <a
className="ant-dropdown-link" className="ant-dropdown-link"
href=" #" href=" #"

View File

@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import cleanAxios from "../../utils/CleanAxios"; import cleanAxios from "../../utils/CleanAxios";
import formatBytes from "../../utils/formatbytes"; import formatBytes from "../../utils/formatbytes";
//import yauzl from "yauzl"; import yauzl from "yauzl";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -36,7 +36,7 @@ export function JobsDocumentsDownloadButton({
); );
const imagesToDownload = [ const imagesToDownload = [
...galleryImages.images.filter((image) => image.isSelected), ...galleryImages.images.filter((image) => image.isSelected),
...galleryImages.other.filter((image) => image.isSelected), ...galleryImages.other.filter((image) => image.isSelected),
]; ];
function downloadProgress(progressEvent) { function downloadProgress(progressEvent) {
@@ -69,42 +69,44 @@ export function JobsDocumentsDownloadButton({
setDownload(null); setDownload(null);
if (Direct_Media_Download.treatment === "on") { if (Direct_Media_Download.treatment === "on") {
try { try {
// const parentDir = await window.showDirectoryPicker({ const parentDir = await window.showDirectoryPicker({
// id: "media", id: "media",
// startIn: "downloads", startIn: "downloads",
// }); });
// const directory = await parentDir.getDirectoryHandle(identifier, {
// create: true, const directory = await parentDir.getDirectoryHandle(identifier, {
// }); create: true,
// yauzl.fromBuffer( });
// Buffer.from(theDownloadedZip.data),
// {}, yauzl.fromBuffer(
// (err, zipFile) => { Buffer.from(theDownloadedZip.data),
// if (err) throw err; {},
// zipFile.on("entry", (entry) => { (err, zipFile) => {
// zipFile.openReadStream(entry, async (readErr, readStream) => { if (err) throw err;
// if (readErr) { zipFile.on("entry", (entry) => {
// zipFile.close(); zipFile.openReadStream(entry, async (readErr, readStream) => {
// throw readErr; if (readErr) {
// } zipFile.close();
// if (err) throw err; throw readErr;
// let fileSystemHandle = await directory.getFileHandle( }
// entry.fileName, if (err) throw err;
// { let fileSystemHandle = await directory.getFileHandle(
// create: true, entry.fileName,
// } {
// ); create: true,
// const writable = await fileSystemHandle.createWritable(); }
// readStream.on("data", async function (chunk) { );
// await writable.write(chunk); const writable = await fileSystemHandle.createWritable();
// }); readStream.on("data", async function (chunk) {
// readStream.on("end", async function () { await writable.write(chunk);
// await writable.close(); });
// }); readStream.on("end", async function () {
// }); await writable.close();
// }); });
// } });
// ); });
}
);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
standardMediaDownload(theDownloadedZip.data); standardMediaDownload(theDownloadedZip.data);
@@ -121,7 +123,7 @@ export function JobsDocumentsDownloadButton({
a.click(); a.click();
} }
}; };
return ( return (
<> <>
<Button <Button

View File

@@ -159,7 +159,7 @@ export function JobsDocumentsGalleryReassign({
); );
return ( return (
<Popover content={popContent} open={visible}> <Popover content={popContent} visible={visible}>
<Button <Button
disabled={selectedImages.length < 1} disabled={selectedImages.length < 1}
onClick={() => setVisible(true)} onClick={() => setVisible(true)}

View File

@@ -1,7 +1,7 @@
import { EditFilled, FileExcelFilled, SyncOutlined } from "@ant-design/icons"; import { EditFilled, FileExcelFilled, SyncOutlined } from "@ant-design/icons";
import { Button, Card, Col, Row, Space } from "antd"; import { Button, Card, Col, Row, Space } from "antd";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } 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 DocumentsUploadComponent from "../documents-upload/documents-upload.component"; import DocumentsUploadComponent from "../documents-upload/documents-upload.component";
import { DetermineFileType } from "../documents-upload/documents-upload.utility"; import { DetermineFileType } from "../documents-upload/documents-upload.utility";
@@ -10,8 +10,7 @@ import JobsDocumentsDownloadButton from "./jobs-document-gallery.download.compon
import JobsDocumentsGalleryReassign from "./jobs-document-gallery.reassign.component"; import JobsDocumentsGalleryReassign from "./jobs-document-gallery.reassign.component";
import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component"; import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component";
import JobsDocumentsGallerySelectAllComponent from "./jobs-documents-gallery.selectall.component"; import JobsDocumentsGallerySelectAllComponent from "./jobs-documents-gallery.selectall.component";
import Lightbox from "react-image-lightbox";
import "react-image-lightbox/style.css";
function JobsDocumentsComponent({ function JobsDocumentsComponent({
data, data,
jobId, jobId,
@@ -24,7 +23,11 @@ function JobsDocumentsComponent({
}) { }) {
const [galleryImages, setgalleryImages] = useState({ images: [], other: [] }); const [galleryImages, setgalleryImages] = useState({ images: [], other: [] });
const { t } = useTranslation(); const { t } = useTranslation();
const [modalState, setModalState] = useState({ open: false, index: 0 }); const [index, setIndex] = useState(0);
const onCurrentImageChange = (index) => {
setIndex(index);
};
useEffect(() => { useEffect(() => {
let documents = data.reduce( let documents = data.reduce(
@@ -32,12 +35,10 @@ function JobsDocumentsComponent({
const fileType = DetermineFileType(value.type); const fileType = DetermineFileType(value.type);
if (value.type.startsWith("image")) { if (value.type.startsWith("image")) {
acc.images.push({ acc.images.push({
// src: GenerateSrcUrl(value), src: GenerateSrcUrl(value),
// thumbnail: GenerateThumbUrl(value), thumbnail: GenerateThumbUrl(value),
fullsize: GenerateSrcUrl(value), thumbnailHeight: 225,
src: GenerateThumbUrl(value), thumbnailWidth: 225,
height: 225,
width: 225,
isSelected: false, isSelected: false,
key: value.key, key: value.key,
extension: value.extension, extension: value.extension,
@@ -61,8 +62,8 @@ function JobsDocumentsComponent({
const fileName = value.key.split("/").pop(); const fileName = value.key.split("/").pop();
acc.other.push({ acc.other.push({
source: GenerateSrcUrl(value), source: GenerateSrcUrl(value),
//src: "", src: "",
src: thumb, thumbnail: thumb,
tags: [ tags: [
{ {
value: fileName, value: fileName,
@@ -84,8 +85,8 @@ function JobsDocumentsComponent({
] ]
: []), : []),
], ],
height: 225, thumbnailHeight: 225,
width: 225, thumbnailWidth: 225,
isSelected: false, isSelected: false,
extension: value.extension, extension: value.extension,
@@ -147,15 +148,35 @@ function JobsDocumentsComponent({
<Card title={t("jobs.labels.documents-images")}> <Card title={t("jobs.labels.documents-images")}>
<Gallery <Gallery
images={galleryImages.images} images={galleryImages.images}
onClick={(index, item) => { backdropClosesModal={true}
setModalState({ open: true, index: index }); currentImageWillChange={onCurrentImageChange}
// window.open( customControls={[
// item.fullsize, <Button
// "_blank", key="edit-button"
// "toolbar=0,location=0,menubar=0" style={{
// ); float: "right",
zIndex: "5",
}}
onClick={() => {
const newWindow = window.open(
`${window.location.protocol}//${window.location.host}/edit?documentId=${galleryImages.images[index].id}`,
"_blank",
"noopener,noreferrer"
);
if (newWindow) newWindow.opener = null;
}}
>
<EditFilled />
</Button>,
]}
onClickImage={(props) => {
window.open(
props.target.src,
"_blank",
"toolbar=0,location=0,menubar=0"
);
}} }}
onSelect={(index, image) => { onSelectImage={(index, image) => {
setgalleryImages({ setgalleryImages({
...galleryImages, ...galleryImages,
images: galleryImages.images.map((g, idx) => images: galleryImages.images.map((g, idx) =>
@@ -170,6 +191,8 @@ function JobsDocumentsComponent({
<Card title={t("jobs.labels.documents-other")}> <Card title={t("jobs.labels.documents-other")}>
<Gallery <Gallery
images={galleryImages.other} images={galleryImages.other}
backdropClosesModal={true}
enableLightbox={false}
thumbnailStyle={() => { thumbnailStyle={() => {
return { return {
backgroundImage: <FileExcelFilled />, backgroundImage: <FileExcelFilled />,
@@ -178,14 +201,14 @@ function JobsDocumentsComponent({
cursor: "pointer", cursor: "pointer",
}; };
}} }}
onClick={(index) => { onClickThumbnail={(index) => {
window.open( window.open(
galleryImages.other[index].source, galleryImages.other[index].source,
"_blank", "_blank",
"toolbar=0,location=0,menubar=0" "toolbar=0,location=0,menubar=0"
); );
}} }}
onSelect={(index) => { onSelectImage={(index) => {
setgalleryImages({ setgalleryImages({
...galleryImages, ...galleryImages,
other: galleryImages.other.map((g, idx) => other: galleryImages.other.map((g, idx) =>
@@ -197,53 +220,6 @@ function JobsDocumentsComponent({
</Card> </Card>
</Col> </Col>
</Row> </Row>
{modalState.open && (
<Lightbox
toolbarButtons={[
<EditFilled
onClick={() => {
const newWindow = window.open(
`${window.location.protocol}//${
window.location.host
}/edit?documentId=${
galleryImages.images[modalState.index].id
}`,
"_blank",
"noopener,noreferrer"
);
if (newWindow) newWindow.opener = null;
}}
/>,
]}
mainSrc={galleryImages.images[modalState.index].fullsize}
nextSrc={
galleryImages.images[
(modalState.index + 1) % galleryImages.images.length
].fullsize
}
prevSrc={
galleryImages.images[
(modalState.index + galleryImages.images.length - 1) %
galleryImages.images.length
].fullsize
}
onCloseRequest={() => setModalState({ open: false, index: 0 })}
onMovePrevRequest={() =>
setModalState({
...modalState,
index:
(modalState.index + galleryImages.images.length - 1) %
galleryImages.images.length,
})
}
onMoveNextRequest={() =>
setModalState({
...modalState,
index: (modalState.index + 1) % galleryImages.images.length,
})
}
/>
)}
</div> </div>
); );
} }

View File

@@ -1,7 +1,7 @@
import { SyncOutlined, FileExcelFilled } from "@ant-design/icons"; import { SyncOutlined, FileExcelFilled } from "@ant-design/icons";
import { Alert, Button, Card, Space } from "antd"; import { Alert, Button, Card, Space } from "antd";
import React, { useEffect, useState } 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";
@@ -18,8 +18,6 @@ import JobsDocumentsLocalDeleteButton from "./jobs-documents-local-gallery.delet
import JobsLocalGalleryDownloadButton from "./jobs-documents-local-gallery.download"; import JobsLocalGalleryDownloadButton from "./jobs-documents-local-gallery.download";
import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component"; import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component";
import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-gallery.selectall.component"; import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-gallery.selectall.component";
import Lightbox from "react-image-lightbox";
import "react-image-lightbox/style.css";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -51,7 +49,6 @@ export function JobsDocumentsLocalGallery({
vendorid, vendorid,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [modalState, setModalState] = useState({ open: false, index: 0 });
useEffect(() => { useEffect(() => {
if (job) { if (job) {
if (invoice_number) { if (invoice_number) {
@@ -61,7 +58,6 @@ export function JobsDocumentsLocalGallery({
} }
} }
}, [job, invoice_number, getJobMedia, getBillMedia]); }, [job, invoice_number, getJobMedia, getBillMedia]);
let optimized; let optimized;
const jobMedia = const jobMedia =
allMedia && allMedia[job.id] allMedia && allMedia[job.id]
@@ -74,20 +70,12 @@ export function JobsDocumentsLocalGallery({
) { ) {
acc.images.push({ acc.images.push({
...val, ...val,
fullsize: val.src,
src: val.thumbnail,
height: val.thumbnailHeight,
width: val.thumbnailWidth,
...(val.optimized && { src: val.optimized, fullsize: val.src }), ...(val.optimized && { src: val.optimized, fullsize: val.src }),
}); });
if (val.optimized) optimized = true; if (val.optimized) optimized = true;
} else { } else {
acc.other.push({ acc.other.push({
...val, ...val,
fullsize: val.src,
src: val.thumbnail,
height: val.thumbnailHeight,
width: val.thumbnailWidth,
tags: [{ value: val.filename, title: val.filename }], tags: [{ value: val.filename, title: val.filename }],
}); });
} }
@@ -132,7 +120,8 @@ export function JobsDocumentsLocalGallery({
<Card title={t("jobs.labels.documents-images")}> <Card title={t("jobs.labels.documents-images")}>
<Gallery <Gallery
images={jobMedia.images} images={jobMedia.images}
onSelect={(index, image) => { backdropClosesModal={true}
onSelectImage={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename }); toggleMediaSelected({ jobid: job.id, filename: image.filename });
}} }}
{...(optimized && { {...(optimized && {
@@ -144,23 +133,24 @@ export function JobsDocumentsLocalGallery({
/>, />,
], ],
})} })}
onClick={(index, item) => { onClickImage={(props) => {
setModalState({ open: true, index: index }); const media = allMedia[job.id].find(
// const media = allMedia[job.id].find( (m) => m.optimized === props.target.src
// (m) => m.optimized === item.src );
// );
// window.open( window.open(
// media ? media.fullsize : item.fullsize, media ? media.src : props.target.src,
// "_blank", "_blank",
// "toolbar=0,location=0,menubar=0" "toolbar=0,location=0,menubar=0"
// ); );
}} }}
/> />
</Card> </Card>
<Card title={t("jobs.labels.documents-other")}> <Card title={t("jobs.labels.documents-other")}>
<Gallery <Gallery
images={jobMedia.other} images={jobMedia.other}
backdropClosesModal={true}
enableLightbox={false}
thumbnailStyle={() => { thumbnailStyle={() => {
return { return {
backgroundImage: <FileExcelFilled />, backgroundImage: <FileExcelFilled />,
@@ -169,48 +159,18 @@ export function JobsDocumentsLocalGallery({
cursor: "pointer", cursor: "pointer",
}; };
}} }}
onClick={(index) => { onClickThumbnail={(index) => {
window.open( window.open(
jobMedia.other[index].fullsize, jobMedia.other[index].src,
"_blank", "_blank",
"toolbar=0,location=0,menubar=0" "toolbar=0,location=0,menubar=0"
); );
}} }}
onSelect={(index, image) => { onSelectImage={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename }); toggleMediaSelected({ jobid: job.id, filename: image.filename });
}} }}
/> />
</Card> </Card>
{modalState.open && (
<Lightbox
mainSrc={jobMedia.images[modalState.index].fullsize}
nextSrc={
jobMedia.images[(modalState.index + 1) % jobMedia.images.length]
.fullsize
}
prevSrc={
jobMedia.images[
(modalState.index + jobMedia.images.length - 1) %
jobMedia.images.length
].fullsize
}
onCloseRequest={() => setModalState({ open: false, index: 0 })}
onMovePrevRequest={() =>
setModalState({
...modalState,
index:
(modalState.index + jobMedia.images.length - 1) %
jobMedia.images.length,
})
}
onMoveNextRequest={() =>
setModalState({
...modalState,
index: (modalState.index + 1) % jobMedia.images.length,
})
}
/>
)}
</div> </div>
); );
} }

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";
@@ -38,7 +38,7 @@ function JobDocumentsLocalGalleryExternal({
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => { useEffect(() => {
if (jobId) { if ( jobId) {
getJobMedia(jobId); getJobMedia(jobId);
} }
}, [jobId, getJobMedia]); }, [jobId, getJobMedia]);
@@ -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, src: val.thumbnail }); acc.push(val);
} }
return acc; return acc;
}, []) }, [])
@@ -65,6 +65,7 @@ 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

@@ -83,7 +83,7 @@ export function JobsDocumentsLocalGalleryReassign({
); );
return ( return (
<Popover content={popContent} open={visible}> <Popover content={popContent} visible={visible}>
<Button <Button
//disabled={selectedImages.length < 1} //disabled={selectedImages.length < 1}
onClick={() => setVisible(true)} onClick={() => setVisible(true)}

View File

@@ -171,8 +171,8 @@ export function LaborAllocationsAdjustmentEdit({
return ( return (
<Popover <Popover
open={visible} visible={visible}
onOpenChange={(vis) => setVisible(vis)} onVisibleChange={(vis) => setVisible(vis)}
content={overlay} content={overlay}
trigger="click" trigger="click"
> >

View File

@@ -117,7 +117,7 @@ export function NoteUpsertModalContainer({
return ( return (
<Modal <Modal
title={existingNote ? t("notes.actions.edit") : t("notes.actions.new")} title={existingNote ? t("notes.actions.edit") : t("notes.actions.new")}
open={visible} visible={visible}
okText={t("general.actions.save")} okText={t("general.actions.save")}
onOk={() => { onOk={() => {
form.submit(); form.submit();

View File

@@ -1,5 +1,5 @@
import { DownOutlined } from "@ant-design/icons"; import { DownOutlined } from "@ant-design/icons";
import { Dropdown } from "antd"; import { Dropdown, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -21,21 +21,27 @@ export function NotesPresetButton({ bodyshop, form }) {
form.setFieldsValue({ text: item.text }); form.setFieldsValue({ text: item.text });
}; };
const menu = { const menu = (
style: { <Menu
columnCount: Math.floor(bodyshop.md_notes_presets.length / 10) + 1, style={{
}, columnCount: Math.floor(bodyshop.md_notes_presets.length / 10) + 1,
items: bodyshop.md_notes_presets.map((i, idx) => ({ }}
key: idx, >
style: { breakInside: "avoid" }, {bodyshop.md_notes_presets.map((i, idx) => (
onClick: () => handleSelect(i), <Menu.Item
label: i.label, onClick={() => handleSelect(i)}
})), key={idx}
}; style={{ breakInside: "avoid" }}
>
{i.label}
</Menu.Item>
))}
</Menu>
);
return ( return (
<div> <div>
<Dropdown trigger={["click"]} menu={menu}> <Dropdown trigger={["click"]} overlay={menu}>
<a <a
className="ant-dropdown-link" className="ant-dropdown-link"
href="# " href="# "

View File

@@ -1,5 +1,4 @@
import { PageHeader } from '@ant-design/pro-layout'; import { Button, Form, notification, PageHeader } from "antd";
import { Button, Form, notification } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View File

@@ -76,7 +76,7 @@ export function PartsOrderBackorderEta({
<Popover <Popover
destroyTooltipOnHide destroyTooltipOnHide
content={popContent} content={popContent}
open={visibility} visible={visibility}
disabled={disabled} disabled={disabled}
> >
<DateFormatter>{backordered_eta}</DateFormatter> <DateFormatter>{backordered_eta}</DateFormatter>

View File

@@ -92,7 +92,7 @@ export function PartsOrderLineBackorderButton({
<Popover <Popover
destroyTooltipOnHide destroyTooltipOnHide
content={popContent} content={popContent}
open={visibility} visible={visibility}
disabled={disabled} disabled={disabled}
> >
<Button loading={loading} onClick={handlePopover}> <Button loading={loading} onClick={handlePopover}>

View File

@@ -1,7 +1,17 @@
import { DeleteFilled, EyeFilled, SyncOutlined } from "@ant-design/icons"; import { DeleteFilled, EyeFilled, SyncOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { PageHeader } from '@ant-design/pro-layout'; import {
import { Button, Card, Checkbox, Drawer, Grid, Input, Popconfirm, Space, Table } from "antd"; Button,
Card,
Checkbox,
Drawer,
Grid,
Input,
PageHeader,
Popconfirm,
Space,
Table,
} from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -461,7 +471,7 @@ export function PartsOrderListTableComponent({
<Drawer <Drawer
placement="right" placement="right"
onClose={() => handleOnRowClick(null)} onClose={() => handleOnRowClick(null)}
open={selectedpartsorder} visible={selectedpartsorder}
closable closable
width={drawerPercentage} width={drawerPercentage}
> >

View File

@@ -1,11 +1,17 @@
import { DeleteFilled, DownOutlined, WarningFilled } from "@ant-design/icons"; import { DeleteFilled, WarningFilled, DownOutlined } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import { import {
Checkbox, Divider, Dropdown, Form, Divider,
Form,
Input, Input,
InputNumber, InputNumber,
Radio, Select, Space, Radio,
Tag Space,
Tag,
Select,
Menu,
Dropdown,
Checkbox,
} from "antd"; } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -55,14 +61,17 @@ export function PartsOrderModalComponent({
form.setFieldsValue({ comments: item.props.value }); form.setFieldsValue({ comments: item.props.value });
}; };
const menu = { const menu = (
onClick: handleClick, <div>
items: bodyshop.md_parts_order_comment.map((comment, idx) => ({ <Menu onClick={handleClick}>
key: idx, {bodyshop.md_parts_order_comment.map((comment, idx) => (
value: comment.comment, <Menu.Item value={comment.comment} key={idx}>
label: comment.label, {comment.label}
})), </Menu.Item>
}; ))}
</Menu>
</div>
);
return ( return (
<div> <div>
@@ -299,7 +308,7 @@ export function PartsOrderModalComponent({
label={ label={
<Space> <Space>
{t("parts_orders.fields.comments")} {t("parts_orders.fields.comments")}
<Dropdown menu={menu}> <Dropdown overlay={menu}>
<a <a
className="ant-dropdown-link" className="ant-dropdown-link"
href=" #" href=" #"

View File

@@ -350,7 +350,7 @@ export function PartsOrderModalContainer({
return ( return (
<Modal <Modal
open={visible} visible={visible}
title={ title={
isReturn isReturn
? `${t("parts_orders.labels.returnpartsorder")} ${invoiceNumber}` ? `${t("parts_orders.labels.returnpartsorder")} ${invoiceNumber}`

View File

@@ -92,7 +92,7 @@ export function PartsReceiveModalContainer({
return ( return (
<Modal <Modal
open={visible} visible={visible}
title={t("parts_orders.labels.receive")} title={t("parts_orders.labels.receive")}
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}
onOk={() => form.submit()} onOk={() => form.submit()}

View File

@@ -90,7 +90,7 @@ export function BillMarkSelectedExported({
return ( return (
<Popconfirm <Popconfirm
open={visible} visible={visible}
title={t("general.labels.areyousure")} title={t("general.labels.areyousure")}
onCancel={() => setVisible(false)} onCancel={() => setVisible(false)}
onConfirm={handleUpdate} onConfirm={handleUpdate}

View File

@@ -93,7 +93,7 @@ export function PaymentMarkSelectedExported({
return ( return (
<Popconfirm <Popconfirm
open={visible} visible={visible}
title={t("general.labels.areyousure")} title={t("general.labels.areyousure")}
onCancel={() => setVisible(false)} onCancel={() => setVisible(false)}
onConfirm={handleUpdate} onConfirm={handleUpdate}

View File

@@ -138,7 +138,7 @@ function PaymentModalContainer({
? t("payments.labels.new") ? t("payments.labels.new")
: t("payments.labels.edit") : t("payments.labels.edit")
} }
open={visible} visible={visible}
okText={t("general.actions.save")} okText={t("general.actions.save")}
onOk={() => form.submit()} onOk={() => form.submit()}
width="50%" width="50%"

View File

@@ -1,5 +1,4 @@
import { PageHeader } from '@ant-design/pro-layout'; import { Button, Form, Input, PageHeader, Space } from "antd";
import { Button, Form, Input, Space } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";

View File

@@ -123,7 +123,7 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
</Card> </Card>
); );
return ( return (
<Popover content={content} open={isModalVisible}> <Popover content={content} visible={isModalVisible}>
<Button onClick={() => setIsModalVisible(true)}> <Button onClick={() => setIsModalVisible(true)}>
{t("printcenter.jobs.labels.labels")} {t("printcenter.jobs.labels.labels")}
</Button> </Button>

View File

@@ -28,7 +28,7 @@ export function PrintCenterModalContainer({
return ( return (
<Modal <Modal
open={visible} visible={visible}
onOk={() => toggleModalVisible()} onOk={() => toggleModalVisible()}
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}
cancelButtonProps={{ style: { display: "none" } }} cancelButtonProps={{ style: { display: "none" } }}

View File

@@ -1,5 +1,4 @@
import { PageHeader } from '@ant-design/pro-layout'; import { Button, List, PageHeader } from "antd";
import { Button, List } from "antd";
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 { connect } from "react-redux";

View File

@@ -166,7 +166,7 @@ export default function ProductionBoardKanbanCardSettings({
</div> </div>
); );
return ( return (
<Popover content={overlay} open={visible}> <Popover content={overlay} visible={visible}>
<Button loading={loading} onClick={() => setVisible(true)}> <Button loading={loading} onClick={() => setVisible(true)}>
{t("production.labels.cardsettings")} {t("production.labels.cardsettings")}
</Button> </Button>

View File

@@ -1,8 +1,7 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client"; import { useApolloClient } from "@apollo/client";
import Board, { moveCard } from "@asseinfo/react-kanban"; import Board, { moveCard } from "@asseinfo/react-kanban";
import { PageHeader } from '@ant-design/pro-layout'; import { Button, Grid, notification, PageHeader, Space, Statistic } from "antd";
import { Button, Grid, notification, Space, Statistic } from "antd";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";

View File

@@ -1,7 +1,7 @@
import { Button, Dropdown } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { Button, Dropdown, Menu } from "antd";
import dataSource from "./production-list-columns.data"; import dataSource from "./production-list-columns.data";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
@@ -47,24 +47,37 @@ export function ProductionColumnsComponent({
state: tableState, state: tableState,
activeStatuses: bodyshop.md_ro_statuses.active_statuses, activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}); });
const menu = { const menu = (
style: { <Menu
columnCount: Math.max(Math.floor(cols.length / 10), 1), onClick={handleAdd}
}, style={{
onClick: handleAdd, columnCount: Math.max(Math.floor(cols.length / 10), 1),
items: cols }}
.filter((i) => !columnKeys.includes(i.key)) >
.map((item) => ({ {cols
key: item.key, .filter((i) => !columnKeys.includes(i.key))
style: { breakInside: "avoid" }, .map((item) => (
label: item.title, <Menu.Item key={item.key} style={{ breakInside: "avoid" }}>
})), {item.title}
}; </Menu.Item>
))}
</Menu>
);
return ( return (
<div> <div>
<Dropdown menu={menu}> <Dropdown overlay={menu}>
<Button>{t("production.actions.addcolumns")}</Button> <Button>{t("production.actions.addcolumns")}</Button>
</Dropdown> </Dropdown>
</div> </div>
); );
} }
// <Transfer
// dataSource={dataSource}
// titles={["Source", "Target"]}
// targetKeys={columns.map((c) => c.key)}
// render={(item) => item.title}
// onChange={(nextTargetKeys, direction, moveKeys) => {
// setColumns(dataSource.filter((i) => nextTargetKeys.includes(i.key)));
// }}
// />

View File

@@ -1,10 +1,10 @@
import { ExclamationCircleFilled } from "@ant-design/icons"; import { ExclamationCircleFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client"; import { Dropdown, Menu } from "antd";
import { Dropdown } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { useMutation } from "@apollo/client";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { logImEXEvent } from "../../firebase/firebase.utils";
export default function ProductionListColumnAlert({ record }) { export default function ProductionListColumnAlert({ record }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -34,18 +34,15 @@ export default function ProductionListColumnAlert({ record }) {
return ( return (
<Dropdown <Dropdown
menu={{ overlay={
items: [ <Menu>
{ <Menu.Item key="toggleAlert" onClick={handleAlertToggle}>
key: "toggleAlert", {record.production_vars && record.production_vars.alert
onClick: handleAlertToggle, ? t("production.labels.alertoff")
label: : t("production.labels.alerton")}
record.production_vars && record.production_vars.alert </Menu.Item>
? t("production.labels.alertoff") </Menu>
: t("production.labels.alerton"), }
},
],
}}
trigger={["contextMenu"]} trigger={["contextMenu"]}
> >
<div <div

View File

@@ -1,9 +1,9 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Dropdown } from "antd"; import { Dropdown, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { logImEXEvent } from "../../firebase/firebase.utils";
export default function ProductionListColumnBodyPriority({ record }) { export default function ProductionListColumnBodyPriority({ record }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -31,27 +31,25 @@ export default function ProductionListColumnBodyPriority({ record }) {
return ( return (
<Dropdown <Dropdown
menu={{ overlay={
onClick: handleSetBodyPriority, <Menu onClick={handleSetBodyPriority}>
items: [ <Menu.Item key="clearBodyPriority">
{ {t("production.actions.bodypriority-clear")}
key: "clearBodyPriority", </Menu.Item>
label: t("production.actions.bodypriority-clear"), <Menu.SubMenu
}, key="set"
{ title={t("production.actions.bodypriority-set")}
key: "set", >
label: t("production.actions.bodypriority-set"), {new Array(15).fill().map((value, index) => (
children: new Array(15).fill().map((value, index) => ({ <Menu.Item key={index + 1}>
key: index + 1,
label: (
<div style={{ marginLeft: "2rem", marginRight: "2rem" }}> <div style={{ marginLeft: "2rem", marginRight: "2rem" }}>
{index + 1} {index + 1}
</div> </div>
), </Menu.Item>
})), ))}
}, </Menu.SubMenu>
], </Menu>
}} }
trigger={["click"]} trigger={["click"]}
> >
<div style={{ width: "100%", height: "19px" }}> <div style={{ width: "100%", height: "19px" }}>

View File

@@ -41,8 +41,8 @@ export default function ProductionListColumnComment({ record }) {
return ( return (
<Popover <Popover
onOpenChange={handleVisibleChange} onVisibleChange={handleVisibleChange}
open={visible} visible={visible}
content={ content={
<div style={{ width: "30em" }}> <div style={{ width: "30em" }}>
<Input.TextArea <Input.TextArea

View File

@@ -52,7 +52,7 @@ export default function ProductionListDate({
return ( return (
<Dropdown <Dropdown
//trigger={["click"]} //trigger={["click"]}
open={visible} visible={visible}
style={{ style={{
height: "19px", height: "19px",
}} }}

View File

@@ -1,9 +1,9 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Dropdown } from "antd"; import { Dropdown, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { logImEXEvent } from "../../firebase/firebase.utils";
export default function ProductionListColumnDetailPriority({ record }) { export default function ProductionListColumnDetailPriority({ record }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -31,27 +31,25 @@ export default function ProductionListColumnDetailPriority({ record }) {
return ( return (
<Dropdown <Dropdown
menu={{ overlay={
onClick: handleSetDetailPriority, <Menu onClick={handleSetDetailPriority}>
items: [ <Menu.Item key="clearDetailPriority">
{ {t("production.actions.detailpriority-clear")}
key: "clearDetailPriority", </Menu.Item>
label: t("production.actions.detailpriority-clear"), <Menu.SubMenu
}, key="set"
{ title={t("production.actions.detailpriority-set")}
key: "set", >
label: t("production.actions.detailpriority-set"), {new Array(15).fill().map((value, index) => (
children: new Array(15).fill().map((value, index) => ({ <Menu.Item key={index + 1}>
key: index + 1,
label: (
<div style={{ marginLeft: "2rem", marginRight: "2rem" }}> <div style={{ marginLeft: "2rem", marginRight: "2rem" }}>
{index + 1} {index + 1}
</div> </div>
), </Menu.Item>
})), ))}
}, </Menu.SubMenu>
], </Menu>
}} }
trigger={["click"]} trigger={["click"]}
> >
<div style={{ width: "100%", height: "19px" }}> <div style={{ width: "100%", height: "19px" }}>

View File

@@ -153,7 +153,7 @@ export function ProductionListEmpAssignment({
theEmployee = bodyshop.employees.find((e) => e.id === record[type]); theEmployee = bodyshop.employees.find((e) => e.id === record[type]);
return ( return (
<Popover destroyTooltipOnHide content={popContent} open={visibility}> <Popover destroyTooltipOnHide content={popContent} visible={visibility}>
<Spin spinning={loading}> <Spin spinning={loading}>
{record[type] ? ( {record[type] ? (
<div> <div>

View File

@@ -93,7 +93,7 @@ export function ProductionLastContacted({ currentUser, record }) {
<div> <div>
<Dropdown <Dropdown
//trigger={["click"]} //trigger={["click"]}
open={visible} visible={visible}
style={{ style={{
height: "19px", height: "19px",
}} }}

View File

@@ -1,9 +1,9 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Dropdown } from "antd"; import { Dropdown, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { logImEXEvent } from "../../firebase/firebase.utils";
export default function ProductionListColumnPaintPriority({ record }) { export default function ProductionListColumnPaintPriority({ record }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -31,27 +31,25 @@ export default function ProductionListColumnPaintPriority({ record }) {
return ( return (
<Dropdown <Dropdown
menu={{ overlay={
onClick: handleSetPaintPriority, <Menu onClick={handleSetPaintPriority}>
items: [ <Menu.Item key="clearPaintPriority">
{ {t("production.actions.paintpriority-clear")}
key: "clearPaintPriority", </Menu.Item>
label: t("production.actions.paintpriority-clear"), <Menu.SubMenu
}, key="set"
{ title={t("production.actions.paintpriority-set")}
key: "set", >
label: t("production.actions.paintpriority-set"), {new Array(15).fill().map((value, index) => (
children: new Array(15).fill().map((value, index) => ({ <Menu.Item key={index + 1}>
key: index + 1,
label: (
<div style={{ marginLeft: "2rem", marginRight: "2rem" }}> <div style={{ marginLeft: "2rem", marginRight: "2rem" }}>
{index + 1} {index + 1}
</div> </div>
), </Menu.Item>
})), ))}
}, </Menu.SubMenu>
], </Menu>
}} }
trigger={["click"]} trigger={["click"]}
> >
<div style={{ width: "100%", height: "19px" }}> <div style={{ width: "100%", height: "19px" }}>

View File

@@ -49,8 +49,8 @@ export default function ProductionListColumnProductionNote({ record }) {
return ( return (
<Popover <Popover
onOpenChange={handleVisibleChange} onVisibleChange={handleVisibleChange}
open={visible} visible={visible}
content={ content={
<div style={{ width: "30em" }}> <div style={{ width: "30em" }}>
<Input.TextArea <Input.TextArea

View File

@@ -1,5 +1,5 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Dropdown, Spin } from "antd"; import { Dropdown, Menu, Spin } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
@@ -38,14 +38,16 @@ export function ProductionListColumnCategory({ record, bodyshop }) {
return ( return (
<Dropdown <Dropdown
menu={{ overlay={
style: { maxHeight: "200px", overflowY: "auto" }, <Menu
onClick: handleSetStatus, style={{ maxHeight: "200px", overflowY: "auto" }}
items: bodyshop.md_categories.map((item) => ({ onClick={handleSetStatus}
key: item, >
label: item, {bodyshop.md_categories.map((item) => (
})), <Menu.Item key={item}>{item}</Menu.Item>
}} ))}
</Menu>
}
trigger={["click"]} trigger={["click"]}
> >
<div style={{ width: "100%", height: "19px", cursor: "pointer" }}> <div style={{ width: "100%", height: "19px", cursor: "pointer" }}>

View File

@@ -1,12 +1,12 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Dropdown, Spin } from "antd"; import { Dropdown, Menu, Spin } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings"; import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -47,14 +47,16 @@ export function ProductionListColumnStatus({
return ( return (
<Dropdown <Dropdown
menu={{ overlay={
style: { maxHeight: "200px", overflowY: "auto" }, <Menu
onClick: handleSetStatus, style={{ maxHeight: "200px", overflowY: "auto" }}
items: bodyshop.md_ro_statuses.production_statuses.map((item) => ({ onClick={handleSetStatus}
key: item, >
label: item, {bodyshop.md_ro_statuses.production_statuses.map((item) => (
})), <Menu.Item key={item}>{item}</Menu.Item>
}} ))}
</Menu>
}
trigger={["click"]} trigger={["click"]}
> >
<div style={{ width: "100%", height: "19px", cursor: "pointer" }}> <div style={{ width: "100%", height: "19px", cursor: "pointer" }}>

View File

@@ -1,6 +1,5 @@
import { useQuery } from "@apollo/client"; import { useQuery } from "@apollo/client";
import { PageHeader } from '@ant-design/pro-layout'; import { Descriptions, Drawer, Space, PageHeader, Button } from "antd";
import { Descriptions, Drawer, Space, Button } from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -91,7 +90,7 @@ export function ProductionListDetail({
placement="right" placement="right"
width={"33%"} width={"33%"}
onClose={handleClose} onClose={handleClose}
open={selected} visible={selected}
> >
{loading && <LoadingSkeleton />} {loading && <LoadingSkeleton />}
{error && <AlertComponent error={JSON.stringify(error)} />} {error && <AlertComponent error={JSON.stringify(error)} />}

View File

@@ -88,7 +88,7 @@ export function ProductionListSaveConfigButton({
); );
return ( return (
<Popover open={visible} content={popMenu}> <Popover visible={visible} content={popMenu}>
<Button loading={loading} onClick={() => setVisible(true)}> <Button loading={loading} onClick={() => setVisible(true)}>
{t("production.actions.saveconfig")} {t("production.actions.saveconfig")}
</Button> </Button>

View File

@@ -1,8 +1,8 @@
import { Button, Dropdown } from "antd"; import { Button, Dropdown, Menu } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { TemplateList } from "../../utils/TemplateConstants";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { GenerateDocument } from "../../utils/RenderTemplate"; import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
@@ -31,86 +31,98 @@ export function ProductionListPrint({ bodyshop }) {
return ( return (
<Dropdown <Dropdown
trigger="click" trigger="click"
menu={{ overlay={
items: [ <Menu>
...Object.keys(ProdTemplates).map((key) => ({ {Object.keys(ProdTemplates).map((key) => (
key: key, <Menu.Item
onClick: async () => { key={key}
setLoading(true); onClick={async () => {
await GenerateDocument(
{
name: ProdTemplates[key].key,
// variables: { id: contract.id },
},
{},
"p"
);
setLoading(false);
},
label: ProdTemplates[key].title,
})),
{
key: "prodbyonetech",
label: t("reportcenter.templates.production_by_technician_one"),
children: bodyshop.employees.map((e) => ({
key: e.id,
onClick: async () => {
setLoading(true); setLoading(true);
await GenerateDocument( await GenerateDocument(
{ {
name: production_by_technician_one.key, name: ProdTemplates[key].key,
variables: { id: e.id }, // variables: { id: contract.id },
}, },
{}, {},
"p" "p"
); );
setLoading(false); setLoading(false);
}, }}
label: `${e.first_name || ""} ${e.last_name || ""}`.trim(), >
})), {ProdTemplates[key].title}
}, </Menu.Item>
{ ))}
key: "prodbycategory", <Menu.SubMenu
label: t("reportcenter.templates.production_by_category_one"), title={t("reportcenter.templates.production_by_technician_one")}
children: bodyshop.md_categories.map((e) => ({ >
key: e, {bodyshop.employees.map((e) => (
onClick: async () => { <Menu.Item
setLoading(true); key={e.id}
await GenerateDocument( onClick={async () => {
{ setLoading(true);
name: production_by_category_one.key, await GenerateDocument(
variables: { category: e }, {
}, name: production_by_technician_one.key,
{}, variables: { id: e.id },
"p" },
); {},
setLoading(false); "p"
}, );
label: e, setLoading(false);
})), }}
}, >
{ {e.first_name} {e.last_name}
key: "prodbyrepairstatus", </Menu.Item>
label: t("reportcenter.templates.production_by_repair_status_one"), ))}
children: bodyshop.md_ro_statuses.production_statuses.map((e) => ({ </Menu.SubMenu>
key: e, <Menu.SubMenu
onClick: async () => { title={t("reportcenter.templates.production_by_category_one")}
setLoading(true); >
await GenerateDocument( {bodyshop.md_categories.map((e) => (
{ <Menu.Item
name: production_by_repair_status_one.key, key={e}
variables: { status: e }, onClick={async () => {
}, setLoading(true);
{}, await GenerateDocument(
"p" {
); name: production_by_category_one.key,
setLoading(false); variables: { category: e },
}, },
label: e, {},
})), "p"
}, );
], setLoading(false);
}} }}
>
{e}
</Menu.Item>
))}
</Menu.SubMenu>
<Menu.SubMenu
title={t("reportcenter.templates.production_by_repair_status_one")}
>
{bodyshop.md_ro_statuses.production_statuses.map((e) => (
<Menu.Item
key={e}
onClick={async () => {
setLoading(true);
await GenerateDocument(
{
name: production_by_repair_status_one.key,
variables: { status: e },
},
{},
"p"
);
setLoading(false);
}}
>
{e}
</Menu.Item>
))}
</Menu.SubMenu>
</Menu>
}
> >
<Button loading={loading}>{t("general.labels.print")}</Button> <Button loading={loading}>{t("general.labels.print")}</Button>
</Dropdown> </Dropdown>

View File

@@ -1,7 +1,15 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import { PageHeader } from '@ant-design/pro-layout'; import {
import { Button, Dropdown, Input, Space, Statistic, Table } from "antd"; Button,
Dropdown,
Input,
Menu,
PageHeader,
Space,
Statistic,
Table,
} from "antd";
import React, { useMemo, useState } from "react"; import React, { useMemo, useState } from "react";
import ReactDragListView from "react-drag-listview"; import ReactDragListView from "react-drag-listview";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -113,10 +121,13 @@ export function ProductionListTable({
const headerItem = (col) => ( const headerItem = (col) => (
<Dropdown <Dropdown
className="prod-header-dropdown" className="prod-header-dropdown"
menu={{ overlay={
onClick: removeColumn, <Menu onClick={removeColumn}>
items: [{ key: col.key, label: t("production.actions.removecolumn") }], <Menu.Item key={col.key}>
}} {t("production.actions.removecolumn")}
</Menu.Item>
</Menu>
}
trigger={["contextMenu"]} trigger={["contextMenu"]}
> >
<span>{col.title}</span> <span>{col.title}</span>

View File

@@ -2,13 +2,14 @@ import { Space } from "antd";
import Axios from "axios"; import Axios from "axios";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useCookies } from "react-cookie";
import { useHistory, useLocation } from "react-router-dom"; import { useHistory, useLocation } from "react-router-dom";
import QboSignIn from "../../assets/qbo/C2QB_green_btn_med_default.svg"; import QboSignIn from "../../assets/qbo/C2QB_green_btn_med_default.svg";
export default function QboAuthorizeComponent() { export default function QboAuthorizeComponent() {
const location = useLocation(); const location = useLocation();
const history = useHistory(); const history = useHistory();
const [, setCookie] = useCookies(["access_token", "refresh_token"]);
const handleQbSignIn = async () => { const handleQbSignIn = async () => {
const result = await Axios.post("/qbo/authorize"); const result = await Axios.post("/qbo/authorize");
@@ -41,7 +42,7 @@ export default function QboAuthorizeComponent() {
history.push({ pathname: `/manage/accounting/receivables` }); history.push({ pathname: `/manage/accounting/receivables` });
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [qs, location]); }, [qs, location, setCookie]);
return ( return (
<Space> <Space>

View File

@@ -25,7 +25,7 @@ export function ReportCenterModalContainer({
return ( return (
<Modal <Modal
open={visible} visible={visible}
title={t("printcenter.labels.reportcentermodal")} title={t("printcenter.labels.reportcentermodal")}
onOk={() => toggleModalVisible()} onOk={() => toggleModalVisible()}
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}

View File

@@ -1,13 +1,13 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Dropdown, notification } from "antd"; import { Dropdown, Menu, notification } from "antd";
import moment from "moment"; import moment from "moment";
import React from "react"; import React from "react";
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";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_APPOINTMENT_BLOCK } from "../../graphql/appointments.queries"; import { INSERT_APPOINTMENT_BLOCK } from "../../graphql/appointments.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -55,12 +55,15 @@ export function ScheduleBlockDay({
} }
}; };
const menu = (
<Menu onClick={handleMenu}>
<Menu.Item key="block">{t("appointments.actions.block")}</Menu.Item>
</Menu>
);
return ( return (
<Dropdown <Dropdown
menu={{ overlay={menu}
onClick: handleMenu,
items: [{ key: 'block', label: t("appointments.actions.block") }],
}}
disabled={alreadyBlocked} disabled={alreadyBlocked}
trigger={["contextMenu"]} trigger={["contextMenu"]}
> >

View File

@@ -1,6 +1,5 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { PageHeader } from '@ant-design/pro-layout'; import { Button, Card, Checkbox, Col, PageHeader, Row, Space } from "antd";
import { Button, Card, Checkbox, Col, Row, Space } from "antd";
import { t } from "i18next"; import { t } from "i18next";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import useLocalStorage from "../../utils/useLocalStorage"; import useLocalStorage from "../../utils/useLocalStorage";

View File

@@ -184,7 +184,7 @@ export function ScheduleJobModalContainer({
return ( return (
<Modal <Modal
open={visible} visible={visible}
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}
onOk={() => form.submit()} onOk={() => form.submit()}
width={"90%"} width={"90%"}

View File

@@ -154,7 +154,7 @@ export function ScheduleManualEvent({ bodyshop, event }) {
}; };
return ( return (
<Popover content={overlay} open={visibility}> <Popover content={overlay} visible={visibility}>
<Button loading={loading} onClick={handleClick}> <Button loading={loading} onClick={handleClick}>
{event {event
? t("appointments.actions.reschedule") ? t("appointments.actions.reschedule")

View File

@@ -90,7 +90,7 @@ export default function ScoreboardEntryEdit({ entry }) {
return ( return (
<div> <div>
<Dropdown open={visible} overlay={popContent}> <Dropdown visible={visible} overlay={popContent}>
<Button <Button
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();

View File

@@ -100,7 +100,7 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
return ( return (
<> <>
<Modal <Modal
open={state.visible} visible={state.visible}
destroyOnClose destroyOnClose
width="80%" width="80%"
cancelButtonProps={{ style: { display: "none" } }} cancelButtonProps={{ style: { display: "none" } }}

Some files were not shown because too many files have changed in this diff Show More