53
.ebextensions/eb-redirect-https.config
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
####################################################################################################
|
||||||
|
#### Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
####
|
||||||
|
#### Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
|
||||||
|
#### except in compliance with the License. A copy of the License is located at
|
||||||
|
####
|
||||||
|
#### http://aws.amazon.com/apache2.0/
|
||||||
|
####
|
||||||
|
#### or in the "license" file accompanying this file. This file is distributed on an "AS IS"
|
||||||
|
#### BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
#### License for the specific language governing permissions and limitations under the License.
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
#### This configuration file adds a listener to the Application Load Balancer for port 443, this new listener
|
||||||
|
#### requires the ARN of a public website certificate create residing in the certificate manager service.
|
||||||
|
#### The configuration file also modifies the default port 80 listener attached to an Application Load Balancer
|
||||||
|
#### to automatically redirect incoming connections on HTTP to HTTPS.
|
||||||
|
#### This will not work with an environment using the load balancer type Classic or Network.
|
||||||
|
#### Do not use this configuration file if a listener has already been created for port 443 from the console.
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
|
Resources:
|
||||||
|
AWSEBV2LoadBalancerListener:
|
||||||
|
Type: 'AWS::ElasticLoadBalancingV2::Listener'
|
||||||
|
Properties:
|
||||||
|
DefaultActions:
|
||||||
|
- Type: redirect
|
||||||
|
RedirectConfig:
|
||||||
|
Protocol: HTTPS
|
||||||
|
Port: '443'
|
||||||
|
Host: '#{host}'
|
||||||
|
Path: '/#{path}'
|
||||||
|
Query: '#{query}'
|
||||||
|
StatusCode: HTTP_301
|
||||||
|
LoadBalancerArn:
|
||||||
|
Ref: AWSEBV2LoadBalancer
|
||||||
|
Port: 80
|
||||||
|
Protocol: HTTP
|
||||||
|
AWSEBV2LoadBalancerListenerHTTPS:
|
||||||
|
Type: 'AWS::ElasticLoadBalancingV2::Listener'
|
||||||
|
Properties:
|
||||||
|
Certificates:
|
||||||
|
- CertificateArn: arn:aws:acm:ca-central-1:714144183158:certificate/c6a0fcde-b959-4aee-afc6-934e27c4962b
|
||||||
|
DefaultActions:
|
||||||
|
- Type: forward
|
||||||
|
TargetGroupArn:
|
||||||
|
Ref: AWSEBV2LoadBalancerTargetGroup
|
||||||
|
LoadBalancerArn:
|
||||||
|
Ref: AWSEBV2LoadBalancer
|
||||||
|
Port: 443
|
||||||
|
Protocol: HTTPS
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta
|
||||||
|
|||||||
13
client/madge-graph.svg
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<!-- Generated by graphviz version 2.44.1 (20200629.0846)
|
||||||
|
-->
|
||||||
|
<!-- Title: G Pages: 1 -->
|
||||||
|
<svg width="43pt" height="43pt"
|
||||||
|
viewBox="0.00 0.00 43.20 43.20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(21.6 21.6)">
|
||||||
|
<title>G</title>
|
||||||
|
<polygon fill="#111111" stroke="transparent" points="-21.6,21.6 -21.6,-21.6 21.6,-21.6 21.6,21.6 -21.6,21.6"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 613 B |
@@ -11,11 +11,7 @@
|
|||||||
"@tinymce/tinymce-react": "^3.7.0",
|
"@tinymce/tinymce-react": "^3.7.0",
|
||||||
"antd": "^4.6.6",
|
"antd": "^4.6.6",
|
||||||
"apollo-boost": "^0.4.9",
|
"apollo-boost": "^0.4.9",
|
||||||
"apollo-link-context": "^1.0.20",
|
|
||||||
"apollo-link-error": "^1.1.13",
|
|
||||||
"apollo-link-logger": "^2.0.0",
|
"apollo-link-logger": "^2.0.0",
|
||||||
"apollo-link-retry": "^2.2.16",
|
|
||||||
"apollo-link-ws": "^1.0.20",
|
|
||||||
"axios": "^0.20.0",
|
"axios": "^0.20.0",
|
||||||
"codemirror": "^5.58.1",
|
"codemirror": "^5.58.1",
|
||||||
"codemirror-graphql": "^0.12.2",
|
"codemirror-graphql": "^0.12.2",
|
||||||
@@ -45,7 +41,6 @@
|
|||||||
"react-email-editor": "^1.1.1",
|
"react-email-editor": "^1.1.1",
|
||||||
"react-ga": "^3.1.2",
|
"react-ga": "^3.1.2",
|
||||||
"react-grid-gallery": "^0.5.5",
|
"react-grid-gallery": "^0.5.5",
|
||||||
"react-grid-layout": "^1.1.1",
|
|
||||||
"react-i18next": "^11.7.3",
|
"react-i18next": "^11.7.3",
|
||||||
"react-icons": "^3.11.0",
|
"react-icons": "^3.11.0",
|
||||||
"react-moment": "^1.0.0",
|
"react-moment": "^1.0.0",
|
||||||
@@ -70,7 +65,8 @@
|
|||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` react-scripts build",
|
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject",
|
||||||
|
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular ."
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
|
|||||||
BIN
client/public/favicon.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#002366" />
|
<meta name="theme-color" content="#002366" />
|
||||||
<meta
|
<meta
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
content="Web site created using create-react-app"
|
content="Web site created using create-react-app"
|
||||||
/>
|
/>
|
||||||
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
|
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
|
||||||
<link rel="apple-touch-icon" href="logo240.png" />
|
<link rel="apple-touch-icon" href="logo192.png" />
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"type": "image/x-icon"
|
"type": "image/x-icon"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "logo240.png",
|
"src": "logo192.png",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"sizes": "192x192"
|
"sizes": "192x192"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,153 +1,16 @@
|
|||||||
import { ApolloProvider } from "@apollo/react-common";
|
import { ApolloProvider } from "@apollo/react-common";
|
||||||
import { ConfigProvider } from "antd";
|
import { ConfigProvider } from "antd";
|
||||||
import enLocale from "antd/es/locale/en_US";
|
import enLocale from "antd/es/locale/en_US";
|
||||||
import { ApolloLink } from "apollo-boost";
|
|
||||||
import { InMemoryCache } from "apollo-cache-inmemory";
|
|
||||||
import ApolloClient from "apollo-client";
|
|
||||||
import { split } from "apollo-link";
|
|
||||||
import { setContext } from "apollo-link-context";
|
|
||||||
import { HttpLink } from "apollo-link-http";
|
|
||||||
import apolloLogger from "apollo-link-logger";
|
|
||||||
import { RetryLink } from "apollo-link-retry";
|
|
||||||
import { WebSocketLink } from "apollo-link-ws";
|
|
||||||
import { getMainDefinition } from "apollo-utilities";
|
|
||||||
import axios from "axios";
|
|
||||||
import LogRocket from "logrocket";
|
import LogRocket from "logrocket";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
||||||
import { auth } from "../firebase/firebase.utils";
|
import client from "../utils/GraphQLClient";
|
||||||
import errorLink from "../graphql/apollo-error-handling";
|
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
||||||
moment.locale("en-US");
|
moment.locale("en-US");
|
||||||
|
|
||||||
export const axiosAuthInterceptorId = axios.interceptors.request.use(
|
|
||||||
async (config) => {
|
|
||||||
if (!config.headers.Authorization) {
|
|
||||||
const token =
|
|
||||||
auth.currentUser && (await auth.currentUser.getIdToken(true));
|
|
||||||
if (token) {
|
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
(error) => Promise.reject(error)
|
|
||||||
);
|
|
||||||
|
|
||||||
export const cleanAxios = axios.create();
|
|
||||||
cleanAxios.interceptors.request.eject(axiosAuthInterceptorId);
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
||||||
|
|
||||||
const httpLink = new HttpLink({
|
|
||||||
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
|
|
||||||
});
|
|
||||||
|
|
||||||
const wsLink = new WebSocketLink({
|
|
||||||
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT_WS,
|
|
||||||
options: {
|
|
||||||
lazy: true,
|
|
||||||
reconnect: true,
|
|
||||||
connectionParams: async () => {
|
|
||||||
const token =
|
|
||||||
auth.currentUser && (await auth.currentUser.getIdToken(true));
|
|
||||||
if (token) {
|
|
||||||
return {
|
|
||||||
headers: {
|
|
||||||
authorization: token ? `Bearer ${token}` : "",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const subscriptionMiddleware = {
|
|
||||||
applyMiddleware: async (options, next) => {
|
|
||||||
options.authToken =
|
|
||||||
auth.currentUser && (await auth.currentUser.getIdToken(true));
|
|
||||||
next();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
wsLink.subscriptionClient.use([subscriptionMiddleware]);
|
|
||||||
|
|
||||||
const link = split(
|
|
||||||
// split based on operation type
|
|
||||||
({ query }) => {
|
|
||||||
const definition = getMainDefinition(query);
|
|
||||||
// console.log(
|
|
||||||
// "##Intercepted GQL Transaction : " +
|
|
||||||
// definition.operation +
|
|
||||||
// "|" +
|
|
||||||
// definition.name.value +
|
|
||||||
// "##",
|
|
||||||
// query
|
|
||||||
// );
|
|
||||||
return (
|
|
||||||
definition.kind === "OperationDefinition" &&
|
|
||||||
definition.operation === "subscription"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
wsLink,
|
|
||||||
httpLink
|
|
||||||
);
|
|
||||||
|
|
||||||
const authLink = setContext((_, { headers }) => {
|
|
||||||
return (
|
|
||||||
auth.currentUser &&
|
|
||||||
auth.currentUser.getIdToken().then((token) => {
|
|
||||||
if (token) {
|
|
||||||
return {
|
|
||||||
headers: {
|
|
||||||
...headers,
|
|
||||||
authorization: token ? `Bearer ${token}` : "",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return { headers };
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const retryLink = new RetryLink({
|
|
||||||
delay: {
|
|
||||||
initial: 500,
|
|
||||||
max: 5,
|
|
||||||
jitter: true,
|
|
||||||
},
|
|
||||||
attempts: {
|
|
||||||
max: 5,
|
|
||||||
retryIf: (error, _operation) => !!error,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const middlewares = [];
|
|
||||||
if (process.env.NODE_ENV === "development") {
|
|
||||||
middlewares.push(apolloLogger);
|
|
||||||
}
|
|
||||||
|
|
||||||
middlewares.push(retryLink.concat(errorLink.concat(authLink.concat(link))));
|
|
||||||
|
|
||||||
const cache = new InMemoryCache({});
|
|
||||||
|
|
||||||
export const client = new ApolloClient({
|
|
||||||
link: ApolloLink.from(middlewares),
|
|
||||||
cache,
|
|
||||||
connectToDevTools: process.env.NODE_ENV !== "production",
|
|
||||||
defaultOptions: {
|
|
||||||
query: {
|
|
||||||
fetchPolicy: "network-only",
|
|
||||||
},
|
|
||||||
watchQuery: {
|
|
||||||
fetchPolicy: "network-only",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function AppContainer() {
|
export default function AppContainer() {
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//Global Styles.
|
//Global Styles.
|
||||||
|
@import "react-big-calendar/lib/sass/styles";
|
||||||
|
|
||||||
.imex-table-header {
|
.imex-table-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
BIN
client/src/assets/logo512.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
@@ -1,185 +1,185 @@
|
|||||||
import Icon from "@ant-design/icons";
|
// import Icon from "@ant-design/icons";
|
||||||
import { Button, Dropdown, Menu, notification } from "antd";
|
// import { Button, Dropdown, Menu, notification } from "antd";
|
||||||
import React, { useState } from "react";
|
// import React, { useState } from "react";
|
||||||
import { useMutation, useQuery } from "react-apollo";
|
// import { useMutation, useQuery } from "react-apollo";
|
||||||
import { Responsive, WidthProvider } from "react-grid-layout";
|
// import { Responsive, WidthProvider } from "react-grid-layout";
|
||||||
import { useTranslation } from "react-i18next";
|
// import { useTranslation } from "react-i18next";
|
||||||
import { MdClose } from "react-icons/md";
|
// import { MdClose } from "react-icons/md";
|
||||||
import { connect } from "react-redux";
|
// import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
// import { createStructuredSelector } from "reselect";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
// import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { QUERY_DASHBOARD_DETAILS } from "../../graphql/bodyshop.queries";
|
// import { QUERY_DASHBOARD_DETAILS } from "../../graphql/bodyshop.queries";
|
||||||
import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
|
// import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
|
||||||
import {
|
// import {
|
||||||
selectBodyshop,
|
// selectBodyshop,
|
||||||
selectCurrentUser,
|
// selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
// } from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
// import AlertComponent from "../alert/alert.component";
|
||||||
import DashboardMonthlyRevenueGraph from "../dashboard-components/monthly-revenue-graph/monthly-revenue-graph.component";
|
// import DashboardMonthlyRevenueGraph from "../dashboard-components/monthly-revenue-graph/monthly-revenue-graph.component";
|
||||||
import DashboardProjectedMonthlySales from "../dashboard-components/pojected-monthly-sales/projected-monthly-sales.component";
|
// import DashboardProjectedMonthlySales from "../dashboard-components/pojected-monthly-sales/projected-monthly-sales.component";
|
||||||
import DashboardTotalProductionDollars from "../dashboard-components/total-production-dollars/total-production-dollars.component";
|
// import DashboardTotalProductionDollars from "../dashboard-components/total-production-dollars/total-production-dollars.component";
|
||||||
import DashboardTotalProductionHours from "../dashboard-components/total-production-hours/total-production-hours.component";
|
// import DashboardTotalProductionHours from "../dashboard-components/total-production-hours/total-production-hours.component";
|
||||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
// import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
//Combination of the following:
|
// //Combination of the following:
|
||||||
// /node_modules/react-grid-layout/css/styles.css
|
// // /node_modules/react-grid-layout/css/styles.css
|
||||||
// /node_modules/react-resizable/css/styles.css
|
// // /node_modules/react-resizable/css/styles.css
|
||||||
import "./dashboard-grid.styles.css";
|
// import "./dashboard-grid.styles.css";
|
||||||
import "./dashboard-grid.styles.scss";
|
// import "./dashboard-grid.styles.scss";
|
||||||
|
|
||||||
const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
// const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
// const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
// currentUser: selectCurrentUser,
|
||||||
bodyshop: selectBodyshop,
|
// bodyshop: selectBodyshop,
|
||||||
});
|
// });
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
// const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
// //setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
// });
|
||||||
|
|
||||||
export function DashboardGridComponent({ currentUser, bodyshop }) {
|
// export function DashboardGridComponent({ currentUser, bodyshop }) {
|
||||||
const { loading, error, data } = useQuery(QUERY_DASHBOARD_DETAILS);
|
// const { loading, error, data } = useQuery(QUERY_DASHBOARD_DETAILS);
|
||||||
const { t } = useTranslation();
|
// const { t } = useTranslation();
|
||||||
const [state, setState] = useState({
|
// const [state, setState] = useState({
|
||||||
layout: bodyshop.associations[0].user.dashboardlayout || [
|
// layout: bodyshop.associations[0].user.dashboardlayout || [
|
||||||
{ i: "ProductionDollars", x: 0, y: 0, w: 2, h: 2 },
|
// { i: "ProductionDollars", x: 0, y: 0, w: 2, h: 2 },
|
||||||
// { i: "ProductionHours", x: 2, y: 0, w: 2, h: 2 },
|
// // { i: "ProductionHours", x: 2, y: 0, w: 2, h: 2 },
|
||||||
],
|
// ],
|
||||||
});
|
// });
|
||||||
const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
|
// const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
|
||||||
|
|
||||||
const handleLayoutChange = async (newLayout) => {
|
// const handleLayoutChange = async (newLayout) => {
|
||||||
logImEXEvent("dashboard_change_layout");
|
// logImEXEvent("dashboard_change_layout");
|
||||||
setState({ ...state, layout: newLayout });
|
// setState({ ...state, layout: newLayout });
|
||||||
const result = await updateLayout({
|
// const result = await updateLayout({
|
||||||
variables: { email: currentUser.email, layout: newLayout },
|
// variables: { email: currentUser.email, layout: newLayout },
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (!!result.errors) {
|
// if (!!result.errors) {
|
||||||
notification["error"]({
|
// notification["error"]({
|
||||||
message: t("dashboard.errors.updatinglayout", {
|
// message: t("dashboard.errors.updatinglayout", {
|
||||||
message: JSON.stringify(result.errors),
|
// message: JSON.stringify(result.errors),
|
||||||
}),
|
// }),
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
const handleRemoveComponent = (key) => {
|
// const handleRemoveComponent = (key) => {
|
||||||
logImEXEvent("dashboard_remove_component", { name: key });
|
// logImEXEvent("dashboard_remove_component", { name: key });
|
||||||
|
|
||||||
const idxToRemove = state.layout.findIndex((i) => i.i === key);
|
// const idxToRemove = state.layout.findIndex((i) => i.i === key);
|
||||||
const newLayout = state.layout;
|
// const newLayout = state.layout;
|
||||||
newLayout.splice(idxToRemove, 1);
|
// newLayout.splice(idxToRemove, 1);
|
||||||
handleLayoutChange(newLayout);
|
// handleLayoutChange(newLayout);
|
||||||
};
|
// };
|
||||||
|
|
||||||
const handleAddComponent = (e) => {
|
// const handleAddComponent = (e) => {
|
||||||
logImEXEvent("dashboard_add_component", { name: e });
|
// logImEXEvent("dashboard_add_component", { name: e });
|
||||||
|
|
||||||
handleLayoutChange([
|
// handleLayoutChange([
|
||||||
...state.layout,
|
// ...state.layout,
|
||||||
{
|
// {
|
||||||
i: e.key,
|
// i: e.key,
|
||||||
x: (state.layout.length * 2) % (state.cols || 12),
|
// x: (state.layout.length * 2) % (state.cols || 12),
|
||||||
y: Infinity, // puts it at the bottom
|
// y: Infinity, // puts it at the bottom
|
||||||
w: componentList[e.key].w || 2,
|
// w: componentList[e.key].w || 2,
|
||||||
h: componentList[e.key].h || 2,
|
// h: componentList[e.key].h || 2,
|
||||||
},
|
// },
|
||||||
]);
|
// ]);
|
||||||
};
|
// };
|
||||||
|
|
||||||
const onBreakpointChange = (breakpoint, cols) => {
|
// const onBreakpointChange = (breakpoint, cols) => {
|
||||||
setState({ ...state, breakpoint: breakpoint, cols: cols });
|
// setState({ ...state, breakpoint: breakpoint, cols: cols });
|
||||||
};
|
// };
|
||||||
|
|
||||||
const existingLayoutKeys = state.layout.map((i) => i.i);
|
// const existingLayoutKeys = state.layout.map((i) => i.i);
|
||||||
const addComponentOverlay = (
|
// const addComponentOverlay = (
|
||||||
<Menu onClick={handleAddComponent}>
|
// <Menu onClick={handleAddComponent}>
|
||||||
{Object.keys(componentList).map((key) => (
|
// {Object.keys(componentList).map((key) => (
|
||||||
<Menu.Item
|
// <Menu.Item
|
||||||
key={key}
|
// key={key}
|
||||||
value={key}
|
// value={key}
|
||||||
disabled={existingLayoutKeys.includes(key)}
|
// disabled={existingLayoutKeys.includes(key)}
|
||||||
>
|
// >
|
||||||
{componentList[key].label}
|
// {componentList[key].label}
|
||||||
</Menu.Item>
|
// </Menu.Item>
|
||||||
))}
|
// ))}
|
||||||
</Menu>
|
// </Menu>
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
// if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
return (
|
// return (
|
||||||
<div>
|
// <div>
|
||||||
<Dropdown overlay={addComponentOverlay} trigger={["click"]}>
|
// <Dropdown overlay={addComponentOverlay} trigger={["click"]}>
|
||||||
<Button>{t("dashboard.actions.addcomponent")}</Button>
|
// <Button>{t("dashboard.actions.addcomponent")}</Button>
|
||||||
</Dropdown>
|
// </Dropdown>
|
||||||
<ResponsiveReactGridLayout
|
// <ResponsiveReactGridLayout
|
||||||
className="layout"
|
// className="layout"
|
||||||
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
// breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
||||||
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
// cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
||||||
width="100%"
|
// width="100%"
|
||||||
onLayoutChange={handleLayoutChange}
|
// onLayoutChange={handleLayoutChange}
|
||||||
onBreakpointChange={onBreakpointChange}
|
// onBreakpointChange={onBreakpointChange}
|
||||||
>
|
// >
|
||||||
{state.layout.map((item, index) => {
|
// {state.layout.map((item, index) => {
|
||||||
const TheComponent = componentList[item.i].component;
|
// const TheComponent = componentList[item.i].component;
|
||||||
return (
|
// return (
|
||||||
<div key={item.i} data-grid={item}>
|
// <div key={item.i} data-grid={item}>
|
||||||
<LoadingSkeleton loading={loading}>
|
// <LoadingSkeleton loading={loading}>
|
||||||
<Icon
|
// <Icon
|
||||||
component={MdClose}
|
// component={MdClose}
|
||||||
key={item.i}
|
// key={item.i}
|
||||||
style={{
|
// style={{
|
||||||
position: "absolute",
|
// position: "absolute",
|
||||||
zIndex: "2",
|
// zIndex: "2",
|
||||||
right: ".25rem",
|
// right: ".25rem",
|
||||||
top: ".25rem",
|
// top: ".25rem",
|
||||||
cursor: "pointer",
|
// cursor: "pointer",
|
||||||
}}
|
// }}
|
||||||
onClick={() => handleRemoveComponent(item.i)}
|
// onClick={() => handleRemoveComponent(item.i)}
|
||||||
/>
|
// />
|
||||||
<TheComponent
|
// <TheComponent
|
||||||
className="dashboard-card"
|
// className="dashboard-card"
|
||||||
size="small"
|
// size="small"
|
||||||
style={{ height: "100%", width: "100%" }}
|
// style={{ height: "100%", width: "100%" }}
|
||||||
/>
|
// />
|
||||||
</LoadingSkeleton>
|
// </LoadingSkeleton>
|
||||||
</div>
|
// </div>
|
||||||
);
|
// );
|
||||||
})}
|
// })}
|
||||||
</ResponsiveReactGridLayout>
|
// </ResponsiveReactGridLayout>
|
||||||
</div>
|
// </div>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
export default connect(
|
// export default connect(
|
||||||
mapStateToProps,
|
// mapStateToProps,
|
||||||
mapDispatchToProps
|
// mapDispatchToProps
|
||||||
)(DashboardGridComponent);
|
// )(DashboardGridComponent);
|
||||||
|
|
||||||
const componentList = {
|
// const componentList = {
|
||||||
ProductionDollars: {
|
// ProductionDollars: {
|
||||||
label: "Production Dollars",
|
// label: "Production Dollars",
|
||||||
component: DashboardTotalProductionDollars,
|
// component: DashboardTotalProductionDollars,
|
||||||
w: 2,
|
// w: 2,
|
||||||
h: 1,
|
// h: 1,
|
||||||
},
|
// },
|
||||||
ProductionHours: {
|
// ProductionHours: {
|
||||||
label: "Production Hours",
|
// label: "Production Hours",
|
||||||
component: DashboardTotalProductionHours,
|
// component: DashboardTotalProductionHours,
|
||||||
w: 2,
|
// w: 2,
|
||||||
h: 1,
|
// h: 1,
|
||||||
},
|
// },
|
||||||
ProjectedMonthlySales: {
|
// ProjectedMonthlySales: {
|
||||||
label: "Projected Monthly Sales",
|
// label: "Projected Monthly Sales",
|
||||||
component: DashboardProjectedMonthlySales,
|
// component: DashboardProjectedMonthlySales,
|
||||||
w: 2,
|
// w: 2,
|
||||||
h: 1,
|
// h: 1,
|
||||||
},
|
// },
|
||||||
MonthlyRevenueGraph: {
|
// MonthlyRevenueGraph: {
|
||||||
label: "Monthly Sales Graph",
|
// label: "Monthly Sales Graph",
|
||||||
component: DashboardMonthlyRevenueGraph,
|
// component: DashboardMonthlyRevenueGraph,
|
||||||
w: 2,
|
// w: 2,
|
||||||
h: 2,
|
// h: 2,
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { notification } from "antd";
|
import { notification } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import { axiosAuthInterceptorId, client } from "../../App/App.container";
|
import client, { axiosAuthInterceptorId } from "../../utils/CleanAxios";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries";
|
import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries";
|
||||||
//Context: currentUserEmail, bodyshop, jobid, invoiceid
|
//Context: currentUserEmail, bodyshop, jobid, invoiceid
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { Button, notification, Popconfirm } from "antd";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { cleanAxios } from "../../App/App.container";
|
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { DELETE_DOCUMENT } from "../../graphql/documents.queries";
|
import { DELETE_DOCUMENT } from "../../graphql/documents.queries";
|
||||||
|
import cleanAxios from "../../utils/CleanAxios";
|
||||||
//Context: currentUserEmail, bodyshop, jobid, invoiceid
|
//Context: currentUserEmail, bodyshop, jobid, invoiceid
|
||||||
|
|
||||||
export default function JobsDocumentsDeleteButton({
|
export default function JobsDocumentsDeleteButton({
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
@import "react-big-calendar/lib/sass/styles";
|
Ø.rbc-time-header-cell-single-day {
|
||||||
|
|
||||||
.rbc-time-header-cell-single-day {
|
|
||||||
display: unset;
|
display: unset;
|
||||||
}
|
}
|
||||||
.rbc-time-view .rbc-allday-cell {
|
.rbc-time-view .rbc-allday-cell {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { client } from "../../App/App.container";
|
import client from "../../utils/GraphQLClient";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import React, { useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { client } from "../../App/App.container";
|
import client from "../../utils/GraphQLClient";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { displayTemplateInWindowNoprint } from "../../utils/RenderTemplate";
|
import { displayTemplateInWindowNoprint } from "../../utils/RenderTemplate";
|
||||||
|
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link, Redirect, useLocation } from "react-router-dom";
|
import { Link, Redirect, useLocation } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import ImEXOnlineLogo from "../../assets/logo240.png";
|
import ImEXOnlineLogo from "../../assets/logo192.png";
|
||||||
import { UPSERT_USER } from "../../graphql/user.queries";
|
import { UPSERT_USER } from "../../graphql/user.queries";
|
||||||
import {
|
import {
|
||||||
emailSignInStart,
|
emailSignInStart,
|
||||||
sendPasswordReset
|
sendPasswordReset,
|
||||||
} from "../../redux/user/user.actions";
|
} from "../../redux/user/user.actions";
|
||||||
import {
|
import {
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
selectSignInError
|
selectSignInError,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import "./sign-in-form.styles.scss";
|
import "./sign-in-form.styles.scss";
|
||||||
@@ -66,37 +66,39 @@ export function SignInComponent({
|
|||||||
return <Redirect to={redirect || "/manage"} />;
|
return <Redirect to={redirect || "/manage"} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='login-container'>
|
<div className="login-container">
|
||||||
<div className='login-logo-container'>
|
<div className="login-logo-container">
|
||||||
<img src={ImEXOnlineLogo} height='100' width='100' alt='ImEX Online' />
|
<img src={ImEXOnlineLogo} height="100" width="100" alt="ImEX Online" />
|
||||||
<Typography.Title>{t("titles.app")}</Typography.Title>
|
<Typography.Title>{t("titles.app")}</Typography.Title>
|
||||||
</div>
|
</div>
|
||||||
<Form onFinish={handleFinish} form={form} size='large'>
|
<Form onFinish={handleFinish} form={form} size="large">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name='email'
|
name="email"
|
||||||
rules={[
|
rules={[
|
||||||
{ required: true, message: t("general.validation.required") },
|
{ required: true, message: t("general.validation.required") },
|
||||||
]}>
|
]}
|
||||||
|
>
|
||||||
<Input
|
<Input
|
||||||
prefix={<UserOutlined />}
|
prefix={<UserOutlined />}
|
||||||
placeholder={t("general.labels.username")}
|
placeholder={t("general.labels.username")}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name='password'
|
name="password"
|
||||||
rules={[
|
rules={[
|
||||||
{ required: true, message: t("general.validation.required") },
|
{ required: true, message: t("general.validation.required") },
|
||||||
]}>
|
]}
|
||||||
|
>
|
||||||
<Input
|
<Input
|
||||||
prefix={<LockOutlined />}
|
prefix={<LockOutlined />}
|
||||||
type='password'
|
type="password"
|
||||||
placeholder={t("general.labels.password")}
|
placeholder={t("general.labels.password")}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{signInError ? (
|
{signInError ? (
|
||||||
<AlertComponent type='error' message={signInError.message} />
|
<AlertComponent type="error" message={signInError.message} />
|
||||||
) : null}
|
) : null}
|
||||||
<Button className='login-btn' type='primary' htmlType='submit'>
|
<Button className="login-btn" type="primary" htmlType="submit">
|
||||||
{t("general.actions.login")}
|
{t("general.actions.login")}
|
||||||
</Button>
|
</Button>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import ImEXOnlineLogo from "../../assets/logo240.png";
|
import ImEXOnlineLogo from "../../assets/logo192.png";
|
||||||
import { sendPasswordReset } from "../../redux/user/user.actions";
|
import { sendPasswordReset } from "../../redux/user/user.actions";
|
||||||
import { selectPasswordReset } from "../../redux/user/user.selectors";
|
import { selectPasswordReset } from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import ImEXOnlineLogo from "../../assets/logo240.png";
|
import ImEXOnlineLogo from "../../assets/logo192.png";
|
||||||
import { validatePasswordResetStart } from "../../redux/user/user.actions";
|
import { validatePasswordResetStart } from "../../redux/user/user.actions";
|
||||||
import { selectPasswordReset } from "../../redux/user/user.selectors";
|
import { selectPasswordReset } from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
import "firebase/analytics";
|
||||||
import firebase from "firebase/app";
|
import firebase from "firebase/app";
|
||||||
import "firebase/firestore";
|
|
||||||
import "firebase/auth";
|
import "firebase/auth";
|
||||||
import "firebase/database";
|
import "firebase/database";
|
||||||
import "firebase/analytics";
|
import "firebase/firestore";
|
||||||
import "firebase/messaging";
|
import "firebase/messaging";
|
||||||
import { store } from "../redux/store";
|
//import { store } from "../redux/store";
|
||||||
|
|
||||||
const config = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG);
|
const config = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG);
|
||||||
firebase.initializeApp(config);
|
firebase.initializeApp(config);
|
||||||
@@ -48,14 +48,14 @@ try {
|
|||||||
export { messaging };
|
export { messaging };
|
||||||
|
|
||||||
export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
||||||
const state = stateProp || store.getState();
|
// const state = stateProp || store.getState();
|
||||||
const eventParams = {
|
const eventParams = {
|
||||||
shop:
|
// shop:
|
||||||
(state.user && state.user.bodyshop && state.user.bodyshop.shopname) ||
|
// (state.user && state.user.bodyshop && state.user.bodyshop.shopname) ||
|
||||||
null,
|
// null,
|
||||||
user:
|
// user:
|
||||||
(state.user && state.user.currentUser && state.user.currentUser.email) ||
|
// (state.user && state.user.currentUser && state.user.currentUser.email) ||
|
||||||
null,
|
// null,
|
||||||
...additionalParams,
|
...additionalParams,
|
||||||
};
|
};
|
||||||
analytics.logEvent(eventName, eventParams);
|
analytics.logEvent(eventName, eventParams);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Route, Switch } from "react-router-dom";
|
import { Route, Switch } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { client } from "../../App/App.container";
|
import client from "../../utils/GraphQLClient";
|
||||||
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
|
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
|
||||||
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
|
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
|
||||||
import ConflictComponent from "../../components/conflict/conflict.component";
|
import ConflictComponent from "../../components/conflict/conflict.component";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { all, takeLatest, call, put } from "redux-saga/effects";
|
import { all, takeLatest, call, put } from "redux-saga/effects";
|
||||||
import ApplicationActionTypes from "./application.types";
|
import ApplicationActionTypes from "./application.types";
|
||||||
import { client } from "../../App/App.container";
|
import client from "../../utils/GraphQLClient";
|
||||||
import { QUERY_SCHEDULE_LOAD_DATA } from "../../graphql/appointments.queries";
|
import { QUERY_SCHEDULE_LOAD_DATA } from "../../graphql/appointments.queries";
|
||||||
import {
|
import {
|
||||||
scheduleLoadFailure,
|
scheduleLoadFailure,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import phone from "phone";
|
import phone from "phone";
|
||||||
import { all, call, put, select, takeLatest } from "redux-saga/effects";
|
import { all, call, put, select, takeLatest } from "redux-saga/effects";
|
||||||
import { client } from "../../App/App.container";
|
import client from "../../utils/GraphQLClient";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import {
|
import {
|
||||||
CONVERSATION_ID_BY_PHONE,
|
CONVERSATION_ID_BY_PHONE,
|
||||||
|
|||||||
27
client/src/utils/CleanAxios.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { auth } from "../firebase/firebase.utils";
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "prodution") {
|
||||||
|
axios.defaults.baseURL =
|
||||||
|
process.env.REACT_APP_AXIOS_BASE_API_URL || "https://api.imex.online/";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const axiosAuthInterceptorId = axios.interceptors.request.use(
|
||||||
|
async (config) => {
|
||||||
|
if (!config.headers.Authorization) {
|
||||||
|
const token =
|
||||||
|
auth.currentUser && (await auth.currentUser.getIdToken(true));
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error) => Promise.reject(error)
|
||||||
|
);
|
||||||
|
|
||||||
|
const cleanAxios = axios.create();
|
||||||
|
cleanAxios.interceptors.request.eject(axiosAuthInterceptorId);
|
||||||
|
|
||||||
|
export default cleanAxios;
|
||||||
118
client/src/utils/GraphQLClient.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { setContext } from "@apollo/client/link/context";
|
||||||
|
import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http";
|
||||||
|
import { RetryLink } from "@apollo/client/link/retry";
|
||||||
|
import { WebSocketLink } from "@apollo/client/link/ws";
|
||||||
|
import { ApolloLink } from "apollo-boost";
|
||||||
|
import { InMemoryCache } from "apollo-cache-inmemory";
|
||||||
|
import ApolloClient from "apollo-client";
|
||||||
|
import { split } from "apollo-link";
|
||||||
|
import apolloLogger from "apollo-link-logger";
|
||||||
|
import { getMainDefinition } from "apollo-utilities";
|
||||||
|
import { auth } from "../firebase/firebase.utils";
|
||||||
|
import errorLink from "../graphql/apollo-error-handling";
|
||||||
|
|
||||||
|
const httpLink = new HttpLink({
|
||||||
|
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
|
||||||
|
});
|
||||||
|
|
||||||
|
const wsLink = new WebSocketLink({
|
||||||
|
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT_WS,
|
||||||
|
options: {
|
||||||
|
lazy: true,
|
||||||
|
reconnect: true,
|
||||||
|
connectionParams: async () => {
|
||||||
|
const token =
|
||||||
|
auth.currentUser && (await auth.currentUser.getIdToken(true));
|
||||||
|
if (token) {
|
||||||
|
return {
|
||||||
|
headers: {
|
||||||
|
authorization: token ? `Bearer ${token}` : "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const subscriptionMiddleware = {
|
||||||
|
applyMiddleware: async (options, next) => {
|
||||||
|
options.authToken =
|
||||||
|
auth.currentUser && (await auth.currentUser.getIdToken(true));
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
wsLink.subscriptionClient.use([subscriptionMiddleware]);
|
||||||
|
|
||||||
|
const link = split(
|
||||||
|
// split based on operation type
|
||||||
|
({ query }) => {
|
||||||
|
const definition = getMainDefinition(query);
|
||||||
|
// console.log(
|
||||||
|
// "##Intercepted GQL Transaction : " +
|
||||||
|
// definition.operation +
|
||||||
|
// "|" +
|
||||||
|
// definition.name.value +
|
||||||
|
// "##",
|
||||||
|
// query
|
||||||
|
// );
|
||||||
|
return (
|
||||||
|
definition.kind === "OperationDefinition" &&
|
||||||
|
definition.operation === "subscription"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
wsLink,
|
||||||
|
httpLink
|
||||||
|
);
|
||||||
|
|
||||||
|
const authLink = setContext((_, { headers }) => {
|
||||||
|
return (
|
||||||
|
auth.currentUser &&
|
||||||
|
auth.currentUser.getIdToken().then((token) => {
|
||||||
|
if (token) {
|
||||||
|
return {
|
||||||
|
headers: {
|
||||||
|
...headers,
|
||||||
|
authorization: token ? `Bearer ${token}` : "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return { headers };
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const retryLink = new RetryLink({
|
||||||
|
delay: {
|
||||||
|
initial: 500,
|
||||||
|
max: 5,
|
||||||
|
jitter: true,
|
||||||
|
},
|
||||||
|
attempts: {
|
||||||
|
max: 5,
|
||||||
|
retryIf: (error, _operation) => !!error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const middlewares = [];
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
middlewares.push(apolloLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
middlewares.push(retryLink.concat(errorLink.concat(authLink.concat(link))));
|
||||||
|
|
||||||
|
const cache = new InMemoryCache({});
|
||||||
|
|
||||||
|
export default new ApolloClient({
|
||||||
|
link: ApolloLink.from(middlewares),
|
||||||
|
cache,
|
||||||
|
connectToDevTools: process.env.NODE_ENV !== "production",
|
||||||
|
defaultOptions: {
|
||||||
|
query: {
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
},
|
||||||
|
watchQuery: {
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import axios from "axios";
|
||||||
import gql from "graphql-tag";
|
import gql from "graphql-tag";
|
||||||
import { QUERY_TEMPLATES_BY_NAME } from "../graphql/templates.queries";
|
import { QUERY_TEMPLATES_BY_NAME } from "../graphql/templates.queries";
|
||||||
import axios from "axios";
|
import client from "../utils/GraphQLClient";
|
||||||
import { client } from "../App/App.container";
|
|
||||||
|
|
||||||
export default async function RenderTemplate(templateObject, bodyshop) {
|
export default async function RenderTemplate(templateObject, bodyshop) {
|
||||||
const { data: templateRecords } = await client.query({
|
const { data: templateRecords } = await client.query({
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
/* eslint no-fallthrough: off */
|
|
||||||
import * as dates from "date-arithmetic";
|
|
||||||
|
|
||||||
export {
|
|
||||||
milliseconds,
|
|
||||||
seconds,
|
|
||||||
minutes,
|
|
||||||
hours,
|
|
||||||
month,
|
|
||||||
startOf,
|
|
||||||
endOf,
|
|
||||||
add,
|
|
||||||
eq,
|
|
||||||
gte,
|
|
||||||
gt,
|
|
||||||
lte,
|
|
||||||
lt,
|
|
||||||
inRange,
|
|
||||||
min,
|
|
||||||
max
|
|
||||||
} from "date-arithmetic";
|
|
||||||
|
|
||||||
const MILLI = {
|
|
||||||
seconds: 1000,
|
|
||||||
minutes: 1000 * 60,
|
|
||||||
hours: 1000 * 60 * 60,
|
|
||||||
day: 1000 * 60 * 60 * 24
|
|
||||||
};
|
|
||||||
|
|
||||||
const MONTHS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
|
||||||
|
|
||||||
export function monthsInYear(year) {
|
|
||||||
let date = new Date(year, 0, 1);
|
|
||||||
|
|
||||||
return MONTHS.map(i => dates.month(date, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function firstVisibleDay(date, localizer) {
|
|
||||||
let firstOfMonth = dates.startOf(date, "month");
|
|
||||||
|
|
||||||
return dates.startOf(firstOfMonth, "week", localizer.startOfWeek());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function lastVisibleDay(date, localizer) {
|
|
||||||
let endOfMonth = dates.endOf(date, "month");
|
|
||||||
|
|
||||||
return dates.endOf(endOfMonth, "week", localizer.startOfWeek());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function visibleDays(date, localizer) {
|
|
||||||
let current = firstVisibleDay(date, localizer),
|
|
||||||
last = lastVisibleDay(date, localizer),
|
|
||||||
days = [];
|
|
||||||
|
|
||||||
while (dates.lte(current, last, "day")) {
|
|
||||||
days.push(current);
|
|
||||||
current = dates.add(current, 1, "day");
|
|
||||||
}
|
|
||||||
|
|
||||||
return days;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ceil(date, unit) {
|
|
||||||
let floor = dates.startOf(date, unit);
|
|
||||||
|
|
||||||
return dates.eq(floor, date) ? floor : dates.add(floor, 1, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function range(start, end, unit = "day") {
|
|
||||||
let current = start,
|
|
||||||
days = [];
|
|
||||||
|
|
||||||
while (dates.lte(current, end, unit)) {
|
|
||||||
days.push(current);
|
|
||||||
current = dates.add(current, 1, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
return days;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function merge(date, time) {
|
|
||||||
if (time == null && date == null) return null;
|
|
||||||
|
|
||||||
if (time == null) time = new Date();
|
|
||||||
if (date == null) date = new Date();
|
|
||||||
|
|
||||||
date = dates.startOf(date, "day");
|
|
||||||
date = dates.hours(date, dates.hours(time));
|
|
||||||
date = dates.minutes(date, dates.minutes(time));
|
|
||||||
date = dates.seconds(date, dates.seconds(time));
|
|
||||||
return dates.milliseconds(date, dates.milliseconds(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function eqTime(dateA, dateB) {
|
|
||||||
return (
|
|
||||||
dates.hours(dateA) === dates.hours(dateB) &&
|
|
||||||
dates.minutes(dateA) === dates.minutes(dateB) &&
|
|
||||||
dates.seconds(dateA) === dates.seconds(dateB)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isJustDate(date) {
|
|
||||||
return (
|
|
||||||
dates.hours(date) === 0 &&
|
|
||||||
dates.minutes(date) === 0 &&
|
|
||||||
dates.seconds(date) === 0 &&
|
|
||||||
dates.milliseconds(date) === 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function duration(start, end, unit, firstOfWeek) {
|
|
||||||
if (unit === "day") unit = "date";
|
|
||||||
return Math.abs(
|
|
||||||
dates[unit](start, undefined, firstOfWeek) -
|
|
||||||
dates[unit](end, undefined, firstOfWeek)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function diff(dateA, dateB, unit) {
|
|
||||||
if (!unit || unit === "milliseconds") return Math.abs(+dateA - +dateB);
|
|
||||||
|
|
||||||
// the .round() handles an edge case
|
|
||||||
// with DST where the total won't be exact
|
|
||||||
// since one day in the range may be shorter/longer by an hour
|
|
||||||
return Math.round(
|
|
||||||
Math.abs(
|
|
||||||
+dates.startOf(dateA, unit) / MILLI[unit] -
|
|
||||||
+dates.startOf(dateB, unit) / MILLI[unit]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function total(date, unit) {
|
|
||||||
let ms = date.getTime(),
|
|
||||||
div = 1;
|
|
||||||
|
|
||||||
switch (unit) {
|
|
||||||
case "week":
|
|
||||||
div *= 7;
|
|
||||||
case "day":
|
|
||||||
div *= 24;
|
|
||||||
case "hours":
|
|
||||||
div *= 60;
|
|
||||||
case "minutes":
|
|
||||||
div *= 60;
|
|
||||||
case "seconds":
|
|
||||||
div *= 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ms / div;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function week(date) {
|
|
||||||
var d = new Date(date);
|
|
||||||
d.setHours(0, 0, 0);
|
|
||||||
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
|
|
||||||
return Math.ceil(((d - new Date(d.getFullYear(), 0, 1)) / 8.64e7 + 1) / 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function today() {
|
|
||||||
return dates.startOf(new Date(), "day");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function yesterday() {
|
|
||||||
return dates.add(dates.startOf(new Date(), "day"), -1, "day");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tomorrow() {
|
|
||||||
return dates.add(dates.startOf(new Date(), "day"), 1, "day");
|
|
||||||
}
|
|
||||||
14645
client/yarn.lock
@@ -47,7 +47,7 @@ exports.testResponse = async (req, res) => {
|
|||||||
|
|
||||||
// renotify: true,
|
// renotify: true,
|
||||||
//tag: "1234", image: "/logo192.png",
|
//tag: "1234", image: "/logo192.png",
|
||||||
badge: "/logo240.png",
|
badge: "/logo192.png",
|
||||||
//badge: "/badge-icon.png",
|
//badge: "/badge-icon.png",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||