- Merge client update into test-beta

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-01-18 19:20:08 -05:00
696 changed files with 92291 additions and 107075 deletions

View File

@@ -183,6 +183,26 @@ jobs:
- jira/notify - jira/notify
app-beta-build:
docker:
- image: cimg/node:18.18.2
resource_class: large
working_directory: ~/repo/client
steps:
- checkout:
path: ~/repo
- run:
name: Install Dependencies
command: npm i
- run: npm run build
- aws-s3/sync:
from: build
to: "s3://imex-online-beta/"
- jira/notify
test-hasura-migrate: test-hasura-migrate:
docker: docker:
- image: cimg/node:16.15.0 - image: cimg/node:16.15.0
@@ -233,6 +253,27 @@ jobs:
to: "s3://imex-online-test/" to: "s3://imex-online-test/"
- jira/notify - jira/notify
test-app-beta-build:
docker:
- image: cimg/node:18.18.2
resource_class: large
working_directory: ~/repo/client
steps:
- checkout:
path: ~/repo
- run:
name: Install Dependencies
command: npm i
- run: npm run build:test
- aws-s3/sync:
from: build
to: "s3://imex-online-test-beta/"
- jira/notify
admin-app-build: admin-app-build:
docker: docker:
- image: cimg/node:16.15.0 - image: cimg/node:16.15.0
@@ -274,6 +315,10 @@ workflows:
filters: filters:
branches: branches:
only: master only: master
- app-beta-build:
filters:
branches:
only: master-beta
- hasura-migrate: - hasura-migrate:
secret: ${HASURA_PROD_SECRET} secret: ${HASURA_PROD_SECRET}
filters: filters:
@@ -296,6 +341,10 @@ workflows:
filters: filters:
branches: branches:
only: test only: test
- test-app-beta-build:
filters:
branches:
only: test-beta
- test-hasura-migrate: - test-hasura-migrate:
secret: ${HASURA_TEST_SECRET} secret: ${HASURA_TEST_SECRET}
filters: filters:

1
client/.npmrc Normal file
View File

@@ -0,0 +1 @@
legacy-peer-deps=true

View File

@@ -2,6 +2,14 @@
const TerserPlugin = require("terser-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin");
const CracoLessPlugin = require("craco-less"); const CracoLessPlugin = require("craco-less");
const SentryWebpackPlugin = require("@sentry/webpack-plugin"); const SentryWebpackPlugin = require("@sentry/webpack-plugin");
const {convertLegacyToken} = require('@ant-design/compatible/lib');
const {theme} = require('antd/lib');
const {defaultAlgorithm, defaultSeed} = theme;
const mapToken = defaultAlgorithm(defaultSeed);
const v4Token = convertLegacyToken(mapToken);
module.exports = { module.exports = {
plugins: [ plugins: [
@@ -26,8 +34,10 @@ module.exports = {
lessLoaderOptions: { lessLoaderOptions: {
lessOptions: { lessOptions: {
modifyVars: { modifyVars: {
...v4Token,
// TODO: This will no longer work in AntD 5.0
...(process.env.NODE_ENV === "development" ...(process.env.NODE_ENV === "development"
? { "@primary-color": "#B22234" } ? {"colorPrimary": "#B22234"}
: { : {
//"@primary-color": "#1DA57A" //"@primary-color": "#1DA57A"
}), }),
@@ -53,8 +63,14 @@ module.exports = {
}, },
], ],
webpack: { webpack: {
configure: (webpackConfig) => ({ configure: (webpackConfig) => {
return {
...webpackConfig, ...webpackConfig,
// Required for Dev Server
devServer: {
...webpackConfig.devServer,
allowedHosts: 'all',
},
optimization: { optimization: {
...webpackConfig.optimization, ...webpackConfig.optimization,
// Workaround for CircleCI bug caused by the number of CPUs shown // Workaround for CircleCI bug caused by the number of CPUs shown
@@ -67,7 +83,8 @@ module.exports = {
return item; return item;
}), }),
}, },
}), };
},
}, },
devtool: "source-map", devtool: "source-map",
}; };

17
client/cypress.config.js Normal file
View File

@@ -0,0 +1,17 @@
const { defineConfig } = require('cypress')
module.exports = defineConfig({
experimentalStudio: true,
env: {
FIREBASE_USERNAME: 'cypress@imex.test',
FIREBASE_PASSWORD: 'cypress',
},
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'http://localhost:3000',
},
})

View File

@@ -1,8 +0,0 @@
{
"baseUrl": "http://localhost:3000",
"experimentalStudio": true,
"env": {
"FIREBASE_USERNAME": "cypress@imex.test",
"FIREBASE_PASSWORD": "cypress"
}
}

View File

@@ -8872,13 +8872,13 @@
│ ├─ email: luis@luisrudge.net │ ├─ email: luis@luisrudge.net
│ ├─ path: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-flexbugs-fixes │ ├─ path: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-flexbugs-fixes
│ └─ licenseFile: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-flexbugs-fixes/LICENSE │ └─ licenseFile: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-flexbugs-fixes/LICENSE
├─ postcss-focus-visible@4.0.0 ├─ postcss-focus-open@4.0.0
│ ├─ licenses: CC0-1.0 │ ├─ licenses: CC0-1.0
│ ├─ repository: https://github.com/jonathantneal/postcss-focus-visible │ ├─ repository: https://github.com/jonathantneal/postcss-focus-open
│ ├─ publisher: Jonathan Neal │ ├─ publisher: Jonathan Neal
│ ├─ email: jonathantneal@hotmail.com │ ├─ email: jonathantneal@hotmail.com
│ ├─ path: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-focus-visible │ ├─ path: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-focus-open
│ └─ licenseFile: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-focus-visible/LICENSE.md │ └─ licenseFile: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-focus-open/LICENSE.md
├─ postcss-focus-within@3.0.0 ├─ postcss-focus-within@3.0.0
│ ├─ licenses: CC0-1.0 │ ├─ licenses: CC0-1.0
│ ├─ repository: https://github.com/jonathantneal/postcss-focus-within │ ├─ repository: https://github.com/jonathantneal/postcss-focus-within

15965
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,85 +4,83 @@
"private": true, "private": true,
"proxy": "http://localhost:4000", "proxy": "http://localhost:4000",
"dependencies": { "dependencies": {
"@apollo/client": "^3.7.9", "@ant-design/compatible": "^5.1.2",
"@ant-design/pro-layout": "^7.17.16",
"@apollo/client": "^3.8.10",
"@asseinfo/react-kanban": "^2.2.0", "@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^7.0.0", "@craco/craco": "^7.1.0",
"@fingerprintjs/fingerprintjs": "^3.4.2", "@fingerprintjs/fingerprintjs": "^4.2.1",
"@jsreport/browser-client": "^3.1.0", "@jsreport/browser-client": "^3.1.0",
"@sentry/react": "^7.40.0", "@reduxjs/toolkit": "^2.0.1",
"@sentry/tracing": "^7.40.0", "@sentry/react": "^7.93.0",
"@splitsoftware/splitio-react": "^1.8.1", "@sentry/tracing": "^7.93.0",
"@tanem/react-nprogress": "^5.0.8", "@splitsoftware/splitio-react": "^1.11.0",
"antd": "^4.24.8", "@tanem/react-nprogress": "^5.0.51",
"antd": "^5.12.8",
"apollo-link-logger": "^2.0.1", "apollo-link-logger": "^2.0.1",
"axios": "^1.3.4", "axios": "^1.6.5",
"craco-less": "^2.0.0", "craco-less": "^3.0.1",
"dayjs": "^1.11.10",
"dayjs-business-days2": "^1.2.2",
"dinero.js": "^1.9.1", "dinero.js": "^1.9.1",
"dotenv": "^16.0.1", "dotenv": "^16.3.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.17.1", "firebase": "^10.7.2",
"graphql": "^16.6.0", "graphql": "^16.6.0",
"i18next": "^22.4.10", "i18next": "^23.7.16",
"i18next-browser-languagedetector": "^7.0.1", "i18next-browser-languagedetector": "^7.0.2",
"jsoneditor": "^9.9.0", "jsoneditor": "^10.0.0",
"jsreport-browser-client-dist": "^1.3.0", "jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.10.21", "libphonenumber-js": "^1.10.53",
"logrocket": "^3.0.1", "logrocket": "^7.0.0",
"markerjs2": "^2.28.1", "markerjs2": "^2.31.4",
"moment-business-days": "^1.2.0",
"moment-timezone": "^0.5.41",
"normalize-url": "^8.0.0", "normalize-url": "^8.0.0",
"phone": "^3.1.35", "phone": "^3.1.42",
"preval.macro": "^5.0.0", "preval.macro": "^5.0.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"query-string": "^7.1.3", "query-string": "^8.1.0",
"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": "^17.0.2", "react": "^18.2.0",
"react-big-calendar": "^1.6.8", "react-big-calendar": "^1.8.6",
"react-color": "^2.19.3", "react-color": "^2.19.3",
"react-cookie": "^4.1.1", "react-cookie": "^7.0.1",
"react-dom": "^17.0.2", "react-dom": "^18.2.0",
"react-drag-listview": "^0.2.1", "react-drag-listview": "^2.0.0",
"react-grid-gallery": "^1.0.0", "react-grid-gallery": "^1.0.0",
"react-grid-layout": "^1.3.4", "react-grid-layout": "1.3.4",
"react-i18next": "^12.2.0", "react-i18next": "^14.0.0",
"react-icons": "^4.7.1", "react-icons": "^5.0.1",
"react-image-lightbox": "^5.1.4", "react-image-lightbox": "^5.1.4",
"react-intersection-observer": "^9.4.3", "react-intersection-observer": "^9.5.3",
"react-number-format": "^5.1.3", "react-number-format": "^5.1.4",
"react-redux": "^8.0.5", "react-redux": "^9.1.0",
"react-resizable": "^3.0.4", "react-resizable": "^3.0.5",
"react-router-dom": "^5.3.0", "react-router-dom": "^6.21.3",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"react-sticky": "^6.0.3", "react-sticky": "^6.0.3",
"react-sublime-video": "^0.2.5", "react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3", "react-virtualized": "^9.22.5",
"recharts": "^2.4.3", "recharts": "^2.10.4",
"redux": "^4.2.1", "redux": "^5.0.1",
"redux-persist": "^6.0.0", "redux-persist": "^6.0.0",
"redux-saga": "^1.2.2", "redux-saga": "^1.3.0",
"redux-state-sync": "^3.1.4", "redux-state-sync": "^3.1.4",
"reselect": "^4.1.7", "reselect": "^5.1.0",
"sass": "^1.58.3", "sass": "^1.70.0",
"socket.io-client": "^4.6.1", "socket.io-client": "^4.7.4",
"styled-components": "^5.3.6", "styled-components": "^6.1.8",
"subscriptions-transport-ws": "^0.11.0", "subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.4", "terser-webpack-plugin": "^5.3.10",
"workbox-background-sync": "^6.5.3", "web-vitals": "^3.5.1",
"workbox-broadcast-update": "^6.5.3", "workbox-core": "^7.0.0",
"workbox-cacheable-response": "^6.5.3", "workbox-expiration": "^7.0.0",
"workbox-core": "^6.5.3", "workbox-navigation-preload": "^7.0.0",
"workbox-expiration": "^6.5.3", "workbox-precaching": "^7.0.0",
"workbox-google-analytics": "^6.5.3", "workbox-routing": "^7.0.0",
"workbox-navigation-preload": "^6.5.3", "workbox-strategies": "^7.0.0",
"workbox-precaching": "^6.5.3",
"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" "yauzl": "^2.10.0"
}, },
"scripts": { "scripts": {
@@ -119,12 +117,13 @@
"react-error-overlay": "6.0.9" "react-error-overlay": "6.0.9"
}, },
"devDependencies": { "devDependencies": {
"@sentry/webpack-plugin": "^1.20.0", "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@testing-library/cypress": "^8.0.3", "@sentry/webpack-plugin": "^2.10.2",
"cypress": "^10.3.1", "@testing-library/cypress": "^10.0.1",
"eslint-plugin-cypress": "^2.12.1", "cypress": "^13.6.3",
"eslint-plugin-cypress": "^2.15.1",
"react-error-overlay": "6.0.11", "react-error-overlay": "6.0.11",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.2" "source-map-explorer": "^2.5.3"
} }
} }

View File

@@ -190,7 +190,7 @@ This package contains the following license and notice below:
# @firebase/logger # @firebase/logger
This package serves as the base of all logging in the JS SDK. Any logging that This package serves as the base of all logging in the JS SDK. Any logging that
is intended to be visible to Firebase end developers should go through this is intended to be open to Firebase end developers should go through this
module. module.
## Basic Usage ## Basic Usage
@@ -9375,7 +9375,7 @@ parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying. a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices" An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible to the extent that it includes a convenient and prominently open
feature that (1) displays an appropriate copyright notice, and (2) feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the extent that warranties are provided), that licensees may convey the

View File

@@ -1029,7 +1029,7 @@ The following NPM packages may be included in this product:
- postcss-dir-pseudo-class@5.0.0 - postcss-dir-pseudo-class@5.0.0
- postcss-double-position-gradients@1.0.0 - postcss-double-position-gradients@1.0.0
- postcss-env-function@2.0.2 - postcss-env-function@2.0.2
- postcss-focus-visible@4.0.0 - postcss-focus-open@4.0.0
- postcss-focus-within@3.0.0 - postcss-focus-within@3.0.0
- postcss-gap-properties@2.0.0 - postcss-gap-properties@2.0.0
- postcss-image-set-function@3.0.1 - postcss-image-set-function@3.0.1
@@ -1699,7 +1699,7 @@ This package contains the following license and notice below:
# @firebase/logger # @firebase/logger
This package serves as the base of all logging in the JS SDK. Any logging that This package serves as the base of all logging in the JS SDK. Any logging that
is intended to be visible to Firebase end developers should go through this is intended to be open to Firebase end developers should go through this
module. module.
## Basic Usage ## Basic Usage
@@ -24029,7 +24029,7 @@ parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying. a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices" An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible to the extent that it includes a convenient and prominently open
feature that (1) displays an appropriate copyright notice, and (2) feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the extent that warranties are provided), that licensees may convey the

View File

@@ -2,14 +2,15 @@ import { ApolloProvider } from "@apollo/client";
import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react"; import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react";
import { ConfigProvider } from "antd"; import { ConfigProvider } from "antd";
import enLocale from "antd/es/locale/en_US"; import enLocale from "antd/es/locale/en_US";
import moment from "moment"; import dayjs from "../utils/day";
import 'dayjs/locale/en';
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component"; import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
import client from "../utils/GraphQLClient"; import client from "../utils/GraphQLClient";
import App from "./App"; import App from "./App";
moment.locale("en-US"); dayjs.locale("en");
export const factory = SplitSdk({ export const factory = SplitSdk({
core: { core: {

View File

@@ -1,10 +1,10 @@
import { useClient } from "@splitsoftware/splitio-react"; import {useSplitClient} from "@splitsoftware/splitio-react";
import {Button, Result} from "antd"; import {Button, Result} from "antd";
import LogRocket from "logrocket"; import LogRocket from "logrocket";
import React, { lazy, Suspense, useEffect } from "react"; import React, {lazy, Suspense, useEffect, useState} from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {connect} from "react-redux"; import {connect} from "react-redux";
import { Route, Switch } from "react-router-dom"; import {Route, Routes} from "react-router-dom";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import DocumentEditorContainer from "../components/document-editor/document-editor.container"; import DocumentEditorContainer from "../components/document-editor/document-editor.container";
import ErrorBoundary from "../components/error-boundary/error-boundary.component"; import ErrorBoundary from "../components/error-boundary/error-boundary.component";
@@ -16,12 +16,10 @@ import TechPageContainer from "../pages/tech/tech.page.container";
import {setOnline} from "../redux/application/application.actions"; import {setOnline} from "../redux/application/application.actions";
import {selectOnline} from "../redux/application/application.selectors"; import {selectOnline} from "../redux/application/application.selectors";
import {checkUserSession} from "../redux/user/user.actions"; import {checkUserSession} from "../redux/user/user.actions";
import { import {selectBodyshop, selectCurrentUser,} from "../redux/user/user.selectors";
selectBodyshop, import PrivateRoute from "../components/PrivateRoute";
selectCurrentUser,
} from "../redux/user/user.selectors";
import PrivateRoute from "../utils/private-route";
import "./App.styles.scss"; import "./App.styles.scss";
import handleBeta from "../utils/betaHandler";
const ResetPassword = lazy(() => const ResetPassword = lazy(() =>
import("../pages/reset-password/reset-password.component") import("../pages/reset-password/reset-password.component")
@@ -33,7 +31,6 @@ const CsiPage = lazy(() => import("../pages/csi/csi.container.page"));
const MobilePaymentContainer = lazy(() => const MobilePaymentContainer = lazy(() =>
import("../pages/mobile-payment/mobile-payment.container") import("../pages/mobile-payment/mobile-payment.container")
); );
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
online: selectOnline, online: selectOnline,
@@ -44,14 +41,12 @@ const mapDispatchToProps = (dispatch) => ({
setOnline: (isOnline) => dispatch(setOnline(isOnline)), setOnline: (isOnline) => dispatch(setOnline(isOnline)),
}); });
export function App({ export function App({bodyshop, checkUserSession, currentUser, online, setOnline}) {
bodyshop,
checkUserSession, const client = useSplitClient().client;
currentUser, const [listenersAdded, setListenersAdded] = useState(false)
online, const {t} = useTranslation();
setOnline,
}) {
const client = useClient();
useEffect(() => { useEffect(() => {
if (!navigator.onLine) { if (!navigator.onLine) {
@@ -64,21 +59,38 @@ export function App({
//const b = Grid.useBreakpoint(); //const b = Grid.useBreakpoint();
// console.log("Breakpoints:", b); // console.log("Breakpoints:", b);
const { t } = useTranslation(); // Associate event listeners, memoize to prevent multiple listeners being added
useEffect(() => {
window.addEventListener("offline", function (e) { const offlineListener = (e) => {
setOnline(false); setOnline(false);
}); }
window.addEventListener("online", function (e) { const onlineListener = (e) => {
setOnline(true); setOnline(true);
}); }
if (!listenersAdded) {
console.log('Added events for offline and online');
window.addEventListener("offline", offlineListener);
window.addEventListener("online", onlineListener);
setListenersAdded(true);
}
return () => {
window.removeEventListener("offline", offlineListener);
window.removeEventListener("online", onlineListener);
}
}, [setOnline, listenersAdded]);
useEffect(() => { useEffect(() => {
if (currentUser.authorized && bodyshop) { if (currentUser.authorized && bodyshop) {
client.setAttribute("imexshopid", bodyshop.imexshopid); client.setAttribute("imexshopid", bodyshop.imexshopid);
LogRocket.init("rome-online/rome-online"); if (
if (client.getTreatment("LogRocket_Tracking") === "on") { client.getTreatment("LogRocket_Tracking") === "on" ||
window.location.hostname === 'beta.romeonline.io'
) {
console.log("LR Start");
LogRocket.init("rome-online/rome-online"); LogRocket.init("rome-online/rome-online");
} }
} }
@@ -88,6 +100,8 @@ export function App({
return <LoadingSpinner message={t("general.labels.loggingin")}/>; return <LoadingSpinner message={t("general.labels.loggingin")}/>;
} }
handleBeta();
if (!online) if (!online)
return ( return (
<Result <Result
@@ -107,54 +121,27 @@ export function App({
/> />
); );
// Any route that is not assigned and matched will default to the Landing Page component
return ( return (
<Switch> <Suspense fallback={<LoadingSpinner message="Rome Online"/>}>
<Suspense fallback={<LoadingSpinner />}> <Routes>
<ErrorBoundary> <Route path="*" element={<ErrorBoundary><LandingPage/></ErrorBoundary>}/>
<Route exact path="/" component={LandingPage} /> <Route path="/signin" element={<ErrorBoundary><SignInPage/></ErrorBoundary>}/>
</ErrorBoundary> <Route path="/resetpassword" element={<ErrorBoundary><ResetPassword/></ErrorBoundary>}/>
<ErrorBoundary> <Route path="/csi/:surveyId" element={<ErrorBoundary><CsiPage/></ErrorBoundary>}/>
<Route exact path="/signin" component={SignInPage} /> <Route path="/disclaimer" element={<ErrorBoundary><DisclaimerPage/></ErrorBoundary>}/>
</ErrorBoundary> <Route path="/mp/:paymentIs" element={<ErrorBoundary><MobilePaymentContainer/></ErrorBoundary>}/>
<ErrorBoundary> <Route path="/manage/*" element={<ErrorBoundary><PrivateRoute isAuthorized={currentUser.authorized}/></ErrorBoundary>}>
<Route exact path="/resetpassword" component={ResetPassword} /> <Route path="*" element={<ManagePage/>}/>
</ErrorBoundary> </Route>
<ErrorBoundary> <Route path="/tech/*" element={<ErrorBoundary><PrivateRoute isAuthorized={currentUser.authorized}/></ErrorBoundary>}>
<Route exact path="/csi/:surveyId" component={CsiPage} /> <Route path="*" element={<TechPageContainer/>}/>
</ErrorBoundary> </Route>
<ErrorBoundary> <Route path="/edit/*" element={<PrivateRoute isAuthorized={currentUser.authorized}/>}>
<Route exact path="/disclaimer" component={DisclaimerPage} /> <Route path="*" element={<DocumentEditorContainer/>}/>
</ErrorBoundary> </Route>
<ErrorBoundary> </Routes>
<Route
exact
path="/mp/:paymentIs"
component={MobilePaymentContainer}
/>
</ErrorBoundary>
<ErrorBoundary>
<PrivateRoute
isAuthorized={currentUser.authorized}
path="/manage"
component={ManagePage}
/>
</ErrorBoundary>
<ErrorBoundary>
<PrivateRoute
isAuthorized={currentUser.authorized}
path="/tech"
component={TechPageContainer}
/>
</ErrorBoundary>
<ErrorBoundary>
<PrivateRoute
isAuthorized={currentUser.authorized}
path="/edit"
component={DocumentEditorContainer}
/>
</ErrorBoundary>
</Suspense> </Suspense>
</Switch>
); );
} }

View File

@@ -1,6 +1,14 @@
//Global Styles. //Global Styles.
@import "react-big-calendar/lib/sass/styles"; @import "react-big-calendar/lib/sass/styles";
.ant-menu-item-divider {
border-bottom: 1px solid #74695c !important;
}
.ant-menu-dark .ant-menu-item:hover {
background-color: #1890ff !important;
}
.imex-table-header { .imex-table-header {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;

View File

@@ -0,0 +1,17 @@
import React, {useEffect} from "react";
import {Outlet, useLocation, useNavigate} from "react-router-dom";
function PrivateRoute({component: Component, isAuthorized, ...rest}) {
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
if (!isAuthorized) {
navigate(`/signin?redirect=${location.pathname}`);
}
}, [isAuthorized, navigate, location]);
return <Outlet/>;
}
export default PrivateRoute;

View File

@@ -1,4 +1,4 @@
import { Input, Table, Checkbox, Card, Space } from "antd"; import {Card, Checkbox, Input, Space, Table} from "antd";
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";

View File

@@ -1,4 +1,4 @@
import { Select, Button, Popover, InputNumber } from "antd"; import {Button, InputNumber, Popover, Select} 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";
@@ -61,7 +61,7 @@ export function AllocationsAssignmentComponent({
); );
return ( return (
<Popover content={popContent} visible={visibility}> <Popover content={popContent} open={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} visible={visibility}> <Popover content={popContent} open={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

@@ -2,6 +2,7 @@ import React from "react";
import {List} from "antd"; import {List} from "antd";
import Icon from "@ant-design/icons"; import Icon from "@ant-design/icons";
import {FaArrowRight} from "react-icons/fa"; import {FaArrowRight} from "react-icons/fa";
export default function AuditTrailValuesComponent({oldV, newV}) { export default function AuditTrailValuesComponent({oldV, newV}) {
if (!oldV && !newV) return <div></div>; if (!oldV && !newV) return <div></div>;

View File

@@ -1,7 +1,8 @@
import { Tag, Popover } from "antd"; import {Popover, Tag} from "antd";
import React from "react"; import React from "react";
import Barcode from "react-barcode"; import Barcode from "react-barcode";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
export default function BarcodePopupComponent({value, children}) { export default function BarcodePopupComponent({value, children}) {
const {t} = useTranslation(); const {t} = useTranslation();
return ( return (

View File

@@ -3,6 +3,7 @@ import React, { useEffect } from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component"; import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
import "./bill-cm-returns-table.styles.scss"; import "./bill-cm-returns-table.styles.scss";
export default function BillCmdReturnsTableComponent({ export default function BillCmdReturnsTableComponent({
form, form,
returnLoading, returnLoading,

View File

@@ -1,17 +1,13 @@
import {useMutation, useQuery} from "@apollo/client"; import {useMutation, useQuery} from "@apollo/client";
import { Button, Form, PageHeader, Popconfirm, Space } from "antd"; import {Button, Form, Popconfirm, Space} from "antd";
import moment from "moment"; import dayjs from "../../utils/day";
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";
import {connect} from "react-redux"; import {connect} from "react-redux";
import {useLocation} from "react-router-dom"; import {useLocation} from "react-router-dom";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import { import {DELETE_BILL_LINE, INSERT_NEW_BILL_LINES, UPDATE_BILL_LINE} from "../../graphql/bill-lines.queries";
DELETE_BILL_LINE,
INSERT_NEW_BILL_LINES,
UPDATE_BILL_LINE,
} from "../../graphql/bill-lines.queries";
import {QUERY_BILL_BY_PK, UPDATE_BILL} from "../../graphql/bills.queries"; import {QUERY_BILL_BY_PK, UPDATE_BILL} from "../../graphql/bills.queries";
import {insertAuditTrail} from "../../redux/application/application.actions"; import {insertAuditTrail} from "../../redux/application/application.actions";
import {setModalContext} from "../../redux/modals/modals.actions"; import {setModalContext} from "../../redux/modals/modals.actions";
@@ -26,6 +22,7 @@ import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-galler
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container"; import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import BillDetailEditReturn from "./bill-detail-edit-return.component"; import BillDetailEditReturn from "./bill-detail-edit-return.component";
import {PageHeader} from "@ant-design/pro-layout";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -42,16 +39,12 @@ export default connect(
mapDispatchToProps mapDispatchToProps
)(BillDetailEditcontainer); )(BillDetailEditcontainer);
export function BillDetailEditcontainer({ export function BillDetailEditcontainer({setPartsOrderContext, insertAuditTrail, bodyshop,}) {
setPartsOrderContext,
insertAuditTrail,
bodyshop,
}) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const {t} = useTranslation(); const {t} = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [visible, setVisible] = useState(false); const [open, setOpen] = useState(false);
const [updateLoading, setUpdateLoading] = useState(false); const [updateLoading, setUpdateLoading] = useState(false);
const [update_bill] = useMutation(UPDATE_BILL); const [update_bill] = useMutation(UPDATE_BILL);
const [insertBillLine] = useMutation(INSERT_NEW_BILL_LINES); const [insertBillLine] = useMutation(INSERT_NEW_BILL_LINES);
@@ -65,6 +58,8 @@ export function BillDetailEditcontainer({
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
}); });
// ... rest of the code remains the same
const handleSave = () => { const handleSave = () => {
//It's got a previously deducted bill line! //It's got a previously deducted bill line!
if ( if (
@@ -72,7 +67,7 @@ export function BillDetailEditcontainer({
form.getFieldValue("billlines").filter((b) => b.deductedfromlbr).length > form.getFieldValue("billlines").filter((b) => b.deductedfromlbr).length >
0 0
) )
setVisible(true); setOpen(true);
else { else {
form.submit(); form.submit();
} }
@@ -155,7 +150,7 @@ export function BillDetailEditcontainer({
await refetch(); await refetch();
form.setFieldsValue(transformData(data)); form.setFieldsValue(transformData(data));
form.resetFields(); form.resetFields();
setVisible(false); setOpen(false);
setUpdateLoading(false); setUpdateLoading(false);
}; };
@@ -179,9 +174,9 @@ export function BillDetailEditcontainer({
<BillDetailEditReturn data={data}/> <BillDetailEditReturn data={data}/>
<BillPrintButton billid={search.billid}/> <BillPrintButton billid={search.billid}/>
<Popconfirm <Popconfirm
visible={visible} open={open}
onConfirm={() => form.submit()} onConfirm={() => form.submit()}
onCancel={() => setVisible(false)} onCancel={() => setOpen(false)}
okButtonProps={{loading: updateLoading}} okButtonProps={{loading: updateLoading}}
title={t("bills.labels.editadjwarning")} title={t("bills.labels.editadjwarning")}
> >
@@ -246,7 +241,7 @@ const transformData = (data) => {
}, },
}; };
}), }),
date: data.bills_by_pk ? moment(data.bills_by_pk.date) : null, date: data.bills_by_pk ? dayjs(data.bills_by_pk.date) : null,
} }
: {}; : {};
}; };

View File

@@ -3,7 +3,7 @@ import queryString from "query-string";
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";
import { useHistory, useLocation } from "react-router-dom"; import {useLocation, useNavigate} from "react-router-dom";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import {insertAuditTrail} from "../../redux/application/application.actions"; import {insertAuditTrail} from "../../redux/application/application.actions";
import {setModalContext} from "../../redux/modals/modals.actions"; import {setModalContext} from "../../redux/modals/modals.actions";
@@ -33,10 +33,10 @@ export function BillDetailEditReturn({
disabled, disabled,
}) { }) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const history = useHistory(); const history = useNavigate();
const {t} = useTranslation(); const {t} = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [visible, setVisible] = useState(false); const [open, setOpen] = useState(false);
const handleFinish = ({billlines}) => { const handleFinish = ({billlines}) => {
const selectedLines = billlines.filter((l) => l.selected).map((l) => l.id); const selectedLines = billlines.filter((l) => l.selected).map((l) => l.id);
@@ -67,18 +67,18 @@ export function BillDetailEditReturn({
}); });
delete search.billid; delete search.billid;
history.push({ search: queryString.stringify(search) }); history({search: queryString.stringify(search)});
setVisible(false); setOpen(false);
}; };
useEffect(() => { useEffect(() => {
if (visible === false) form.resetFields(); if (open === false) form.resetFields();
}, [visible, form]); }, [open, form]);
return ( return (
<> <>
<Modal <Modal
visible={visible} open={open}
onCancel={() => setVisible(false)} onCancel={() => setOpen(false)}
destroyOnClose destroyOnClose
title={t("bills.actions.return")} title={t("bills.actions.return")}
onOk={() => form.submit()} onOk={() => form.submit()}
@@ -175,7 +175,7 @@ export function BillDetailEditReturn({
<Button <Button
disabled={data.bills_by_pk.is_credit_memo || disabled} disabled={data.bills_by_pk.is_credit_memo || disabled}
onClick={() => { onClick={() => {
setVisible(true); setOpen(true);
}} }}
> >
{t("bills.actions.return")} {t("bills.actions.return")}

View File

@@ -1,12 +1,12 @@
import {Drawer, Grid} from "antd"; import {Drawer, Grid} from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React from "react"; import React from "react";
import { useHistory, useLocation } from "react-router-dom"; import {useLocation, useNavigate} from "react-router-dom";
import BillDetailEditComponent from "./bill-detail-edit-component"; import BillDetailEditComponent from "./bill-detail-edit-component";
export default function BillDetailEditcontainer() { export default function BillDetailEditcontainer() {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const history = useHistory(); const history = useNavigate();
const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1]) .filter((screen) => !!screen[1])
@@ -29,10 +29,10 @@ export default function BillDetailEditcontainer() {
width={drawerPercentage} width={drawerPercentage}
onClose={() => { onClose={() => {
delete search.billid; delete search.billid;
history.push({ search: queryString.stringify(search) }); history({search: queryString.stringify(search)});
}} }}
destroyOnClose destroyOnClose
visible={search.billid} open={search.billid}
> >
<BillDetailEditComponent/> <BillDetailEditComponent/>
</Drawer> </Drawer>

View File

@@ -1,6 +1,6 @@
import {useApolloClient, useMutation} from "@apollo/client"; import {useApolloClient, useMutation} from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react"; import {useSplitTreatments} from "@splitsoftware/splitio-react";
import { Button, Checkbox, Form, Modal, Space, notification } from "antd"; import {Button, Checkbox, Form, Modal, notification, Space} from "antd";
import _ from "lodash"; import _ from "lodash";
import React, {useEffect, useMemo, useState} from "react"; import React, {useEffect, useMemo, useState} from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
@@ -9,18 +9,12 @@ import { createStructuredSelector } from "reselect";
import {INSERT_NEW_BILL} from "../../graphql/bills.queries"; import {INSERT_NEW_BILL} from "../../graphql/bills.queries";
import {UPDATE_INVENTORY_LINES} from "../../graphql/inventory.queries"; import {UPDATE_INVENTORY_LINES} from "../../graphql/inventory.queries";
import {UPDATE_JOB_LINE} from "../../graphql/jobs-lines.queries"; import {UPDATE_JOB_LINE} from "../../graphql/jobs-lines.queries";
import { import {QUERY_JOB_LBR_ADJUSTMENTS, UPDATE_JOB,} from "../../graphql/jobs.queries";
QUERY_JOB_LBR_ADJUSTMENTS,
UPDATE_JOB,
} from "../../graphql/jobs.queries";
import {MUTATION_MARK_RETURN_RECEIVED} from "../../graphql/parts-orders.queries"; import {MUTATION_MARK_RETURN_RECEIVED} from "../../graphql/parts-orders.queries";
import {insertAuditTrail} from "../../redux/application/application.actions"; import {insertAuditTrail} from "../../redux/application/application.actions";
import {toggleModalVisible} from "../../redux/modals/modals.actions"; import {toggleModalVisible} from "../../redux/modals/modals.actions";
import {selectBillEnterModal} from "../../redux/modals/modals.selectors"; import {selectBillEnterModal} from "../../redux/modals/modals.selectors";
import { import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings"; import AuditTrailMapping from "../../utils/AuditTrailMappings";
import {GenerateDocument} from "../../utils/RenderTemplate"; import {GenerateDocument} from "../../utils/RenderTemplate";
import {TemplateList} from "../../utils/TemplateConstants"; import {TemplateList} from "../../utils/TemplateConstants";
@@ -64,11 +58,13 @@ function BillEnterModalContainer({
"enter_bill_generate_label", "enter_bill_generate_label",
false false
); );
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"], const {treatments: {Enhanced_Payroll}} = useSplitTreatments({
{}, attributes: {},
bodyshop.imexshopid names: ["Enhanced_Payroll"],
); splitKey: bodyshop.imexshopid,
});
const formValues = useMemo(() => { const formValues = useMemo(() => {
return { return {
...billEnterModal.context.bill, ...billEnterModal.context.bill,
@@ -399,18 +395,18 @@ function BillEnterModalContainer({
}, [enterAgain, form]); }, [enterAgain, form]);
useEffect(() => { useEffect(() => {
if (billEnterModal.visible) { if (billEnterModal.open) {
form.setFieldsValue(formValues); form.setFieldsValue(formValues);
} else { } else {
form.resetFields(); form.resetFields();
} }
}, [billEnterModal.visible, form, formValues]); }, [billEnterModal.open, form, formValues]);
return ( return (
<Modal <Modal
title={t("bills.labels.new")} title={t("bills.labels.new")}
width={"98%"} width={"98%"}
visible={billEnterModal.visible} open={billEnterModal.open}
okText={t("general.actions.save")} okText={t("general.actions.save")}
keyboard="false" keyboard="false"
onOk={() => form.submit()} onOk={() => form.submit()}

View File

@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import {alphaSort} from "../../utils/sorters"; import {alphaSort} from "../../utils/sorters";
import BillFormItemsExtendedFormItem from "./bill-form-lines.extended.formitem.component"; import BillFormItemsExtendedFormItem from "./bill-form-lines.extended.formitem.component";
export default function BillFormLinesExtended({ export default function BillFormLinesExtended({
lineData, lineData,
discount, discount,

View File

@@ -1,10 +1,6 @@
import React from "react"; import React from "react";
import { import {MinusCircleFilled, PlusCircleFilled, WarningOutlined,} from "@ant-design/icons";
PlusCircleFilled, import {Button, Form, Input, InputNumber, Select, Space, Switch} from "antd";
MinusCircleFilled,
WarningOutlined,
} from "@ant-design/icons";
import { Form, Button, InputNumber, Input, Select, Switch, Space } from "antd";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {connect} from "react-redux"; import {connect} from "react-redux";

View File

@@ -1,18 +1,8 @@
import Icon, {UploadOutlined} from "@ant-design/icons"; import Icon, {UploadOutlined} from "@ant-design/icons";
import {useApolloClient} from "@apollo/client"; import {useApolloClient} from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react"; import {useSplitTreatments} from "@splitsoftware/splitio-react";
import { import {Alert, Divider, Form, Input, Select, Space, Statistic, Switch, Upload,} from "antd";
Alert, import dayjs from "../../utils/day";
Divider,
Form,
Input,
Select,
Space,
Statistic,
Switch,
Upload,
} from "antd";
import moment from "moment";
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {MdOpenInNew} from "react-icons/md"; import {MdOpenInNew} from "react-icons/md";
@@ -50,21 +40,19 @@ export function BillFormComponent({
job, job,
loadOutstandingReturns, loadOutstandingReturns,
loadInventory, loadInventory,
preferredMake, preferredMake
}) { }) {
const {t} = useTranslation(); const {t} = useTranslation();
const client = useApolloClient(); const client = useApolloClient();
const [discount, setDiscount] = useState(0); const [discount, setDiscount] = useState(0);
const { Extended_Bill_Posting } = useTreatments(
["Extended_Bill_Posting"], const {treatments: {Extended_Bill_Posting, ClosingPeriod}} = useSplitTreatments({
{}, attributes: {},
bodyshop.imexshopid names: ["Extended_Bill_Posting", "ClosingPeriod"],
); splitKey: bodyshop.imexshopid,
const { ClosingPeriod } = useTreatments( });
["ClosingPeriod"],
{},
bodyshop.imexshopid
);
const handleVendorSelect = (props, opt) => { const handleVendorSelect = (props, opt) => {
setDiscount(opt.discount); setDiscount(opt.discount);
@@ -288,17 +276,17 @@ export function BillFormComponent({
bodyshop.accountingconfig.ClosingPeriod bodyshop.accountingconfig.ClosingPeriod
) { ) {
if ( if (
moment(value) dayjs(value)
.startOf("day") .startOf("day")
.isSameOrAfter( .isSameOrAfter(
moment( dayjs(
bodyshop.accountingconfig.ClosingPeriod[0] bodyshop.accountingconfig.ClosingPeriod[0]
).startOf("day") ).startOf("day")
) && ) &&
moment(value) dayjs(value)
.startOf("day") .startOf("day")
.isSameOrBefore( .isSameOrBefore(
moment( dayjs(
bodyshop.accountingconfig.ClosingPeriod[1] bodyshop.accountingconfig.ClosingPeriod[1]
).endOf("day") ).endOf("day")
) )
@@ -396,27 +384,22 @@ export function BillFormComponent({
> >
<CurrencyInput min={0} disabled={disabled}/> <CurrencyInput min={0} disabled={disabled}/>
</Form.Item> </Form.Item>
{ {/*<Form.Item*/}
// <Form.Item {/* span={3}*/}
// span={3} {/* label={t("bills.fields.local_tax_rate")}*/}
// label={t("bills.fields.local_tax_rate")} {/* name="local_tax_rate"*/}
// name="local_tax_rate" {/*>*/}
// > {/* <CurrencyInput min={0} />*/}
// <CurrencyInput min={0} /> {/*</Form.Item>*/}
// </Form.Item> {/* {bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (*/}
} {/* <Form.Item*/}
{ {/* span={2}*/}
//Removed as a part of the merge to Rome Online. Federal tax not applicable. {/* label={t("bills.labels.federal_tax_exempt")}*/}
// bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? ( {/* name="federal_tax_exempt"*/}
// <Form.Item {/* >*/}
// span={2} {/* <Switch onChange={handleFederalTaxExemptSwitchToggle} />*/}
// label={t("bills.labels.federal_tax_exempt")} {/* </Form.Item>*/}
// name="federal_tax_exempt" {/* ) : null}*/}
// >
// <Switch onChange={handleFederalTaxExemptSwitchToggle} />
// </Form.Item>
// ) : null
}
<Form.Item shouldUpdate span={13}> <Form.Item shouldUpdate span={13}>
{() => { {() => {
const values = form.getFieldsValue([ const values = form.getFieldsValue([

View File

@@ -1,5 +1,5 @@
import {useLazyQuery, useQuery} from "@apollo/client"; import {useLazyQuery, useQuery} from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react"; import {useSplitTreatments} from "@splitsoftware/splitio-react";
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";
@@ -23,11 +23,11 @@ export function BillFormContainer({
disabled, disabled,
disableInvNumber, disableInvNumber,
}) { }) {
const { Simple_Inventory } = useTreatments( const {treatments: {Simple_Inventory}} = useSplitTreatments({
["Simple_Inventory"], attributes: {},
{}, names: ["Simple_Inventory"],
bodyshop && bodyshop.imexshopid splitKey: bodyshop && bodyshop.imexshopid,
); });
const {data: VendorAutoCompleteData} = useQuery( const {data: VendorAutoCompleteData} = useQuery(
SEARCH_VENDOR_AUTOCOMPLETE, SEARCH_VENDOR_AUTOCOMPLETE,
@@ -79,4 +79,5 @@ export function BillFormContainer({
</> </>
); );
} }
export default connect(mapStateToProps, null)(BillFormContainer); export default connect(mapStateToProps, null)(BillFormContainer);

View File

@@ -1,17 +1,6 @@
import {DeleteFilled, DollarCircleFilled} from "@ant-design/icons"; import {DeleteFilled, DollarCircleFilled} from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react"; import {useSplitTreatments} from "@splitsoftware/splitio-react";
import { import {Button, Checkbox, Form, Input, InputNumber, Select, Space, Switch, Table, Tooltip,} from "antd";
Button,
Checkbox,
Form,
Input,
InputNumber,
Select,
Space,
Switch,
Table,
Tooltip,
} 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";
@@ -42,17 +31,13 @@ export function BillEnterModalLinesComponent({
}) { }) {
const {t} = useTranslation(); const {t} = useTranslation();
const {setFieldsValue, getFieldsValue, getFieldValue} = form; const {setFieldsValue, getFieldsValue, getFieldValue} = form;
const { Simple_Inventory } = useTreatments(
["Simple_Inventory"],
{},
bodyshop && bodyshop.imexshopid
);
const { Enhanced_Payroll } = useTreatments( const {treatments: {Simple_Inventory, Enhanced_Payroll}} = useSplitTreatments({
["Enhanced_Payroll"], attributes: {},
{}, names: ["Simple_Inventory", "Enhanced_Payroll"],
bodyshop.imexshopid splitKey: bodyshop && bodyshop.imexshopid,
); });
const columns = (remove) => { const columns = (remove) => {
return [ return [

View File

@@ -15,7 +15,8 @@ const BillLineSearchSelect = (
disabled={disabled} disabled={disabled}
ref={ref} ref={ref}
showSearch showSearch
dropdownMatchSelectWidth={false} popupMatchSelectWidth={false}
optionLabelProp={"name"}
// optionFilterProp="line_desc" // optionFilterProp="line_desc"
filterOption={(inputValue, option) => { filterOption={(inputValue, option) => {
return ( return (
@@ -57,6 +58,9 @@ const BillLineSearchSelect = (
style={{ style={{
...(item.removed ? {textDecoration: "line-through"} : {}), ...(item.removed ? {textDecoration: "line-through"} : {}),
}} }}
name={`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
item.oem_partno ? ` - ${item.oem_partno}` : ""
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
> >
<span> <span>
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${ {`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${

View File

@@ -1,18 +1,14 @@
import { useMutation } from "@apollo/client"; import {gql, useMutation} from "@apollo/client";
import {Button, notification} from "antd"; import {Button, notification} from "antd";
import { gql } from "@apollo/client";
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";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import { import {selectAuthLevel, selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
selectAuthLevel,
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import {HasRbacAccess} from "../rbac-wrapper/rbac-wrapper.component"; import {HasRbacAccess} from "../rbac-wrapper/rbac-wrapper.component";
import {INSERT_EXPORT_LOG} from "../../graphql/accounting.queries"; import {INSERT_EXPORT_LOG} from "../../graphql/accounting.queries";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
authLevel: selectAuthLevel, authLevel: selectAuthLevel,

View File

@@ -1,16 +1,13 @@
import { useMutation } from "@apollo/client"; import {gql, useMutation} from "@apollo/client";
import {Button, notification} from "antd"; import {Button, notification} from "antd";
import { gql } from "@apollo/client";
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";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import { import {selectAuthLevel, selectBodyshop,} from "../../redux/user/user.selectors";
selectAuthLevel,
selectBodyshop,
} from "../../redux/user/user.selectors";
import {HasRbacAccess} from "../rbac-wrapper/rbac-wrapper.component"; import {HasRbacAccess} from "../rbac-wrapper/rbac-wrapper.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
authLevel: selectAuthLevel, authLevel: selectAuthLevel,

View File

@@ -2,15 +2,12 @@ import { FileAddFilled } from "@ant-design/icons";
import {useMutation} from "@apollo/client"; import {useMutation} from "@apollo/client";
import {Button, notification, Tooltip} from "antd"; import {Button, notification, Tooltip} from "antd";
import {t} from "i18next"; import {t} from "i18next";
import moment from "moment"; import dayjs from "./../../utils/day";
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 {INSERT_INVENTORY_AND_CREDIT} from "../../graphql/inventory.queries"; import {INSERT_INVENTORY_AND_CREDIT} from "../../graphql/inventory.queries";
import { import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import {CalculateBillTotal} from "../bill-form/bill-form.totals.utility"; import {CalculateBillTotal} from "../bill-form/bill-form.totals.utility";
import queryString from "query-string"; import queryString from "query-string";
import {useLocation} from "react-router-dom"; import {useLocation} from "react-router-dom";
@@ -36,7 +33,6 @@ export function BilllineAddInventory({
}) { }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const {billid} = queryString.parse(useLocation().search); const {billid} = queryString.parse(useLocation().search);
const [insertInventoryLine] = useMutation(INSERT_INVENTORY_AND_CREDIT); const [insertInventoryLine] = useMutation(INSERT_INVENTORY_AND_CREDIT);
const addToInventory = async () => { const addToInventory = async () => {
@@ -50,7 +46,7 @@ export function BilllineAddInventory({
jobid: jobid, jobid: jobid,
isinhouse: true, isinhouse: true,
is_credit_memo: true, is_credit_memo: true,
date: moment().format("YYYY-MM-DD"), date: dayjs().format("YYYY-MM-DD"),
federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate, federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate,
state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate, state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate,
local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate, local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate,
@@ -92,7 +88,7 @@ export function BilllineAddInventory({
pol: { pol: {
returnfrombill: billid, returnfrombill: billid,
vendorid: bodyshop.inhousevendorid, vendorid: bodyshop.inhousevendorid,
deliver_by: moment().format("YYYY-MM-DD"), deliver_by: dayjs().format("YYYY-MM-DD"),
parts_order_lines: { parts_order_lines: {
data: [ data: [
{ {

View File

@@ -229,6 +229,7 @@ export function BillsListTableComponent({
</Card> </Card>
); );
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps

View File

@@ -2,15 +2,15 @@ import React, { useState } from "react";
import {QUERY_ALL_VENDORS} from "../../graphql/vendors.queries"; import {QUERY_ALL_VENDORS} from "../../graphql/vendors.queries";
import {useQuery} from "@apollo/client"; import {useQuery} from "@apollo/client";
import queryString from "query-string"; import queryString from "query-string";
import { useHistory, useLocation } from "react-router-dom"; import {useLocation, useNavigate} from "react-router-dom";
import { Table, Input } from "antd"; import {Input, Table} from "antd";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {alphaSort} from "../../utils/sorters"; import {alphaSort} from "../../utils/sorters";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
export default function BillsVendorsList() { export default function BillsVendorsList() {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const history = useHistory(); const history = useNavigate();
const {loading, error, data} = useQuery(QUERY_ALL_VENDORS, { const {loading, error, data} = useQuery(QUERY_ALL_VENDORS, {
fetchPolicy: "network-only", fetchPolicy: "network-only",

View File

@@ -1,5 +1,5 @@
import {HomeFilled} from "@ant-design/icons"; import {HomeFilled} from "@ant-design/icons";
import { Breadcrumb, Row, Col } from "antd"; import {Breadcrumb, Col, Row} from "antd";
import React from "react"; import React from "react";
import {connect} from "react-redux"; import {connect} from "react-redux";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
@@ -9,7 +9,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import GlobalSearch from "../global-search/global-search.component"; import GlobalSearch from "../global-search/global-search.component";
import GlobalSearchOs from "../global-search/global-search-os.component"; import GlobalSearchOs from "../global-search/global-search-os.component";
import "./breadcrumbs.styles.scss"; import "./breadcrumbs.styles.scss";
import { useTreatments } from "@splitsoftware/splitio-react"; import {useSplitTreatments} from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
breadcrumbs: selectBreadcrumbs, breadcrumbs: selectBreadcrumbs,
@@ -17,33 +17,42 @@ const mapStateToProps = createStructuredSelector({
}); });
export function BreadCrumbs({breadcrumbs, bodyshop}) { export function BreadCrumbs({breadcrumbs, bodyshop}) {
const { OpenSearch } = useTreatments(
["OpenSearch"],
{},
bodyshop && bodyshop.imexshopid
);
const {treatments: {OpenSearch}} = useSplitTreatments({
attributes: {},
names: ["OpenSearch"],
splitKey: bodyshop && bodyshop.imexshopid,
});
// TODO - Client Update - Technically key is not doing anything here
return ( return (
<Row className="breadcrumb-container"> <Row className="breadcrumb-container">
<Col xs={24} sm={24} md={16}> <Col xs={24} sm={24} md={16}>
<Breadcrumb separator=">"> <Breadcrumb
<Breadcrumb.Item> separator=">"
<Link to={`/manage`}> items={[
{
key: "home",
title: (
<Link to={`/manage/`}>
<HomeFilled/>{" "} <HomeFilled/>{" "}
{(bodyshop && bodyshop.shopname && `(${bodyshop.shopname})`) || {(bodyshop && bodyshop.shopname && `(${bodyshop.shopname})`) ||
""} ""}
</Link> </Link>
</Breadcrumb.Item> ),
{breadcrumbs.map((item) => },
item.link ? ( ...breadcrumbs.map((item) =>
<Breadcrumb.Item key={item.label}> item.link
<Link to={item.link}>{item.label} </Link> ? {
</Breadcrumb.Item> key: item.label,
) : ( title: <Link to={item.link}>{item.label}</Link>,
<Breadcrumb.Item key={item.label}>{item.label}</Breadcrumb.Item> }
) : {
)} key: item.label,
</Breadcrumb> title: item.label,
}
),
]}
/>
</Col> </Col>
<Col xs={24} sm={24} md={8}> <Col xs={24} sm={24} md={8}>
{OpenSearch.treatment === "on" ? <GlobalSearchOs/> : <GlobalSearch/>} {OpenSearch.treatment === "on" ? <GlobalSearchOs/> : <GlobalSearch/>}
@@ -51,4 +60,5 @@ export function BreadCrumbs({ breadcrumbs, bodyshop }) {
</Row> </Row>
); );
} }
export default connect(mapStateToProps, null)(BreadCrumbs); export default connect(mapStateToProps, null)(BreadCrumbs);

View File

@@ -25,7 +25,7 @@ export function ContractsFindModalContainer({
}) { }) {
const {t} = useTranslation(); const {t} = useTranslation();
const { visible } = caBcEtfTableModal; const {open} = caBcEtfTableModal;
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [form] = Form.useForm(); const [form] = Form.useForm();
const EtfTemplate = TemplateList("special").ca_bc_etf_table; const EtfTemplate = TemplateList("special").ca_bc_etf_table;
@@ -63,14 +63,14 @@ export function ContractsFindModalContainer({
}; };
useEffect(() => { useEffect(() => {
if (visible) { if (open) {
form.resetFields(); form.resetFields();
} }
}, [visible, form]); }, [open, form]);
return ( return (
<Modal <Modal
visible={visible} open={open}
width="70%" width="70%"
title={t("payments.labels.findermodal")} title={t("payments.labels.findermodal")}
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}

View File

@@ -3,6 +3,7 @@ import { Button, Form, InputNumber, Popover } from "antd";
import {logImEXEvent} from "../../firebase/firebase.utils"; import {logImEXEvent} from "../../firebase/firebase.utils";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {CalculatorFilled} from "@ant-design/icons"; import {CalculatorFilled} from "@ant-design/icons";
export default function CABCpvrtCalculator({disabled, form}) { export default function CABCpvrtCalculator({disabled, form}) {
const [visibility, setVisibility] = useState(false); const [visibility, setVisibility] = useState(false);
@@ -38,7 +39,7 @@ export default function CABCpvrtCalculator({ disabled, form }) {
<Popover <Popover
destroyTooltipOnHide destroyTooltipOnHide
content={popContent} content={popContent}
visible={visibility} open={visibility}
disabled={disabled} disabled={disabled}
> >
<Button disabled={disabled} onClick={() => setVisibility(true)}> <Button disabled={disabled} onClick={() => setVisibility(true)}>

View File

@@ -1,27 +1,13 @@
import {DeleteFilled} from "@ant-design/icons"; import {DeleteFilled} from "@ant-design/icons";
import {useLazyQuery, useMutation} from "@apollo/client"; import {useLazyQuery, useMutation} from "@apollo/client";
import { import {Button, Card, Col, Form, Input, notification, Row, Space, Spin, Statistic,} from "antd";
Button,
Card,
Col,
Form,
Input,
Row,
Space,
Spin,
Statistic,
notification,
} from "antd";
import axios from "axios"; import axios from "axios";
import moment from "moment"; import dayjs from "../../utils/day";
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";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import { import {INSERT_PAYMENT_RESPONSE, QUERY_RO_AND_OWNER_BY_JOB_PKS,} from "../../graphql/payment_response.queries";
INSERT_PAYMENT_RESPONSE,
QUERY_RO_AND_OWNER_BY_JOB_PKS,
} from "../../graphql/payment_response.queries";
import {INSERT_NEW_PAYMENT} from "../../graphql/payments.queries"; import {INSERT_NEW_PAYMENT} from "../../graphql/payments.queries";
import {insertAuditTrail} from "../../redux/application/application.actions"; import {insertAuditTrail} from "../../redux/application/application.actions";
import {toggleModalVisible} from "../../redux/modals/modals.actions"; import {toggleModalVisible} from "../../redux/modals/modals.actions";
@@ -117,7 +103,7 @@ const CardPaymentModalComponent = ({
payer: t("payments.labels.customer"), payer: t("payments.labels.customer"),
type: values.paymentResponse.cardbrand, type: values.paymentResponse.cardbrand,
jobid: payment.jobid, jobid: payment.jobid,
date: moment(Date.now()), date: dayjs(Date.now()),
payment_responses: { payment_responses: {
data: [ data: [
{ {

View File

@@ -22,7 +22,7 @@ function CardPaymentModalContainer({
toggleModalVisible, toggleModalVisible,
bodyshop, bodyshop,
}) { }) {
const { visible } = cardPaymentModal; const {open} = cardPaymentModal;
const {t} = useTranslation(); const {t} = useTranslation();
const handleCancel = () => { const handleCancel = () => {
@@ -35,7 +35,7 @@ function CardPaymentModalContainer({
return ( return (
<Modal <Modal
open={visible} open={open}
onOk={handleOK} onOk={handleOK}
onCancel={handleCancel} onCancel={handleCancel}
footer={[ footer={[

View File

@@ -4,20 +4,11 @@ import { Button, notification, Space } from "antd";
import axios from "axios"; import axios from "axios";
import React, {useEffect} from "react"; import React, {useEffect} from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {messaging, requestForToken} from "../../firebase/firebase.utils"; import {messaging, requestForToken} from "../../firebase/firebase.utils";
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import FcmHandler from "../../utils/fcm-handler"; import FcmHandler from "../../utils/fcm-handler";
import ChatPopupComponent from "../chat-popup/chat-popup.component"; import ChatPopupComponent from "../chat-popup/chat-popup.component";
import "./chat-affix.styles.scss"; import "./chat-affix.styles.scss";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
chatVisible: selectChatVisible,
});
export function ChatAffixContainer({bodyshop, chatVisible}) { export function ChatAffixContainer({bodyshop, chatVisible}) {
const {t} = useTranslation(); const {t} = useTranslation();
const client = useApolloClient(); const client = useApolloClient();
@@ -47,7 +38,6 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
<Button <Button
onClick={async () => { onClick={async () => {
await requestForToken(); await requestForToken();
SubscribeToTopic(); SubscribeToTopic();
}} }}
> >
@@ -81,16 +71,17 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
payload: (payload && payload.data && payload.data.data) || payload.data, payload: (payload && payload.data && payload.data.data) || payload.data,
}); });
} }
let stopMessageListenr, channel;
let stopMessageListener, channel;
try { try {
stopMessageListenr = onMessage(messaging, handleMessage); stopMessageListener = onMessage(messaging, handleMessage);
channel = new BroadcastChannel("imex-sw-messages"); channel = new BroadcastChannel("imex-sw-messages");
channel.addEventListener("message", handleMessage); channel.addEventListener("message", handleMessage);
} catch (error) { } catch (error) {
console.log("Unable to set event listeners."); console.log("Unable to set event listeners.");
} }
return () => { return () => {
stopMessageListenr && stopMessageListenr(); stopMessageListener && stopMessageListener();
channel && channel.removeEventListener("message", handleMessage); channel && channel.removeEventListener("message", handleMessage);
}; };
}, [client]); }, [client]);
@@ -103,4 +94,5 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
</div> </div>
); );
} }
export default connect(mapStateToProps, null)(ChatAffixContainer);
export default ChatAffixContainer;

View File

@@ -1,19 +1,14 @@
import { Badge, List, Tag } from "antd"; import {Badge, Card, List, Space, Tag} from "antd";
import React from "react"; import React from "react";
import {connect} from "react-redux"; import {connect} from "react-redux";
import { import {AutoSizer, CellMeasurer, CellMeasurerCache, List as VirtualizedList,} from "react-virtualized";
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List as VirtualizedList,
} from "react-virtualized";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import {setSelectedConversation} from "../../redux/messaging/messaging.actions"; import {setSelectedConversation} from "../../redux/messaging/messaging.actions";
import {selectSelectedConversation} from "../../redux/messaging/messaging.selectors"; import {selectSelectedConversation} from "../../redux/messaging/messaging.selectors";
import {TimeAgoFormatter} from "../../utils/DateFormatter"; import {TimeAgoFormatter} from "../../utils/DateFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter"; import PhoneFormatter from "../../utils/PhoneFormatter";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import {OwnerNameDisplayFunction} from "../owner-name-display/owner-name-display.component";
import _ from "lodash";
import "./chat-conversation-list.styles.scss"; import "./chat-conversation-list.styles.scss";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -38,6 +33,36 @@ function ChatConversationListComponent({
const rowRenderer = ({index, key, style, parent}) => { const rowRenderer = ({index, key, style, parent}) => {
const item = conversationList[index]; const item = conversationList[index];
const cardContentRight =
<TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>;
const cardContentLeft = item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx}>{j.job.ro_number}</Tag>
))
: null;
const names = <>{_.uniq(item.job_conversations.map((j, idx) =>
OwnerNameDisplayFunction(j.job)
))}</>
const cardTitle = <>
{item.label && <Tag color="blue">{item.label}</Tag>}
{item.job_conversations.length > 0 ? (
<Space direction="vertical">
{names}
</Space>
) : (
<Space>
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
</Space>
)}
</>
const cardExtra = <Badge count={item.messages_aggregate.aggregate.count || 0}/>
const getCardStyle = () =>
item.id === selectedConversation
? {backgroundColor: 'rgba(128, 128, 128, 0.2)'}
: {backgroundColor: index % 2 === 0 ? '#f0f2f5' : '#ffffff'};
return ( return (
<CellMeasurer <CellMeasurer
@@ -49,44 +74,21 @@ function ChatConversationListComponent({
> >
<List.Item <List.Item
onClick={() => setSelectedConversation(item.id)} onClick={() => setSelectedConversation(item.id)}
className={`chat-list-item ${ style={style}
className={`chat-list-item
${
item.id === selectedConversation item.id === selectedConversation
? "chat-list-selected-conversation" ? "chat-list-selected-conversation"
: null : null
}`} }`}
style={style}
> >
<Card style={getCardStyle()} bordered={false} size="small" extra={cardExtra} title={cardTitle}>
<div style={{display: 'inline-block', width: '70%', textAlign: 'left'}}>
{cardContentLeft}
</div>
<div <div
style={{ style={{display: 'inline-block', width: '30%', textAlign: 'right'}}>{cardContentRight}</div>
display: "inline-block", </Card>
}}
>
{item.label && <div className="chat-name">{item.label}</div>}
{item.job_conversations.length > 0 ? (
<div className="chat-name">
{item.job_conversations.map((j, idx) => (
<div key={idx}>
<OwnerNameDisplay ownerObject={j.job} />
</div>
))}
</div>
) : (
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
)}
</div>
<div style={{ display: "inline-block" }}>
<div>
{item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx} className="ro-number-tag">
{j.job.ro_number}
</Tag>
))
: null}
</div>
<TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>
</div>
<Badge count={item.messages_aggregate.aggregate.count || 0} />
</List.Item> </List.Item>
</CellMeasurer> </CellMeasurer>
); );

View File

@@ -1,27 +1,16 @@
.chat-list-selected-conversation {
background-color: rgba(128, 128, 128, 0.2);
}
.chat-list-container { .chat-list-container {
flex: 1;
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
border: 1px solid gainsboro; border: 1px solid gainsboro;
} }
.chat-list-item { .chat-list-item {
display: flex; .ant-card-head {
flex-direction: row; border: none;
}
&:hover { &:hover {
cursor: pointer; cursor: pointer;
color: #ff7a00; color: #ff7a00;
} }
.chat-name {
flex: 1;
display: inline;
}
.ro-number-tag {
align-self: baseline;
}
padding: 12px 24px;
border-bottom: 1px solid gainsboro;
} }

View File

@@ -2,15 +2,13 @@ import { useMutation, useQuery, useSubscription } from "@apollo/client";
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 { import {CONVERSATION_SUBSCRIPTION_BY_PK, GET_CONVERSATION_DETAILS,} from "../../graphql/conversations.queries";
CONVERSATION_SUBSCRIPTION_BY_PK,
GET_CONVERSATION_DETAILS,
} from "../../graphql/conversations.queries";
import {MARK_MESSAGES_AS_READ_BY_CONVERSATION} from "../../graphql/messages.queries"; import {MARK_MESSAGES_AS_READ_BY_CONVERSATION} from "../../graphql/messages.queries";
import {selectSelectedConversation} from "../../redux/messaging/messaging.selectors"; import {selectSelectedConversation} from "../../redux/messaging/messaging.selectors";
import ChatConversationComponent from "./chat-conversation.component"; import ChatConversationComponent from "./chat-conversation.component";
import axios from "axios"; import axios from "axios";
import {selectBodyshop} from "../../redux/user/user.selectors"; import {selectBodyshop} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation, selectedConversation: selectSelectedConversation,
bodyshop: selectBodyshop, bodyshop: selectBodyshop,

View File

@@ -4,6 +4,7 @@ import { Input, notification, Spin, Tag, Tooltip } from "antd";
import React, {useState} from "react"; import React, {useState} from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {UPDATE_CONVERSATION_LABEL} from "../../graphql/conversations.queries"; import {UPDATE_CONVERSATION_LABEL} from "../../graphql/conversations.queries";
export default function ChatLabel({conversation}) { export default function ChatLabel({conversation}) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [editing, setEditing] = useState(false); const [editing, setEditing] = useState(false);

View File

@@ -9,7 +9,8 @@ import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
import {selectBodyshop} from "../../redux/user/user.selectors"; import {selectBodyshop} from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component"; import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component";
import JobDocumentsLocalGalleryExternal from "../jobs-documents-local-gallery/jobs-documents-local-gallery.external.component"; import JobDocumentsLocalGalleryExternal
from "../jobs-documents-local-gallery/jobs-documents-local-gallery.external.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -27,7 +28,7 @@ export function ChatMediaSelector({
conversation, conversation,
}) { }) {
const {t} = useTranslation(); const {t} = useTranslation();
const [visible, setVisible] = useState(false); const [open, setOpen] = useState(false);
const {loading, error, data} = useQuery(GET_DOCUMENTS_BY_JOB, { const {loading, error, data} = useQuery(GET_DOCUMENTS_BY_JOB, {
fetchPolicy: "network-only", fetchPolicy: "network-only",
@@ -39,13 +40,13 @@ export function ChatMediaSelector({
}, },
skip: skip:
!visible || !open ||
!conversation.job_conversations || !conversation.job_conversations ||
conversation.job_conversations.length === 0, conversation.job_conversations.length === 0,
}); });
const handleVisibleChange = (visible) => { const handleVisibleChange = (change) => {
setVisible(visible); setOpen(change);
}; };
useEffect(() => { useEffect(() => {
@@ -65,7 +66,7 @@ export function ChatMediaSelector({
externalMediaState={[selectedMedia, setSelectedMedia]} externalMediaState={[selectedMedia, setSelectedMedia]}
/> />
)} )}
{bodyshop.uselocalmediaserver && visible && ( {bodyshop.uselocalmediaserver && open && (
<JobDocumentsLocalGalleryExternal <JobDocumentsLocalGalleryExternal
externalMediaState={[selectedMedia, setSelectedMedia]} externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={ jobId={
@@ -88,8 +89,8 @@ export function ChatMediaSelector({
} }
title={t("messaging.labels.selectmedia")} title={t("messaging.labels.selectmedia")}
trigger="click" trigger="click"
visible={visible} open={open}
onVisibleChange={handleVisibleChange} onOpenChange={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,15 +1,10 @@
import Icon from "@ant-design/icons"; import Icon from "@ant-design/icons";
import {Tooltip} from "antd"; import {Tooltip} from "antd";
import i18n from "i18next"; import i18n from "i18next";
import moment from "moment"; import dayjs from "../../utils/day";
import React, {useEffect, useRef} from "react"; import React, {useEffect, useRef} from "react";
import {MdDone, MdDoneAll} from "react-icons/md"; import {MdDone, MdDoneAll} from "react-icons/md";
import { import {AutoSizer, CellMeasurer, CellMeasurerCache, List,} from "react-virtualized";
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List,
} from "react-virtualized";
import {DateTimeFormatter} from "../../utils/DateFormatter"; import {DateTimeFormatter} from "../../utils/DateFormatter";
import "./chat-message-list.styles.scss"; import "./chat-message-list.styles.scss";
@@ -52,7 +47,7 @@ export default function ChatMessageListComponent({ messages }) {
<div style={{fontSize: 10}}> <div style={{fontSize: 10}}>
{i18n.t("messaging.labels.sentby", { {i18n.t("messaging.labels.sentby", {
by: messages[index].userid, by: messages[index].userid,
time: moment(messages[index].created_at).format( time: dayjs(messages[index].created_at).format(
"MM/DD/YYYY @ hh:mm a" "MM/DD/YYYY @ hh:mm a"
), ),
})} })}

View File

@@ -44,6 +44,7 @@
.yours { .yours {
align-items: flex-start; align-items: flex-start;
} }
.msgmargin { .msgmargin {
margin-top: 0.1rem; margin-top: 0.1rem;
margin-bottom: 0.1rem; margin-bottom: 0.1rem;
@@ -66,6 +67,7 @@
background: #eee; background: #eee;
border-bottom-right-radius: 15px; border-bottom-right-radius: 15px;
} }
.yours .message.last:after { .yours .message.last:after {
content: ""; content: "";
position: absolute; position: absolute;

View File

@@ -5,9 +5,7 @@ import { useTranslation } from "react-i18next";
import {connect} from "react-redux"; import {connect} from "react-redux";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import {openChatByPhone} from "../../redux/messaging/messaging.actions"; import {openChatByPhone} from "../../redux/messaging/messaging.actions";
import PhoneFormItem, { import PhoneFormItem, {PhoneItemFormatterValidation,} from "../form-items-formatted/phone-form-item.component";
PhoneItemFormatterValidation,
} from "../form-items-formatted/phone-form-item.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser

View File

@@ -9,6 +9,7 @@ import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import {selectBodyshop} from "../../redux/user/user.selectors"; import {selectBodyshop} from "../../redux/user/user.selectors";
import {searchingForConversation} from "../../redux/messaging/messaging.selectors"; import {searchingForConversation} from "../../redux/messaging/messaging.selectors";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
searchingForConversation: searchingForConversation, searchingForConversation: searchingForConversation,
@@ -49,4 +50,5 @@ export function ChatOpenButton({
</a> </a>
); );
} }
export default connect(mapStateToProps, mapDispatchToProps)(ChatOpenButton); export default connect(mapStateToProps, mapDispatchToProps)(ChatOpenButton);

View File

@@ -1,24 +1,13 @@
import { import {InfoCircleOutlined, MessageOutlined, ShrinkOutlined, SyncOutlined,} from "@ant-design/icons";
InfoCircleOutlined,
MessageOutlined,
ShrinkOutlined,
SyncOutlined,
} from "@ant-design/icons";
import {useLazyQuery, useQuery} from "@apollo/client"; import {useLazyQuery, useQuery} from "@apollo/client";
import {Badge, Card, Col, Row, Space, Tag, Tooltip, Typography} from "antd"; import {Badge, Card, Col, Row, Space, Tag, Tooltip, Typography} from "antd";
import React, {useCallback, useEffect, useState} from "react"; import React, {useCallback, useEffect, useState} 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 { import {CONVERSATION_LIST_QUERY, UNREAD_CONVERSATION_COUNT,} from "../../graphql/conversations.queries";
CONVERSATION_LIST_QUERY,
UNREAD_CONVERSATION_COUNT,
} from "../../graphql/conversations.queries";
import {toggleChatVisible} from "../../redux/messaging/messaging.actions"; import {toggleChatVisible} from "../../redux/messaging/messaging.actions";
import { import {selectChatVisible, selectSelectedConversation,} from "../../redux/messaging/messaging.selectors";
selectChatVisible,
selectSelectedConversation,
} from "../../redux/messaging/messaging.selectors";
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component"; import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
import ChatConversationContainer from "../chat-conversation/chat-conversation.container"; import ChatConversationContainer from "../chat-conversation/chat-conversation.container";
import ChatNewConversation from "../chat-new-conversation/chat-new-conversation.component"; import ChatNewConversation from "../chat-new-conversation/chat-new-conversation.component";
@@ -140,4 +129,5 @@ export function ChatPopupComponent({
</Badge> </Badge>
); );
} }
export default connect(mapStateToProps, mapDispatchToProps)(ChatPopupComponent); export default connect(mapStateToProps, mapDispatchToProps)(ChatPopupComponent);

View File

@@ -13,6 +13,7 @@
height: 100%; height: 100%;
} }
} }
.chat-popup-info-icon { .chat-popup-info-icon {
margin-right: 5px; margin-right: 5px;
} }

View File

@@ -1,5 +1,5 @@
import {PlusCircleOutlined} from "@ant-design/icons"; import {PlusCircleOutlined} from "@ant-design/icons";
import { Dropdown, Menu } from "antd"; import {Dropdown} 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,24 +16,22 @@ const mapDispatchToProps = (dispatch) => ({
}); });
export function ChatPresetsComponent({bodyshop, setMessage, className}) { export function ChatPresetsComponent({bodyshop, setMessage, className}) {
const menu = (
<Menu> const items = bodyshop.md_messaging_presets.map((i, idx) => ({
{bodyshop.md_messaging_presets.map((i, idx) => ( key: idx,
<Menu.Item onClick={() => setMessage(i.text)} key={idx}> label: (i.label),
{i.label} onClick: () => setMessage(i.text),
</Menu.Item> }));
))}
</Menu>
);
return ( return (
<div className={className}> <div className={className}>
<Dropdown trigger={["click"]} overlay={menu}> <Dropdown trigger={["click"]} menu={{items}}>
<PlusCircleOutlined/> <PlusCircleOutlined/>
</Dropdown> </Dropdown>
</div> </div>
); );
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps

View File

@@ -16,44 +16,31 @@ const mapDispatchToProps = (dispatch) => ({
export function ChatPrintButton({conversation}) { export function ChatPrintButton({conversation}) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const generateDocument = (type) => {
setLoading(true);
GenerateDocument(
{
name: TemplateList("messaging").conversation_list.key,
variables: {id: conversation.id},
},
{
subject: TemplateList("messaging").conversation_list.subject,
},
type,
conversation.id
).catch(e => {
console.warn('Something went wrong generating a document.');
});
setLoading(false);
}
return ( return (
<Space wrap> <Space wrap>
<PrinterOutlined <PrinterOutlined onClick={() => generateDocument('p')}/>
onClick={() => { <MailOutlined onClick={() => generateDocument('e')}/>
setLoading(true);
GenerateDocument(
{
name: TemplateList("messaging").conversation_list.key,
variables: { id: conversation.id },
},
{
subject: TemplateList("messaging").conversation_list.subject,
},
"p",
conversation.id
);
setLoading(false);
}}
/>
<MailOutlined
onClick={() => {
setLoading(true);
GenerateDocument(
{
name: TemplateList("messaging").conversation_list.key,
variables: { id: conversation.id },
},
{
subject: TemplateList("messaging").conversation_list.subject,
},
"e",
conversation.id
);
setLoading(false);
}}
/>
{loading && <Spin/>} {loading && <Spin/>}
</Space> </Space>
); );
} }
export default connect(mapStateToProps, mapDispatchToProps)(ChatPrintButton); export default connect(mapStateToProps, mapDispatchToProps)(ChatPrintButton);

View File

@@ -5,14 +5,8 @@ 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 {logImEXEvent} from "../../firebase/firebase.utils";
import { import {sendMessage, setMessage,} from "../../redux/messaging/messaging.actions";
sendMessage, import {selectIsSending, selectMessage,} from "../../redux/messaging/messaging.selectors";
setMessage,
} from "../../redux/messaging/messaging.actions";
import {
selectIsSending,
selectMessage,
} from "../../redux/messaging/messaging.selectors";
import {selectBodyshop} from "../../redux/user/user.selectors"; import {selectBodyshop} from "../../redux/user/user.selectors";
import ChatMediaSelector from "../chat-media-selector/chat-media-selector.component"; import ChatMediaSelector from "../chat-media-selector/chat-media-selector.component";
import ChatPresetsComponent from "../chat-presets/chat-presets.component"; import ChatPresetsComponent from "../chat-presets/chat-presets.component";
@@ -110,6 +104,7 @@ function ChatSendMessageComponent({
</div> </div>
); );
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps

View File

@@ -9,17 +9,17 @@ export default function ChatTagRoComponent({
loading, loading,
handleSearch, handleSearch,
handleInsertTag, handleInsertTag,
setVisible, setOpen,
}) { }) {
const {t} = useTranslation(); const {t} = useTranslation();
return ( return (
<Space flex> <Space>
<div style={{width: "15rem"}}> <div style={{width: "15rem"}}>
<Select <Select
showSearch showSearch
autoFocus autoFocus
dropdownMatchSelectWidth popupMatchSelectWidth
placeholder={t("general.labels.search")} placeholder={t("general.labels.search")}
filterOption={false} filterOption={false}
onSearch={handleSearch} onSearch={handleSearch}
@@ -38,7 +38,7 @@ export default function ChatTagRoComponent({
{loading ? ( {loading ? (
<LoadingOutlined/> <LoadingOutlined/>
) : ( ) : (
<CloseCircleOutlined onClick={() => setVisible(false)} /> <CloseCircleOutlined onClick={() => setOpen(false)}/>
)} )}
</Space> </Space>
); );

View File

@@ -11,7 +11,7 @@ import ChatTagRo from "./chat-tag-ro.component";
export default function ChatTagRoContainer({conversation}) { export default function ChatTagRoContainer({conversation}) {
const {t} = useTranslation(); const {t} = useTranslation();
const [visible, setVisible] = useState(false); const [open, setOpen] = useState(false);
const [loadRo, {loading, data}] = useLazyQuery(SEARCH_FOR_JOBS); const [loadRo, {loading, data}] = useLazyQuery(SEARCH_FOR_JOBS);
@@ -33,7 +33,7 @@ export default function ChatTagRoContainer({ conversation }) {
const handleInsertTag = (value, option) => { const handleInsertTag = (value, option) => {
logImEXEvent("messaging_add_job_tag"); logImEXEvent("messaging_add_job_tag");
insertTag({variables: {jobId: option.key}}); insertTag({variables: {jobId: option.key}});
setVisible(false); setOpen(false);
}; };
const existingJobTags = const existingJobTags =
@@ -47,16 +47,16 @@ export default function ChatTagRoContainer({ conversation }) {
return ( return (
<div> <div>
{visible ? ( {open ? (
<ChatTagRo <ChatTagRo
loading={loading} loading={loading}
roOptions={roOptions} roOptions={roOptions}
handleSearch={handleSearch} handleSearch={handleSearch}
handleInsertTag={handleInsertTag} handleInsertTag={handleInsertTag}
setVisible={setVisible} setOpen={setOpen}
/> />
) : ( ) : (
<Tag onClick={() => setVisible(true)}> <Tag onClick={() => setOpen(true)}>
<PlusOutlined/> <PlusOutlined/>
{t("messaging.actions.link")} {t("messaging.actions.link")}
</Tag> </Tag>

View File

@@ -3,6 +3,7 @@ import Rate from "./rate/rate.component";
import Slider from "./slider/slider.component"; import Slider from "./slider/slider.component";
import Text from "./text/text.component"; import Text from "./text/text.component";
import Textarea from "./textarea/textarea.component"; import Textarea from "./textarea/textarea.component";
const e = { const e = {
checkbox: CheckboxFormItem, checkbox: CheckboxFormItem,
slider: Slider, slider: Slider,

View File

@@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { Result, Button } from "antd"; import {Button, Result} from "antd";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
export default function ConflictComponent() { export default function ConflictComponent() {

View File

@@ -1,5 +1,5 @@
import {useQuery} from "@apollo/client"; import {useQuery} from "@apollo/client";
import moment from "moment"; import dayjs from "../../utils/day";
import React from "react"; import React from "react";
import {QUERY_AVAILABLE_CC} from "../../graphql/courtesy-car.queries"; import {QUERY_AVAILABLE_CC} from "../../graphql/courtesy-car.queries";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
@@ -7,7 +7,7 @@ import ContractCarsComponent from "./contract-cars.component";
export default function ContractCarsContainer({selectedCarState, form}) { export default function ContractCarsContainer({selectedCarState, form}) {
const {loading, error, data} = useQuery(QUERY_AVAILABLE_CC, { const {loading, error, data} = useQuery(QUERY_AVAILABLE_CC, {
variables: { today: moment().format("YYYY-MM-DD") }, variables: {today: dayjs().format("YYYY-MM-DD")},
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
}); });

View File

@@ -1,26 +1,14 @@
import {useMutation} from "@apollo/client"; import {useMutation} from "@apollo/client";
import { import {Button, Form, InputNumber, notification, Popover, Radio, Select, Space,} from "antd";
Button,
Form,
InputNumber,
notification,
Popover,
Radio,
Select,
Space,
} from "antd";
import axios from "axios"; import axios from "axios";
import moment from "moment"; import dayjs from "../../utils/day";
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";
import { useHistory } from "react-router-dom"; import {useNavigate} from "react-router-dom";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import {INSERT_NEW_JOB} from "../../graphql/jobs.queries"; import {INSERT_NEW_JOB} from "../../graphql/jobs.queries";
import { import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
@@ -38,17 +26,17 @@ export function ContractConvertToRo({
disabled, disabled,
}) { }) {
const {t} = useTranslation(); const {t} = useTranslation();
const [visible, setVisible] = useState(false); const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [insertJob] = useMutation(INSERT_NEW_JOB); const [insertJob] = useMutation(INSERT_NEW_JOB);
const history = useHistory(); const history = useNavigate();
const handleFinish = async (values) => { const handleFinish = async (values) => {
setLoading(true); setLoading(true);
const contractLength = moment(contract.actualreturn).diff( const contractLength = dayjs(contract.actualreturn).diff(
moment(contract.start), dayjs(contract.start),
"days" "day"
); );
const billingLines = []; const billingLines = [];
if (contractLength > 0) if (contractLength > 0)
@@ -306,7 +294,7 @@ export function ContractConvertToRo({
}); });
} }
setVisible(false); setOpen(false);
setLoading(false); setLoading(false);
}; };
@@ -380,7 +368,7 @@ export function ContractConvertToRo({
<Button type="primary" htmlType="submit" loading={loading}> <Button type="primary" htmlType="submit" loading={loading}>
{t("contracts.actions.convertoro")} {t("contracts.actions.convertoro")}
</Button> </Button>
<Button onClick={() => setVisible(false)}> <Button onClick={() => setOpen(false)}>
{t("general.actions.close")} {t("general.actions.close")}
</Button> </Button>
</Space> </Space>
@@ -390,9 +378,9 @@ export function ContractConvertToRo({
return ( return (
<div> <div>
<Popover content={popContent} visible={visible}> <Popover content={popContent} open={open}>
<Button <Button
onClick={() => setVisible(true)} onClick={() => setOpen(true)}
loading={loading} loading={loading}
disabled={!contract.dailyrate || !contract.actualreturn || disabled} disabled={!contract.dailyrate || !contract.actualreturn || disabled}
> >
@@ -402,6 +390,7 @@ export function ContractConvertToRo({
</div> </div>
); );
} }
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps

View File

@@ -3,6 +3,7 @@ import { Button, notification } from "antd";
import React, {useEffect} from "react"; import React, {useEffect} from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {GET_JOB_FOR_CC_CONTRACT} from "../../graphql/jobs.queries"; import {GET_JOB_FOR_CC_CONTRACT} from "../../graphql/jobs.queries";
export default function ContractCreateJobPrefillComponent({jobId, form}) { export default function ContractCreateJobPrefillComponent({jobId, form}) {
const [call, {loading, error, data}] = useLazyQuery( const [call, {loading, error, data}] = useLazyQuery(
GET_JOB_FOR_CC_CONTRACT GET_JOB_FOR_CC_CONTRACT

View File

@@ -1,6 +1,6 @@
import {WarningFilled} from "@ant-design/icons"; import {WarningFilled} from "@ant-design/icons";
import {Form, Input, InputNumber, Space} from "antd"; import {Form, Input, InputNumber, Space} from "antd";
import moment from "moment"; import dayjs from "../../utils/day";
import React from "react"; import React from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {DateFormatter} from "../../utils/DateFormatter"; import {DateFormatter} from "../../utils/DateFormatter";
@@ -11,9 +11,7 @@ import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel
import FormDatePicker from "../form-date-picker/form-date-picker.component"; import FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component"; import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import InputPhone, { import InputPhone, {PhoneItemFormatterValidation,} from "../form-items-formatted/phone-form-item.component";
PhoneItemFormatterValidation,
} from "../form-items-formatted/phone-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import ContractFormJobPrefill from "./contract-form-job-prefill.component"; import ContractFormJobPrefill from "./contract-form-job-prefill.component";
@@ -96,8 +94,8 @@ export default function ContractFormComponent({
const dueForService = const dueForService =
selectedCar && selectedCar &&
selectedCar.nextservicedate && selectedCar.nextservicedate &&
moment(selectedCar.nextservicedate).isBefore( dayjs(selectedCar.nextservicedate).isBefore(
moment(form.getFieldValue("scheduledreturn")) dayjs(form.getFieldValue("scheduledreturn"))
); );
if (mileageOver || dueForService) if (mileageOver || dueForService)
@@ -190,9 +188,9 @@ export default function ContractFormComponent({
} }
> >
{() => { {() => {
const dlExpiresBeforeReturn = moment( const dlExpiresBeforeReturn = dayjs(
form.getFieldValue("driver_dlexpiry") form.getFieldValue("driver_dlexpiry")
).isBefore(moment(form.getFieldValue("scheduledreturn"))); ).isBefore(dayjs(form.getFieldValue("scheduledreturn")));
return ( return (
<div> <div>

View File

@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
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 OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
export default function ContractJobBlock({job}) { export default function ContractJobBlock({job}) {
const {t} = useTranslation(); const {t} = useTranslation();
return ( return (

View File

@@ -11,6 +11,7 @@ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function ContractJobsContainer({selectedJobState, bodyshop}) { export function ContractJobsContainer({selectedJobState, bodyshop}) {
const {loading, error, data} = useQuery(QUERY_ALL_ACTIVE_JOBS, { const {loading, error, data} = useQuery(QUERY_ALL_ACTIVE_JOBS, {
variables: { variables: {
@@ -36,4 +37,5 @@ export function ContractJobsContainer({ selectedJobState, bodyshop }) {
/> />
); );
} }
export default connect(mapStateToProps, null)(ContractJobsContainer); export default connect(mapStateToProps, null)(ContractJobsContainer);

View File

@@ -1,5 +1,5 @@
import {Button, Input, Modal, Typography} from "antd"; import {Button, Input, Modal, Typography} from "antd";
import moment from "moment"; import dayjs from "../../utils/day";
import React, {useState} from "react"; import React, {useState} from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import aamva from "../../utils/aamva"; import aamva from "../../utils/aamva";
@@ -26,8 +26,8 @@ export default function ContractLicenseDecodeButton({ form }) {
const values = { const values = {
driver_dlnumber: decodedBarcode.dl, driver_dlnumber: decodedBarcode.dl,
driver_dlexpiry: moment( driver_dlexpiry: dayjs(
`20${decodedBarcode.expiration_date}${moment( `20${decodedBarcode.expiration_date}${dayjs(
decodedBarcode.birthday decodedBarcode.birthday
).format("DD")}` ).format("DD")}`
), ),
@@ -38,7 +38,7 @@ export default function ContractLicenseDecodeButton({ form }) {
driver_city: decodedBarcode.city, driver_city: decodedBarcode.city,
driver_state: decodedBarcode.state, driver_state: decodedBarcode.state,
driver_zip: decodedBarcode.postal_code, driver_zip: decodedBarcode.postal_code,
driver_dob: moment(decodedBarcode.birthday), driver_dob: dayjs(decodedBarcode.birthday),
}; };
form.setFieldsValue(values); form.setFieldsValue(values);
@@ -55,7 +55,7 @@ export default function ContractLicenseDecodeButton({ form }) {
return ( return (
<div> <div>
<Modal <Modal
visible={modalVisible} open={modalVisible}
okText={t("contracts.actions.senddltoform")} okText={t("contracts.actions.senddltoform")}
onOk={handleInsertForm} onOk={handleInsertForm}
okButtonProps={{disabled: !!!decodedBarcode}} okButtonProps={{disabled: !!!decodedBarcode}}
@@ -94,14 +94,14 @@ export default function ContractLicenseDecodeButton({ form }) {
{decodedBarcode.address} {decodedBarcode.address}
</DataLabel> </DataLabel>
<DataLabel label={t("contracts.fields.driver_dlexpiry")}> <DataLabel label={t("contracts.fields.driver_dlexpiry")}>
{moment( {dayjs(
`20${decodedBarcode.expiration_date}${moment( `20${decodedBarcode.expiration_date}${dayjs(
decodedBarcode.birthday decodedBarcode.birthday
).format("DD")}` ).format("DD")}`
).format("MM/DD/YYYY")} ).format("MM/DD/YYYY")}
</DataLabel> </DataLabel>
<DataLabel label={t("contracts.fields.driver_dob")}> <DataLabel label={t("contracts.fields.driver_dob")}>
{moment(decodedBarcode.birthday).format("MM/DD/YYYY")} {dayjs(decodedBarcode.birthday).format("MM/DD/YYYY")}
</DataLabel> </DataLabel>
<div> <div>
<Typography.Title level={4}> <Typography.Title level={4}>

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect, forwardRef } from "react"; import React, {forwardRef, useEffect, useState} from "react";
import {Select} from "antd"; import {Select} from "antd";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
const {Option} = Select; const {Option} = Select;
const ContractStatusComponent = ({value, onChange}, ref) => { const ContractStatusComponent = ({value, onChange}, ref) => {

View File

@@ -31,7 +31,7 @@ export function ContractsFindModalContainer({
}) { }) {
const {t} = useTranslation(); const {t} = useTranslation();
const { visible } = contractFinderModal; const {open} = contractFinderModal;
const [form] = Form.useForm(); const [form] = Form.useForm();
@@ -52,14 +52,14 @@ export function ContractsFindModalContainer({
}; };
useEffect(() => { useEffect(() => {
if (visible) { if (open) {
form.resetFields(); form.resetFields();
} }
}, [visible, form]); }, [open, form]);
return ( return (
<Modal <Modal
visible={visible} open={open}
width="70%" width="70%"
title={t("contracts.labels.findermodal")} title={t("contracts.labels.findermodal")}
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}

View File

@@ -3,13 +3,13 @@ import { Button, Card, Input, Space, Table, Typography } 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";
import { Link, useHistory, useLocation } from "react-router-dom"; import {Link, useLocation, useNavigate} from "react-router-dom";
import {setModalContext} from "../../redux/modals/modals.actions"; import {setModalContext} from "../../redux/modals/modals.actions";
import {DateTimeFormatter} from "../../utils/DateFormatter"; import {DateTimeFormatter} from "../../utils/DateFormatter";
import {alphaSort} from "../../utils/sorters"; import {alphaSort} from "../../utils/sorters";
import ContractsFindModalContainer from "../contracts-find-modal/contracts-find-modal.container"; import ContractsFindModalContainer from "../contracts-find-modal/contracts-find-modal.container";
import moment from "moment"; import dayjs from "../../utils/day";
import {connect} from "react-redux"; import {connect} from "react-redux";
import {createStructuredSelector} from "reselect"; import {createStructuredSelector} from "reselect";
import {selectBodyshop} from "../../redux/user/user.selectors"; import {selectBodyshop} from "../../redux/user/user.selectors";
@@ -39,7 +39,7 @@ export function ContractsList({
sortedInfo: {}, sortedInfo: {},
filteredInfo: {text: ""}, filteredInfo: {text: ""},
}); });
const history = useHistory(); const history = useNavigate();
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const {page} = search; const {page} = search;
@@ -152,8 +152,8 @@ export function ContractsList({
render: (text, record) => render: (text, record) =>
(record.actualreturn && (record.actualreturn &&
record.start && record.start &&
`${moment(record.actualreturn) `${dayjs(record.actualreturn)
.diff(moment(record.start), "days", true) .diff(dayjs(record.start), "day", true)
.toFixed(1)} days`) || .toFixed(1)} days`) ||
"", "",
}, },
@@ -164,7 +164,7 @@ export function ContractsList({
search.page = pagination.current; search.page = pagination.current;
search.sortcolumn = sorter.columnKey; search.sortcolumn = sorter.columnKey;
search.sortorder = sorter.order; search.sortorder = sorter.order;
history.push({ search: queryString.stringify(search) }); history({search: queryString.stringify(search)});
}; };
return ( return (
@@ -179,7 +179,7 @@ export function ContractsList({
<Button <Button
onClick={() => { onClick={() => {
delete search.search; delete search.search;
history.push({ search: queryString.stringify(search) }); history({search: queryString.stringify(search)});
}} }}
> >
{t("general.actions.clear")} {t("general.actions.clear")}
@@ -196,7 +196,7 @@ export function ContractsList({
placeholder={search.searh || t("general.labels.search")} placeholder={search.searh || t("general.labels.search")}
onSearch={(value) => { onSearch={(value) => {
search.search = value; search.search = value;
history.push({ search: queryString.stringify(search) }); history({search: queryString.stringify(search)});
}} }}
/> />
</Space> </Space>

View File

@@ -1,5 +1,5 @@
import {DownOutlined} from "@ant-design/icons"; import {DownOutlined} from "@ant-design/icons";
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 {connect} from "react-redux"; import {connect} from "react-redux";
@@ -18,20 +18,16 @@ export function ContractsRatesChangeButton({ disabled, form, bodyshop }) {
form.setFieldsValue(rate); form.setFieldsValue(rate);
}; };
const menu = ( const menuItems = bodyshop.md_ccc_rates.map((i, idx) => ({
<div> key: idx,
<Menu onClick={handleClick}> label: i.label,
{bodyshop.md_ccc_rates.map((rate, idx) => ( value: i,
<Menu.Item value={rate} key={idx}> }));
{rate.label}
</Menu.Item> const menu = {items: menuItems, onClick: handleClick};
))}
</Menu>
</div>
);
return ( return (
<Dropdown overlay={menu} disabled={disabled}> <Dropdown menu={menu} disabled={disabled}>
<a <a
className="ant-dropdown-link" className="ant-dropdown-link"
href=" #" href=" #"

View File

@@ -2,7 +2,7 @@ import { Card, Table } 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";
import { Link, useHistory, useLocation } from "react-router-dom"; import {Link, useLocation, useNavigate} from "react-router-dom";
import {DateFormatter} from "../../utils/DateFormatter"; import {DateFormatter} from "../../utils/DateFormatter";
import {alphaSort} from "../../utils/sorters"; import {alphaSort} from "../../utils/sorters";
import {pageLimit} from "../../utils/config"; import {pageLimit} from "../../utils/config";
@@ -13,7 +13,7 @@ export default function CourtesyCarContractListComponent({
}) { }) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const {page, sortcolumn, sortorder} = search; const {page, sortcolumn, sortorder} = search;
const history = useHistory(); const history = useNavigate();
const {t} = useTranslation(); const {t} = useTranslation();
@@ -81,7 +81,7 @@ export default function CourtesyCarContractListComponent({
search.page = pagination.current; search.page = pagination.current;
search.sortcolumn = sorter.columnKey; search.sortcolumn = sorter.columnKey;
search.sortorder = sorter.order; search.sortorder = sorter.order;
history.push({ search: queryString.stringify(search) }); history({search: queryString.stringify(search)});
}; };
return ( return (

View File

@@ -1,7 +1,8 @@
import {WarningFilled} from "@ant-design/icons"; import {WarningFilled} from "@ant-design/icons";
import {useApolloClient} from "@apollo/client"; import {useApolloClient} from "@apollo/client";
import { Button, Form, Input, InputNumber, PageHeader, Space } from "antd"; import {Button, Form, Input, InputNumber, Space} from "antd";
import moment from "moment"; import {PageHeader} from "@ant-design/pro-layout";
import dayjs from "../../utils/day";
import React from "react"; import React from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import {CHECK_CC_FLEET_NUMBER} from "../../graphql/courtesy-car.queries"; import {CHECK_CC_FLEET_NUMBER} from "../../graphql/courtesy-car.queries";
@@ -263,7 +264,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
const nextservicedate = form.getFieldValue("nextservicedate"); const nextservicedate = form.getFieldValue("nextservicedate");
const dueForService = const dueForService =
nextservicedate && nextservicedate &&
moment(nextservicedate).endOf("day").isSameOrBefore(moment()); dayjs(nextservicedate).endOf("day").isSameOrBefore(dayjs());
if (dueForService) if (dueForService)
return ( return (
@@ -304,7 +305,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
const expires = form.getFieldValue("registrationexpires"); const expires = form.getFieldValue("registrationexpires");
const dateover = const dateover =
expires && moment(expires).endOf("day").isBefore(moment()); expires && dayjs(expires).endOf("day").isBefore(dayjs());
if (dateover) if (dateover)
return ( return (
@@ -340,7 +341,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
const expires = form.getFieldValue("insuranceexpires"); const expires = form.getFieldValue("insuranceexpires");
const dateover = const dateover =
expires && moment(expires).endOf("day").isBefore(moment()); expires && dayjs(expires).endOf("day").isBefore(dayjs());
if (dateover) if (dateover)
return ( return (

View File

@@ -1,6 +1,7 @@
import {Select} from "antd"; import {Select} from "antd";
import React, {forwardRef, useEffect, useState} from "react"; import React, {forwardRef, useEffect, useState} from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
const {Option} = Select; const {Option} = Select;
const CourtesyCarReadinessComponent = ({value, onChange}, ref) => { const CourtesyCarReadinessComponent = ({value, onChange}, ref) => {

View File

@@ -7,7 +7,7 @@ import { toggleModalVisible } from "../../redux/modals/modals.actions";
import {selectCourtesyCarReturn} from "../../redux/modals/modals.selectors"; import {selectCourtesyCarReturn} from "../../redux/modals/modals.selectors";
import {selectBodyshop} from "../../redux/user/user.selectors"; import {selectBodyshop} from "../../redux/user/user.selectors";
import CourtesyCarReturnModalComponent from "./courtesy-car-return-modal.component"; import CourtesyCarReturnModalComponent from "./courtesy-car-return-modal.component";
import moment from "moment"; import dayjs from "../../utils/day";
import {RETURN_CONTRACT} from "../../graphql/cccontracts.queries"; import {RETURN_CONTRACT} from "../../graphql/cccontracts.queries";
import {useMutation} from "@apollo/client"; import {useMutation} from "@apollo/client";
@@ -26,7 +26,7 @@ export function CCReturnModalContainer({
bodyshop, bodyshop,
}) { }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { visible, context, actions } = courtesyCarReturnModal; const {open, context, actions} = courtesyCarReturnModal;
const {t} = useTranslation(); const {t} = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [updateContract] = useMutation(RETURN_CONTRACT); const [updateContract] = useMutation(RETURN_CONTRACT);
@@ -64,7 +64,7 @@ export function CCReturnModalContainer({
return ( return (
<Modal <Modal
title={t("courtesycars.labels.return")} title={t("courtesycars.labels.return")}
visible={visible} open={open}
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}
width={"90%"} width={"90%"}
okText={t("general.actions.save")} okText={t("general.actions.save")}
@@ -74,7 +74,7 @@ export function CCReturnModalContainer({
<Form <Form
form={form} form={form}
onFinish={handleFinish} onFinish={handleFinish}
initialValues={{ fuel: 100, actualreturn: moment(new Date()) }} initialValues={{fuel: 100, actualreturn: dayjs(new Date())}}
> >
<CourtesyCarReturnModalComponent/> <CourtesyCarReturnModalComponent/>
</Form> </Form>

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect, forwardRef } from "react"; import React, {forwardRef, useEffect, useState} from "react";
import {Select} from "antd"; import {Select} from "antd";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
const {Option} = Select; const {Option} = Select;
const CourtesyCarStatusComponent = ({value, onChange}, ref) => { const CourtesyCarStatusComponent = ({value, onChange}, ref) => {

View File

@@ -1,15 +1,6 @@
import {SyncOutlined, WarningFilled} from "@ant-design/icons"; import {SyncOutlined, WarningFilled} from "@ant-design/icons";
import { import {Button, Card, Dropdown, Input, Space, Table, Tooltip,} from "antd";
Button, import dayjs from "../../utils/day";
Card,
Dropdown,
Input,
Menu,
Space,
Table,
Tooltip,
} 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";
@@ -18,6 +9,7 @@ import { GenerateDocument } from "../../utils/RenderTemplate";
import {TemplateList} from "../../utils/TemplateConstants"; import {TemplateList} from "../../utils/TemplateConstants";
import {alphaSort} from "../../utils/sorters"; import {alphaSort} from "../../utils/sorters";
import {OwnerNameDisplayFunction} from "../owner-name-display/owner-name-display.component"; import {OwnerNameDisplayFunction} from "../owner-name-display/owner-name-display.component";
export default function CourtesyCarsList({loading, courtesycars, refetch}) { export default function CourtesyCarsList({loading, courtesycars, refetch}) {
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
@@ -77,8 +69,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
const mileageOver = nextservicekm ? nextservicekm <= mileage : false; const mileageOver = nextservicekm ? nextservicekm <= mileage : false;
const dueForService = const dueForService =
nextservicedate && nextservicedate && dayjs(nextservicedate).endOf('day').isSameOrBefore(dayjs());
moment(nextservicedate).endOf("day").isSameOrBefore(moment());
return ( return (
<Space> <Space>
@@ -229,6 +220,27 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
(t(c.status) || "").toLowerCase().includes(searchText.toLowerCase()) (t(c.status) || "").toLowerCase().includes(searchText.toLowerCase())
) )
: courtesycars; : courtesycars;
const items = [
{
key: "courtesycar_inventory",
label: t("printcenter.courtesycarcontract.courtesy_car_inventory"),
onClick: () =>
GenerateDocument(
{
name: TemplateList("courtesycar").courtesy_car_inventory.key,
variables: {
//id: contract.id
},
},
{},
"p"
),
},
];
const menu = {items};
return ( return (
<Card <Card
title={t("menus.header.courtesycars")} title={t("menus.header.courtesycars")}
@@ -237,30 +249,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
<Button onClick={() => refetch()}> <Button onClick={() => refetch()}>
<SyncOutlined/> <SyncOutlined/>
</Button> </Button>
<Dropdown <Dropdown trigger="click" menu={menu}>
trigger="click"
overlay={
<Menu>
<Menu.Item
onClick={() =>
GenerateDocument(
{
name: TemplateList("courtesycar").courtesy_car_inventory
.key,
variables: {
//id: contract.id
},
},
{},
"p"
)
}
>
{t("printcenter.courtesycarcontract.courtesy_car_inventory")}
</Menu.Item>
</Menu>
}
>
<Button>{t("general.labels.print")}</Button> <Button>{t("general.labels.print")}</Button>
</Dropdown> </Dropdown>
<Link to={`/manage/courtesycars/new`}> <Link to={`/manage/courtesycars/new`}>

View File

@@ -3,7 +3,7 @@ import { Button, Card, 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";
import { Link, useHistory, useLocation } from "react-router-dom"; import {Link, useLocation, useNavigate} from "react-router-dom";
import {DateFormatter} from "../../utils/DateFormatter"; import {DateFormatter} from "../../utils/DateFormatter";
import {alphaSort} from "../../utils/sorters"; import {alphaSort} from "../../utils/sorters";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
@@ -17,7 +17,7 @@ export default function CsiResponseListPaginated({
}) { }) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const {responseid, page, sortcolumn, sortorder} = search; const {responseid, page, sortcolumn, sortorder} = search;
const history = useHistory(); const history = useNavigate();
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
filteredInfo: {text: ""}, filteredInfo: {text: ""},
@@ -80,18 +80,18 @@ export default function CsiResponseListPaginated({
search.page = pagination.current; search.page = pagination.current;
search.sortcolumn = sorter.columnKey; search.sortcolumn = sorter.columnKey;
search.sortorder = sorter.order; search.sortorder = sorter.order;
history.push({ search: queryString.stringify(search) }); history({search: queryString.stringify(search)});
}; };
const handleOnRowClick = (record) => { const handleOnRowClick = (record) => {
if (record) { if (record) {
if (record.id) { if (record.id) {
search.responseid = record.id; search.responseid = record.id;
history.push({ search: queryString.stringify(search) }); history({search: queryString.stringify(search)});
} }
} else { } else {
delete search.responseid; delete search.responseid;
history.push({ search: queryString.stringify(search) }); history({search: queryString.stringify(search)});
} }
}; };

View File

@@ -1,19 +1,9 @@
import {Card} from "antd"; import {Card} from "antd";
import _ from "lodash"; import _ from "lodash";
import moment from "moment"; import dayjs from "../../../utils/day";
import React from "react"; import React from "react";
import {useTranslation} from "react-i18next"; import {useTranslation} from "react-i18next";
import { import {Bar, CartesianGrid, ComposedChart, Legend, Line, ResponsiveContainer, Tooltip, XAxis, YAxis,} from "recharts";
Bar,
CartesianGrid,
ComposedChart,
Legend,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import * as Utils from "../../scoreboard-targets-table/scoreboard-targets-table.util"; import * as Utils from "../../scoreboard-targets-table/scoreboard-targets-table.util";
import DashboardRefreshRequired from "../refresh-required.component"; import DashboardRefreshRequired from "../refresh-required.component";
@@ -27,7 +17,7 @@ export default function DashboardMonthlyEmployeeEfficiency({
return <DashboardRefreshRequired {...cardProps} />; return <DashboardRefreshRequired {...cardProps} />;
const ticketsByDate = _.groupBy(data.monthly_employee_efficiency, (item) => const ticketsByDate = _.groupBy(data.monthly_employee_efficiency, (item) =>
moment(item.date).format("YYYY-MM-DD") dayjs(item.date).format("YYYY-MM-DD")
); );
const listOfDays = Utils.ListOfDaysInCurrentMonth(); const listOfDays = Utils.ListOfDaysInCurrentMonth();
@@ -53,7 +43,7 @@ export default function DashboardMonthlyEmployeeEfficiency({
((dailyHrs.productive - dailyHrs.actual) / dailyHrs.actual + 1) * 100; ((dailyHrs.productive - dailyHrs.actual) / dailyHrs.actual + 1) * 100;
const theValue = { const theValue = {
date: moment(val).format("DD"), date: dayjs(val).format("DD"),
// ...dailyHrs, // ...dailyHrs,
actual: dailyHrs.actual.toFixed(1), actual: dailyHrs.actual.toFixed(1),
productive: dailyHrs.productive.toFixed(1), productive: dailyHrs.productive.toFixed(1),
@@ -159,9 +149,9 @@ export default function DashboardMonthlyEmployeeEfficiency({
} }
export const DashboardMonthlyEmployeeEfficiencyGql = ` export const DashboardMonthlyEmployeeEfficiencyGql = `
monthly_employee_efficiency: timetickets(where: {_and: [{date: {_gte: "${moment() monthly_employee_efficiency: timetickets(where: {_and: [{date: {_gte: "${dayjs()
.startOf("month") .startOf("month")
.format("YYYY-MM-DD")}"}},{date: {_lte: "${moment() .format("YYYY-MM-DD")}"}},{date: {_lte: "${dayjs()
.endOf("month") .endOf("month")
.format("YYYY-MM-DD")}"}} ]}) { .format("YYYY-MM-DD")}"}} ]}) {
actualhrs actualhrs

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