diff --git a/client/craco.config.js b/client/craco.config.js index 1a1a3b3cd..bcf11d876 100644 --- a/client/craco.config.js +++ b/client/craco.config.js @@ -1,6 +1,5 @@ // craco.config.js const TerserPlugin = require("terser-webpack-plugin"); -const CracoLessPlugin = require("craco-less"); const SentryWebpackPlugin = require("@sentry/webpack-plugin"); module.exports = { @@ -20,37 +19,6 @@ module.exports = { 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: { configure: (webpackConfig) => ({ diff --git a/client/package.json b/client/package.json index 0d61af0eb..881b8bb40 100644 --- a/client/package.json +++ b/client/package.json @@ -4,59 +4,55 @@ "private": true, "proxy": "http://localhost:4000", "dependencies": { + "@ant-design/pro-layout": "^7.6.1", "@apollo/client": "^3.6.9", "@asseinfo/react-kanban": "^2.2.0", - "@craco/craco": "^6.4.5", - "@fingerprintjs/fingerprintjs": "^3.3.3", + "@craco/craco": "^7.0.0", "@jsreport/browser-client": "^3.1.0", "@sentry/react": "^7.28.1", "@sentry/tracing": "^7.28.1", "@splitsoftware/splitio-react": "^1.6.0", "@tanem/react-nprogress": "^5.0.8", - "antd": "^4.22.3", + "antd": "5.1.5", "apollo-link-logger": "^2.0.0", - "axios": "^0.27.2", - "craco-less": "^1.20.0", + "axios": "^1.2.3", "dinero.js": "^1.9.1", - "dotenv": "^16.0.1", "enquire-js": "^0.2.1", "env-cmd": "^10.1.0", "exifr": "^7.1.3", "firebase": "^9.9.1", "graphql": "^16.5.0", - "i18next": "^21.8.14", - "i18next-browser-languagedetector": "^6.1.4", - "jsoneditor": "^9.9.0", + "i18next": "^22.4.6", + "i18next-browser-languagedetector": "^7.0.1", "jsreport-browser-client-dist": "^1.3.0", "libphonenumber-js": "^1.10.9", "logrocket": "^3.0.1", "markerjs2": "^2.22.0", "moment-business-days": "^1.2.0", "moment-timezone": "^0.5.34", - "normalize-url": "^7.0.3", - "phone": "^3.1.23", + "normalize-url": "^8.0.0", + "phone": "^3.1.32", "preval.macro": "^5.0.0", "prop-types": "^15.8.1", - "query-string": "^7.1.1", + "query-string": "^8.1.0", "rc-queue-anim": "^2.0.0", "rc-scroll-anim": "^2.7.6", - "react": "^17.0.2", - "react-big-calendar": "^1.5.0", + "react": "^18.2.0", + "react-big-calendar": "^1.5.2", "react-color": "^2.19.3", - "react-cookie": "^4.1.1", - "react-dom": "^17.0.2", - "react-drag-listview": "^0.2.1", - "react-grid-gallery": "^0.5.5", + "react-dom": "^18.2.0", + "react-drag-listview": "^2.0.0", + "react-grid-gallery": "^1.0.0", "react-grid-layout": "^1.3.4", - "react-i18next": "^11.18.1", - "react-icons": "^4.4.0", - "react-number-format": "^4.9.3", - "react-redux": "^7.2.8", + "react-i18next": "^12.1.1", + "react-icons": "^4.7.1", + "react-image-lightbox": "^5.1.4", + "react-number-format": "^5.1.2", + "react-redux": "^8.0.5", "react-resizable": "^3.0.4", "react-router-dom": "^5.3.0", - "react-scripts": "^4.0.3", + "react-scripts": "^5.0.1", "react-sticky": "^6.0.3", - "react-sublime-video": "^0.2.5", "react-virtualized": "^9.22.3", "recharts": "^2.1.12", "redux": "^4.2.0", @@ -68,7 +64,7 @@ "socket.io-client": "^4.5.1", "styled-components": "^5.3.5", "subscriptions-transport-ws": "^0.11.0", - "web-vitals": "^2.1.4", + "web-vitals": "^3.1.0", "workbox-background-sync": "^6.5.3", "workbox-broadcast-update": "^6.5.3", "workbox-cacheable-response": "^6.5.3", @@ -80,12 +76,12 @@ "workbox-range-requests": "^6.5.3", "workbox-routing": "^6.5.3", "workbox-strategies": "^6.5.3", - "workbox-streams": "^6.5.3", - "yauzl": "^2.10.0" + "workbox-streams": "^6.5.3" }, "scripts": { "analyze": "source-map-explorer 'build/static/js/*.js'", "start": "craco start", + "startrs": "react-scripts start", "build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco 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!'", @@ -118,8 +114,8 @@ }, "devDependencies": { "@sentry/webpack-plugin": "^1.20.0", - "@testing-library/cypress": "^8.0.3", - "cypress": "^10.3.1", + "@testing-library/cypress": "^9.0.0", + "cypress": "^12.3.0", "eslint-plugin-cypress": "^2.12.1", "react-error-overlay": "6.0.11", "redux-logger": "^3.0.6", diff --git a/client/src/components/allocations-assignment/allocations-assignment.component.jsx b/client/src/components/allocations-assignment/allocations-assignment.component.jsx index bc6273bdb..086a3f477 100644 --- a/client/src/components/allocations-assignment/allocations-assignment.component.jsx +++ b/client/src/components/allocations-assignment/allocations-assignment.component.jsx @@ -61,7 +61,7 @@ export function AllocationsAssignmentComponent({ ); return ( - + diff --git a/client/src/components/allocations-bulk-assignment/allocations-bulk-assignment.component.jsx b/client/src/components/allocations-bulk-assignment/allocations-bulk-assignment.component.jsx index 9c0bef386..5638fa4b3 100644 --- a/client/src/components/allocations-bulk-assignment/allocations-bulk-assignment.component.jsx +++ b/client/src/components/allocations-bulk-assignment/allocations-bulk-assignment.component.jsx @@ -59,7 +59,7 @@ export default connect( ); return ( - + diff --git a/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx b/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx index e92bb175b..32da29203 100644 --- a/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx +++ b/client/src/components/bill-detail-edit/bill-detail-edit-component.jsx @@ -1,5 +1,6 @@ import { useMutation, useQuery } from "@apollo/client"; -import { Button, Form, PageHeader, Popconfirm, Space } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Form, Popconfirm, Space } from "antd"; import moment from "moment"; import queryString from "query-string"; import React, { useState } from "react"; @@ -178,7 +179,7 @@ export function BillDetailEditcontainer({ form.submit()} onCancel={() => setVisible(false)} okButtonProps={{ loading: updateLoading }} diff --git a/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx b/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx index 81571f8b0..986920caa 100644 --- a/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx +++ b/client/src/components/bill-detail-edit/bill-detail-edit-return.component.jsx @@ -77,7 +77,7 @@ export function BillDetailEditReturn({ return ( <> setVisible(false)} destroyOnClose title={t("bills.actions.return")} diff --git a/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx b/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx index 4df0959c7..61c9b44f9 100644 --- a/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx +++ b/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx @@ -32,7 +32,7 @@ export default function BillDetailEditcontainer() { history.push({ search: queryString.stringify(search) }); }} destroyOnClose - visible={search.billid} + open={search.billid} > diff --git a/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx b/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx index 6fb7692a4..475560df1 100644 --- a/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx +++ b/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx @@ -344,7 +344,7 @@ function BillEnterModalContainer({ form.submit()} diff --git a/client/src/components/ca-bc-etf-table-modal/ca-bc-etf-table-modal.container.jsx b/client/src/components/ca-bc-etf-table-modal/ca-bc-etf-table-modal.container.jsx index 872d2aec3..b0e07ac65 100644 --- a/client/src/components/ca-bc-etf-table-modal/ca-bc-etf-table-modal.container.jsx +++ b/client/src/components/ca-bc-etf-table-modal/ca-bc-etf-table-modal.container.jsx @@ -70,7 +70,7 @@ export function ContractsFindModalContainer({ return ( toggleModalVisible()} diff --git a/client/src/components/ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component.jsx b/client/src/components/ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component.jsx index b1c1ebcc6..1f979aa97 100644 --- a/client/src/components/ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component.jsx +++ b/client/src/components/ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component.jsx @@ -35,7 +35,7 @@ export default function CABCpvrtCalculator({ disabled, form }) { - + menu={{ + items: [ + { + label: t( + "printcenter.courtesycarcontract.courtesy_car_inventory" + ), + key: "cc_inv", + onClick: () => GenerateDocument( { name: TemplateList("courtesycar").courtesy_car_inventory @@ -193,13 +195,10 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) { }, {}, "p" - ) - } - > - {t("printcenter.courtesycarcontract.courtesy_car_inventory")} - - - } + ), + }, + ], + }} > diff --git a/client/src/components/dashboard-grid/dashboard-grid.component.jsx b/client/src/components/dashboard-grid/dashboard-grid.component.jsx index 39a5432c2..d7b661d54 100644 --- a/client/src/components/dashboard-grid/dashboard-grid.component.jsx +++ b/client/src/components/dashboard-grid/dashboard-grid.component.jsx @@ -1,6 +1,7 @@ import Icon, { SyncOutlined } from "@ant-design/icons"; import { gql, useMutation, useQuery } from "@apollo/client"; -import { Button, Dropdown, Menu, notification, PageHeader, Space } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Dropdown, Menu, notification, Space } from "antd"; import i18next from "i18next"; import _ from "lodash"; import moment from "moment"; @@ -117,17 +118,15 @@ export function DashboardGridComponent({ currentUser, bodyshop }) { ); const existingLayoutKeys = state.items.map((i) => i.i); const addComponentOverlay = ( - - {Object.keys(componentList).map((key) => ( - - {componentList[key].label} - - ))} - + ({ + key: key, + value: key, + disabled: existingLayoutKeys.includes(key), + label: componentList[key].label, + }))} + > ); if (error) return ; @@ -283,8 +282,12 @@ const createDashboardQuery = (state) => { monthly_sales: jobs(where: {_and: [ { voided: {_eq: false}}, {date_invoiced: {_gte: "${moment() - .startOf("month").startOf('day').toISOString()}"}}, {date_invoiced: {_lte: "${moment() - .endOf("month").endOf('day').toISOString()}"}}]}) { + .startOf("month") + .startOf("day") + .toISOString()}"}}, {date_invoiced: {_lte: "${moment() + .endOf("month") + .endOf("day") + .toISOString()}"}}]}) { id ro_number date_invoiced diff --git a/client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx b/client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx index 262d1720c..8913ef8f7 100644 --- a/client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx +++ b/client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx @@ -51,7 +51,7 @@ export function DmsCdkVehicles({ bodyshop, form, socket, job }) { <> setVisible(false)} onOk={() => { form.setFieldsValue({ diff --git a/client/src/components/dms-post-form/dms-post-form.component.jsx b/client/src/components/dms-post-form/dms-post-form.component.jsx index c9043d050..5f4dd1dd5 100644 --- a/client/src/components/dms-post-form/dms-post-form.component.jsx +++ b/client/src/components/dms-post-form/dms-post-form.component.jsx @@ -6,12 +6,10 @@ import { Dropdown, Form, Input, - InputNumber, - Menu, - Select, + InputNumber, Select, Space, Statistic, - Typography, + Typography } from "antd"; import Dinero from "dinero.js"; import moment from "moment"; @@ -277,36 +275,32 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
{t("jobs.fields.dms.payer.controlnumber")}{" "} - {bodyshop.cdk_configuration.controllist && - bodyshop.cdk_configuration.controllist.map( - (key, idx) => ( - { - form.setFieldsValue({ - payers: form - .getFieldValue("payers") - .map((row, mapIndex) => { - if (index !== mapIndex) - return row; + menu={{ + items: + bodyshop.cdk_configuration.controllist && + bodyshop.cdk_configuration.controllist.map( + (key, idx) => ({ + key: idx, + label: key.name, + onClick: () => { + form.setFieldsValue({ + payers: form + .getFieldValue("payers") + .map((row, mapIndex) => { + if (index !== mapIndex) + return row; - return { - ...row, - controlnumber: - key.controlnumber, - }; - }), - }); - }} - > - {key.name} - - ) - )} - - } + return { + ...row, + controlnumber: + key.controlnumber, + }; + }), + }); + }, + }) + ), + }} > e.preventDefault()}> diff --git a/client/src/components/email-overlay/email-overlay.component.jsx b/client/src/components/email-overlay/email-overlay.component.jsx index 7db03471d..a302f5bb4 100644 --- a/client/src/components/email-overlay/email-overlay.component.jsx +++ b/client/src/components/email-overlay/email-overlay.component.jsx @@ -57,20 +57,23 @@ export function EmailOverlayComponent({ const menu = (
- - {bodyshop.employees - .filter((e) => e.user_email) - .map((e, idx) => ( - - {`${e.first_name} ${e.last_name}`} - - ))} - {bodyshop.md_to_emails.map((e, idx) => ( - - {e.label} - - ))} - + e.user_email) + .map((e, idx) => ({ + value: e.user_email, + key: idx, + label: `${e.first_name} ${e.last_name}`, + })), + ...bodyshop.md_to_emails.map((e, idx) => ({ + value: e.emails, + key: idx + "group", + label: e.label, + })), + ]} + />
); diff --git a/client/src/components/email-overlay/email-overlay.container.jsx b/client/src/components/email-overlay/email-overlay.container.jsx index 6c469c2d9..8b927e143 100644 --- a/client/src/components/email-overlay/email-overlay.container.jsx +++ b/client/src/components/email-overlay/email-overlay.container.jsx @@ -174,7 +174,7 @@ export function EmailOverlayContainer({ return ( form.submit()} diff --git a/client/src/components/form-input-number-calculator/form-input-number-calculator.component.jsx b/client/src/components/form-input-number-calculator/form-input-number-calculator.component.jsx index 5f34a43b1..36f879a6f 100644 --- a/client/src/components/form-input-number-calculator/form-input-number-calculator.component.jsx +++ b/client/src/components/form-input-number-calculator/form-input-number-calculator.component.jsx @@ -94,7 +94,7 @@ const FormInputNUmberCalculator = ( return (
- 0}> + 0}> - }> - {t("menus.header.home")} - - }> - {t("menus.header.schedule")} - - } - title={t("menus.header.jobs")} - > - }> - {t("menus.header.activejobs")} - - }> - {t("menus.header.readyjobs")} - - }> - {t("menus.header.parts-queue")} - - }> - - {t("menus.header.availablejobs")} - - - }> - {t("menus.header.newjob")} - - - }> - {t("menus.header.alljobs")} - - - }> - - {t("menus.header.productionlist")} - - - }> - - {t("menus.header.productionboard")} - - - - }> - {t("menus.header.scoreboard")} - - - } - title={t("menus.header.customers")} - > - }> - {t("menus.header.owners")} - - }> - {t("menus.header.vehicles")} - - - } - title={t("menus.header.courtesycars")} - > - }> - - {t("menus.header.courtesycars-all")} - - - }> - - {t("menus.header.courtesycars-contracts")} - - - }> - - {t("menus.header.courtesycars-newcontract")} - - - - } - title={t("menus.header.accounting")} - > - } - > - {t("menus.header.bills")} - - } - onClick={() => { - setBillEnterContext({ - actions: {}, - context: {}, - }); - }} - > - {t("menus.header.enterbills")} - - {Simple_Inventory.treatment === "on" && ( - <> - - } - > - - {t("menus.header.inventory")} - - - - )} - - }> - {t("menus.header.allpayments")} - - { - setPaymentContext({ - actions: {}, - context: null, - }); - }} - icon={} - > - {t("menus.header.enterpayment")} - - - }> - - {t("menus.header.timetickets")} - - - } - onClick={() => { - setTimeTicketContext({ - actions: {}, - context: {}, - }); - }} - > - {t("menus.header.entertimeticket")} - - - } - > - - - {t("menus.header.accounting-receivables")} - - - {(!( - (bodyshop && bodyshop.cdk_dealerid) || - (bodyshop && bodyshop.pbs_serialnumber) - ) || - DmsAp.treatment === "on") && ( - - - {t("menus.header.accounting-payables")} - - - )} - {!( - (bodyshop && bodyshop.cdk_dealerid) || - (bodyshop && bodyshop.pbs_serialnumber) - ) && ( - - - {t("menus.header.accounting-payments")} - - - )} - - - {t("menus.header.export-logs")} - - - - - }> - {t("menus.header.phonebook")} - - }> - - {t("menus.header.temporarydocs")} - - - } - > - }> - {t("menus.header.shop_config")} - - }> - {t("menus.header.dashboard")} - - } - onClick={() => { - setReportCenterContext({ - actions: {}, - context: {}, - }); - }} - > - {t("menus.header.reportcenter")} - - } - > - - {t("menus.header.shop_vendors")} - - - }> - {t("menus.header.shop_csi")} - - - - signOutStart()}> - {t("user.actions.signout")} - - { - window.open("https://help.imex.online/", "_blank"); - }} - icon={} - > - {t("menus.header.help")} - - { - window.open("https://imexrescue.com/", "_blank"); - }} - > - {t("menus.header.rescueme")} - - - {t("menus.header.shiftclock")} - - - {t("menus.currentuser.profile")} - + items={[ { - // - // - // {t("menus.currentuser.languageselector")} - // - // } - // > - // - // {t("general.languages.english")} - // - // - // {t("general.languages.french")} - // - // - // {t("general.languages.spanish")} - // - // - } - - }> - {recentItems.map((i, idx) => ( - - {i.label} - - ))} - - + key: "home", + icon: , + label: {t("menus.header.home")}, + }, + { + key: "schedule", + icon: , + label: ( + {t("menus.header.schedule")} + ), + }, + { + key: "jobssubmenu", + icon: , + label: t("menus.header.jobs"), + children: [ + { + key: "activejobs", + icon: , + label: ( + {t("menus.header.activejobs")} + ), + }, + { + key: "readyjobs", + icon: , + label: ( + + {t("menus.header.readyjobs")} + + ), + }, + { + key: "parts-queue", + icon: , + label: ( + + {t("menus.header.parts-queue")} + + ), + }, + { + key: "availablejobs", + icon: , + label: ( + + {t("menus.header.availablejobs")} + + ), + }, + { + key: "newjob", + icon: , + label: ( + {t("menus.header.newjob")} + ), + }, + { type: "divider" }, + { + key: "alljobs", + icon: , + label: ( + {t("menus.header.alljobs")} + ), + }, + { type: "divider" }, + { + key: "productionlist", + icon: , + label: ( + + {t("menus.header.productionlist")} + + ), + }, + { + key: "productionboard", + icon: , + label: ( + + {t("menus.header.productionboard")} + + ), + }, + { + type: "divider", + }, + { + key: "scoreboard", + icon: , + label: ( + + {t("menus.header.scoreboard")} + + ), + }, + ], + }, + { + key: "customers", + icon: , + label: t("menus.header.customers"), + children: [ + { + key: "owners", + icon: , + label: ( + {t("menus.header.owners")} + ), + }, + { + key: "vehicles", + icon: , + label: ( + + {t("menus.header.vehicles")} + + ), + }, + ], + }, + { + key: "ccs", + icon: , + label: t("menus.header.courtesycars"), + children: [ + { + key: "courtesycarsall", + icon: , + label: ( + + {t("menus.header.courtesycars-all")} + + ), + }, + { + key: "contracts", + icon: , + label: ( + + {t("menus.header.courtesycars-contracts")} + + ), + }, + { + key: "newcontract", + icon: , + label: ( + + {t("menus.header.courtesycars-newcontract")} + + ), + }, + ], + }, + { + key: "accounting", + icon: , + label: t("menus.header.accounting"), + children: [ + { + key: "bills", + icon: , + label: ( + {t("menus.header.bills")} + ), + }, + { + key: "enterbills", + icon: , + onClick: () => { + setBillEnterContext({ + actions: {}, + context: {}, + }); + }, + label: t("menus.header.enterbills"), + }, + ...(Simple_Inventory.treatment === "on" + ? [ + { + type: "divider", + }, + { + key: "inventory", + icon: , + label: ( + + {t("menus.header.inventory")} + + ), + }, + ] + : []), + { type: "divider" }, + { + key: "allpayments", + icon: , + label: ( + + {t("menus.header.allpayments")} + + ), + }, + { + key: "enterpayments", + onClick: () => { + setPaymentContext({ + actions: {}, + context: null, + }); + }, + icon: , + label: t("menus.header.enterpayment"), + }, + { type: "divider" }, + { + key: "timetickets", + icon: , + label: ( + + {t("menus.header.timetickets")} + + ), + }, + { + key: "entertimetickets", + icon: , + onClick: () => { + setTimeTicketContext({ + actions: {}, + context: {}, + }); + }, + label: t("menus.header.entertimeticket"), + }, + { type: "divider" }, + { + key: "accountingexport", + icon: , + + label: t("menus.header.export"), + children: [ + { + key: "receivables", + label: ( + + {t("menus.header.accounting-receivables")} + + ), + }, + ...(!( + (bodyshop && bodyshop.cdk_dealerid) || + (bodyshop && bodyshop.pbs_serialnumber) + ) || DmsAp.treatment === "on" + ? [ + { + key: "payables", + label: ( + + {t("menus.header.accounting-payables")} + + ), + }, + ] + : []), + + ...(!( + (bodyshop && bodyshop.cdk_dealerid) || + (bodyshop && bodyshop.pbs_serialnumber) + ) + ? [ + { + key: "payments", + label: ( + + {t("menus.header.accounting-payments")} + + ), + }, + ] + : []), + { + key: "export-logs", + label: ( + + {t("menus.header.export-logs")} + + ), + }, + ], + }, + ], + }, + + { + key: "phonebook", + icon: , + label: ( + {t("menus.header.phonebook")} + ), + }, + { + key: "temporarydocs", + icon: , + label: ( + + {t("menus.header.temporarydocs")} + + ), + }, + + { + key: "shopsubmenu", + icon: , + label: t("menus.header.shop"), + children: [ + { + key: "shop", + icon: , + label: ( + {t("menus.header.shop_config")} + ), + }, + { + key: "dashboard", + icon: , + label: ( + + {t("menus.header.dashboard")} + + ), + }, + { + key: "reportcenter", + icon: , + onClick: () => { + setReportCenterContext({ + actions: {}, + context: {}, + }); + }, + label: t("menus.header.reportcenter"), + }, + { + key: "shop-vendors", + icon: , + label: ( + + {t("menus.header.shop_vendors")} + + ), + }, + { + key: "shop-csi", + icon: , + label: ( + + {t("menus.header.shop_csi")} + + ), + }, + ], + }, + { + 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: , + onClick: () => { + window.open("https://help.imex.online/", "_blank"); + }, + label: t("menus.header.help"), + }, + { + key: "rescue", + onClick: () => { + window.open("https://imexrescue.com/", "_blank"); + }, + label: t("menus.header.rescueme"), + }, + { + key: "shiftclock", + label: ( + + {t("menus.header.shiftclock")} + + ), + }, + { + key: "profile", + label: ( + + {t("menus.currentuser.profile")} + + ), + }, + ], + }, + { + key: "recent", + label: , + children: recentItems.map((i, idx) => ({ + key: idx, + label: {i.label}, + })), + }, + ]} + > ); } diff --git a/client/src/components/inventory-upsert-modal/inventory-upsert-modal.container.jsx b/client/src/components/inventory-upsert-modal/inventory-upsert-modal.container.jsx index 715131b02..ea41970e3 100644 --- a/client/src/components/inventory-upsert-modal/inventory-upsert-modal.container.jsx +++ b/client/src/components/inventory-upsert-modal/inventory-upsert-modal.container.jsx @@ -103,7 +103,7 @@ export function InventoryUpsertModalContainer({ ? t("inventory.actions.edit") : t("inventory.actions.new") } - visible={visible} + open={visible} okText={t("general.actions.save")} onOk={() => { form.submit(); diff --git a/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx b/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx index af6c4fc3d..251bc8f86 100644 --- a/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx +++ b/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx @@ -96,7 +96,7 @@ export function Jobd3RdPartyModal({ bodyshop, jobId, job }) { return ( <> - +
- {bodyshop.appt_alt_transport && - bodyshop.appt_alt_transport.map((alt) => ( - {alt} - ))} - - {t("general.actions.clear")} - - ); + const menu = [ + ...(bodyshop.appt_alt_transport && + bodyshop.appt_alt_transport.map((alt) => ({ + key: alt, + label: alt, + value: alt, + onClick, + }))), + { type: "divider" }, + { key: "null", label: t("general.actions.clear"), onClick }, + ]; return ( - + e.preventDefault()}> diff --git a/client/src/components/job-at-change/schedule-event.color.component.jsx b/client/src/components/job-at-change/schedule-event.color.component.jsx index 632bbdfcf..16956b710 100644 --- a/client/src/components/job-at-change/schedule-event.color.component.jsx +++ b/client/src/components/job-at-change/schedule-event.color.component.jsx @@ -1,11 +1,11 @@ -import React from "react"; +import { DownOutlined } from "@ant-design/icons"; import { useMutation } from "@apollo/client"; -import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries"; +import { Dropdown, notification } from "antd"; +import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; -import { Dropdown, Menu, notification } from "antd"; -import { DownOutlined } from "@ant-design/icons"; +import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; const mapStateToProps = createStructuredSelector({ @@ -44,21 +44,23 @@ export function ScheduleEventColor({ bodyshop, event }) { bodyshop.appt_colors.filter((color) => color.color.hex === event.color)[0] ?.label; - const menu = ( - - {bodyshop.appt_colors && - bodyshop.appt_colors.map((color) => ( - - {color.label} - - ))} - - {t("general.actions.clear")} - - ); + const menu = { + items: [ + ...(bodyshop.appt_colors && + bodyshop.appt_colors.map((color) => ({ + style: { color: color.color.hex }, + key: color.color.hex, + label: color.label, + }))), + { type: "divider" }, + { key: "null", value: t("general.actions.clear") }, + ], + selectedKeys: [event.color], + onClick: onClick, + }; return ( - + e.preventDefault()}> {selectedColor} diff --git a/client/src/components/job-at-change/schedule-event.component.jsx b/client/src/components/job-at-change/schedule-event.component.jsx index e0c0ccc4d..3c4e9dcf9 100644 --- a/client/src/components/job-at-change/schedule-event.component.jsx +++ b/client/src/components/job-at-change/schedule-event.component.jsx @@ -2,11 +2,9 @@ import { AlertFilled } from "@ant-design/icons"; import { Button, Divider, - Dropdown, - Menu, - notification, + Dropdown, notification, Popover, - Space, + Space } from "antd"; import parsePhoneNumber from "libphonenumber-js"; import moment from "moment"; @@ -18,7 +16,7 @@ import { Link, useHistory, useLocation } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import { openChatByPhone, - setMessage, + setMessage } from "../../redux/messaging/messaging.actions"; import { setModalContext } from "../../redux/modals/modals.actions"; import { selectBodyshop } from "../../redux/user/user.selectors"; @@ -27,11 +25,11 @@ import { GenerateDocument } from "../../utils/RenderTemplate"; import { TemplateList } from "../../utils/TemplateConstants"; import ChatOpenButton from "../chat-open-button/chat-open-button.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 ScheduleAtChange from "./job-at-change.component"; import ScheduleEventColor from "./schedule-event.color.component"; import ScheduleEventNote from "./schedule-event.note.component"; -import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -149,10 +147,12 @@ export function ScheduleEventComponent({ ) : null} {event.job ? ( - { + menu={{ + items: [ + { + disabled: event.arrived, + label: t("general.labels.email"), + onClick: () => { const Template = TemplateList("job").appointment_reminder; GenerateDocument( { @@ -166,13 +166,12 @@ export function ScheduleEventComponent({ "e", event.job && event.job.id ); - }} - disabled={event.arrived} - > - {t("general.labels.email")} - - { + }, + }, + { + label: t("general.labels.sms"), + disabled: event.arrived || !bodyshop.messagingservicesid, + onClick: () => { const p = parsePhoneNumber(event.job.ownr_ph1, "CA"); if (p && p.isValid()) { openChatByPhone({ @@ -192,13 +191,10 @@ export function ScheduleEventComponent({ message: t("messaging.error.invalidphone"), }); } - }} - disabled={event.arrived || !bodyshop.messagingservicesid} - > - {t("general.labels.sms")} - - - } + }, + }, + ], + }} > @@ -249,7 +245,7 @@ export function ScheduleEventComponent({ const RegularEvent = event.isintake ? ( !event.vacation && setVisible(vis)} + open={visible} + onOpenChange={(vis) => !event.vacation && setVisible(vis)} trigger="click" content={event.block ? blockContent : popoverContent} style={{ diff --git a/client/src/components/job-costing-modal/job-costing-modal.container.jsx b/client/src/components/job-costing-modal/job-costing-modal.container.jsx index fd15d6345..1d2d8080c 100644 --- a/client/src/components/job-costing-modal/job-costing-modal.container.jsx +++ b/client/src/components/job-costing-modal/job-costing-modal.container.jsx @@ -39,7 +39,7 @@ export function JobCostingModalContainer({ return ( { toggleModalVisible(); diff --git a/client/src/components/job-detail-cards/job-detail-cards.component.jsx b/client/src/components/job-detail-cards/job-detail-cards.component.jsx index 1dc6f0ad5..a14917af0 100644 --- a/client/src/components/job-detail-cards/job-detail-cards.component.jsx +++ b/client/src/components/job-detail-cards/job-detail-cards.component.jsx @@ -76,7 +76,7 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) { return ( alphaSort(a.line_desc, b.line_desc), - onCell: (record) => ({ className: record.manual_line && "job-line-manual" }), + onCell: (record) => ({ + className: record.manual_line && "job-line-manual", + }), sortOrder: state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order, ellipsis: true, @@ -405,16 +399,17 @@ export function JobLinesComponent({ } }; - const markMenu = ( - - {t("joblines.fields.part_types.PAA")} - {t("joblines.fields.part_types.PAN")} - {t("joblines.fields.part_types.PAL")} - {t("joblines.fields.part_types.PAS")} - - {t("general.labels.clear")} - - ); + const markMenu = { + onClick: handleMark, + items: [ + { key: "PAA", label: t("joblines.fields.part_types.PAA") }, + { key: "PAN", label: t("joblines.fields.part_types.PAN") }, + { key: "PAL", label: t("joblines.fields.part_types.PAL") }, + { key: "PAS", label: t("joblines.fields.part_types.PAS") }, + { type: "divider" }, + { key: "clear", label: t("general.labels.clear") }, + ], + }; return (
@@ -544,7 +539,7 @@ export function JobLinesComponent({ > {t("jobs.actions.filterpartsonly")} - + ) : ( diff --git a/client/src/components/jobs-convert-button/jobs-convert-button.component.jsx b/client/src/components/jobs-convert-button/jobs-convert-button.component.jsx index e20523880..3e8648598 100644 --- a/client/src/components/jobs-convert-button/jobs-convert-button.component.jsx +++ b/client/src/components/jobs-convert-button/jobs-convert-button.component.jsx @@ -226,7 +226,7 @@ export function JobsConvertButton({ if (job.converted) return <>; return ( - +
- ); + const menu = { + onClick: handleClick, + items: bodyshop.md_estimators.map((est, idx) => ({ + value: est, + key: idx, + label: `${est.est_ct_fn || ""} ${est.est_ct_ln || ""}`.trim(), + })), + }; return ( - + - {bodyshop.md_filehandlers.map((est, idx) => ( - - {`${est.ins_ct_fn} ${est.ins_ct_ln}`} - - ))} - - ); + const menu = { + onClick: handleClick, + style: { + columnCount: Math.floor(bodyshop.md_filehandlers.length / 10) + 1, + }, + items: bodyshop.md_filehandlers.map((est, idx) => ({ + value: est, + key: idx, + style: { breakInside: "avoid" }, + label: `${est.ins_ct_fn || ""} ${est.ins_ct_ln || ""}`.trim(), + })), + }; return ( - + ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) -}); -export default connect( - mapStateToProps, - mapDispatchToProps -)(JobsDetailHeaderAddEvent); - -export function JobsDetailHeaderAddEvent({ bodyshop, jobid, ...props }) { +export default function JobsDetailHeaderAddEvent({ bodyshop, jobid }) { const { t } = useTranslation(); const [insertAppointment] = useMutation(INSERT_MANUAL_APPT); @@ -153,11 +138,12 @@ export function JobsDetailHeaderAddEvent({ bodyshop, jobid, ...props }) { setVisibility(true); }; - return ( - - + return { + key: "addmanualevent", + label: ( + {t("appointments.labels.manualevent")} - - - ); + + ), + }; } diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx index 5002a0e5d..24a3967dc 100644 --- a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx @@ -1,6 +1,6 @@ import { DownCircleFilled } from "@ant-design/icons"; import { useApolloClient, useMutation } from "@apollo/client"; -import { Button, Dropdown, Menu, notification, Popconfirm } from "antd"; +import { Button, Dropdown, notification, Popconfirm } from "antd"; import React, { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; @@ -13,7 +13,7 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors import { setModalContext } from "../../redux/modals/modals.actions"; import { selectBodyshop, - selectCurrentUser, + selectCurrentUser } from "../../redux/user/user.selectors"; import JobsDetailHeaderActionsAddevent from "./jobs-detail-header-actions.addevent"; import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util"; @@ -105,13 +105,14 @@ export function JobsDetailHeaderActions({ }); }; - const statusmenu = ( - - { + const statusmenu = { + items: [ + { + key: "schedule", + disabled: !jobInPreProduction || !job.converted || jobRO, + label: t("jobs.actions.schedule"), + onClick: () => { logImEXEvent("job_header_schedule"); - setScheduleContext({ actions: { refetch: refetch }, context: { @@ -120,200 +121,228 @@ export function JobsDetailHeaderActions({ alt_transport: job.alt_transport, }, }); - }} - > - {t("jobs.actions.schedule")} - - - e.stopPropagation()} - disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled} - onConfirm={async () => { - const jobUpdate = await cancelAllAppointments({ - variables: { - jobid: job.id, - job: { - date_scheduled: null, - scheduled_in: null, - scheduled_completion: null, - status: bodyshop.md_ro_statuses.default_imported, + }, + }, + { + key: "cancelappts", + disabled: job.status !== bodyshop.md_ro_statuses.default_scheduled, + label: ( + e.stopPropagation()} + disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled} + onConfirm={async () => { + const jobUpdate = await cancelAllAppointments({ + variables: { + jobid: job.id, + job: { + date_scheduled: null, + scheduled_in: null, + scheduled_completion: null, + status: bodyshop.md_ro_statuses.default_imported, + }, }, - }, - }); - if (!jobUpdate.errors) { - notification["success"]({ - message: t("appointments.successes.canceled"), }); - return; - } - }} - getPopupContainer={(trigger) => trigger.parentNode} - > - {t("menus.jobsactions.cancelallappointments")} - - - trigger.parentNode} + > + {t("menus.jobsactions.cancelallappointments")} + + ), + }, + { + key: "intake", + disabled: !!job.intakechecklist || !jobInPreProduction || !job.converted || - jobRO - } - > - {!!job.intakechecklist || - !jobInPreProduction || - !job.converted || - jobRO ? ( - t("jobs.actions.intake") - ) : ( - - {t("jobs.actions.intake")} - - )} - - - {!jobInProduction ? ( + jobRO, + label: + !!job.intakechecklist || + !jobInPreProduction || + !job.converted || + jobRO ? ( + t("jobs.actions.intake") + ) : ( + + {t("jobs.actions.intake")} + + ), + }, + { + disabled: !jobInProduction || jobRO, + key: "deliver", + label: !jobInProduction ? ( t("jobs.actions.deliver") ) : ( {t("jobs.actions.deliver")} - )} - - - - {t("jobs.actions.viewchecklist")} - - - + {t("jobs.actions.viewchecklist")} + + ), + }, + { + key: "entertimetickets", + disabled: !job.converted || - (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced) - } - onClick={() => { + (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced), + onClick: () => { logImEXEvent("job_header_enter_time_ticekts"); setTimeTicketContext({ actions: {}, context: { jobId: job.id }, }); - }} - > - {t("timetickets.actions.enter")} - - { + }, + label: t("timetickets.actions.enter"), + }, + { + key: "enterpayments", + disabled: !job.converted, + label: t("menus.header.enterpayment"), + onClick: () => { logImEXEvent("job_header_enter_payment"); setPaymentContext({ actions: {}, context: { jobid: job.id }, }); - }} - > - {t("menus.header.enterpayment")} - - - - {t("menus.jobsactions.newcccontract")} - - - {job.inproduction ? ( - AddToProduction(client, job.id, refetch, true)} - > - {t("jobs.actions.removefromproduction")} - - ) : ( - AddToProduction(client, job.id, refetch)} - > - {t("jobs.actions.addtoproduction")} - - )} - - {job.suspended + }, + }, + { + key: "cccontract", + disabled: jobRO || !job.converted, + label: ( + + {t("menus.jobsactions.newcccontract")} + + ), + }, + ...(job.inproduction + ? [ + { + key: "removetoproduction", + disabled: !job.converted, + onClick: () => AddToProduction(client, job.id, refetch, true), + label: t("jobs.actions.removefromproduction"), + }, + ] + : [ + { + key: "addtoproduction", + disabled: !job.converted, + onClick: () => AddToProduction(client, job.id, refetch), + label: t("jobs.actions.addtoproduction"), + }, + ]), + { + key: "togglesuspend", + onClick: handleSuspend, + label: job.suspended ? t("production.actions.unsuspend") - : t("production.actions.suspend")} - - - {job.production_vars && job.production_vars.alert - ? t("production.labels.alertoff") - : t("production.labels.alerton")} - - - - 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"), - }); - }, - true - ) - } - getPopupContainer={(trigger) => trigger.parentNode} - > - {t("menus.jobsactions.duplicate")} - - - - 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"), - }); + : t("production.actions.suspend"), + }, + { + key: "toggleAlert", + onClick: handleAlertToggle, + label: + job.production_vars && job.production_vars.alert + ? t("production.labels.alertoff") + : t("production.labels.alerton"), + }, + { + key: "dupe", + label: t("menus.jobsactions.duplicate"), + children: [ + { + key: "dupewithlines", + label: ( + 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"), + }); + }, + true + ) } - ) - } - getPopupContainer={(trigger) => trigger.parentNode} - > - {t("menus.jobsactions.duplicatenolines")} - - - - - { + getPopupContainer={(trigger) => trigger.parentNode} + > + {t("menus.jobsactions.duplicate")} + + ), + }, + { + key: "dupewithoutlines", + label: ( + 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")} + + ), + }, + ], + }, + { + key: "postbills", + disabled: !job.converted, + label: t("jobs.actions.postbills"), + onClick: () => { logImEXEvent("job_header_enter_bills"); setBillEnterContext({ @@ -322,14 +351,13 @@ export function JobsDetailHeaderActions({ job: job, }, }); - }} - > - {t("jobs.actions.postbills")} - - { + }, + }, + { + key: "addtopartsqueue", + disabled: !job.converted || !jobInProduction || jobRO, + label: t("jobs.actions.addtopartsqueue"), + onClick: async () => { const result = await updateJob({ variables: { jobId: job.id, @@ -348,12 +376,12 @@ export function JobsDetailHeaderActions({ }), }); } - }} - > - {t("jobs.actions.addtopartsqueue")} - - - {!jobInPostProduction ? ( + }, + }, + { + disabled: !jobInPostProduction, + key: "closejob", + label: !jobInPostProduction ? ( t("menus.jobsactions.closejob") ) : ( {t("menus.jobsactions.closejob")} - )} - - - - {t("menus.jobsactions.admin")} - - - - - { + ), + }, + { + key: "admin", + label: ( + + {t("menus.jobsactions.admin")} + + ), + }, + JobsDetailHeaderActionsExportcustdataComponent({ bodyshop, job }), + JobsDetaiLheaderCsi({ job, bodyshop }), + { + key: "jobcosting", + label: t("jobs.labels.jobcosting"), + disabled: !job.converted, + onClick: () => { logImEXEvent("job_header_job_costing"); setJobCostingContext({ actions: { refetch: refetch }, @@ -387,96 +419,108 @@ export function JobsDetailHeaderActions({ jobId: job.id, }, }); - }} - > - {t("jobs.labels.jobcosting")} - - {job && !job.converted && ( - - e.stopPropagation()} - onConfirm={async () => { - //delete the job. - const result = await deleteJob({ variables: { id: job.id } }); + }, + }, + ...(job && !job.converted + ? [ + { + key: "deletejob", + label: ( + e.stopPropagation()} + onConfirm={async () => { + //delete the job. + const result = await deleteJob({ + variables: { id: job.id }, + }); - if (!!!result.errors) { - notification["success"]({ - message: t("jobs.successes.delete"), - }); - //go back to jobs list. - history.push(`/manage/`); - } else { - notification["error"]({ - message: t("jobs.errors.deleted", { - error: JSON.stringify(result.errors), - }), - }); - } - }} - getPopupContainer={(trigger) => trigger.parentNode} - > - {t("menus.jobsactions.deletejob")} - - - )} - - {!jobRO && job.converted && ( - - e.stopPropagation()} - onConfirm={async () => { - //delete the job. - const result = await voidJob({ - variables: { - jobId: job.id, - job: { - status: bodyshop.md_ro_statuses.default_void, - voided: true, - scheduled_in: null, - scheduled_completion: null, - inproduction: false, - }, - note: [ - { - jobid: job.id, - created_by: currentUser.email, - audit: true, - text: t("jobs.labels.voidnote"), - }, - ], - }, - }); + if (!!!result.errors) { + notification["success"]({ + message: t("jobs.successes.delete"), + }); + //go back to jobs list. + history.push(`/manage/`); + } else { + notification["error"]({ + message: t("jobs.errors.deleted", { + error: JSON.stringify(result.errors), + }), + }); + } + }} + getPopupContainer={(trigger) => trigger.parentNode} + > + {t("menus.jobsactions.deletejob")} + + ), + }, + ] + : []), + ///////HEADER ADD EVENT ITEM + JobsDetailHeaderActionsAddevent({ jobid: job.id, bodyshop }), + ...(!jobRO && job.converted + ? [ + { + key: "voidjob", + label: ( + e.stopPropagation()} + onConfirm={async () => { + //delete the job. + const result = await voidJob({ + variables: { + jobId: job.id, + job: { + status: bodyshop.md_ro_statuses.default_void, + voided: true, + scheduled_in: null, + scheduled_completion: null, + inproduction: false, + }, + 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")} + + ), + }, + ] + : []), + ], + }; - 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")} - - - )} - - ); return ( - + , - ]} - onClickImage={(props) => { - window.open( - props.target.src, - "_blank", - "toolbar=0,location=0,menubar=0" - ); + onClick={(index, item) => { + setModalState({ open: true, index: index }); + // window.open( + // item.fullsize, + // "_blank", + // "toolbar=0,location=0,menubar=0" + // ); }} - onSelectImage={(index, image) => { + onSelect={(index, image) => { setgalleryImages({ ...galleryImages, images: galleryImages.images.map((g, idx) => @@ -191,8 +170,6 @@ function JobsDocumentsComponent({ { return { backgroundImage: , @@ -201,14 +178,14 @@ function JobsDocumentsComponent({ cursor: "pointer", }; }} - onClickThumbnail={(index) => { + onClick={(index) => { window.open( galleryImages.other[index].source, "_blank", "toolbar=0,location=0,menubar=0" ); }} - onSelectImage={(index) => { + onSelect={(index) => { setgalleryImages({ ...galleryImages, other: galleryImages.other.map((g, idx) => @@ -220,6 +197,53 @@ function JobsDocumentsComponent({ + {modalState.open && ( + { + 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, + }) + } + /> + )}
); } diff --git a/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx b/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx index 5370971d9..ff326043b 100644 --- a/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx +++ b/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.container.jsx @@ -1,7 +1,7 @@ import { SyncOutlined, FileExcelFilled } from "@ant-design/icons"; import { Alert, Button, Card, Space } from "antd"; -import React, { useEffect } from "react"; -import Gallery from "react-grid-gallery"; +import React, { useEffect, useState } from "react"; +import { Gallery } from "react-grid-gallery"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -18,6 +18,8 @@ import JobsDocumentsLocalDeleteButton from "./jobs-documents-local-gallery.delet import JobsLocalGalleryDownloadButton from "./jobs-documents-local-gallery.download"; import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.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({ bodyshop: selectBodyshop, @@ -49,6 +51,7 @@ export function JobsDocumentsLocalGallery({ vendorid, }) { const { t } = useTranslation(); + const [modalState, setModalState] = useState({ open: false, index: 0 }); useEffect(() => { if (job) { if (invoice_number) { @@ -58,6 +61,7 @@ export function JobsDocumentsLocalGallery({ } } }, [job, invoice_number, getJobMedia, getBillMedia]); + let optimized; const jobMedia = allMedia && allMedia[job.id] @@ -70,12 +74,20 @@ export function JobsDocumentsLocalGallery({ ) { acc.images.push({ ...val, + fullsize: val.src, + src: val.thumbnail, + height: val.thumbnailHeight, + width: val.thumbnailWidth, ...(val.optimized && { src: val.optimized, fullsize: val.src }), }); if (val.optimized) optimized = true; } else { acc.other.push({ ...val, + fullsize: val.src, + src: val.thumbnail, + height: val.thumbnailHeight, + width: val.thumbnailWidth, tags: [{ value: val.filename, title: val.filename }], }); } @@ -120,8 +132,7 @@ export function JobsDocumentsLocalGallery({ { + onSelect={(index, image) => { toggleMediaSelected({ jobid: job.id, filename: image.filename }); }} {...(optimized && { @@ -133,24 +144,23 @@ export function JobsDocumentsLocalGallery({ />, ], })} - onClickImage={(props) => { - const media = allMedia[job.id].find( - (m) => m.optimized === props.target.src - ); + onClick={(index, item) => { + setModalState({ open: true, index: index }); + // const media = allMedia[job.id].find( + // (m) => m.optimized === item.src + // ); - window.open( - media ? media.src : props.target.src, - "_blank", - "toolbar=0,location=0,menubar=0" - ); + // window.open( + // media ? media.fullsize : item.fullsize, + // "_blank", + // "toolbar=0,location=0,menubar=0" + // ); }} /> { return { backgroundImage: , @@ -159,18 +169,48 @@ export function JobsDocumentsLocalGallery({ cursor: "pointer", }; }} - onClickThumbnail={(index) => { + onClick={(index) => { window.open( - jobMedia.other[index].src, + jobMedia.other[index].fullsize, "_blank", "toolbar=0,location=0,menubar=0" ); }} - onSelectImage={(index, image) => { + onSelect={(index, image) => { toggleMediaSelected({ jobid: job.id, filename: image.filename }); }} /> + {modalState.open && ( + 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, + }) + } + /> + )}
); } diff --git a/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.external.component.jsx b/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.external.component.jsx index 46625f419..5b1b524c1 100644 --- a/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.external.component.jsx +++ b/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.external.component.jsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import Gallery from "react-grid-gallery"; +import { Gallery } from "react-grid-gallery"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -38,7 +38,7 @@ function JobDocumentsLocalGalleryExternal({ const { t } = useTranslation(); useEffect(() => { - if ( jobId) { + if (jobId) { getJobMedia(jobId); } }, [jobId, getJobMedia]); @@ -52,7 +52,7 @@ function JobDocumentsLocalGalleryExternal({ val.type.mime && val.type.mime.startsWith("image") ) { - acc.push(val); + acc.push({ ...val, src: val.thumbnail }); } return acc; }, []) @@ -65,7 +65,6 @@ function JobDocumentsLocalGalleryExternal({
{ setgalleryImages( galleryImages.map((g, idx) => diff --git a/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.reassign.component.jsx b/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.reassign.component.jsx index 74c5a52b9..550be2b87 100644 --- a/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.reassign.component.jsx +++ b/client/src/components/jobs-documents-local-gallery/jobs-documents-local-gallery.reassign.component.jsx @@ -83,7 +83,7 @@ export function JobsDocumentsLocalGalleryReassign({ ); return ( - + diff --git a/client/src/components/print-center-modal/print-center-modal.container.jsx b/client/src/components/print-center-modal/print-center-modal.container.jsx index 37dba6614..b63640c04 100644 --- a/client/src/components/print-center-modal/print-center-modal.container.jsx +++ b/client/src/components/print-center-modal/print-center-modal.container.jsx @@ -28,7 +28,7 @@ export function PrintCenterModalContainer({ return ( toggleModalVisible()} onCancel={() => toggleModalVisible()} cancelButtonProps={{ style: { display: "none" } }} diff --git a/client/src/components/print-center-speed-print/print-center-speed-print.component.jsx b/client/src/components/print-center-speed-print/print-center-speed-print.component.jsx index e2f6eadac..51bd82c3b 100644 --- a/client/src/components/print-center-speed-print/print-center-speed-print.component.jsx +++ b/client/src/components/print-center-speed-print/print-center-speed-print.component.jsx @@ -1,4 +1,5 @@ -import { Button, List, PageHeader } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, List } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; diff --git a/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx b/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx index 25c77e307..8a1f33363 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.card-settings.component.jsx @@ -166,7 +166,7 @@ export default function ProductionBoardKanbanCardSettings({
); return ( - + diff --git a/client/src/components/production-board-kanban/production-board-kanban.component.jsx b/client/src/components/production-board-kanban/production-board-kanban.component.jsx index 1abfbecbb..706da55ca 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.component.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.component.jsx @@ -1,7 +1,8 @@ import { SyncOutlined } from "@ant-design/icons"; import { useApolloClient } from "@apollo/client"; import Board, { moveCard } from "@asseinfo/react-kanban"; -import { Button, Grid, notification, PageHeader, Space, Statistic } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Grid, notification, Space, Statistic } from "antd"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; diff --git a/client/src/components/production-list-columns/production-list-columns.add.component.jsx b/client/src/components/production-list-columns/production-list-columns.add.component.jsx index 82f884673..5bf3627b0 100644 --- a/client/src/components/production-list-columns/production-list-columns.add.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.add.component.jsx @@ -1,7 +1,7 @@ +import { Button, Dropdown } from "antd"; import React from "react"; -import { Button, Dropdown, Menu } from "antd"; -import dataSource from "./production-list-columns.data"; import { useTranslation } from "react-i18next"; +import dataSource from "./production-list-columns.data"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -47,37 +47,24 @@ export function ProductionColumnsComponent({ state: tableState, activeStatuses: bodyshop.md_ro_statuses.active_statuses, }); - const menu = ( - - {cols - .filter((i) => !columnKeys.includes(i.key)) - .map((item) => ( - - {item.title} - - ))} - - ); + const menu = { + style: { + columnCount: Math.max(Math.floor(cols.length / 10), 1), + }, + onClick: handleAdd, + items: cols + .filter((i) => !columnKeys.includes(i.key)) + .map((item) => ({ + key: item.key, + style: { breakInside: "avoid" }, + label: item.title, + })), + }; return (
- +
); } - -// c.key)} -// render={(item) => item.title} -// onChange={(nextTargetKeys, direction, moveKeys) => { -// setColumns(dataSource.filter((i) => nextTargetKeys.includes(i.key))); -// }} -// /> diff --git a/client/src/components/production-list-columns/production-list-columns.alert.component.jsx b/client/src/components/production-list-columns/production-list-columns.alert.component.jsx index 8823afb46..72107b607 100644 --- a/client/src/components/production-list-columns/production-list-columns.alert.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.alert.component.jsx @@ -1,10 +1,10 @@ import { ExclamationCircleFilled } from "@ant-design/icons"; -import { Dropdown, Menu } from "antd"; +import { useMutation } from "@apollo/client"; +import { Dropdown } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; -import { useMutation } from "@apollo/client"; -import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { logImEXEvent } from "../../firebase/firebase.utils"; +import { UPDATE_JOB } from "../../graphql/jobs.queries"; export default function ProductionListColumnAlert({ record }) { const { t } = useTranslation(); @@ -34,15 +34,18 @@ export default function ProductionListColumnAlert({ record }) { return ( - - {record.production_vars && record.production_vars.alert - ? t("production.labels.alertoff") - : t("production.labels.alerton")} - - - } + menu={{ + items: [ + { + key: "toggleAlert", + onClick: handleAlertToggle, + label: + record.production_vars && record.production_vars.alert + ? t("production.labels.alertoff") + : t("production.labels.alerton"), + }, + ], + }} trigger={["contextMenu"]} >
- - {t("production.actions.bodypriority-clear")} - - - {new Array(15).fill().map((value, index) => ( - + menu={{ + onClick: handleSetBodyPriority, + items: [ + { + key: "clearBodyPriority", + label: t("production.actions.bodypriority-clear"), + }, + { + key: "set", + label: t("production.actions.bodypriority-set"), + children: new Array(15).fill().map((value, index) => ({ + key: index + 1, + label: (
{index + 1}
-
- ))} -
- - } + ), + })), + }, + ], + }} trigger={["click"]} >
diff --git a/client/src/components/production-list-columns/production-list-columns.comment.component.jsx b/client/src/components/production-list-columns/production-list-columns.comment.component.jsx index d66f72c75..4ab1b73d3 100644 --- a/client/src/components/production-list-columns/production-list-columns.comment.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.comment.component.jsx @@ -41,8 +41,8 @@ export default function ProductionListColumnComment({ record }) { return ( - - {t("production.actions.detailpriority-clear")} - - - {new Array(15).fill().map((value, index) => ( - + menu={{ + onClick: handleSetDetailPriority, + items: [ + { + key: "clearDetailPriority", + label: t("production.actions.detailpriority-clear"), + }, + { + key: "set", + label: t("production.actions.detailpriority-set"), + children: new Array(15).fill().map((value, index) => ({ + key: index + 1, + label: (
{index + 1}
-
- ))} -
- - } + ), + })), + }, + ], + }} trigger={["click"]} >
diff --git a/client/src/components/production-list-columns/production-list-columns.empassignment.component.jsx b/client/src/components/production-list-columns/production-list-columns.empassignment.component.jsx index e2ed28896..72e4f5359 100644 --- a/client/src/components/production-list-columns/production-list-columns.empassignment.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.empassignment.component.jsx @@ -153,7 +153,7 @@ export function ProductionListEmpAssignment({ theEmployee = bodyshop.employees.find((e) => e.id === record[type]); return ( - + {record[type] ? (
diff --git a/client/src/components/production-list-columns/production-list-columns.lastcontacted.component.jsx b/client/src/components/production-list-columns/production-list-columns.lastcontacted.component.jsx index 95532e0a3..6f4bd5668 100644 --- a/client/src/components/production-list-columns/production-list-columns.lastcontacted.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.lastcontacted.component.jsx @@ -93,7 +93,7 @@ export function ProductionLastContacted({ currentUser, record }) {
- - {t("production.actions.paintpriority-clear")} - - - {new Array(15).fill().map((value, index) => ( - + menu={{ + onClick: handleSetPaintPriority, + items: [ + { + key: "clearPaintPriority", + label: t("production.actions.paintpriority-clear"), + }, + { + key: "set", + label: t("production.actions.paintpriority-set"), + children: new Array(15).fill().map((value, index) => ({ + key: index + 1, + label: (
{index + 1}
-
- ))} -
- - } + ), + })), + }, + ], + }} trigger={["click"]} >
diff --git a/client/src/components/production-list-columns/production-list-columns.productionnote.component.jsx b/client/src/components/production-list-columns/production-list-columns.productionnote.component.jsx index 5ff201972..4b4762104 100644 --- a/client/src/components/production-list-columns/production-list-columns.productionnote.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.productionnote.component.jsx @@ -49,8 +49,8 @@ export default function ProductionListColumnProductionNote({ record }) { return ( - {bodyshop.md_categories.map((item) => ( - {item} - ))} - - } + menu={{ + style: { maxHeight: "200px", overflowY: "auto" }, + onClick: handleSetStatus, + items: bodyshop.md_categories.map((item) => ({ + key: item, + label: item, + })), + }} trigger={["click"]} >
diff --git a/client/src/components/production-list-columns/production-list-columns.status.component.jsx b/client/src/components/production-list-columns/production-list-columns.status.component.jsx index e29ffdbeb..18e4c8c9c 100644 --- a/client/src/components/production-list-columns/production-list-columns.status.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.status.component.jsx @@ -1,12 +1,12 @@ import { useMutation } from "@apollo/client"; -import { Dropdown, Menu, Spin } from "antd"; +import { Dropdown, Spin } from "antd"; import React, { useState } from "react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { logImEXEvent } from "../../firebase/firebase.utils"; import { UPDATE_JOB } from "../../graphql/jobs.queries"; -import { selectBodyshop } from "../../redux/user/user.selectors"; import { insertAuditTrail } from "../../redux/application/application.actions"; +import { selectBodyshop } from "../../redux/user/user.selectors"; import AuditTrailMapping from "../../utils/AuditTrailMappings"; const mapStateToProps = createStructuredSelector({ @@ -47,16 +47,14 @@ export function ProductionListColumnStatus({ return ( - {bodyshop.md_ro_statuses.production_statuses.map((item) => ( - {item} - ))} - - } + menu={{ + style: { maxHeight: "200px", overflowY: "auto" }, + onClick: handleSetStatus, + items: bodyshop.md_ro_statuses.production_statuses.map((item) => ({ + key: item, + label: item, + })), + }} trigger={["click"]} >
diff --git a/client/src/components/production-list-detail/production-list-detail.component.jsx b/client/src/components/production-list-detail/production-list-detail.component.jsx index ac5b4146c..cb2391db0 100644 --- a/client/src/components/production-list-detail/production-list-detail.component.jsx +++ b/client/src/components/production-list-detail/production-list-detail.component.jsx @@ -1,5 +1,6 @@ import { useQuery } from "@apollo/client"; -import { Descriptions, Drawer, Space, PageHeader, Button } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Descriptions, Drawer, Space, Button } from "antd"; import queryString from "query-string"; import React from "react"; import { useTranslation } from "react-i18next"; @@ -90,7 +91,7 @@ export function ProductionListDetail({ placement="right" width={"33%"} onClose={handleClose} - visible={selected} + open={selected} > {loading && } {error && } diff --git a/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx b/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx index 246177367..6db861329 100644 --- a/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx +++ b/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx @@ -88,7 +88,7 @@ export function ProductionListSaveConfigButton({ ); return ( - + diff --git a/client/src/components/production-list-table/production-list-print.component.jsx b/client/src/components/production-list-table/production-list-print.component.jsx index 4e31b33d0..355541cfc 100644 --- a/client/src/components/production-list-table/production-list-print.component.jsx +++ b/client/src/components/production-list-table/production-list-print.component.jsx @@ -1,8 +1,8 @@ -import { Button, Dropdown, Menu } from "antd"; +import { Button, Dropdown } from "antd"; import React, { useState } from "react"; -import { TemplateList } from "../../utils/TemplateConstants"; import { useTranslation } from "react-i18next"; import { GenerateDocument } from "../../utils/RenderTemplate"; +import { TemplateList } from "../../utils/TemplateConstants"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -31,98 +31,86 @@ export function ProductionListPrint({ bodyshop }) { return ( - {Object.keys(ProdTemplates).map((key) => ( - { + menu={{ + items: [ + ...Object.keys(ProdTemplates).map((key) => ({ + key: key, + onClick: async () => { + setLoading(true); + 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); await GenerateDocument( { - name: ProdTemplates[key].key, - // variables: { id: contract.id }, + name: production_by_technician_one.key, + variables: { id: e.id }, }, {}, "p" ); setLoading(false); - }} - > - {ProdTemplates[key].title} - - ))} - - {bodyshop.employees.map((e) => ( - { - setLoading(true); - await GenerateDocument( - { - name: production_by_technician_one.key, - variables: { id: e.id }, - }, - {}, - "p" - ); - setLoading(false); - }} - > - {e.first_name} {e.last_name} - - ))} - - - {bodyshop.md_categories.map((e) => ( - { - setLoading(true); - await GenerateDocument( - { - name: production_by_category_one.key, - variables: { category: e }, - }, - {}, - "p" - ); - setLoading(false); - }} - > - {e} - - ))} - - - {bodyshop.md_ro_statuses.production_statuses.map((e) => ( - { - setLoading(true); - await GenerateDocument( - { - name: production_by_repair_status_one.key, - variables: { status: e }, - }, - {}, - "p" - ); - setLoading(false); - }} - > - {e} - - ))} - - - } + }, + label: `${e.first_name || ""} ${e.last_name || ""}`.trim(), + })), + }, + { + key: "prodbycategory", + label: t("reportcenter.templates.production_by_category_one"), + children: bodyshop.md_categories.map((e) => ({ + key: e, + onClick: async () => { + setLoading(true); + await GenerateDocument( + { + name: production_by_category_one.key, + variables: { category: e }, + }, + {}, + "p" + ); + setLoading(false); + }, + label: e, + })), + }, + { + key: "prodbyrepairstatus", + label: t("reportcenter.templates.production_by_repair_status_one"), + children: bodyshop.md_ro_statuses.production_statuses.map((e) => ({ + key: e, + onClick: async () => { + setLoading(true); + await GenerateDocument( + { + name: production_by_repair_status_one.key, + variables: { status: e }, + }, + {}, + "p" + ); + setLoading(false); + }, + label: e, + })), + }, + ], + }} > diff --git a/client/src/components/production-list-table/production-list-table.component.jsx b/client/src/components/production-list-table/production-list-table.component.jsx index 6595476d5..cb0b0458f 100644 --- a/client/src/components/production-list-table/production-list-table.component.jsx +++ b/client/src/components/production-list-table/production-list-table.component.jsx @@ -1,15 +1,7 @@ import { SyncOutlined } from "@ant-design/icons"; import { useTreatments } from "@splitsoftware/splitio-react"; -import { - Button, - Dropdown, - Input, - Menu, - PageHeader, - Space, - Statistic, - Table, -} from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Dropdown, Input, Space, Statistic, Table } from "antd"; import React, { useMemo, useState } from "react"; import ReactDragListView from "react-drag-listview"; import { useTranslation } from "react-i18next"; @@ -121,13 +113,10 @@ export function ProductionListTable({ const headerItem = (col) => ( - - {t("production.actions.removecolumn")} - - - } + menu={{ + onClick: removeColumn, + items: [{ key: col.key, label: t("production.actions.removecolumn") }], + }} trigger={["contextMenu"]} > {col.title} diff --git a/client/src/components/qbo-authorize/qbo-authorize.component.jsx b/client/src/components/qbo-authorize/qbo-authorize.component.jsx index 8d333a989..307786a6a 100644 --- a/client/src/components/qbo-authorize/qbo-authorize.component.jsx +++ b/client/src/components/qbo-authorize/qbo-authorize.component.jsx @@ -2,14 +2,13 @@ import { Space } from "antd"; import Axios from "axios"; import queryString from "query-string"; import React, { useEffect } from "react"; -import { useCookies } from "react-cookie"; + import { useHistory, useLocation } from "react-router-dom"; import QboSignIn from "../../assets/qbo/C2QB_green_btn_med_default.svg"; export default function QboAuthorizeComponent() { const location = useLocation(); const history = useHistory(); - const [, setCookie] = useCookies(["access_token", "refresh_token"]); const handleQbSignIn = async () => { const result = await Axios.post("/qbo/authorize"); @@ -42,7 +41,7 @@ export default function QboAuthorizeComponent() { history.push({ pathname: `/manage/accounting/receivables` }); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [qs, location, setCookie]); + }, [qs, location]); return ( diff --git a/client/src/components/report-center-modal/report-center-modal.container.jsx b/client/src/components/report-center-modal/report-center-modal.container.jsx index f0d361785..665d51b4c 100644 --- a/client/src/components/report-center-modal/report-center-modal.container.jsx +++ b/client/src/components/report-center-modal/report-center-modal.container.jsx @@ -25,7 +25,7 @@ export function ReportCenterModalContainer({ return ( toggleModalVisible()} onCancel={() => toggleModalVisible()} diff --git a/client/src/components/schedule-block-day/schedule-block-day.component.jsx b/client/src/components/schedule-block-day/schedule-block-day.component.jsx index 548da1f9f..5c58eb3c7 100644 --- a/client/src/components/schedule-block-day/schedule-block-day.component.jsx +++ b/client/src/components/schedule-block-day/schedule-block-day.component.jsx @@ -1,13 +1,13 @@ import { useMutation } from "@apollo/client"; -import { Dropdown, Menu, notification } from "antd"; +import { Dropdown, notification } from "antd"; import moment from "moment"; import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import { logImEXEvent } from "../../firebase/firebase.utils"; import { INSERT_APPOINTMENT_BLOCK } from "../../graphql/appointments.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; -import { logImEXEvent } from "../../firebase/firebase.utils"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -55,15 +55,12 @@ export function ScheduleBlockDay({ } }; - const menu = ( - - {t("appointments.actions.block")} - - ); - return ( diff --git a/client/src/components/schedule-calendar/schedule-calendar.component.jsx b/client/src/components/schedule-calendar/schedule-calendar.component.jsx index 27830beff..81177f2c9 100644 --- a/client/src/components/schedule-calendar/schedule-calendar.component.jsx +++ b/client/src/components/schedule-calendar/schedule-calendar.component.jsx @@ -1,5 +1,6 @@ import { SyncOutlined } from "@ant-design/icons"; -import { Button, Card, Checkbox, Col, PageHeader, Row, Space } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Card, Checkbox, Col, Row, Space } from "antd"; import { t } from "i18next"; import React, { useMemo } from "react"; import useLocalStorage from "../../utils/useLocalStorage"; diff --git a/client/src/components/schedule-job-modal/schedule-job-modal.container.jsx b/client/src/components/schedule-job-modal/schedule-job-modal.container.jsx index 08bb4747e..c6b8fcdde 100644 --- a/client/src/components/schedule-job-modal/schedule-job-modal.container.jsx +++ b/client/src/components/schedule-job-modal/schedule-job-modal.container.jsx @@ -184,7 +184,7 @@ export function ScheduleJobModalContainer({ return ( toggleModalVisible()} onOk={() => form.submit()} width={"90%"} diff --git a/client/src/components/schedule-manual-event/schedule-manual-event.component.jsx b/client/src/components/schedule-manual-event/schedule-manual-event.component.jsx index c36d3549d..8afb7bfb0 100644 --- a/client/src/components/schedule-manual-event/schedule-manual-event.component.jsx +++ b/client/src/components/schedule-manual-event/schedule-manual-event.component.jsx @@ -154,7 +154,7 @@ export function ScheduleManualEvent({ bodyshop, event }) { }; return ( - + diff --git a/client/src/components/shop-templates-list/shop-templates-list.container.jsx b/client/src/components/shop-templates-list/shop-templates-list.container.jsx index 3b6331906..ca515e4ea 100644 --- a/client/src/components/shop-templates-list/shop-templates-list.container.jsx +++ b/client/src/components/shop-templates-list/shop-templates-list.container.jsx @@ -35,10 +35,10 @@ export default function ShopTemplatesListContainer({ visibleState }) { }; return ( - setVisible(false)} >
@@ -64,7 +64,7 @@ export default function ShopTemplatesListContainer({ visibleState }) { ]} > -
+
{TemplateList()[item.name].title}
{TemplateList()[item.name].description}
{TemplateList()[item.name].drivingid}
@@ -74,6 +74,6 @@ export default function ShopTemplatesListContainer({ visibleState }) { )} />
- + ) ); } diff --git a/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx b/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx index e09691a65..2dbd728c4 100644 --- a/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx +++ b/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx @@ -116,7 +116,7 @@ export function TechJobPrintTickets({ technician, event }) { }; return ( - + diff --git a/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx b/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx index 02e62d274..5802e6e52 100644 --- a/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx +++ b/client/src/components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component.jsx @@ -1,6 +1,7 @@ import { PrinterFilled } from "@ant-design/icons"; import { useQuery } from "@apollo/client"; -import { Button, Divider, Drawer, Grid, PageHeader, Tabs } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Divider, Drawer, Grid, Tabs } from "antd"; import queryString from "query-string"; import React from "react"; import { useTranslation } from "react-i18next"; @@ -73,7 +74,7 @@ export function TechLookupJobsDrawer({ bodyshop, setPrintCenterContext }) { return ( - - } - > - {t("menus.tech.login")} - - }> - {t("menus.tech.joblookup")} - - } - > - {t("menus.tech.jobclockin")} - - } - > - {t("menus.tech.shiftclockin")} - - }> - {t("menus.tech.productionlist")} - - } - > - {t("menus.tech.productionboard")} - - techLogout()} - icon={} - > - {t("menus.tech.logout")} - - + , + label: {t("menus.tech.login")}, + }, + { + key: "joblookup", + disabled: !!!technician, + icon: , + label: ( + {t("menus.tech.joblookup")} + ), + }, + { + key: "jobclockin", + disabled: !!!technician, + icon: , + label: ( + {t("menus.tech.jobclockin")} + ), + }, + { + key: "shiftclock", + disabled: !!!technician, + icon: , + label: ( + + {t("menus.tech.shiftclockin")} + + ), + }, + { + key: "prodlist", + disabled: !!!technician, + icon: , + label: ( + {t("menus.tech.productionlist")} + ), + }, + { + key: "prodboard", + disabled: !!!technician, + icon: , + label: ( + {t("menus.tech.productionboard")} + ), + }, + { + key: "logout", + disabled: !!!technician, + onClick: () => techLogout(), + icon: , + label: t("menus.tech.logout"), + }, + ]} + > ); } diff --git a/client/src/components/time-ticket-modal/time-ticket-modal.container.jsx b/client/src/components/time-ticket-modal/time-ticket-modal.container.jsx index 463b2a80c..a258e3811 100644 --- a/client/src/components/time-ticket-modal/time-ticket-modal.container.jsx +++ b/client/src/components/time-ticket-modal/time-ticket-modal.container.jsx @@ -1,5 +1,6 @@ import { useMutation, useQuery } from "@apollo/client"; -import { Button, Form, Modal, notification, PageHeader, Space } from "antd"; +import { PageHeader } from "@ant-design/pro-layout"; +import { Button, Form, Modal, notification, Space } from "antd"; import moment from "moment"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -167,8 +168,7 @@ export function TimeTicketModalContainer({ : t("timetickets.labels.new") } width={"90%"} - visible={timeTicketModal.visible} - forceRender + open={timeTicketModal.visible} onCancel={handleCancel} afterClose={() => form.resetFields()} footer={ diff --git a/client/src/components/vehicle-detail-form/vehicle-detail-form.container.jsx b/client/src/components/vehicle-detail-form/vehicle-detail-form.container.jsx index 3438d2f28..45dd1ff8e 100644 --- a/client/src/components/vehicle-detail-form/vehicle-detail-form.container.jsx +++ b/client/src/components/vehicle-detail-form/vehicle-detail-form.container.jsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; -import { Button, Form, notification, PageHeader } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Form, notification } from "antd"; import { useMutation } from "@apollo/client"; import VehicleDetailFormComponent from "./vehicle-detail-form.component"; import { useTranslation } from "react-i18next"; diff --git a/client/src/components/vendors-form/vendors-form.component.jsx b/client/src/components/vendors-form/vendors-form.component.jsx index 95693041b..4b8fd8aae 100644 --- a/client/src/components/vendors-form/vendors-form.component.jsx +++ b/client/src/components/vendors-form/vendors-form.component.jsx @@ -1,16 +1,8 @@ import { DeleteFilled } from "@ant-design/icons"; import { useApolloClient } from "@apollo/client"; import { useTreatments } from "@splitsoftware/splitio-react"; -import { - Button, - Divider, - Form, - Input, - InputNumber, - PageHeader, - Space, - Switch, -} from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Divider, Form, Input, InputNumber, Space, Switch } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; import { CHECK_VENDOR_NAME } from "../../graphql/vendors.queries"; diff --git a/client/src/index.js b/client/src/index.js index 2006eccee..f9365f2e3 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -1,9 +1,7 @@ import * as Sentry from "@sentry/react"; -//import "antd/dist/antd.css"; -import "antd/dist/antd.less"; import Dinero from "dinero.js"; import React from "react"; -import ReactDOM from "react-dom"; +import { createRoot } from "react-dom/client"; import { Provider } from "react-redux"; import { BrowserRouter } from "react-router-dom"; import { PersistGate } from "redux-persist/integration/react"; @@ -47,7 +45,10 @@ if (process.env.NODE_ENV !== "development") { }); } -ReactDOM.render( +const container = document.getElementById("root"); +const root = createRoot(container); // createRoot(container!) if you use TypeScript + +root.render( - , - document.getElementById("root") + ); - // const onServiceWorkerUpdate = (registration) => { // console.log("onServiceWorkerUpdate", registration); diff --git a/client/src/landing/Content4.jsx b/client/src/landing/Content4.jsx index 836ca4d14..ab92d2106 100644 --- a/client/src/landing/Content4.jsx +++ b/client/src/landing/Content4.jsx @@ -1,8 +1,8 @@ -import React from 'react'; -import TweenOne from 'rc-tween-one'; -import OverPack from 'rc-scroll-anim/lib/ScrollOverPack'; -import VideoPlay from 'react-sublime-video'; -import { getChildrenToRender } from './utils'; +import React from "react"; +import TweenOne from "rc-tween-one"; +import OverPack from "rc-scroll-anim/lib/ScrollOverPack"; +//import VideoPlay from 'react-sublime-video'; +import { getChildrenToRender } from "./utils"; function Content4(props) { const { ...tagProps } = props; @@ -10,13 +10,13 @@ function Content4(props) { delete tagProps.dataSource; delete tagProps.isMobile; const animation = { - y: '+=30', + y: "+=30", opacity: 0, - type: 'from', - ease: 'easeOutQuad', + type: "from", + ease: "easeOutQuad", }; const videoChildren = dataSource.video.children.video; - const videoNameArray = videoChildren.split('.'); + const videoNameArray = videoChildren.split("."); const type = videoNameArray[videoNameArray.length - 1]; return (
@@ -40,15 +40,15 @@ function Content4(props) { - ) : ( - - - - )} + ) : null + // + // + // + }
diff --git a/client/src/landing/less/custom.less b/client/src/landing/less/custom.less index 710e47c9d..38e912f72 100644 --- a/client/src/landing/less/custom.less +++ b/client/src/landing/less/custom.less @@ -1,4 +1,5 @@ -@import "~antd/lib/style/themes/default.less"; +/* @import "~antd/lib/style/themes/default.less"; +*/ @line-color: #e9e9e9; diff --git a/client/src/pages/contract-create/contract-create.page.component.jsx b/client/src/pages/contract-create/contract-create.page.component.jsx index 6703c10a8..c4b119c4b 100644 --- a/client/src/pages/contract-create/contract-create.page.component.jsx +++ b/client/src/pages/contract-create/contract-create.page.component.jsx @@ -1,4 +1,5 @@ -import { Button, Col, PageHeader, Row, Space, Form, Switch } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Col, Row, Space, Form, Switch } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; diff --git a/client/src/pages/contract-detail/contract-detail.page.component.jsx b/client/src/pages/contract-detail/contract-detail.page.component.jsx index 3cf317236..90f3b4945 100644 --- a/client/src/pages/contract-detail/contract-detail.page.component.jsx +++ b/client/src/pages/contract-detail/contract-detail.page.component.jsx @@ -1,14 +1,5 @@ -import { - Button, - Col, - Dropdown, - Form, - Menu, - PageHeader, - Row, - Space, - Typography, -} from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Col, Dropdown, Form, Row, Space, Typography } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; @@ -76,10 +67,11 @@ export function ContractDetailPage({ - + menu={{ + items: [ + { + key: "contract", + onClick: () => GenerateDocument( { name: TemplateList("courtesycarcontract") @@ -88,13 +80,15 @@ export function ContractDetailPage({ }, {}, "p" - ) - } - > - {t("contracts.actions.printcontract")} - - + ), + label: t("contracts.actions.printcontract"), + }, + { + key: "terms", + label: t( + "printcenter.courtesycarcontract.courtesy_car_terms" + ), + onClick: () => GenerateDocument( { name: TemplateList("courtesycarcontract") @@ -103,15 +97,14 @@ export function ContractDetailPage({ }, {}, "p" - ) - } - > - {t( - "printcenter.courtesycarcontract.courtesy_car_terms" - )} - - + ), + }, + { + key: "impound", + label: t( + "printcenter.courtesycarcontract.courtesy_car_impound" + ), + onClick: () => GenerateDocument( { name: TemplateList("courtesycarcontract") @@ -120,15 +113,10 @@ export function ContractDetailPage({ }, {}, "p" - ) - } - > - {t( - "printcenter.courtesycarcontract.courtesy_car_impound" - )} - - - } + ), + }, + ], + }} > diff --git a/client/src/pages/jobs-available/jobs-available.page.container.jsx b/client/src/pages/jobs-available/jobs-available.page.container.jsx index eaa14b4be..236dd91be 100644 --- a/client/src/pages/jobs-available/jobs-available.page.container.jsx +++ b/client/src/pages/jobs-available/jobs-available.page.container.jsx @@ -1,4 +1,5 @@ -import { Button, PageHeader } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button } from "antd"; import React, { useEffect } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; diff --git a/client/src/pages/jobs-close/jobs-close.component.jsx b/client/src/pages/jobs-close/jobs-close.component.jsx index f7fcbfe1a..2e028cce3 100644 --- a/client/src/pages/jobs-close/jobs-close.component.jsx +++ b/client/src/pages/jobs-close/jobs-close.component.jsx @@ -1,5 +1,6 @@ import { DeleteFilled } from "@ant-design/icons"; import { useApolloClient, useMutation } from "@apollo/client"; +import { PageHeader } from '@ant-design/pro-layout'; import { Alert, Button, @@ -9,7 +10,6 @@ import { Input, InputNumber, notification, - PageHeader, Popconfirm, Row, Select, diff --git a/client/src/pages/jobs-create/jobs-create.component.jsx b/client/src/pages/jobs-create/jobs-create.component.jsx index b170f8bc0..1d86a6d67 100644 --- a/client/src/pages/jobs-create/jobs-create.component.jsx +++ b/client/src/pages/jobs-create/jobs-create.component.jsx @@ -1,4 +1,5 @@ -import { Button, PageHeader, Result, Space, Steps } from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Result, Space, Steps } from "antd"; import React, { useContext, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; diff --git a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx index f2614e9c9..6c621d440 100644 --- a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx +++ b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx @@ -8,15 +8,8 @@ import Icon, { HistoryOutlined, SyncOutlined, } from "@ant-design/icons"; -import { - Button, - Divider, - Form, - notification, - PageHeader, - Space, - Tabs, -} from "antd"; +import { PageHeader } from '@ant-design/pro-layout'; +import { Button, Divider, Form, notification, Space, Tabs } from "antd"; import Axios from "axios"; import moment from "moment"; import queryString from "query-string"; diff --git a/client/src/pages/manage/manage.page.component.jsx b/client/src/pages/manage/manage.page.component.jsx index 7a71f2cd1..cde78cb89 100644 --- a/client/src/pages/manage/manage.page.component.jsx +++ b/client/src/pages/manage/manage.page.component.jsx @@ -1,4 +1,4 @@ -import { BackTop, Layout } from "antd"; +import { Layout } from "antd"; import preval from "preval.macro"; import React, { lazy, Suspense, useEffect } from "react"; import { useTranslation } from "react-i18next"; @@ -11,6 +11,7 @@ import ConflictComponent from "../../components/conflict/conflict.component"; import ErrorBoundary from "../../components/error-boundary/error-boundary.component"; //import FooterComponent from "../../components/footer/footer.component"; //Component Imports +import * as Sentry from "@sentry/react"; import HeaderContainer from "../../components/header/header.container"; import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; import PartnerPingComponent from "../../components/partner-ping/partner-ping.component"; @@ -20,9 +21,8 @@ import TestComponent from "../../components/_test/test.component"; import { requestForToken } from "../../firebase/firebase.utils"; import { selectBodyshop, - selectInstanceConflict, + selectInstanceConflict } from "../../redux/user/user.selectors"; -import * as Sentry from "@sentry/react"; import "./manage.page.styles.scss"; @@ -249,7 +249,7 @@ export function Manage({ match, conflict, bodyshop }) { /> @@ -403,7 +403,7 @@ export function Manage({ match, conflict, bodyshop }) { {PageContent} - +