@@ -1,7 +1,7 @@
|
|||||||
import {useSplitClient} 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, Routes} from "react-router-dom";
|
import {Route, Routes} from "react-router-dom";
|
||||||
@@ -19,6 +19,7 @@ import {checkUserSession} from "../redux/user/user.actions";
|
|||||||
import {selectBodyshop, selectCurrentUser,} from "../redux/user/user.selectors";
|
import {selectBodyshop, selectCurrentUser,} from "../redux/user/user.selectors";
|
||||||
import PrivateRoute from "../components/PrivateRoute";
|
import PrivateRoute from "../components/PrivateRoute";
|
||||||
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")
|
||||||
@@ -40,14 +41,16 @@ 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,
|
|
||||||
currentUser,
|
|
||||||
online,
|
|
||||||
setOnline,
|
|
||||||
}) {
|
|
||||||
const client = useSplitClient().client;
|
const client = useSplitClient().client;
|
||||||
|
const [listenersAdded, setListenersAdded] = useState(false)
|
||||||
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
// Handle The Beta Switch.
|
||||||
|
useEffect(() => {
|
||||||
|
handleBeta();
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!navigator.onLine) {
|
if (!navigator.onLine) {
|
||||||
@@ -60,15 +63,28 @@ export function App({
|
|||||||
//const b = Grid.useBreakpoint();
|
//const b = Grid.useBreakpoint();
|
||||||
// console.log("Breakpoints:", b);
|
// console.log("Breakpoints:", b);
|
||||||
|
|
||||||
const {t} = useTranslation();
|
const offlineListener = (e) => {
|
||||||
|
|
||||||
window.addEventListener("offline", function (e) {
|
|
||||||
setOnline(false);
|
setOnline(false);
|
||||||
});
|
}
|
||||||
|
|
||||||
window.addEventListener("online", function (e) {
|
const onlineListener = (e) => {
|
||||||
setOnline(true);
|
setOnline(true);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Associate event listeners, memoize to prevent multiple listeners being added
|
||||||
|
useEffect(() => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}, [listenersAdded]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentUser.authorized && bodyshop) {
|
if (currentUser.authorized && bodyshop) {
|
||||||
|
|||||||
@@ -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,16 @@ 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 +93,5 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default connect(mapStateToProps, null)(ChatAffixContainer);
|
|
||||||
|
export default ChatAffixContainer;
|
||||||
@@ -12,7 +12,7 @@ import Icon, {
|
|||||||
FileAddOutlined,
|
FileAddOutlined,
|
||||||
FileFilled,
|
FileFilled,
|
||||||
HomeFilled,
|
HomeFilled,
|
||||||
ImportOutlined,
|
ImportOutlined, InfoCircleOutlined,
|
||||||
LineChartOutlined,
|
LineChartOutlined,
|
||||||
PaperClipOutlined,
|
PaperClipOutlined,
|
||||||
PhoneOutlined,
|
PhoneOutlined,
|
||||||
@@ -25,8 +25,8 @@ import Icon, {
|
|||||||
UserOutlined,
|
UserOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
||||||
import {Layout, Menu} from "antd";
|
import {Layout, Menu, Switch, Tooltip} from "antd";
|
||||||
import React from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {BsKanban} from "react-icons/bs";
|
import {BsKanban} from "react-icons/bs";
|
||||||
import {FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar,} from "react-icons/fa";
|
import {FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar,} from "react-icons/fa";
|
||||||
@@ -41,6 +41,7 @@ import {setModalContext} from "../../redux/modals/modals.actions";
|
|||||||
import {signOutStart} from "../../redux/user/user.actions";
|
import {signOutStart} from "../../redux/user/user.actions";
|
||||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||||
import {FiLogOut} from "react-icons/fi";
|
import {FiLogOut} from "react-icons/fi";
|
||||||
|
import handleBeta, {checkBeta, setBeta} from "../../utils/betaHandler";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
@@ -70,9 +71,21 @@ function Header({handleMenuClick, currentUser, bodyshop, selectedHeader, signOut
|
|||||||
names: ["ImEXPay", "DmsAp", "Simple_Inventory"],
|
names: ["ImEXPay", "DmsAp", "Simple_Inventory"],
|
||||||
splitKey: bodyshop && bodyshop.imexshopid,
|
splitKey: bodyshop && bodyshop.imexshopid,
|
||||||
});
|
});
|
||||||
|
const [betaSwitch, setBetaSwitch] = useState(false);
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const isBeta = checkBeta();
|
||||||
|
setBetaSwitch(isBeta );
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
const betaSwitchChange = (checked) => {
|
||||||
|
setBeta(checked);
|
||||||
|
setBetaSwitch(checked);
|
||||||
|
handleBeta();
|
||||||
|
}
|
||||||
|
|
||||||
const accountingChildren = [
|
const accountingChildren = [
|
||||||
{
|
{
|
||||||
key: 'bills',
|
key: 'bills',
|
||||||
@@ -451,13 +464,28 @@ function Header({handleMenuClick, currentUser, bodyshop, selectedHeader, signOut
|
|||||||
|
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
|
||||||
|
|
||||||
|
menuItems.push({
|
||||||
|
key: 'beta-switch',
|
||||||
|
style: { marginLeft: 'auto' },
|
||||||
|
label: (
|
||||||
|
<Tooltip title="A faster more modern ImEX Online is ready for you to try! You can switch back at any time.">
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
<span style={{marginRight: 8}}>Try the new ImEX Online</span>
|
||||||
|
<Switch
|
||||||
|
checked={betaSwitch}
|
||||||
|
onChange={betaSwitchChange}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Header>
|
<Layout.Header>
|
||||||
<Menu
|
<Menu
|
||||||
mode="horizontal"
|
mode="horizontal"
|
||||||
//theme="light"
|
|
||||||
theme={"dark"}
|
theme={"dark"}
|
||||||
selectedKeys={[selectedHeader]}
|
selectedKeys={[selectedHeader]}
|
||||||
onClick={handleMenuClick}
|
onClick={handleMenuClick}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FloatButton, Layout} from "antd";
|
import {FloatButton, Layout} from "antd";
|
||||||
import preval from "preval.macro";
|
import preval from "preval.macro";
|
||||||
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 {Link, Route, Routes} from "react-router-dom";
|
import {Link, Route, Routes} from "react-router-dom";
|
||||||
@@ -177,6 +177,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
|
|
||||||
export function Manage({conflict, bodyshop}) {
|
export function Manage({conflict, bodyshop}) {
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
|
const [chatVisible, setChatVisible] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const widgetId = "IABVNO4scRKY11XBQkNr";
|
const widgetId = "IABVNO4scRKY11XBQkNr";
|
||||||
@@ -360,7 +362,7 @@ export function Manage({conflict, bodyshop}) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ChatAffixContainer/>
|
<ChatAffixContainer bodyshop={bodyshop} chatVisible={chatVisible} />
|
||||||
<Layout className="layout-container">
|
<Layout className="layout-container">
|
||||||
<UpdateAlert/>
|
<UpdateAlert/>
|
||||||
<HeaderContainer/>
|
<HeaderContainer/>
|
||||||
|
|||||||
@@ -42,9 +42,7 @@ if (process.env.NODE_ENV === "development") {
|
|||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: rootReducer,
|
reducer: rootReducer,
|
||||||
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
|
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
|
||||||
serializableCheck: {
|
serializableCheck: false,
|
||||||
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
|
|
||||||
},
|
|
||||||
}).concat(middlewares),
|
}).concat(middlewares),
|
||||||
// middleware: middlewares,
|
// middleware: middlewares,
|
||||||
devTools: process.env.NODE_ENV !== 'production',
|
devTools: process.env.NODE_ENV !== 'production',
|
||||||
|
|||||||
32
client/src/utils/betaHandler.js
Normal file
32
client/src/utils/betaHandler.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
export const BETA_KEY = 'betaSwitchImex';
|
||||||
|
|
||||||
|
export const checkBeta = () => document.cookie.split('; ').find(row => row.startsWith(BETA_KEY)).split('=')[1] === 'true';
|
||||||
|
|
||||||
|
|
||||||
|
export const setBeta = (value) => {
|
||||||
|
const domain = window.location.hostname.split('.').slice(-2).join('.');
|
||||||
|
document.cookie = `${BETA_KEY}=${value}; path=/; domain=.${domain}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleBeta = () => {
|
||||||
|
// If the current host name does not start with beta or test, then we don't need to do anything.
|
||||||
|
if (window.location.hostname.startsWith('localhost')) {
|
||||||
|
console.log('Not on beta or test, so no need to handle beta.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isBeta = checkBeta();
|
||||||
|
|
||||||
|
const currentHostName = window.location.hostname;
|
||||||
|
|
||||||
|
// Beta is enabled, but the current host name does start with beta.
|
||||||
|
if (isBeta && !currentHostName.startsWith('beta')) {
|
||||||
|
window.location.href = `${window.location.protocol}//beta.${currentHostName}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beta is not enabled, but the current host name does start with beta.
|
||||||
|
else if (!isBeta && currentHostName.startsWith('beta')) {
|
||||||
|
window.location.href = `${window.location.protocol}//${currentHostName.replace('beta.', '')}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default handleBeta;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useCallback, useReducer } from "react";
|
import {useCallback, useEffect, useReducer, useState} from "react";
|
||||||
//Based on https://www.fullstacklabs.co/blog/keyboard-shortcuts-with-react-hooks
|
//Based on https://www.fullstacklabs.co/blog/keyboard-shortcuts-with-react-hooks
|
||||||
const blacklistedTargets = []; // ["INPUT", "TEXTAREA"];
|
const blacklistedTargets = []; // ["INPUT", "TEXTAREA"];
|
||||||
export const useKeyboardSaveShortcut = (callback) =>
|
export const useKeyboardSaveShortcut = (callback) =>
|
||||||
@@ -43,6 +43,7 @@ const useKeyboardShortcut = (shortcutKeys, callback, options) => {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const [keys, setKeys] = useReducer(keysReducer, initalKeyMapping);
|
const [keys, setKeys] = useReducer(keysReducer, initalKeyMapping);
|
||||||
|
const [listenersAdded, setListenersAdded] = useState(false);
|
||||||
|
|
||||||
const keydownListener = useCallback(
|
const keydownListener = useCallback(
|
||||||
(assignedKey) => (keydownEvent) => {
|
(assignedKey) => (keydownEvent) => {
|
||||||
@@ -94,26 +95,24 @@ const useKeyboardShortcut = (shortcutKeys, callback, options) => {
|
|||||||
}, [callback, keys]);
|
}, [callback, keys]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
shortcutKeys.forEach((k) =>
|
if (!listenersAdded) {
|
||||||
window.addEventListener("keydown", keydownListener(k))
|
console.log('Added events for keyup and keydown');
|
||||||
);
|
shortcutKeys.forEach((k) => {
|
||||||
return () =>
|
window.addEventListener("keydown", keydownListener(k));
|
||||||
shortcutKeys.forEach((k) =>
|
|
||||||
window.removeEventListener("keydown", keydownListener(k))
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
shortcutKeys.forEach((k) =>
|
|
||||||
window.addEventListener("keyup", keyupListener(k))
|
window.addEventListener("keyup", keyupListener(k))
|
||||||
);
|
});
|
||||||
return () =>
|
}
|
||||||
shortcutKeys.forEach((k) =>
|
|
||||||
window.removeEventListener("keyup", keyupListener(k))
|
setListenersAdded(true);
|
||||||
);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
return () => {
|
||||||
}, []);
|
shortcutKeys.forEach((k) => {
|
||||||
|
window.removeEventListener("keydown", keydownListener(k));
|
||||||
|
window.removeEventListener("keyup", keyupListener(k));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [listenersAdded]);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useKeyboardShortcut;
|
export default useKeyboardShortcut;
|
||||||
|
|||||||
Reference in New Issue
Block a user