- 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
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:
docker:
- image: cimg/node:16.15.0
@@ -233,6 +253,27 @@ jobs:
to: "s3://imex-online-test/"
- 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:
docker:
- image: cimg/node:16.15.0
@@ -274,6 +315,10 @@ workflows:
filters:
branches:
only: master
- app-beta-build:
filters:
branches:
only: master-beta
- hasura-migrate:
secret: ${HASURA_PROD_SECRET}
filters:
@@ -296,6 +341,10 @@ workflows:
filters:
branches:
only: test
- test-app-beta-build:
filters:
branches:
only: test-beta
- test-hasura-migrate:
secret: ${HASURA_TEST_SECRET}
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 CracoLessPlugin = require("craco-less");
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 = {
plugins: [
@@ -26,8 +34,10 @@ module.exports = {
lessLoaderOptions: {
lessOptions: {
modifyVars: {
...v4Token,
// TODO: This will no longer work in AntD 5.0
...(process.env.NODE_ENV === "development"
? { "@primary-color": "#B22234" }
? {"colorPrimary": "#B22234"}
: {
//"@primary-color": "#1DA57A"
}),
@@ -53,8 +63,14 @@ module.exports = {
},
],
webpack: {
configure: (webpackConfig) => ({
configure: (webpackConfig) => {
return {
...webpackConfig,
// Required for Dev Server
devServer: {
...webpackConfig.devServer,
allowedHosts: 'all',
},
optimization: {
...webpackConfig.optimization,
// Workaround for CircleCI bug caused by the number of CPUs shown
@@ -67,7 +83,8 @@ module.exports = {
return item;
}),
},
}),
};
},
},
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
│ ├─ 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
├─ postcss-focus-visible@4.0.0
├─ postcss-focus-open@4.0.0
│ ├─ licenses: CC0-1.0
│ ├─ repository: https://github.com/jonathantneal/postcss-focus-visible
│ ├─ repository: https://github.com/jonathantneal/postcss-focus-open
│ ├─ publisher: Jonathan Neal
│ ├─ email: jonathantneal@hotmail.com
│ ├─ path: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-focus-visible
│ └─ licenseFile: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-focus-visible/LICENSE.md
│ ├─ path: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-focus-open
│ └─ licenseFile: /Users/pfic/Documents/Development/bodyshop/client/node_modules/postcss-focus-open/LICENSE.md
├─ postcss-focus-within@3.0.0
│ ├─ licenses: CC0-1.0
│ ├─ 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,
"proxy": "http://localhost:4000",
"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",
"@craco/craco": "^7.0.0",
"@fingerprintjs/fingerprintjs": "^3.4.2",
"@craco/craco": "^7.1.0",
"@fingerprintjs/fingerprintjs": "^4.2.1",
"@jsreport/browser-client": "^3.1.0",
"@sentry/react": "^7.40.0",
"@sentry/tracing": "^7.40.0",
"@splitsoftware/splitio-react": "^1.8.1",
"@tanem/react-nprogress": "^5.0.8",
"antd": "^4.24.8",
"@reduxjs/toolkit": "^2.0.1",
"@sentry/react": "^7.93.0",
"@sentry/tracing": "^7.93.0",
"@splitsoftware/splitio-react": "^1.11.0",
"@tanem/react-nprogress": "^5.0.51",
"antd": "^5.12.8",
"apollo-link-logger": "^2.0.1",
"axios": "^1.3.4",
"craco-less": "^2.0.0",
"axios": "^1.6.5",
"craco-less": "^3.0.1",
"dayjs": "^1.11.10",
"dayjs-business-days2": "^1.2.2",
"dinero.js": "^1.9.1",
"dotenv": "^16.0.1",
"dotenv": "^16.3.1",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^9.17.1",
"firebase": "^10.7.2",
"graphql": "^16.6.0",
"i18next": "^22.4.10",
"i18next-browser-languagedetector": "^7.0.1",
"jsoneditor": "^9.9.0",
"i18next": "^23.7.16",
"i18next-browser-languagedetector": "^7.0.2",
"jsoneditor": "^10.0.0",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.10.21",
"logrocket": "^3.0.1",
"markerjs2": "^2.28.1",
"moment-business-days": "^1.2.0",
"moment-timezone": "^0.5.41",
"libphonenumber-js": "^1.10.53",
"logrocket": "^7.0.0",
"markerjs2": "^2.31.4",
"normalize-url": "^8.0.0",
"phone": "^3.1.35",
"phone": "^3.1.42",
"preval.macro": "^5.0.0",
"prop-types": "^15.8.1",
"query-string": "^7.1.3",
"query-string": "^8.1.0",
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6",
"react": "^17.0.2",
"react-big-calendar": "^1.6.8",
"react": "^18.2.0",
"react-big-calendar": "^1.8.6",
"react-color": "^2.19.3",
"react-cookie": "^4.1.1",
"react-dom": "^17.0.2",
"react-drag-listview": "^0.2.1",
"react-cookie": "^7.0.1",
"react-dom": "^18.2.0",
"react-drag-listview": "^2.0.0",
"react-grid-gallery": "^1.0.0",
"react-grid-layout": "^1.3.4",
"react-i18next": "^12.2.0",
"react-icons": "^4.7.1",
"react-grid-layout": "1.3.4",
"react-i18next": "^14.0.0",
"react-icons": "^5.0.1",
"react-image-lightbox": "^5.1.4",
"react-intersection-observer": "^9.4.3",
"react-number-format": "^5.1.3",
"react-redux": "^8.0.5",
"react-resizable": "^3.0.4",
"react-router-dom": "^5.3.0",
"react-intersection-observer": "^9.5.3",
"react-number-format": "^5.1.4",
"react-redux": "^9.1.0",
"react-resizable": "^3.0.5",
"react-router-dom": "^6.21.3",
"react-scripts": "^5.0.1",
"react-sticky": "^6.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.4.3",
"redux": "^4.2.1",
"react-virtualized": "^9.22.5",
"recharts": "^2.10.4",
"redux": "^5.0.1",
"redux-persist": "^6.0.0",
"redux-saga": "^1.2.2",
"redux-saga": "^1.3.0",
"redux-state-sync": "^3.1.4",
"reselect": "^4.1.7",
"sass": "^1.58.3",
"socket.io-client": "^4.6.1",
"styled-components": "^5.3.6",
"reselect": "^5.1.0",
"sass": "^1.70.0",
"socket.io-client": "^4.7.4",
"styled-components": "^6.1.8",
"subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.4",
"workbox-background-sync": "^6.5.3",
"workbox-broadcast-update": "^6.5.3",
"workbox-cacheable-response": "^6.5.3",
"workbox-core": "^6.5.3",
"workbox-expiration": "^6.5.3",
"workbox-google-analytics": "^6.5.3",
"workbox-navigation-preload": "^6.5.3",
"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",
"terser-webpack-plugin": "^5.3.10",
"web-vitals": "^3.5.1",
"workbox-core": "^7.0.0",
"workbox-expiration": "^7.0.0",
"workbox-navigation-preload": "^7.0.0",
"workbox-precaching": "^7.0.0",
"workbox-routing": "^7.0.0",
"workbox-strategies": "^7.0.0",
"yauzl": "^2.10.0"
},
"scripts": {
@@ -119,12 +117,13 @@
"react-error-overlay": "6.0.9"
},
"devDependencies": {
"@sentry/webpack-plugin": "^1.20.0",
"@testing-library/cypress": "^8.0.3",
"cypress": "^10.3.1",
"eslint-plugin-cypress": "^2.12.1",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@sentry/webpack-plugin": "^2.10.2",
"@testing-library/cypress": "^10.0.1",
"cypress": "^13.6.3",
"eslint-plugin-cypress": "^2.15.1",
"react-error-overlay": "6.0.11",
"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
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.
## 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.
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)
tells the user that there is no warranty for the work (except to 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-double-position-gradients@1.0.0
- 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-gap-properties@2.0.0
- postcss-image-set-function@3.0.1
@@ -1699,7 +1699,7 @@ This package contains the following license and notice below:
# @firebase/logger
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.
## 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.
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)
tells the user that there is no warranty for the work (except to 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 { ConfigProvider } from "antd";
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 { useTranslation } from "react-i18next";
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
import client from "../utils/GraphQLClient";
import App from "./App";
moment.locale("en-US");
dayjs.locale("en");
export const factory = SplitSdk({
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 LogRocket from "logrocket";
import React, { lazy, Suspense, useEffect } from "react";
import React, {lazy, Suspense, useEffect, useState} from "react";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import { Route, Switch } from "react-router-dom";
import {Route, Routes} from "react-router-dom";
import {createStructuredSelector} from "reselect";
import DocumentEditorContainer from "../components/document-editor/document-editor.container";
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 {selectOnline} from "../redux/application/application.selectors";
import {checkUserSession} from "../redux/user/user.actions";
import {
selectBodyshop,
selectCurrentUser,
} from "../redux/user/user.selectors";
import PrivateRoute from "../utils/private-route";
import {selectBodyshop, selectCurrentUser,} from "../redux/user/user.selectors";
import PrivateRoute from "../components/PrivateRoute";
import "./App.styles.scss";
import handleBeta from "../utils/betaHandler";
const ResetPassword = lazy(() =>
import("../pages/reset-password/reset-password.component")
@@ -33,7 +31,6 @@ const CsiPage = lazy(() => import("../pages/csi/csi.container.page"));
const MobilePaymentContainer = lazy(() =>
import("../pages/mobile-payment/mobile-payment.container")
);
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
online: selectOnline,
@@ -44,14 +41,12 @@ const mapDispatchToProps = (dispatch) => ({
setOnline: (isOnline) => dispatch(setOnline(isOnline)),
});
export function App({
bodyshop,
checkUserSession,
currentUser,
online,
setOnline,
}) {
const client = useClient();
export function App({bodyshop, checkUserSession, currentUser, online, setOnline}) {
const client = useSplitClient().client;
const [listenersAdded, setListenersAdded] = useState(false)
const {t} = useTranslation();
useEffect(() => {
if (!navigator.onLine) {
@@ -64,21 +59,38 @@ export function App({
//const b = Grid.useBreakpoint();
// console.log("Breakpoints:", b);
const { t } = useTranslation();
window.addEventListener("offline", function (e) {
// Associate event listeners, memoize to prevent multiple listeners being added
useEffect(() => {
const offlineListener = (e) => {
setOnline(false);
});
}
window.addEventListener("online", function (e) {
const onlineListener = (e) => {
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(() => {
if (currentUser.authorized && bodyshop) {
client.setAttribute("imexshopid", bodyshop.imexshopid);
LogRocket.init("rome-online/rome-online");
if (client.getTreatment("LogRocket_Tracking") === "on") {
if (
client.getTreatment("LogRocket_Tracking") === "on" ||
window.location.hostname === 'beta.romeonline.io'
) {
console.log("LR Start");
LogRocket.init("rome-online/rome-online");
}
}
@@ -88,6 +100,8 @@ export function App({
return <LoadingSpinner message={t("general.labels.loggingin")}/>;
}
handleBeta();
if (!online)
return (
<Result
@@ -107,54 +121,27 @@ export function App({
/>
);
// Any route that is not assigned and matched will default to the Landing Page component
return (
<Switch>
<Suspense fallback={<LoadingSpinner />}>
<ErrorBoundary>
<Route exact path="/" component={LandingPage} />
</ErrorBoundary>
<ErrorBoundary>
<Route exact path="/signin" component={SignInPage} />
</ErrorBoundary>
<ErrorBoundary>
<Route exact path="/resetpassword" component={ResetPassword} />
</ErrorBoundary>
<ErrorBoundary>
<Route exact path="/csi/:surveyId" component={CsiPage} />
</ErrorBoundary>
<ErrorBoundary>
<Route exact path="/disclaimer" component={DisclaimerPage} />
</ErrorBoundary>
<ErrorBoundary>
<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 fallback={<LoadingSpinner message="Rome Online"/>}>
<Routes>
<Route path="*" element={<ErrorBoundary><LandingPage/></ErrorBoundary>}/>
<Route path="/signin" element={<ErrorBoundary><SignInPage/></ErrorBoundary>}/>
<Route path="/resetpassword" element={<ErrorBoundary><ResetPassword/></ErrorBoundary>}/>
<Route path="/csi/:surveyId" element={<ErrorBoundary><CsiPage/></ErrorBoundary>}/>
<Route path="/disclaimer" element={<ErrorBoundary><DisclaimerPage/></ErrorBoundary>}/>
<Route path="/mp/:paymentIs" element={<ErrorBoundary><MobilePaymentContainer/></ErrorBoundary>}/>
<Route path="/manage/*" element={<ErrorBoundary><PrivateRoute isAuthorized={currentUser.authorized}/></ErrorBoundary>}>
<Route path="*" element={<ManagePage/>}/>
</Route>
<Route path="/tech/*" element={<ErrorBoundary><PrivateRoute isAuthorized={currentUser.authorized}/></ErrorBoundary>}>
<Route path="*" element={<TechPageContainer/>}/>
</Route>
<Route path="/edit/*" element={<PrivateRoute isAuthorized={currentUser.authorized}/>}>
<Route path="*" element={<DocumentEditorContainer/>}/>
</Route>
</Routes>
</Suspense>
</Switch>
);
}

View File

@@ -1,6 +1,14 @@
//Global 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 {
display: flex;
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 {useTranslation} from "react-i18next";
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 {useTranslation} from "react-i18next";
import {connect} from "react-redux";
@@ -61,7 +61,7 @@ export function AllocationsAssignmentComponent({
);
return (
<Popover content={popContent} visible={visibility}>
<Popover content={popContent} open={visibility}>
<Button onClick={() => setVisibility(true)}>
{t("allocations.actions.assign")}
</Button>

View File

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

View File

@@ -2,6 +2,7 @@ import React from "react";
import {List} from "antd";
import Icon from "@ant-design/icons";
import {FaArrowRight} from "react-icons/fa";
export default function AuditTrailValuesComponent({oldV, newV}) {
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 Barcode from "react-barcode";
import {useTranslation} from "react-i18next";
export default function BarcodePopupComponent({value, children}) {
const {t} = useTranslation();
return (

View File

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

View File

@@ -1,17 +1,13 @@
import {useMutation, useQuery} from "@apollo/client";
import { Button, Form, PageHeader, Popconfirm, Space } from "antd";
import moment from "moment";
import {Button, Form, Popconfirm, Space} from "antd";
import dayjs from "../../utils/day";
import queryString from "query-string";
import React, {useState} from "react";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import {useLocation} from "react-router-dom";
import {createStructuredSelector} from "reselect";
import {
DELETE_BILL_LINE,
INSERT_NEW_BILL_LINES,
UPDATE_BILL_LINE,
} from "../../graphql/bill-lines.queries";
import {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 {insertAuditTrail} from "../../redux/application/application.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 LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import BillDetailEditReturn from "./bill-detail-edit-return.component";
import {PageHeader} from "@ant-design/pro-layout";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -42,16 +39,12 @@ export default connect(
mapDispatchToProps
)(BillDetailEditcontainer);
export function BillDetailEditcontainer({
setPartsOrderContext,
insertAuditTrail,
bodyshop,
}) {
export function BillDetailEditcontainer({setPartsOrderContext, insertAuditTrail, bodyshop,}) {
const search = queryString.parse(useLocation().search);
const {t} = useTranslation();
const [form] = Form.useForm();
const [visible, setVisible] = useState(false);
const [open, setOpen] = useState(false);
const [updateLoading, setUpdateLoading] = useState(false);
const [update_bill] = useMutation(UPDATE_BILL);
const [insertBillLine] = useMutation(INSERT_NEW_BILL_LINES);
@@ -65,6 +58,8 @@ export function BillDetailEditcontainer({
nextFetchPolicy: "network-only",
});
// ... rest of the code remains the same
const handleSave = () => {
//It's got a previously deducted bill line!
if (
@@ -72,7 +67,7 @@ export function BillDetailEditcontainer({
form.getFieldValue("billlines").filter((b) => b.deductedfromlbr).length >
0
)
setVisible(true);
setOpen(true);
else {
form.submit();
}
@@ -155,7 +150,7 @@ export function BillDetailEditcontainer({
await refetch();
form.setFieldsValue(transformData(data));
form.resetFields();
setVisible(false);
setOpen(false);
setUpdateLoading(false);
};
@@ -179,9 +174,9 @@ export function BillDetailEditcontainer({
<BillDetailEditReturn data={data}/>
<BillPrintButton billid={search.billid}/>
<Popconfirm
visible={visible}
open={open}
onConfirm={() => form.submit()}
onCancel={() => setVisible(false)}
onCancel={() => setOpen(false)}
okButtonProps={{loading: updateLoading}}
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 {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import { useHistory, useLocation } from "react-router-dom";
import {useLocation, useNavigate} from "react-router-dom";
import {createStructuredSelector} from "reselect";
import {insertAuditTrail} from "../../redux/application/application.actions";
import {setModalContext} from "../../redux/modals/modals.actions";
@@ -33,10 +33,10 @@ export function BillDetailEditReturn({
disabled,
}) {
const search = queryString.parse(useLocation().search);
const history = useHistory();
const history = useNavigate();
const {t} = useTranslation();
const [form] = Form.useForm();
const [visible, setVisible] = useState(false);
const [open, setOpen] = useState(false);
const handleFinish = ({billlines}) => {
const selectedLines = billlines.filter((l) => l.selected).map((l) => l.id);
@@ -67,18 +67,18 @@ export function BillDetailEditReturn({
});
delete search.billid;
history.push({ search: queryString.stringify(search) });
setVisible(false);
history({search: queryString.stringify(search)});
setOpen(false);
};
useEffect(() => {
if (visible === false) form.resetFields();
}, [visible, form]);
if (open === false) form.resetFields();
}, [open, form]);
return (
<>
<Modal
visible={visible}
onCancel={() => setVisible(false)}
open={open}
onCancel={() => setOpen(false)}
destroyOnClose
title={t("bills.actions.return")}
onOk={() => form.submit()}
@@ -175,7 +175,7 @@ export function BillDetailEditReturn({
<Button
disabled={data.bills_by_pk.is_credit_memo || disabled}
onClick={() => {
setVisible(true);
setOpen(true);
}}
>
{t("bills.actions.return")}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,8 @@ const BillLineSearchSelect = (
disabled={disabled}
ref={ref}
showSearch
dropdownMatchSelectWidth={false}
popupMatchSelectWidth={false}
optionLabelProp={"name"}
// optionFilterProp="line_desc"
filterOption={(inputValue, option) => {
return (
@@ -57,6 +58,9 @@ const BillLineSearchSelect = (
style={{
...(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>
{`${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 { gql } from "@apollo/client";
import React, {useState} from "react";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import {createStructuredSelector} from "reselect";
import {
selectAuthLevel,
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import {selectAuthLevel, selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
import {HasRbacAccess} from "../rbac-wrapper/rbac-wrapper.component";
import {INSERT_EXPORT_LOG} from "../../graphql/accounting.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
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 { gql } from "@apollo/client";
import React, {useState} from "react";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import {createStructuredSelector} from "reselect";
import {
selectAuthLevel,
selectBodyshop,
} from "../../redux/user/user.selectors";
import {selectAuthLevel, selectBodyshop,} from "../../redux/user/user.selectors";
import {HasRbacAccess} from "../rbac-wrapper/rbac-wrapper.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
authLevel: selectAuthLevel,

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import {HomeFilled} from "@ant-design/icons";
import { Breadcrumb, Row, Col } from "antd";
import {Breadcrumb, Col, Row} from "antd";
import React from "react";
import {connect} from "react-redux";
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 GlobalSearchOs from "../global-search/global-search-os.component";
import "./breadcrumbs.styles.scss";
import { useTreatments } from "@splitsoftware/splitio-react";
import {useSplitTreatments} from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
breadcrumbs: selectBreadcrumbs,
@@ -17,33 +17,42 @@ const mapStateToProps = createStructuredSelector({
});
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 (
<Row className="breadcrumb-container">
<Col xs={24} sm={24} md={16}>
<Breadcrumb separator=">">
<Breadcrumb.Item>
<Link to={`/manage`}>
<Breadcrumb
separator=">"
items={[
{
key: "home",
title: (
<Link to={`/manage/`}>
<HomeFilled/>{" "}
{(bodyshop && bodyshop.shopname && `(${bodyshop.shopname})`) ||
""}
</Link>
</Breadcrumb.Item>
{breadcrumbs.map((item) =>
item.link ? (
<Breadcrumb.Item key={item.label}>
<Link to={item.link}>{item.label} </Link>
</Breadcrumb.Item>
) : (
<Breadcrumb.Item key={item.label}>{item.label}</Breadcrumb.Item>
)
)}
</Breadcrumb>
),
},
...breadcrumbs.map((item) =>
item.link
? {
key: item.label,
title: <Link to={item.link}>{item.label}</Link>,
}
: {
key: item.label,
title: item.label,
}
),
]}
/>
</Col>
<Col xs={24} sm={24} md={8}>
{OpenSearch.treatment === "on" ? <GlobalSearchOs/> : <GlobalSearch/>}
@@ -51,4 +60,5 @@ export function BreadCrumbs({ breadcrumbs, bodyshop }) {
</Row>
);
}
export default connect(mapStateToProps, null)(BreadCrumbs);

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,20 +4,11 @@ import { Button, notification, Space } from "antd";
import axios from "axios";
import React, {useEffect} from "react";
import {useTranslation} from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
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 ChatPopupComponent from "../chat-popup/chat-popup.component";
import "./chat-affix.styles.scss";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
chatVisible: selectChatVisible,
});
export function ChatAffixContainer({bodyshop, chatVisible}) {
const {t} = useTranslation();
const client = useApolloClient();
@@ -47,7 +38,6 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
<Button
onClick={async () => {
await requestForToken();
SubscribeToTopic();
}}
>
@@ -81,16 +71,17 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
payload: (payload && payload.data && payload.data.data) || payload.data,
});
}
let stopMessageListenr, channel;
let stopMessageListener, channel;
try {
stopMessageListenr = onMessage(messaging, handleMessage);
stopMessageListener = onMessage(messaging, handleMessage);
channel = new BroadcastChannel("imex-sw-messages");
channel.addEventListener("message", handleMessage);
} catch (error) {
console.log("Unable to set event listeners.");
}
return () => {
stopMessageListenr && stopMessageListenr();
stopMessageListener && stopMessageListener();
channel && channel.removeEventListener("message", handleMessage);
};
}, [client]);
@@ -103,4 +94,5 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
</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 {connect} from "react-redux";
import {
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List as VirtualizedList,
} from "react-virtualized";
import {AutoSizer, CellMeasurer, CellMeasurerCache, List as VirtualizedList,} from "react-virtualized";
import {createStructuredSelector} from "reselect";
import {setSelectedConversation} from "../../redux/messaging/messaging.actions";
import {selectSelectedConversation} from "../../redux/messaging/messaging.selectors";
import {TimeAgoFormatter} from "../../utils/DateFormatter";
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";
const mapStateToProps = createStructuredSelector({
@@ -38,6 +33,36 @@ function ChatConversationListComponent({
const rowRenderer = ({index, key, style, parent}) => {
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 (
<CellMeasurer
@@ -49,44 +74,21 @@ function ChatConversationListComponent({
>
<List.Item
onClick={() => setSelectedConversation(item.id)}
className={`chat-list-item ${
style={style}
className={`chat-list-item
${
item.id === selectedConversation
? "chat-list-selected-conversation"
: 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
style={{
display: "inline-block",
}}
>
{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} />
style={{display: 'inline-block', width: '30%', textAlign: 'right'}}>{cardContentRight}</div>
</Card>
</List.Item>
</CellMeasurer>
);

View File

@@ -1,27 +1,16 @@
.chat-list-selected-conversation {
background-color: rgba(128, 128, 128, 0.2);
}
.chat-list-container {
flex: 1;
overflow: hidden;
height: 100%;
border: 1px solid gainsboro;
}
.chat-list-item {
display: flex;
flex-direction: row;
.ant-card-head {
border: none;
}
&:hover {
cursor: pointer;
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 {connect} from "react-redux";
import {createStructuredSelector} from "reselect";
import {
CONVERSATION_SUBSCRIPTION_BY_PK,
GET_CONVERSATION_DETAILS,
} from "../../graphql/conversations.queries";
import {CONVERSATION_SUBSCRIPTION_BY_PK, GET_CONVERSATION_DETAILS,} from "../../graphql/conversations.queries";
import {MARK_MESSAGES_AS_READ_BY_CONVERSATION} from "../../graphql/messages.queries";
import {selectSelectedConversation} from "../../redux/messaging/messaging.selectors";
import ChatConversationComponent from "./chat-conversation.component";
import axios from "axios";
import {selectBodyshop} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
bodyshop: selectBodyshop,

View File

@@ -4,6 +4,7 @@ import { Input, notification, Spin, Tag, Tooltip } from "antd";
import React, {useState} from "react";
import {useTranslation} from "react-i18next";
import {UPDATE_CONVERSATION_LABEL} from "../../graphql/conversations.queries";
export default function ChatLabel({conversation}) {
const [loading, setLoading] = 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 AlertComponent from "../alert/alert.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";
const mapStateToProps = createStructuredSelector({
@@ -27,7 +28,7 @@ export function ChatMediaSelector({
conversation,
}) {
const {t} = useTranslation();
const [visible, setVisible] = useState(false);
const [open, setOpen] = useState(false);
const {loading, error, data} = useQuery(GET_DOCUMENTS_BY_JOB, {
fetchPolicy: "network-only",
@@ -39,13 +40,13 @@ export function ChatMediaSelector({
},
skip:
!visible ||
!open ||
!conversation.job_conversations ||
conversation.job_conversations.length === 0,
});
const handleVisibleChange = (visible) => {
setVisible(visible);
const handleVisibleChange = (change) => {
setOpen(change);
};
useEffect(() => {
@@ -65,7 +66,7 @@ export function ChatMediaSelector({
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
{bodyshop.uselocalmediaserver && visible && (
{bodyshop.uselocalmediaserver && open && (
<JobDocumentsLocalGalleryExternal
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={
@@ -88,8 +89,8 @@ export function ChatMediaSelector({
}
title={t("messaging.labels.selectmedia")}
trigger="click"
visible={visible}
onVisibleChange={handleVisibleChange}
open={open}
onOpenChange={handleVisibleChange}
>
<Badge count={selectedMedia.filter((s) => s.isSelected).length}>
<PictureFilled style={{margin: "0 .5rem"}}/>

View File

@@ -1,15 +1,10 @@
import Icon from "@ant-design/icons";
import {Tooltip} from "antd";
import i18n from "i18next";
import moment from "moment";
import dayjs from "../../utils/day";
import React, {useEffect, useRef} from "react";
import {MdDone, MdDoneAll} from "react-icons/md";
import {
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List,
} from "react-virtualized";
import {AutoSizer, CellMeasurer, CellMeasurerCache, List,} from "react-virtualized";
import {DateTimeFormatter} from "../../utils/DateFormatter";
import "./chat-message-list.styles.scss";
@@ -52,7 +47,7 @@ export default function ChatMessageListComponent({ messages }) {
<div style={{fontSize: 10}}>
{i18n.t("messaging.labels.sentby", {
by: messages[index].userid,
time: moment(messages[index].created_at).format(
time: dayjs(messages[index].created_at).format(
"MM/DD/YYYY @ hh:mm a"
),
})}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,44 +16,31 @@ const mapDispatchToProps = (dispatch) => ({
export function ChatPrintButton({conversation}) {
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 (
<Space wrap>
<PrinterOutlined
onClick={() => {
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);
}}
/>
<PrinterOutlined onClick={() => generateDocument('p')}/>
<MailOutlined onClick={() => generateDocument('e')}/>
{loading && <Spin/>}
</Space>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatPrintButton);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import {useQuery} from "@apollo/client";
import moment from "moment";
import dayjs from "../../utils/day";
import React from "react";
import {QUERY_AVAILABLE_CC} from "../../graphql/courtesy-car.queries";
import AlertComponent from "../alert/alert.component";
@@ -7,7 +7,7 @@ import ContractCarsComponent from "./contract-cars.component";
export default function ContractCarsContainer({selectedCarState, form}) {
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",
nextFetchPolicy: "network-only",
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
import {Select} from "antd";
import React, {forwardRef, useEffect, useState} from "react";
import {useTranslation} from "react-i18next";
const {Option} = Select;
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 {selectBodyshop} from "../../redux/user/user.selectors";
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 {useMutation} from "@apollo/client";
@@ -26,7 +26,7 @@ export function CCReturnModalContainer({
bodyshop,
}) {
const [loading, setLoading] = useState(false);
const { visible, context, actions } = courtesyCarReturnModal;
const {open, context, actions} = courtesyCarReturnModal;
const {t} = useTranslation();
const [form] = Form.useForm();
const [updateContract] = useMutation(RETURN_CONTRACT);
@@ -64,7 +64,7 @@ export function CCReturnModalContainer({
return (
<Modal
title={t("courtesycars.labels.return")}
visible={visible}
open={open}
onCancel={() => toggleModalVisible()}
width={"90%"}
okText={t("general.actions.save")}
@@ -74,7 +74,7 @@ export function CCReturnModalContainer({
<Form
form={form}
onFinish={handleFinish}
initialValues={{ fuel: 100, actualreturn: moment(new Date()) }}
initialValues={{fuel: 100, actualreturn: dayjs(new Date())}}
>
<CourtesyCarReturnModalComponent/>
</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 {useTranslation} from "react-i18next";
const {Option} = Select;
const CourtesyCarStatusComponent = ({value, onChange}, ref) => {

View File

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

View File

@@ -3,7 +3,7 @@ import { Button, Card, Table } from "antd";
import queryString from "query-string";
import React, {useState} from "react";
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 {alphaSort} from "../../utils/sorters";
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 {responseid, page, sortcolumn, sortorder} = search;
const history = useHistory();
const history = useNavigate();
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: {text: ""},
@@ -80,18 +80,18 @@ export default function CsiResponseListPaginated({
search.page = pagination.current;
search.sortcolumn = sorter.columnKey;
search.sortorder = sorter.order;
history.push({ search: queryString.stringify(search) });
history({search: queryString.stringify(search)});
};
const handleOnRowClick = (record) => {
if (record) {
if (record.id) {
search.responseid = record.id;
history.push({ search: queryString.stringify(search) });
history({search: queryString.stringify(search)});
}
} else {
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 _ from "lodash";
import moment from "moment";
import dayjs from "../../../utils/day";
import React from "react";
import {useTranslation} from "react-i18next";
import {
Bar,
CartesianGrid,
ComposedChart,
Legend,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import {Bar, CartesianGrid, ComposedChart, Legend, Line, ResponsiveContainer, Tooltip, XAxis, YAxis,} from "recharts";
import * as Utils from "../../scoreboard-targets-table/scoreboard-targets-table.util";
import DashboardRefreshRequired from "../refresh-required.component";
@@ -27,7 +17,7 @@ export default function DashboardMonthlyEmployeeEfficiency({
return <DashboardRefreshRequired {...cardProps} />;
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();
@@ -53,7 +43,7 @@ export default function DashboardMonthlyEmployeeEfficiency({
((dailyHrs.productive - dailyHrs.actual) / dailyHrs.actual + 1) * 100;
const theValue = {
date: moment(val).format("DD"),
date: dayjs(val).format("DD"),
// ...dailyHrs,
actual: dailyHrs.actual.toFixed(1),
productive: dailyHrs.productive.toFixed(1),
@@ -159,9 +149,9 @@ export default function DashboardMonthlyEmployeeEfficiency({
}
export const DashboardMonthlyEmployeeEfficiencyGql = `
monthly_employee_efficiency: timetickets(where: {_and: [{date: {_gte: "${moment()
monthly_employee_efficiency: timetickets(where: {_and: [{date: {_gte: "${dayjs()
.startOf("month")
.format("YYYY-MM-DD")}"}},{date: {_lte: "${moment()
.format("YYYY-MM-DD")}"}},{date: {_lte: "${dayjs()
.endOf("month")
.format("YYYY-MM-DD")}"}} ]}) {
actualhrs

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