@@ -1,4 +1,4 @@
|
|||||||
<babeledit_project version="1.2" be_version="2.7.1">
|
<babeledit_project be_version="2.7.1" version="1.2">
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
BabelEdit project file
|
BabelEdit project file
|
||||||
@@ -14199,6 +14199,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>tryagain</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>view</name>
|
<name>view</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -14246,6 +14267,27 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>errors</name>
|
<name>errors</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>fcm</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>notfound</name>
|
<name>notfound</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -14634,6 +14676,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>help</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>hours</name>
|
<name>hours</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -29864,6 +29927,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>nopush</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>phonenumber</name>
|
<name>phonenumber</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -4,21 +4,21 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"proxy": "http://localhost:4000",
|
"proxy": "http://localhost:4000",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.5.5",
|
"@apollo/client": "^3.4.17",
|
||||||
"@asseinfo/react-kanban": "^2.2.0",
|
"@asseinfo/react-kanban": "^2.2.0",
|
||||||
"@craco/craco": "^6.4.2",
|
"@craco/craco": "^6.4.0",
|
||||||
"@fingerprintjs/fingerprintjs": "^3.3.0",
|
"@fingerprintjs/fingerprintjs": "^3.3.0",
|
||||||
"@openreplay/tracker": "^3.4.10",
|
"@openreplay/tracker": "^3.4.7",
|
||||||
"@openreplay/tracker-assist": "^3.4.9",
|
"@openreplay/tracker-assist": "^3.4.4",
|
||||||
"@openreplay/tracker-graphql": "^3.0.0",
|
"@openreplay/tracker-graphql": "^3.0.0",
|
||||||
"@openreplay/tracker-redux": "^3.4.8",
|
"@openreplay/tracker-redux": "^3.0.0",
|
||||||
"@sentry/react": "^6.15.0",
|
"@sentry/react": "^6.14.3",
|
||||||
"@sentry/tracing": "^6.15.0",
|
"@sentry/tracing": "^6.14.3",
|
||||||
"@splitsoftware/splitio-react": "^1.3.0",
|
"@splitsoftware/splitio-react": "^1.3.0",
|
||||||
"@stripe/react-stripe-js": "^1.6.0",
|
"@stripe/react-stripe-js": "^1.6.0",
|
||||||
"@stripe/stripe-js": "^1.21.2",
|
"@stripe/stripe-js": "^1.21.1",
|
||||||
"@tanem/react-nprogress": "^3.0.82",
|
"@tanem/react-nprogress": "^3.0.82",
|
||||||
"antd": "^4.17.3",
|
"antd": "^4.16.13",
|
||||||
"apollo-link-logger": "^2.0.0",
|
"apollo-link-logger": "^2.0.0",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"craco-less": "^1.20.0",
|
"craco-less": "^1.20.0",
|
||||||
@@ -27,17 +27,17 @@
|
|||||||
"enquire-js": "^0.2.1",
|
"enquire-js": "^0.2.1",
|
||||||
"env-cmd": "^10.1.0",
|
"env-cmd": "^10.1.0",
|
||||||
"exifr": "^7.1.3",
|
"exifr": "^7.1.3",
|
||||||
"firebase": "^9.6.0",
|
"firebase": "^9.4.1",
|
||||||
"graphql": "^16.0.1",
|
"graphql": "^16.0.1",
|
||||||
"i18next": "^21.5.4",
|
"i18next": "^21.4.2",
|
||||||
"i18next-browser-languagedetector": "^6.1.2",
|
"i18next-browser-languagedetector": "^6.1.2",
|
||||||
"jsoneditor": "^9.5.7",
|
"jsoneditor": "^9.5.7",
|
||||||
"jsreport-browser-client-dist": "^1.3.0",
|
"jsreport-browser-client-dist": "^1.3.0",
|
||||||
"libphonenumber-js": "^1.9.44",
|
"libphonenumber-js": "^1.9.42",
|
||||||
"logrocket": "^2.1.2",
|
"logrocket": "^2.1.1",
|
||||||
"markerjs2": "^2.17.2",
|
"markerjs2": "^2.17.0",
|
||||||
"moment-business-days": "^1.2.0",
|
"moment-business-days": "^1.2.0",
|
||||||
"phone": "^3.1.10",
|
"phone": "^3.1.9",
|
||||||
"preval.macro": "^5.0.0",
|
"preval.macro": "^5.0.0",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"query-string": "^7.0.1",
|
"query-string": "^7.0.1",
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
"react-drag-listview": "^0.1.8",
|
"react-drag-listview": "^0.1.8",
|
||||||
"react-grid-gallery": "^0.5.5",
|
"react-grid-gallery": "^0.5.5",
|
||||||
"react-grid-layout": "^1.3.0",
|
"react-grid-layout": "^1.3.0",
|
||||||
"react-i18next": "^11.14.3",
|
"react-i18next": "^11.14.2",
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^4.3.1",
|
||||||
"react-number-format": "^4.8.0",
|
"react-number-format": "^4.8.0",
|
||||||
"react-redux": "^7.2.6",
|
"react-redux": "^7.2.6",
|
||||||
@@ -65,24 +65,24 @@
|
|||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"redux-saga": "^1.1.3",
|
"redux-saga": "^1.1.3",
|
||||||
"redux-state-sync": "^3.1.2",
|
"redux-state-sync": "^3.1.2",
|
||||||
"reselect": "^4.1.5",
|
"reselect": "^4.1.2",
|
||||||
"sass": "^1.44.0",
|
"sass": "^1.43.4",
|
||||||
"socket.io-client": "^4.4.0",
|
"socket.io-client": "^4.3.2",
|
||||||
"styled-components": "^5.3.3",
|
"styled-components": "^5.3.3",
|
||||||
"subscriptions-transport-ws": "^0.11.0",
|
"subscriptions-transport-ws": "^0.11.0",
|
||||||
"web-vitals": "^2.1.2",
|
"web-vitals": "^2.1.2",
|
||||||
"workbox-background-sync": "^6.4.2",
|
"workbox-background-sync": "^6.3.0",
|
||||||
"workbox-broadcast-update": "^6.4.2",
|
"workbox-broadcast-update": "^6.3.0",
|
||||||
"workbox-cacheable-response": "^6.4.2",
|
"workbox-cacheable-response": "^6.3.0",
|
||||||
"workbox-core": "^6.4.2",
|
"workbox-core": "^6.3.0",
|
||||||
"workbox-expiration": "^6.4.2",
|
"workbox-expiration": "^6.3.0",
|
||||||
"workbox-google-analytics": "^6.4.2",
|
"workbox-google-analytics": "^6.3.0",
|
||||||
"workbox-navigation-preload": "^6.4.2",
|
"workbox-navigation-preload": "^6.3.0",
|
||||||
"workbox-precaching": "^6.4.2",
|
"workbox-precaching": "^6.3.0",
|
||||||
"workbox-range-requests": "^6.4.2",
|
"workbox-range-requests": "^6.3.0",
|
||||||
"workbox-routing": "^6.4.2",
|
"workbox-routing": "^6.3.0",
|
||||||
"workbox-strategies": "^6.4.2",
|
"workbox-strategies": "^6.3.0",
|
||||||
"workbox-streams": "^6.4.2"
|
"workbox-streams": "^6.3.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "patch-package",
|
"postinstall": "patch-package",
|
||||||
|
|||||||
@@ -1,53 +1,56 @@
|
|||||||
importScripts("https://www.gstatic.com/firebasejs/7.14.2/firebase-app.js");
|
// Scripts for firebase and firebase messaging
|
||||||
importScripts(
|
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js");
|
||||||
"https://www.gstatic.com/firebasejs/7.14.2/firebase-messaging.js"
|
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-messaging.js");
|
||||||
);
|
|
||||||
|
|
||||||
firebase.initializeApp({
|
// Initialize the Firebase app in the service worker by passing the generated config
|
||||||
apiKey: "AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU",
|
let firebaseConfig;
|
||||||
authDomain: "imex-prod.firebaseapp.com",
|
switch (this.location.hostname) {
|
||||||
databaseURL: "https://imex-prod.firebaseio.com",
|
case "localhost":
|
||||||
projectId: "imex-prod",
|
firebaseConfig = {
|
||||||
storageBucket: "imex-prod.appspot.com",
|
apiKey: "AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc",
|
||||||
messagingSenderId: "253497221485",
|
authDomain: "imex-dev.firebaseapp.com",
|
||||||
appId: "1:253497221485:web:3c81c483b94db84b227a64",
|
databaseURL: "https://imex-dev.firebaseio.com",
|
||||||
measurementId: "G-NTWBKG2L0M",
|
projectId: "imex-dev",
|
||||||
});
|
storageBucket: "imex-dev.appspot.com",
|
||||||
|
messagingSenderId: "759548147434",
|
||||||
|
appId: "1:759548147434:web:e8239868a48ceb36700993",
|
||||||
|
measurementId: "G-K5XRBVVB4S",
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case "test.imex.online":
|
||||||
|
firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c",
|
||||||
|
authDomain: "imex-test.firebaseapp.com",
|
||||||
|
projectId: "imex-test",
|
||||||
|
storageBucket: "imex-test.appspot.com",
|
||||||
|
messagingSenderId: "991923618608",
|
||||||
|
appId: "1:991923618608:web:633437569cdad78299bef5",
|
||||||
|
// measurementId: "${config.measurementId}",
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case "imex.online":
|
||||||
|
default:
|
||||||
|
firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU",
|
||||||
|
authDomain: "imex-prod.firebaseapp.com",
|
||||||
|
databaseURL: "https://imex-prod.firebaseio.com",
|
||||||
|
projectId: "imex-prod",
|
||||||
|
storageBucket: "imex-prod.appspot.com",
|
||||||
|
messagingSenderId: "253497221485",
|
||||||
|
appId: "1:253497221485:web:3c81c483b94db84b227a64",
|
||||||
|
measurementId: "G-NTWBKG2L0M",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// firebase.initializeApp({
|
firebase.initializeApp(firebaseConfig);
|
||||||
// apiKey: "AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc",
|
|
||||||
// authDomain: "imex-dev.firebaseapp.com",
|
|
||||||
// databaseURL: "https://imex-dev.firebaseio.com",
|
|
||||||
// projectId: "imex-dev",
|
|
||||||
// storageBucket: "imex-dev.appspot.com",
|
|
||||||
// messagingSenderId: "759548147434",
|
|
||||||
// appId: "1:759548147434:web:e8239868a48ceb36700993",
|
|
||||||
// measurementId: "G-K5XRBVVB4S",
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
// Retrieve firebase messaging
|
||||||
const messaging = firebase.messaging();
|
const messaging = firebase.messaging();
|
||||||
|
|
||||||
self.addEventListener("fetch", (fetch) => {
|
messaging.onBackgroundMessage(function (payload) {
|
||||||
//required for installation as a PWA. Can ignore for now.
|
// Customize notification here
|
||||||
//console.log("fetch", fetch);
|
const channel = new BroadcastChannel("imex-sw-messages");
|
||||||
});
|
channel.postMessage(payload);
|
||||||
|
|
||||||
messaging.setBackgroundMessageHandler(function (payload) {
|
//self.registration.showNotification(notificationTitle, notificationOptions);
|
||||||
return self.registration.showNotification(
|
|
||||||
"[SW]" + payload.notification.title,
|
|
||||||
payload.notification
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
//Handles the notification getting clicked.
|
|
||||||
self.addEventListener("notificationclick", function (event) {
|
|
||||||
console.log("SW notificationclick", event);
|
|
||||||
// event.notification.close();
|
|
||||||
if (event.action === "archive") {
|
|
||||||
// Archive action was clicked
|
|
||||||
archiveEmail();
|
|
||||||
} else {
|
|
||||||
// Main body of notification was clicked
|
|
||||||
clients.openWindow("/inbox");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import { ApolloProvider } from "@apollo/client";
|
import { ApolloProvider } from "@apollo/client";
|
||||||
//import trackerRedux from "@openreplay/tracker-redux";
|
|
||||||
import Tracker from "@openreplay/tracker";
|
|
||||||
import trackerGraphQL from "@openreplay/tracker-graphql";
|
|
||||||
import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react";
|
import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react";
|
||||||
import { ConfigProvider } from "antd";
|
import { ConfigProvider } from "antd";
|
||||||
import enLocale from "antd/es/locale/en_US";
|
import enLocale from "antd/es/locale/en_US";
|
||||||
@@ -10,30 +7,13 @@ import moment from "moment";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
||||||
//import trackerAssist from "@openreplay/tracker-assist";
|
|
||||||
import { getCurrentUser } from "../firebase/firebase.utils";
|
|
||||||
import client from "../utils/GraphQLClient";
|
import client from "../utils/GraphQLClient";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
||||||
|
|
||||||
moment.locale("en-US");
|
moment.locale("en-US");
|
||||||
|
|
||||||
export const tracker = new Tracker({
|
|
||||||
projectKey: "trDmOZlEXUpjGsMtHroA",
|
|
||||||
ingestPoint: "https://replay.imex.online/ingest",
|
|
||||||
...(process.env.NODE_ENV === null || process.env.NODE_ENV === "development"
|
|
||||||
? { __DISABLE_SECURE_MODE: true }
|
|
||||||
: {}),
|
|
||||||
// beaconSize: 10485760,
|
|
||||||
onStart: async ({ sessionID }) => {
|
|
||||||
const user = await getCurrentUser();
|
|
||||||
if (user) tracker.setUserID(user.email);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// tracker.use(
|
|
||||||
// trackerAssist({ confirmText: "Technical support is about to assist you." })
|
|
||||||
// ); // check the list of available options below
|
|
||||||
export const recordGraphQL = tracker.use(trackerGraphQL());
|
|
||||||
//tracker.start();
|
//tracker.start();
|
||||||
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
import { MessageOutlined } from "@ant-design/icons";
|
|
||||||
import { Badge, Card } from "antd";
|
|
||||||
import React from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
|
||||||
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
|
|
||||||
import ChatPopupComponent from '../chat-popup/chat-popup.component'
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
|
||||||
chatVisible: selectChatVisible,
|
|
||||||
});
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
|
||||||
toggleChatVisible: () => dispatch(toggleChatVisible()),
|
|
||||||
});
|
|
||||||
|
|
||||||
export function ChatAffixComponent({
|
|
||||||
chatVisible,
|
|
||||||
toggleChatVisible,
|
|
||||||
conversationList,
|
|
||||||
unreadCount,
|
|
||||||
}) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Badge count={unreadCount}>
|
|
||||||
<Card size='small'>
|
|
||||||
{chatVisible ? (
|
|
||||||
<ChatPopupComponent conversationList={conversationList} />
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
onClick={() => toggleChatVisible()}
|
|
||||||
style={{ cursor: "pointer" }}>
|
|
||||||
<MessageOutlined />
|
|
||||||
<strong>{t("messaging.labels.messaging")}</strong>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
</Badge>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ChatAffixComponent);
|
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
import { useSubscription } from "@apollo/client";
|
import { useApolloClient } from "@apollo/client";
|
||||||
import React from "react";
|
import { getToken, onMessage } from "@firebase/messaging";
|
||||||
|
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 { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { CONVERSATION_LIST_SUBSCRIPTION } from "../../graphql/conversations.queries";
|
import { messaging, requestForToken } from "../../firebase/firebase.utils";
|
||||||
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
|
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import FcmHandler from "../../utils/fcm-handler";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import ChatPopupComponent from "../chat-popup/chat-popup.component";
|
||||||
import ChatAffixComponent from "./chat-affix.component";
|
|
||||||
import "./chat-affix.styles.scss";
|
import "./chat-affix.styles.scss";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -16,32 +19,87 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function ChatAffixContainer({ bodyshop, chatVisible }) {
|
export function ChatAffixContainer({ bodyshop, chatVisible }) {
|
||||||
const { loading, error, data } = useSubscription(
|
const { t } = useTranslation();
|
||||||
CONVERSATION_LIST_SUBSCRIPTION,
|
const client = useApolloClient();
|
||||||
{
|
useEffect(() => {
|
||||||
skip: !bodyshop || (bodyshop && !bodyshop.messagingservicesid),
|
if (!bodyshop || !bodyshop.messagingservicesid) return;
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (loading) return <LoadingSpinner />;
|
async function SubscribeToTopic() {
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
try {
|
||||||
|
const r = await axios.post("/notifications/subscribe", {
|
||||||
|
fcm_tokens: await getToken(messaging, {
|
||||||
|
vapidKey: process.env.REACT_APP_FIREBASE_PUBLIC_VAPID_KEY,
|
||||||
|
}),
|
||||||
|
type: "messaging",
|
||||||
|
imexshopid: bodyshop.imexshopid,
|
||||||
|
});
|
||||||
|
console.log("FCM Topic Subscription", r.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(
|
||||||
|
"Error attempting to subscribe to messaging topic: ",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
notification.open({
|
||||||
|
type: "warning",
|
||||||
|
message: t("general.errors.fcm"),
|
||||||
|
btn: (
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
await requestForToken();
|
||||||
|
|
||||||
|
SubscribeToTopic();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("general.actions.tryagain")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
const win = window.open(
|
||||||
|
"https://help.imex.online/en/article/enabling-notifications-o978xi/",
|
||||||
|
"_blank"
|
||||||
|
);
|
||||||
|
win.focus();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("general.labels.help")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubscribeToTopic();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [bodyshop]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleMessage(payload) {
|
||||||
|
FcmHandler({
|
||||||
|
client,
|
||||||
|
payload: (payload && payload.data && payload.data.data) || payload.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let stopMessageListenr, channel;
|
||||||
|
try {
|
||||||
|
stopMessageListenr = 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();
|
||||||
|
channel && channel.removeEventListener("message", handleMessage);
|
||||||
|
};
|
||||||
|
}, [client]);
|
||||||
|
|
||||||
if (!bodyshop || !bodyshop.messagingservicesid) return <></>;
|
if (!bodyshop || !bodyshop.messagingservicesid) return <></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`chat-affix ${chatVisible ? "chat-affix-open" : ""}`}>
|
<div className={`chat-affix ${chatVisible ? "chat-affix-open" : ""}`}>
|
||||||
{bodyshop && bodyshop.messagingservicesid ? (
|
{bodyshop && bodyshop.messagingservicesid ? <ChatPopupComponent /> : null}
|
||||||
<ChatAffixComponent
|
|
||||||
conversationList={(data && data.conversations) || []}
|
|
||||||
unreadCount={
|
|
||||||
(data &&
|
|
||||||
data.conversations.reduce((acc, val) => {
|
|
||||||
return (acc = acc + val.messages_aggregate.aggregate.count);
|
|
||||||
}, 0)) ||
|
|
||||||
0
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { Badge, List, Tag, Tooltip } from "antd";
|
import { Badge, List, Tag } from "antd";
|
||||||
import { AlertFilled } from "@ant-design/icons";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
|
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
|
||||||
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
||||||
|
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||||
import "./chat-conversation-list.styles.scss";
|
import "./chat-conversation-list.styles.scss";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
selectedConversation: selectSelectedConversation,
|
selectedConversation: selectSelectedConversation,
|
||||||
@@ -24,8 +22,6 @@ export function ChatConversationListComponent({
|
|||||||
selectedConversation,
|
selectedConversation,
|
||||||
setSelectedConversation,
|
setSelectedConversation,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="chat-list-container">
|
<div className="chat-list-container">
|
||||||
<List
|
<List
|
||||||
@@ -33,6 +29,7 @@ export function ChatConversationListComponent({
|
|||||||
dataSource={conversationList}
|
dataSource={conversationList}
|
||||||
renderItem={(item) => (
|
renderItem={(item) => (
|
||||||
<List.Item
|
<List.Item
|
||||||
|
key={item.id}
|
||||||
onClick={() => setSelectedConversation(item.id)}
|
onClick={() => setSelectedConversation(item.id)}
|
||||||
className={`chat-list-item ${
|
className={`chat-list-item ${
|
||||||
item.id === selectedConversation
|
item.id === selectedConversation
|
||||||
@@ -43,19 +40,9 @@ export function ChatConversationListComponent({
|
|||||||
{item.job_conversations.length > 0 ? (
|
{item.job_conversations.length > 0 ? (
|
||||||
<div className="chat-name">
|
<div className="chat-name">
|
||||||
{item.job_conversations.map((j, idx) => (
|
{item.job_conversations.map((j, idx) => (
|
||||||
<div key={idx} style={{ display: "flex" }}>
|
<div key={idx}>{`${j.job.ownr_fn || ""} ${
|
||||||
{j.job.owner && !j.job.owner.allow_text_message && (
|
j.job.ownr_ln || ""
|
||||||
<Tooltip title={t("messaging.labels.noallowtxt")}>
|
} ${j.job.ownr_co_nm || ""} `}</div>
|
||||||
<AlertFilled
|
|
||||||
className="production-alert"
|
|
||||||
style={{ marginRight: ".3rem", alignItems: "center" }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
<div>{`${j.job.ownr_fn || ""} ${j.job.ownr_ln || ""} ${
|
|
||||||
j.job.ownr_co_nm || ""
|
|
||||||
} `}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import "./chat-conversation.styles.scss";
|
|||||||
export default function ChatConversationComponent({
|
export default function ChatConversationComponent({
|
||||||
subState,
|
subState,
|
||||||
conversation,
|
conversation,
|
||||||
|
messages,
|
||||||
handleMarkConversationAsRead,
|
handleMarkConversationAsRead,
|
||||||
}) {
|
}) {
|
||||||
const [loading, error] = subState;
|
const [loading, error] = subState;
|
||||||
@@ -16,8 +17,6 @@ export default function ChatConversationComponent({
|
|||||||
if (loading) return <LoadingSkeleton />;
|
if (loading) return <LoadingSkeleton />;
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
const messages = (conversation && conversation.messages) || [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="chat-conversation"
|
className="chat-conversation"
|
||||||
|
|||||||
@@ -1,18 +1,32 @@
|
|||||||
import { useMutation, useSubscription } from "@apollo/client";
|
import { useMutation, useQuery, useSubscription } from "@apollo/client";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { CONVERSATION_SUBSCRIPTION_BY_PK } 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 { MARK_MESSAGES_AS_READ_BY_CONVERSATION } from "../../graphql/messages.queries";
|
||||||
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
||||||
import ChatConversationComponent from "./chat-conversation.component";
|
import ChatConversationComponent from "./chat-conversation.component";
|
||||||
|
import axios from "axios";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
selectedConversation: selectSelectedConversation,
|
selectedConversation: selectSelectedConversation,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(ChatConversationContainer);
|
export default connect(mapStateToProps, null)(ChatConversationContainer);
|
||||||
|
|
||||||
export function ChatConversationContainer({ selectedConversation }) {
|
export function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
||||||
|
const {
|
||||||
|
loading: convoLoading,
|
||||||
|
error: convoError,
|
||||||
|
data: convoData,
|
||||||
|
} = useQuery(GET_CONVERSATION_DETAILS, {
|
||||||
|
variables: { conversationId: selectedConversation },
|
||||||
|
});
|
||||||
|
|
||||||
const { loading, error, data } = useSubscription(
|
const { loading, error, data } = useSubscription(
|
||||||
CONVERSATION_SUBSCRIPTION_BY_PK,
|
CONVERSATION_SUBSCRIPTION_BY_PK,
|
||||||
{
|
{
|
||||||
@@ -26,30 +40,46 @@ export function ChatConversationContainer({ selectedConversation }) {
|
|||||||
MARK_MESSAGES_AS_READ_BY_CONVERSATION,
|
MARK_MESSAGES_AS_READ_BY_CONVERSATION,
|
||||||
{
|
{
|
||||||
variables: { conversationId: selectedConversation },
|
variables: { conversationId: selectedConversation },
|
||||||
|
update(cache) {
|
||||||
|
cache.modify({
|
||||||
|
id: cache.identify({
|
||||||
|
__typename: "conversations",
|
||||||
|
id: selectedConversation,
|
||||||
|
}),
|
||||||
|
fields: {
|
||||||
|
messages_aggregate(cached) {
|
||||||
|
return { aggregate: { count: 0 } };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const unreadCount =
|
const unreadCount =
|
||||||
(data &&
|
data &&
|
||||||
data.conversations_by_pk &&
|
data.messages &&
|
||||||
data.conversations_by_pk &&
|
data.messages.reduce((acc, val) => {
|
||||||
data.conversations_by_pk.messages_aggregate &&
|
return !val.read && !val.isoutbound ? acc + 1 : acc;
|
||||||
data.conversations_by_pk.messages_aggregate.aggregate &&
|
}, 0);
|
||||||
data.conversations_by_pk.messages_aggregate.aggregate.count) ||
|
|
||||||
0;
|
|
||||||
|
|
||||||
const handleMarkConversationAsRead = async () => {
|
const handleMarkConversationAsRead = async () => {
|
||||||
if (unreadCount > 0 && !!selectedConversation && !markingAsReadInProgress) {
|
if (unreadCount > 0 && !!selectedConversation && !markingAsReadInProgress) {
|
||||||
setMarkingAsReadInProgress(true);
|
setMarkingAsReadInProgress(true);
|
||||||
await markConversationRead();
|
await markConversationRead({});
|
||||||
|
await axios.post("/sms/markConversationRead", {
|
||||||
|
conversationid: selectedConversation,
|
||||||
|
imexshopid: bodyshop.imexshopid,
|
||||||
|
});
|
||||||
setMarkingAsReadInProgress(false);
|
setMarkingAsReadInProgress(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChatConversationComponent
|
<ChatConversationComponent
|
||||||
subState={[loading, error]}
|
subState={[loading || convoLoading, error || convoError]}
|
||||||
conversation={data ? data.conversations_by_pk : {}}
|
conversation={convoData ? convoData.conversations_by_pk : {}}
|
||||||
|
messages={data ? data.messages : []}
|
||||||
handleMarkConversationAsRead={handleMarkConversationAsRead}
|
handleMarkConversationAsRead={handleMarkConversationAsRead}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export function ChatNewConversation({ openChatByPhone }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover trigger="click" content={popContent}>
|
<Popover trigger="click" content={popContent}>
|
||||||
<PlusCircleFilled style={{ margin: "1rem" }} />
|
<PlusCircleFilled />
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,119 @@
|
|||||||
import { ShrinkOutlined, InfoCircleOutlined } from "@ant-design/icons";
|
import {
|
||||||
import { Col, Row, Tooltip, Typography } from "antd";
|
InfoCircleOutlined,
|
||||||
import React from "react";
|
MessageOutlined,
|
||||||
|
ShrinkOutlined,
|
||||||
|
SyncOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { CONVERSATION_LIST_QUERY } from "../../graphql/conversations.queries";
|
||||||
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
||||||
|
import {
|
||||||
|
selectChatVisible,
|
||||||
|
selectSelectedConversation,
|
||||||
|
} from "../../redux/messaging/messaging.selectors";
|
||||||
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
|
import ChatConversationListComponent from "../chat-conversation-list/chat-conversation-list.component";
|
||||||
import ChatConversationContainer from "../chat-conversation/chat-conversation.container";
|
import ChatConversationContainer from "../chat-conversation/chat-conversation.container";
|
||||||
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
|
|
||||||
import "./chat-popup.styles.scss";
|
|
||||||
import ChatNewConversation from "../chat-new-conversation/chat-new-conversation.component";
|
import ChatNewConversation from "../chat-new-conversation/chat-new-conversation.component";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
import "./chat-popup.styles.scss";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
selectedConversation: selectSelectedConversation,
|
selectedConversation: selectSelectedConversation,
|
||||||
|
chatVisible: selectChatVisible,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
toggleChatVisible: () => dispatch(toggleChatVisible()),
|
toggleChatVisible: () => dispatch(toggleChatVisible()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ChatPopupComponent({
|
export function ChatPopupComponent({
|
||||||
conversationList,
|
chatVisible,
|
||||||
selectedConversation,
|
selectedConversation,
|
||||||
toggleChatVisible,
|
toggleChatVisible,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
const [pollInterval, setpollInterval] = useState(0);
|
||||||
<div className="chat-popup">
|
const { loading, data, refetch, called } = useQuery(CONVERSATION_LIST_QUERY, {
|
||||||
<div style={{ display: "flex", alignItems: "center" }}>
|
...(pollInterval > 0 ? { pollInterval } : {}),
|
||||||
<Typography.Title level={4}>
|
});
|
||||||
{t("messaging.labels.messaging")}
|
|
||||||
</Typography.Title>
|
|
||||||
<ChatNewConversation />
|
|
||||||
<Tooltip title={t("messaging.labels.recentonly")}>
|
|
||||||
<InfoCircleOutlined />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<ShrinkOutlined
|
|
||||||
onClick={() => toggleChatVisible()}
|
|
||||||
style={{ position: "absolute", right: ".5rem", top: ".5rem" }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Row gutter={[8, 8]} className="chat-popup-content">
|
const fcmToken = sessionStorage.getItem("fcmtoken");
|
||||||
<Col span={8}>
|
|
||||||
<ChatConversationListComponent conversationList={conversationList} />
|
useEffect(() => {
|
||||||
</Col>
|
if (fcmToken) {
|
||||||
<Col span={16}>
|
setpollInterval(0);
|
||||||
{selectedConversation ? <ChatConversationContainer /> : null}
|
} else {
|
||||||
</Col>
|
setpollInterval(60000);
|
||||||
</Row>
|
}
|
||||||
</div>
|
}, [fcmToken]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (called && chatVisible) refetch();
|
||||||
|
}, [chatVisible, called, refetch]);
|
||||||
|
|
||||||
|
const unreadCount = data
|
||||||
|
? data.conversations.reduce(
|
||||||
|
(acc, val) => val.messages_aggregate.aggregate.count + acc,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Badge count={unreadCount}>
|
||||||
|
<Card size="small">
|
||||||
|
{chatVisible ? (
|
||||||
|
<div className="chat-popup">
|
||||||
|
<Space align="center">
|
||||||
|
<Typography.Title level={4}>
|
||||||
|
{t("messaging.labels.messaging")}
|
||||||
|
</Typography.Title>
|
||||||
|
<ChatNewConversation />
|
||||||
|
<Tooltip title={t("messaging.labels.recentonly")}>
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
|
<SyncOutlined
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
onClick={() => refetch()}
|
||||||
|
/>
|
||||||
|
{pollInterval > 0 && (
|
||||||
|
<Tag color="yellow">{t("messaging.labels.nopush")}</Tag>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
<ShrinkOutlined
|
||||||
|
onClick={() => toggleChatVisible()}
|
||||||
|
style={{ position: "absolute", right: ".5rem", top: ".5rem" }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Row gutter={[8, 8]} className="chat-popup-content">
|
||||||
|
<Col span={8}>
|
||||||
|
{loading ? (
|
||||||
|
<LoadingSpinner />
|
||||||
|
) : (
|
||||||
|
<ChatConversationListComponent
|
||||||
|
conversationList={data ? data.conversations : []}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
<Col span={16}>
|
||||||
|
{selectedConversation ? <ChatConversationContainer /> : null}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
onClick={() => toggleChatVisible()}
|
||||||
|
style={{ cursor: "pointer" }}
|
||||||
|
>
|
||||||
|
<MessageOutlined />
|
||||||
|
<strong>{t("messaging.labels.messaging")}</strong>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
</Badge>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ChatPopupComponent);
|
export default connect(mapStateToProps, mapDispatchToProps)(ChatPopupComponent);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import { tracker } from "../../App/App.container";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
@@ -37,7 +36,6 @@ class ErrorBoundary extends React.Component {
|
|||||||
componentDidCatch(error, info) {
|
componentDidCatch(error, info) {
|
||||||
console.log("Exception Caught by Error Boundary.", error, info);
|
console.log("Exception Caught by Error Boundary.", error, info);
|
||||||
this.setState({ ...this.state, error, info });
|
this.setState({ ...this.state, error, info });
|
||||||
tracker.event("error_boundary", error, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleErrorSubmit = () => {
|
handleErrorSubmit = () => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import {
|
import {
|
||||||
|
QUERY_EXACT_JOBS_IN_PRODUCTION,
|
||||||
QUERY_EXACT_JOB_IN_PRODUCTION,
|
QUERY_EXACT_JOB_IN_PRODUCTION,
|
||||||
QUERY_JOBS_IN_PRODUCTION,
|
QUERY_JOBS_IN_PRODUCTION,
|
||||||
SUBSCRIPTION_JOBS_IN_PRODUCTION,
|
SUBSCRIPTION_JOBS_IN_PRODUCTION,
|
||||||
@@ -42,16 +43,23 @@ export function ProductionBoardKanbanContainer({ bodyshop, currentUser }) {
|
|||||||
if (!updatedJobs || joblist.length === 0) return;
|
if (!updatedJobs || joblist.length === 0) return;
|
||||||
|
|
||||||
const jobDiff = _.differenceWith(
|
const jobDiff = _.differenceWith(
|
||||||
updatedJobs.jobs,
|
|
||||||
joblist,
|
joblist,
|
||||||
|
updatedJobs.jobs,
|
||||||
(a, b) => a.id === b.id && a.updated_at === b.updated_at
|
(a, b) => a.id === b.id && a.updated_at === b.updated_at
|
||||||
);
|
);
|
||||||
|
|
||||||
jobDiff.forEach((job) => {
|
jobDiff.forEach((job) => {
|
||||||
getUpdatedJobData(job.id);
|
getUpdatedJobData(job.id);
|
||||||
});
|
});
|
||||||
|
if (jobDiff.length > 1) {
|
||||||
|
getUpdatedJobsData(jobDiff.map((j) => j.id));
|
||||||
|
} else if (jobDiff.length === 1) {
|
||||||
|
jobDiff.forEach((job) => {
|
||||||
|
getUpdatedJobData(job.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setJoblist(updatedJobs);
|
setJoblist(updatedJobs.jobs);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [updatedJobs]);
|
}, [updatedJobs]);
|
||||||
|
|
||||||
@@ -61,6 +69,12 @@ export function ProductionBoardKanbanContainer({ bodyshop, currentUser }) {
|
|||||||
variables: { id: jobId },
|
variables: { id: jobId },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const getUpdatedJobsData = async (jobIds) => {
|
||||||
|
client.query({
|
||||||
|
query: QUERY_EXACT_JOBS_IN_PRODUCTION,
|
||||||
|
variables: { ids: jobIds },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const { loading: associationSettingsLoading, data: associationSettings } =
|
const { loading: associationSettingsLoading, data: associationSettings } =
|
||||||
useQuery(QUERY_KANBAN_SETTINGS, {
|
useQuery(QUERY_KANBAN_SETTINGS, {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
|
import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
|
QUERY_EXACT_JOBS_IN_PRODUCTION,
|
||||||
QUERY_EXACT_JOB_IN_PRODUCTION,
|
QUERY_EXACT_JOB_IN_PRODUCTION,
|
||||||
QUERY_JOBS_IN_PRODUCTION,
|
QUERY_JOBS_IN_PRODUCTION,
|
||||||
SUBSCRIPTION_JOBS_IN_PRODUCTION,
|
SUBSCRIPTION_JOBS_IN_PRODUCTION,
|
||||||
@@ -31,16 +32,22 @@ export default function ProductionListTableContainer() {
|
|||||||
if (!updatedJobs || joblist.length === 0) return;
|
if (!updatedJobs || joblist.length === 0) return;
|
||||||
|
|
||||||
const jobDiff = _.differenceWith(
|
const jobDiff = _.differenceWith(
|
||||||
updatedJobs.jobs,
|
|
||||||
joblist,
|
joblist,
|
||||||
|
updatedJobs.jobs,
|
||||||
(a, b) => a.id === b.id && a.updated_at === b.updated_at
|
(a, b) => a.id === b.id && a.updated_at === b.updated_at
|
||||||
);
|
);
|
||||||
|
console.log(jobDiff);
|
||||||
|
if (jobDiff.length > 1) {
|
||||||
|
getUpdatedJobsData(jobDiff.map((j) => j.id));
|
||||||
|
} else if (jobDiff.length === 1) {
|
||||||
|
console.log("length was 1");
|
||||||
|
jobDiff.forEach((job) => {
|
||||||
|
console.log("Job ", job);
|
||||||
|
getUpdatedJobData(job.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
jobDiff.forEach((job) => {
|
setJoblist(updatedJobs.jobs);
|
||||||
getUpdatedJobData(job.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
setJoblist(updatedJobs);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [updatedJobs]);
|
}, [updatedJobs]);
|
||||||
|
|
||||||
@@ -50,6 +57,12 @@ export default function ProductionListTableContainer() {
|
|||||||
variables: { id: jobId },
|
variables: { id: jobId },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const getUpdatedJobsData = async (jobIds) => {
|
||||||
|
client.query({
|
||||||
|
query: QUERY_EXACT_JOBS_IN_PRODUCTION,
|
||||||
|
variables: { ids: jobIds },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProductionListTable
|
<ProductionListTable
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { getAnalytics, logEvent } from "firebase/analytics";
|
|||||||
import { initializeApp } from "firebase/app";
|
import { initializeApp } from "firebase/app";
|
||||||
import { getAuth, updatePassword, updateProfile } from "firebase/auth";
|
import { getAuth, updatePassword, updateProfile } from "firebase/auth";
|
||||||
import { getFirestore } from "firebase/firestore";
|
import { getFirestore } from "firebase/firestore";
|
||||||
import { tracker } from "../App/App.container";
|
import { getMessaging, getToken, onMessage } from "firebase/messaging";
|
||||||
//import { getMessaging } from "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);
|
||||||
@@ -39,18 +38,42 @@ export const updateCurrentPassword = async (password) => {
|
|||||||
|
|
||||||
return updatePassword(currentUser, password);
|
return updatePassword(currentUser, password);
|
||||||
};
|
};
|
||||||
|
let messaging;
|
||||||
|
try {
|
||||||
|
messaging = getMessaging();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
//let messaging;
|
export { messaging };
|
||||||
// try {
|
|
||||||
// messaging = getMessaging();
|
|
||||||
// // Project Settings => Cloud Messaging => Web Push certificates
|
|
||||||
// messaging.usePublicVapidKey(process.env.REACT_APP_FIREBASE_PUBLIC_VAPID_KEY);
|
|
||||||
// console.log("[FCM UTIL] FCM initialized successfully.");
|
|
||||||
// } catch {
|
|
||||||
// console.log("[FCM UTIL] Firebase Messaging is likely unsupported.");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export { messaging };
|
export const requestForToken = () => {
|
||||||
|
return getToken(messaging, {
|
||||||
|
vapidKey: process.env.REACT_APP_FIREBASE_PUBLIC_VAPID_KEY,
|
||||||
|
})
|
||||||
|
.then((currentToken) => {
|
||||||
|
if (currentToken) {
|
||||||
|
console.log("current token for client: ", currentToken);
|
||||||
|
window.sessionStorage.setItem("fcmtoken", currentToken);
|
||||||
|
// Perform any other necessary action with the token
|
||||||
|
} else {
|
||||||
|
// Show permission request UI
|
||||||
|
console.log(
|
||||||
|
"No registration token available. Request permission to generate one."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("An error occurred while retrieving token. ", err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const onMessageListener = () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
onMessage(messaging, (payload) => {
|
||||||
|
console.log("Inbound FCM Message", payload);
|
||||||
|
resolve(payload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
||||||
const state = stateProp || store.getState();
|
const state = stateProp || store.getState();
|
||||||
@@ -70,9 +93,6 @@ export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
|||||||
// eventParams
|
// eventParams
|
||||||
// );
|
// );
|
||||||
logEvent(analytics, eventName, eventParams);
|
logEvent(analytics, eventName, eventParams);
|
||||||
|
|
||||||
//Log event to OpenReplay server.
|
|
||||||
tracker.event(eventName, eventParams);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// if (messaging) {
|
// if (messaging) {
|
||||||
|
|||||||
@@ -1,15 +1,54 @@
|
|||||||
import { gql } from "@apollo/client";
|
import { gql } from "@apollo/client";
|
||||||
|
|
||||||
export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
// export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
||||||
subscription CONVERSATION_LIST_SUBSCRIPTION {
|
// subscription CONVERSATION_LIST_SUBSCRIPTION {
|
||||||
|
// conversations(
|
||||||
|
// order_by: { updated_at: desc }
|
||||||
|
// limit: 50
|
||||||
|
// where: { archived: { _eq: false } }
|
||||||
|
// ) {
|
||||||
|
// phone_num
|
||||||
|
// id
|
||||||
|
// updated_at
|
||||||
|
// unreadcnt
|
||||||
|
// messages_aggregate(
|
||||||
|
// where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
||||||
|
// ) {
|
||||||
|
// aggregate {
|
||||||
|
// count
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// job_conversations {
|
||||||
|
// job {
|
||||||
|
// id
|
||||||
|
// ro_number
|
||||||
|
// ownr_fn
|
||||||
|
// ownr_ln
|
||||||
|
// ownr_co_nm
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// `;
|
||||||
|
|
||||||
|
export const CONVERSATION_LIST_QUERY = gql`
|
||||||
|
query CONVERSATION_LIST_QUERY {
|
||||||
conversations(
|
conversations(
|
||||||
order_by: { updated_at: desc }
|
order_by: { updated_at: desc }
|
||||||
limit: 100
|
limit: 50
|
||||||
where: { archived: { _eq: false } }
|
where: { archived: { _eq: false } }
|
||||||
) {
|
) {
|
||||||
phone_num
|
phone_num
|
||||||
id
|
id
|
||||||
updated_at
|
updated_at
|
||||||
|
unreadcnt
|
||||||
|
messages_aggregate(
|
||||||
|
where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
job_conversations {
|
job_conversations {
|
||||||
job {
|
job {
|
||||||
id
|
id
|
||||||
@@ -17,17 +56,6 @@ export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
|||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
owner {
|
|
||||||
id
|
|
||||||
allow_text_message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
messages_aggregate(
|
|
||||||
where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
|
||||||
) {
|
|
||||||
aggregate {
|
|
||||||
count
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,24 +64,26 @@ export const CONVERSATION_LIST_SUBSCRIPTION = gql`
|
|||||||
|
|
||||||
export const CONVERSATION_SUBSCRIPTION_BY_PK = gql`
|
export const CONVERSATION_SUBSCRIPTION_BY_PK = gql`
|
||||||
subscription CONVERSATION_SUBSCRIPTION_BY_PK($conversationId: uuid!) {
|
subscription CONVERSATION_SUBSCRIPTION_BY_PK($conversationId: uuid!) {
|
||||||
|
messages(
|
||||||
|
order_by: { created_at: asc_nulls_first }
|
||||||
|
where: { conversationid: { _eq: $conversationId } }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
text
|
||||||
|
isoutbound
|
||||||
|
image
|
||||||
|
image_path
|
||||||
|
userid
|
||||||
|
created_at
|
||||||
|
read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GET_CONVERSATION_DETAILS = gql`
|
||||||
|
query GET_CONVERSATION_DETAILS($conversationId: uuid!) {
|
||||||
conversations_by_pk(id: $conversationId) {
|
conversations_by_pk(id: $conversationId) {
|
||||||
messages(order_by: { created_at: asc_nulls_first }) {
|
|
||||||
id
|
|
||||||
status
|
|
||||||
text
|
|
||||||
isoutbound
|
|
||||||
image
|
|
||||||
image_path
|
|
||||||
userid
|
|
||||||
created_at
|
|
||||||
}
|
|
||||||
messages_aggregate(
|
|
||||||
where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
|
||||||
) {
|
|
||||||
aggregate {
|
|
||||||
count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
id
|
id
|
||||||
phone_num
|
phone_num
|
||||||
archived
|
archived
|
||||||
|
|||||||
@@ -185,6 +185,77 @@ export const QUERY_EXACT_JOB_IN_PRODUCTION = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const QUERY_EXACT_JOBS_IN_PRODUCTION = gql`
|
||||||
|
query QUERY_EXACT_JOBS_IN_PRODUCTION($ids: [uuid!]!) {
|
||||||
|
jobs(where: { id: { _in: $ids } }) {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
ro_number
|
||||||
|
ownr_fn
|
||||||
|
ownr_ln
|
||||||
|
ownr_co_nm
|
||||||
|
v_model_yr
|
||||||
|
v_model_desc
|
||||||
|
clm_no
|
||||||
|
v_make_desc
|
||||||
|
v_color
|
||||||
|
plate_no
|
||||||
|
actual_in
|
||||||
|
scheduled_completion
|
||||||
|
scheduled_delivery
|
||||||
|
date_last_contacted
|
||||||
|
date_next_contact
|
||||||
|
ins_co_nm
|
||||||
|
clm_total
|
||||||
|
ownr_ph1
|
||||||
|
ownr_ph2
|
||||||
|
special_coverage_policy
|
||||||
|
owner_owing
|
||||||
|
production_vars
|
||||||
|
kanbanparent
|
||||||
|
alt_transport
|
||||||
|
employee_body
|
||||||
|
employee_refinish
|
||||||
|
employee_prep
|
||||||
|
employee_csr
|
||||||
|
labhrs: joblines_aggregate(
|
||||||
|
where: {
|
||||||
|
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
sum {
|
||||||
|
mod_lb_hrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
larhrs: joblines_aggregate(
|
||||||
|
where: {
|
||||||
|
_and: [{ mod_lbr_ty: { _eq: "LAR" } }, { removed: { _eq: false } }]
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
sum {
|
||||||
|
mod_lb_hrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subletLines: joblines(
|
||||||
|
where: {
|
||||||
|
_and: { part_type: { _in: ["PAS", "PASL"] }, removed: { _eq: false } }
|
||||||
|
}
|
||||||
|
order_by: { line_no: asc }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
sublet_ignored
|
||||||
|
sublet_completed
|
||||||
|
jobid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const QUERY_JOBS_IN_PRODUCTION = gql`
|
export const QUERY_JOBS_IN_PRODUCTION = gql`
|
||||||
query QUERY_JOBS_IN_PRODUCTION {
|
query QUERY_JOBS_IN_PRODUCTION {
|
||||||
jobs(where: { inproduction: { _eq: true } }) {
|
jobs(where: { inproduction: { _eq: true } }) {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export const MARK_MESSAGES_AS_READ_BY_CONVERSATION = gql`
|
|||||||
) {
|
) {
|
||||||
returning {
|
returning {
|
||||||
id
|
id
|
||||||
|
read
|
||||||
|
isoutbound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,11 +146,13 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
|
|||||||
{t("general.actions.close")}
|
{t("general.actions.close")}
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
<Link to={`/manage/dms?jobId=${job.id}`}>
|
{(bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid) && (
|
||||||
<Button disabled={job.date_exported || !jobRO}>
|
<Link to={`/manage/dms?jobId=${job.id}`}>
|
||||||
{t("jobs.actions.sendtodms")}
|
<Button disabled={job.date_exported || !jobRO}>
|
||||||
</Button>
|
{t("jobs.actions.sendtodms")}
|
||||||
</Link>
|
</Button>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
<JobsScoreboardAdd job={job} disabled={false} />
|
<JobsScoreboardAdd job={job} disabled={false} />
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,12 @@ import preval from "preval.macro";
|
|||||||
import React, { lazy, Suspense, useEffect } from "react";
|
import React, { lazy, Suspense, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
|
||||||
import { Link, Route, Switch } from "react-router-dom";
|
import { Link, Route, Switch } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
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";
|
||||||
import FcmNotification from "../../components/fcm-notification/fcm-notification.component";
|
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
||||||
//import FooterComponent from "../../components/footer/footer.component";
|
//import FooterComponent from "../../components/footer/footer.component";
|
||||||
//Component Imports
|
//Component Imports
|
||||||
import HeaderContainer from "../../components/header/header.container";
|
import HeaderContainer from "../../components/header/header.container";
|
||||||
@@ -20,12 +19,11 @@ import PartnerPingComponent from "../../components/partner-ping/partner-ping.com
|
|||||||
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
|
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
|
||||||
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
||||||
import TestComponent from "../../components/_test/test.component";
|
import TestComponent from "../../components/_test/test.component";
|
||||||
import { QUERY_STRIPE_ID } from "../../graphql/bodyshop.queries";
|
import { requestForToken } from "../../firebase/firebase.utils";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectInstanceConflict,
|
selectInstanceConflict,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import client from "../../utils/GraphQLClient";
|
|
||||||
import "./manage.page.styles.scss";
|
import "./manage.page.styles.scss";
|
||||||
|
|
||||||
const ManageRootPage = lazy(() =>
|
const ManageRootPage = lazy(() =>
|
||||||
@@ -166,16 +164,15 @@ const Dms = lazy(() => import("../dms/dms.container"));
|
|||||||
const { Content, Footer } = Layout;
|
const { Content, Footer } = Layout;
|
||||||
|
|
||||||
const stripePromise = new Promise((resolve, reject) => {
|
const stripePromise = new Promise((resolve, reject) => {
|
||||||
client.query({ query: QUERY_STRIPE_ID }).then((resp) => {
|
// client.query({ query: QUERY_STRIPE_ID }).then((resp) => {
|
||||||
if (resp.data.bodyshops[0])
|
// if (resp.data.bodyshops[0])
|
||||||
resolve(
|
resolve(
|
||||||
loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY, {
|
loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY, {
|
||||||
stripeAccount:
|
stripeAccount: "No Stripe Id Resolve",
|
||||||
resp.data.bodyshops[0].stripe_acct_id || "No Stripe Id Resolve",
|
})
|
||||||
})
|
);
|
||||||
);
|
// reject();
|
||||||
reject();
|
// });
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -188,7 +185,13 @@ export function Manage({ match, conflict, bodyshop }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const widgetId = "IABVNO4scRKY11XBQkNr";
|
const widgetId = "IABVNO4scRKY11XBQkNr";
|
||||||
window.noticeable.render("widget", widgetId);
|
window.noticeable.render("widget", widgetId);
|
||||||
|
try {
|
||||||
|
requestForToken();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Unable to request for token.", error);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t("titles.app");
|
document.title = t("titles.app");
|
||||||
}, [t]);
|
}, [t]);
|
||||||
@@ -395,7 +398,6 @@ export function Manage({ match, conflict, bodyshop }) {
|
|||||||
<HeaderContainer />
|
<HeaderContainer />
|
||||||
|
|
||||||
<Content className="content-container">
|
<Content className="content-container">
|
||||||
<FcmNotification />
|
|
||||||
<PartnerPingComponent />
|
<PartnerPingComponent />
|
||||||
<ErrorBoundary>{PageContent}</ErrorBoundary>
|
<ErrorBoundary>{PageContent}</ErrorBoundary>
|
||||||
<BackTop />
|
<BackTop />
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { connect } from "react-redux";
|
|||||||
import { Redirect, Route, Switch } from "react-router-dom";
|
import { Redirect, Route, Switch } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
||||||
import FcmNotification from "../../components/fcm-notification/fcm-notification.component";
|
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
import TechHeader from "../../components/tech-header/tech-header.component";
|
import TechHeader from "../../components/tech-header/tech-header.component";
|
||||||
import TechSider from "../../components/tech-sider/tech-sider.component";
|
import TechSider from "../../components/tech-sider/tech-sider.component";
|
||||||
@@ -58,7 +58,6 @@ export function TechPage({ technician, match }) {
|
|||||||
{technician ? null : <Redirect to={`${match.path}/login`} />}
|
{technician ? null : <Redirect to={`${match.path}/login`} />}
|
||||||
<TechHeader />
|
<TechHeader />
|
||||||
<Content className="tech-content-container">
|
<Content className="tech-content-container">
|
||||||
<FcmNotification />
|
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Suspense
|
<Suspense
|
||||||
fallback={
|
fallback={
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import Fingerprint2 from "@fingerprintjs/fingerprintjs";
|
import Fingerprint2 from "@fingerprintjs/fingerprintjs";
|
||||||
import * as Sentry from "@sentry/browser";
|
import * as Sentry from "@sentry/browser";
|
||||||
import { notification } from "antd";
|
import { notification } from "antd";
|
||||||
import { auth, analytics, firestore } from "../../firebase/firebase.utils";
|
|
||||||
import { setUserId, setUserProperties } from "firebase/analytics";
|
import { setUserId, setUserProperties } from "firebase/analytics";
|
||||||
import {
|
import {
|
||||||
checkActionCode,
|
checkActionCode,
|
||||||
@@ -13,8 +12,11 @@ import { doc } from "firebase/firestore";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import LogRocket from "logrocket";
|
import LogRocket from "logrocket";
|
||||||
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
|
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
|
||||||
import { factory, tracker } from "../../App/App.container";
|
import { factory } from "../../App/App.container";
|
||||||
import {
|
import {
|
||||||
|
analytics,
|
||||||
|
auth,
|
||||||
|
firestore,
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
logImEXEvent,
|
logImEXEvent,
|
||||||
updateCurrentUser,
|
updateCurrentUser,
|
||||||
@@ -75,7 +77,7 @@ export function* isUserAuthenticated() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LogRocket.identify(user.email);
|
LogRocket.identify(user.email);
|
||||||
tracker.setUserID(user.email);
|
|
||||||
yield put(
|
yield put(
|
||||||
signInSuccess({
|
signInSuccess({
|
||||||
uid: user.uid,
|
uid: user.uid,
|
||||||
@@ -174,7 +176,6 @@ export function* onSignInSuccess() {
|
|||||||
|
|
||||||
export function* signInSuccessSaga({ payload }) {
|
export function* signInSuccessSaga({ payload }) {
|
||||||
LogRocket.identify(payload.email);
|
LogRocket.identify(payload.email);
|
||||||
tracker.setUserID(payload.email);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// window.$crisp.push(["set", "user:email", [payload.email]]);
|
// window.$crisp.push(["set", "user:email", [payload.email]]);
|
||||||
|
|||||||
@@ -899,10 +899,12 @@
|
|||||||
"selectall": "Select All",
|
"selectall": "Select All",
|
||||||
"senderrortosupport": "Send Error to Support",
|
"senderrortosupport": "Send Error to Support",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
|
"tryagain": "Try Again",
|
||||||
"view": "View",
|
"view": "View",
|
||||||
"viewreleasenotes": "See What's Changed"
|
"viewreleasenotes": "See What's Changed"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"fcm": "You must allow notification permissions to have real time messaging. Click to try again.",
|
||||||
"notfound": "No record was found."
|
"notfound": "No record was found."
|
||||||
},
|
},
|
||||||
"itemtypes": {
|
"itemtypes": {
|
||||||
@@ -925,6 +927,7 @@
|
|||||||
"exceptiontitle": "An error has occurred.",
|
"exceptiontitle": "An error has occurred.",
|
||||||
"friday": "Friday",
|
"friday": "Friday",
|
||||||
"globalsearch": "Global Search",
|
"globalsearch": "Global Search",
|
||||||
|
"help": "Help",
|
||||||
"hours": "hrs",
|
"hours": "hrs",
|
||||||
"in": "In",
|
"in": "In",
|
||||||
"instanceconflictext": "Your $t(titles.app) account can only be used on one device at any given time. Refresh your session to take control.",
|
"instanceconflictext": "Your $t(titles.app) account can only be used on one device at any given time. Refresh your session to take control.",
|
||||||
@@ -1763,6 +1766,7 @@
|
|||||||
"messaging": "Messaging",
|
"messaging": "Messaging",
|
||||||
"noallowtxt": "This customer has not indicated their permission to be messaged.",
|
"noallowtxt": "This customer has not indicated their permission to be messaged.",
|
||||||
"nojobs": "Not associated to any job.",
|
"nojobs": "Not associated to any job.",
|
||||||
|
"nopush": "Polling Mode Enabled",
|
||||||
"phonenumber": "Phone #",
|
"phonenumber": "Phone #",
|
||||||
"presets": "Presets",
|
"presets": "Presets",
|
||||||
"recentonly": "Only your most recent 50 conversations will be shown here. If you are looking for an older conversation, find the related contact and click their phone number to view the conversation.",
|
"recentonly": "Only your most recent 50 conversations will be shown here. If you are looking for an older conversation, find the related contact and click their phone number to view the conversation.",
|
||||||
|
|||||||
@@ -899,10 +899,12 @@
|
|||||||
"selectall": "",
|
"selectall": "",
|
||||||
"senderrortosupport": "",
|
"senderrortosupport": "",
|
||||||
"submit": "",
|
"submit": "",
|
||||||
|
"tryagain": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"viewreleasenotes": ""
|
"viewreleasenotes": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"fcm": "",
|
||||||
"notfound": ""
|
"notfound": ""
|
||||||
},
|
},
|
||||||
"itemtypes": {
|
"itemtypes": {
|
||||||
@@ -925,6 +927,7 @@
|
|||||||
"exceptiontitle": "",
|
"exceptiontitle": "",
|
||||||
"friday": "",
|
"friday": "",
|
||||||
"globalsearch": "",
|
"globalsearch": "",
|
||||||
|
"help": "",
|
||||||
"hours": "",
|
"hours": "",
|
||||||
"in": "en",
|
"in": "en",
|
||||||
"instanceconflictext": "",
|
"instanceconflictext": "",
|
||||||
@@ -1763,6 +1766,7 @@
|
|||||||
"messaging": "Mensajería",
|
"messaging": "Mensajería",
|
||||||
"noallowtxt": "",
|
"noallowtxt": "",
|
||||||
"nojobs": "",
|
"nojobs": "",
|
||||||
|
"nopush": "",
|
||||||
"phonenumber": "",
|
"phonenumber": "",
|
||||||
"presets": "",
|
"presets": "",
|
||||||
"recentonly": "",
|
"recentonly": "",
|
||||||
|
|||||||
@@ -899,10 +899,12 @@
|
|||||||
"selectall": "",
|
"selectall": "",
|
||||||
"senderrortosupport": "",
|
"senderrortosupport": "",
|
||||||
"submit": "",
|
"submit": "",
|
||||||
|
"tryagain": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"viewreleasenotes": ""
|
"viewreleasenotes": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"fcm": "",
|
||||||
"notfound": ""
|
"notfound": ""
|
||||||
},
|
},
|
||||||
"itemtypes": {
|
"itemtypes": {
|
||||||
@@ -925,6 +927,7 @@
|
|||||||
"exceptiontitle": "",
|
"exceptiontitle": "",
|
||||||
"friday": "",
|
"friday": "",
|
||||||
"globalsearch": "",
|
"globalsearch": "",
|
||||||
|
"help": "",
|
||||||
"hours": "",
|
"hours": "",
|
||||||
"in": "dans",
|
"in": "dans",
|
||||||
"instanceconflictext": "",
|
"instanceconflictext": "",
|
||||||
@@ -1763,6 +1766,7 @@
|
|||||||
"messaging": "Messagerie",
|
"messaging": "Messagerie",
|
||||||
"noallowtxt": "",
|
"noallowtxt": "",
|
||||||
"nojobs": "",
|
"nojobs": "",
|
||||||
|
"nopush": "",
|
||||||
"phonenumber": "",
|
"phonenumber": "",
|
||||||
"presets": "",
|
"presets": "",
|
||||||
"recentonly": "",
|
"recentonly": "",
|
||||||
|
|||||||
@@ -91,13 +91,11 @@ export default function CiecaSelect(parts = true, labor = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function GetPartTypeName(part_type) {
|
export function GetPartTypeName(part_type) {
|
||||||
console.log(part_type);
|
|
||||||
if (!part_type) return null;
|
if (!part_type) return null;
|
||||||
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Get(part_type) {
|
export function Get(part_type) {
|
||||||
console.log(part_type);
|
|
||||||
if (!part_type) return null;
|
if (!part_type) return null;
|
||||||
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
return i18n.t(`joblines.fields.part_types.${part_type.toUpperCase()}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,18 @@ const roundTripLink = new ApolloLink((operation, forward) => {
|
|||||||
const TrackExecutionTime = async (operationName, time) => {
|
const TrackExecutionTime = async (operationName, time) => {
|
||||||
const rdxStore = store.getState();
|
const rdxStore = store.getState();
|
||||||
try {
|
try {
|
||||||
console.log("trying");
|
|
||||||
axios.post("/ioevent", {
|
axios.post("/ioevent", {
|
||||||
operationName,
|
operationName,
|
||||||
time,
|
time,
|
||||||
dbevent: true,
|
dbevent: true,
|
||||||
user: rdxStore.user.currentUser.email,
|
user:
|
||||||
imexshopid: rdxStore.user.bodyshop.imexshopid,
|
rdxStore.user &&
|
||||||
|
rdxStore.user.currentUser &&
|
||||||
|
rdxStore.user.currentUser.email,
|
||||||
|
imexshopid:
|
||||||
|
rdxStore.user &&
|
||||||
|
rdxStore.user.bodyshop &&
|
||||||
|
rdxStore.user.bodyshop.imexshopid,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("IOEvent Error", error);
|
console.log("IOEvent Error", error);
|
||||||
|
|||||||
@@ -148,8 +148,6 @@ export async function RenderTemplates(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("reportRequest", reportRequest);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const render = await jsreport.renderAsync(reportRequest);
|
const render = await jsreport.renderAsync(reportRequest);
|
||||||
if (!renderAsHtml) {
|
if (!renderAsHtml) {
|
||||||
|
|||||||
34
client/src/utils/fcm-handler.js
Normal file
34
client/src/utils/fcm-handler.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
export default async function FcmHandler({ client, payload }) {
|
||||||
|
console.log("Handling payload type", payload);
|
||||||
|
switch (payload.type) {
|
||||||
|
case "messaging-inbound":
|
||||||
|
client.cache.modify({
|
||||||
|
id: client.cache.identify({
|
||||||
|
__typename: "conversations",
|
||||||
|
id: payload.conversationid,
|
||||||
|
}),
|
||||||
|
fields: {
|
||||||
|
messages_aggregate(cached) {
|
||||||
|
return { aggregate: { count: cached.aggregate.count + 1 } };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "messaging-mark-conversation-read":
|
||||||
|
client.cache.modify({
|
||||||
|
id: client.cache.identify({
|
||||||
|
__typename: "conversations",
|
||||||
|
id: payload.conversationid,
|
||||||
|
}),
|
||||||
|
fields: {
|
||||||
|
messages_aggregate(cached) {
|
||||||
|
return { aggregate: { count: 0 } };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("No payload type set.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
717
client/yarn.lock
717
client/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -1172,11 +1172,12 @@
|
|||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
columns:
|
columns:
|
||||||
- id
|
|
||||||
- created_at
|
|
||||||
- updated_at
|
|
||||||
- bodyshopid
|
- bodyshopid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
- phone_num
|
- phone_num
|
||||||
|
- unreadcnt
|
||||||
|
- updated_at
|
||||||
select_permissions:
|
select_permissions:
|
||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
@@ -1186,6 +1187,7 @@
|
|||||||
- created_at
|
- created_at
|
||||||
- id
|
- id
|
||||||
- phone_num
|
- phone_num
|
||||||
|
- unreadcnt
|
||||||
- updated_at
|
- updated_at
|
||||||
filter:
|
filter:
|
||||||
bodyshop:
|
bodyshop:
|
||||||
@@ -1206,6 +1208,7 @@
|
|||||||
- created_at
|
- created_at
|
||||||
- id
|
- id
|
||||||
- phone_num
|
- phone_num
|
||||||
|
- unreadcnt
|
||||||
- updated_at
|
- updated_at
|
||||||
filter:
|
filter:
|
||||||
bodyshop:
|
bodyshop:
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."conversations" add column "unreadcnt" numeric
|
||||||
|
-- not null default '0';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."conversations" add column "unreadcnt" numeric
|
||||||
|
not null default '0';
|
||||||
@@ -66,7 +66,6 @@ app.get("/test", async function (req, res) {
|
|||||||
"git rev-parse --short HEAD"
|
"git rev-parse --short HEAD"
|
||||||
);
|
);
|
||||||
logger.log("test-api-status", "DEBUG", "api", { commit });
|
logger.log("test-api-status", "DEBUG", "api", { commit });
|
||||||
|
|
||||||
res.status(200).send(`OK - ${commit}`);
|
res.status(200).send(`OK - ${commit}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -114,6 +113,7 @@ app.post(
|
|||||||
twilio.webhook({ validate: process.env.NODE_ENV === "PRODUCTION" }),
|
twilio.webhook({ validate: process.env.NODE_ENV === "PRODUCTION" }),
|
||||||
smsStatus.status
|
smsStatus.status
|
||||||
);
|
);
|
||||||
|
app.post("/sms/markConversationRead", smsStatus.markConversationRead);
|
||||||
|
|
||||||
var job = require("./server/job/job");
|
var job = require("./server/job/job");
|
||||||
app.post("/job/totals", fb.validateFirebaseIdToken, job.totals);
|
app.post("/job/totals", fb.validateFirebaseIdToken, job.totals);
|
||||||
@@ -133,9 +133,10 @@ app.post("/render/inlinecss", fb.validateFirebaseIdToken, inlineCss.inlinecss);
|
|||||||
|
|
||||||
app.post(
|
app.post(
|
||||||
"/notifications/send",
|
"/notifications/send",
|
||||||
fb.validateFirebaseIdToken,
|
|
||||||
fb.sendNotification
|
fb.sendNotification
|
||||||
);
|
);
|
||||||
|
app.post("/notifications/subscribe", fb.validateFirebaseIdToken, fb.subscribe);
|
||||||
app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser);
|
app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser);
|
||||||
|
|
||||||
//Stripe Processing
|
//Stripe Processing
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const CalculateAllocations =
|
|||||||
require("../../cdk/cdk-calculate-allocations").default;
|
require("../../cdk/cdk-calculate-allocations").default;
|
||||||
const CdkBase = require("../../web-sockets/web-socket");
|
const CdkBase = require("../../web-sockets/web-socket");
|
||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
|
const Dinero = require("dinero.js");
|
||||||
const axios = AxiosLib.create();
|
const axios = AxiosLib.create();
|
||||||
axios.interceptors.request.use((x) => {
|
axios.interceptors.request.use((x) => {
|
||||||
const socket = x.socket;
|
const socket = x.socket;
|
||||||
@@ -116,8 +116,9 @@ exports.PbsSelectedCustomer = async function PbsSelectedCustomer(
|
|||||||
);
|
);
|
||||||
await UpsertVehicleData(socket, ownerRef.ReferenceId);
|
await UpsertVehicleData(socket, ownerRef.ReferenceId);
|
||||||
CdkBase.createLogEvent(socket, "DEBUG", `Inserting account data.`);
|
CdkBase.createLogEvent(socket, "DEBUG", `Inserting account data.`);
|
||||||
return;
|
|
||||||
await InsertAccountPostingData(socket);
|
await InsertAccountPostingData(socket);
|
||||||
|
|
||||||
CdkBase.createLogEvent(socket, "DEBUG", `Marking job as exported.`);
|
CdkBase.createLogEvent(socket, "DEBUG", `Marking job as exported.`);
|
||||||
await MarkJobExported(socket, socket.JobData.id);
|
await MarkJobExported(socket, socket.JobData.id);
|
||||||
|
|
||||||
@@ -535,7 +536,7 @@ async function InsertAccountPostingData(socket) {
|
|||||||
const item = {
|
const item = {
|
||||||
Account: alloc.profitCenter.dms_acctnumber,
|
Account: alloc.profitCenter.dms_acctnumber,
|
||||||
ControlNumber: socket.JobData.ro_number,
|
ControlNumber: socket.JobData.ro_number,
|
||||||
Amount: alloc.sale.multiply(-1).toFormat("0.0"),
|
Amount: alloc.sale.multiply(-1).toFormat("0.00"),
|
||||||
//Comment: "String",
|
//Comment: "String",
|
||||||
//AdditionalInfo: "String",
|
//AdditionalInfo: "String",
|
||||||
InvoiceNumber: socket.JobData.ro_number,
|
InvoiceNumber: socket.JobData.ro_number,
|
||||||
@@ -549,7 +550,7 @@ async function InsertAccountPostingData(socket) {
|
|||||||
const item = {
|
const item = {
|
||||||
Account: alloc.costCenter.dms_acctnumber,
|
Account: alloc.costCenter.dms_acctnumber,
|
||||||
ControlNumber: socket.JobData.ro_number,
|
ControlNumber: socket.JobData.ro_number,
|
||||||
Amount: alloc.cost.toFormat("0.0"),
|
Amount: alloc.cost.toFormat("0.00"),
|
||||||
//Comment: "String",
|
//Comment: "String",
|
||||||
//AdditionalInfo: "String",
|
//AdditionalInfo: "String",
|
||||||
InvoiceNumber: socket.JobData.ro_number,
|
InvoiceNumber: socket.JobData.ro_number,
|
||||||
@@ -560,7 +561,7 @@ async function InsertAccountPostingData(socket) {
|
|||||||
const itemWip = {
|
const itemWip = {
|
||||||
Account: alloc.costCenter.dms_wip_acctnumber,
|
Account: alloc.costCenter.dms_wip_acctnumber,
|
||||||
ControlNumber: socket.JobData.ro_number,
|
ControlNumber: socket.JobData.ro_number,
|
||||||
Amount: alloc.cost.multiply(-1).toFormat("0.0"),
|
Amount: alloc.cost.multiply(-1).toFormat("0.00"),
|
||||||
//Comment: "String",
|
//Comment: "String",
|
||||||
//AdditionalInfo: "String",
|
//AdditionalInfo: "String",
|
||||||
InvoiceNumber: socket.JobData.ro_number,
|
InvoiceNumber: socket.JobData.ro_number,
|
||||||
@@ -575,7 +576,7 @@ async function InsertAccountPostingData(socket) {
|
|||||||
const item2 = {
|
const item2 = {
|
||||||
Account: alloc.profitCenter.dms_acctnumber,
|
Account: alloc.profitCenter.dms_acctnumber,
|
||||||
ControlNumber: socket.JobData.ro_number,
|
ControlNumber: socket.JobData.ro_number,
|
||||||
Amount: alloc.sale.multiply(-1).toFormat("0.0"),
|
Amount: alloc.sale.multiply(-1).toFormat("0.00"),
|
||||||
//Comment: "String",
|
//Comment: "String",
|
||||||
//AdditionalInfo: "String",
|
//AdditionalInfo: "String",
|
||||||
InvoiceNumber: socket.JobData.ro_number,
|
InvoiceNumber: socket.JobData.ro_number,
|
||||||
@@ -585,6 +586,21 @@ async function InsertAccountPostingData(socket) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
socket.txEnvelope.payers.forEach((payer) => {
|
||||||
|
const item = {
|
||||||
|
Account: payer.dms_acctnumber,
|
||||||
|
ControlNumber: payer.controlnumber,
|
||||||
|
Amount: Dinero({ amount: Math.round(payer.amount * 100) }).toFormat(
|
||||||
|
"0.0"
|
||||||
|
),
|
||||||
|
//Comment: "String",
|
||||||
|
//AdditionalInfo: "String",
|
||||||
|
InvoiceNumber: socket.JobData.ro_number,
|
||||||
|
InvoiceDate: moment(socket.JobData.date_invoiced).toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
wips.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
const { data: AccountPostingChange } = await axios.post(
|
const { data: AccountPostingChange } = await axios.post(
|
||||||
PBS_ENDPOINTS.AccountingPostingChange,
|
PBS_ENDPOINTS.AccountingPostingChange,
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ exports.default = async (req, res) => {
|
|||||||
{
|
{
|
||||||
version: "1.0",
|
version: "1.0",
|
||||||
encoding: "UTF-8",
|
encoding: "UTF-8",
|
||||||
keepNullNodes: true,
|
//keepNullNodes: true,
|
||||||
},
|
},
|
||||||
autoHouseObject
|
autoHouseObject
|
||||||
)
|
)
|
||||||
@@ -110,7 +110,8 @@ exports.default = async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
res.json(allxmlsToUpload);
|
||||||
|
return;
|
||||||
if (process.env.NODE_ENV !== "production") {
|
if (process.env.NODE_ENV !== "production") {
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
return;
|
return;
|
||||||
@@ -184,31 +185,31 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
}${job.est_ct_fn ? job.est_ct_fn : ""}`,
|
}${job.est_ct_fn ? job.est_ct_fn : ""}`,
|
||||||
},
|
},
|
||||||
CustomerInformation: {
|
CustomerInformation: {
|
||||||
FirstName: job.ownr_fn,
|
FirstName: job.ownr_fn || "",
|
||||||
LastName: job.ownr_ln,
|
LastName: job.ownr_ln || "",
|
||||||
Street: job.ownr_addr1,
|
Street: job.ownr_addr1 || "",
|
||||||
City: job.ownr_city,
|
City: job.ownr_city || "",
|
||||||
State: job.ownr_st,
|
State: job.ownr_st || "",
|
||||||
Zip: job.ownr_zip,
|
Zip: job.ownr_zip || "",
|
||||||
Phone1: job.ownr_ph1,
|
Phone1: job.ownr_ph1 || "",
|
||||||
Phone2: null,
|
Phone2: null,
|
||||||
Phone2Extension: null,
|
Phone2Extension: null,
|
||||||
Phone3: null,
|
Phone3: null,
|
||||||
Phone3Extension: null,
|
Phone3Extension: null,
|
||||||
FileComments: null,
|
FileComments: null,
|
||||||
Source: null,
|
Source: null,
|
||||||
Email: job.ownr_ea,
|
Email: job.ownr_ea || "",
|
||||||
RetWhsl: null,
|
RetWhsl: null,
|
||||||
Cat: null,
|
Cat: null,
|
||||||
InsuredorClaimantFlag: null,
|
InsuredorClaimantFlag: null,
|
||||||
},
|
},
|
||||||
VehicleInformation: {
|
VehicleInformation: {
|
||||||
Year: job.v_model_yr,
|
Year: job.v_model_yr || "",
|
||||||
Make: job.v_make_desc,
|
Make: job.v_make_desc || "",
|
||||||
Model: job.v_model_desc,
|
Model: job.v_model_desc || "",
|
||||||
VIN: job.v_vin,
|
VIN: job.v_vin || "",
|
||||||
License: job.plate_no,
|
License: job.plate_no,
|
||||||
MileageIn: job.kmin,
|
MileageIn: job.kmin || 0,
|
||||||
Vehiclecolor: job.v_color,
|
Vehiclecolor: job.v_color,
|
||||||
VehicleProductionDate: null,
|
VehicleProductionDate: null,
|
||||||
VehiclePaintCode: null,
|
VehiclePaintCode: null,
|
||||||
@@ -218,18 +219,18 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
InsuranceInformation: {
|
InsuranceInformation: {
|
||||||
InsuranceCo: job.ins_co_nm,
|
InsuranceCo: job.ins_co_nm || "",
|
||||||
CompanyName: job.ins_co_nm,
|
CompanyName: job.ins_co_nm || "",
|
||||||
Address: job.ins_addr1,
|
Address: job.ins_addr1 || "",
|
||||||
City: job.ins_addr1,
|
City: job.ins_addr1 || "",
|
||||||
State: job.ins_city,
|
State: job.ins_city || "",
|
||||||
Zip: job.ins_zip,
|
Zip: job.ins_zip || "",
|
||||||
Phone: job.ins_ph1,
|
Phone: job.ins_ph1 || "",
|
||||||
Fax: null,
|
Fax: job.ins_fax || "",
|
||||||
ClaimType: null,
|
ClaimType: null,
|
||||||
LossType: null,
|
LossType: job.loss_type || "",
|
||||||
Policy: null,
|
Policy: job.policy_no || "",
|
||||||
Claim: job.clm_no,
|
Claim: job.clm_no || "",
|
||||||
InsuredLastName: null,
|
InsuredLastName: null,
|
||||||
InsuredFirstName: null,
|
InsuredFirstName: null,
|
||||||
ClaimantLastName: null,
|
ClaimantLastName: null,
|
||||||
@@ -242,23 +243,28 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
OutsideAdjuster: null,
|
OutsideAdjuster: null,
|
||||||
},
|
},
|
||||||
Dates: {
|
Dates: {
|
||||||
DateofLoss: job.loss_date && moment(job.loss_date).format(AhDateFormat),
|
DateofLoss:
|
||||||
|
(job.loss_date && moment(job.loss_date).format(AhDateFormat)) || "",
|
||||||
InitialCustomerContactDate: null,
|
InitialCustomerContactDate: null,
|
||||||
FirstFollowUpDate: null,
|
FirstFollowUpDate: null,
|
||||||
ReferralDate: null,
|
ReferralDate: null,
|
||||||
EstimateAppointmentDate: null,
|
EstimateAppointmentDate: null,
|
||||||
SecondFollowUpDate: null,
|
SecondFollowUpDate: null,
|
||||||
AssignedDate:
|
AssignedDate:
|
||||||
job.asgn_date && moment(job.asgn_date).format(AhDateFormat),
|
(job.asgn_date && moment(job.asgn_date).format(AhDateFormat)) || "",
|
||||||
EstComplete: null,
|
EstComplete: null,
|
||||||
CustomerAuthorizationDate: null,
|
CustomerAuthorizationDate: null,
|
||||||
InsuranceAuthorizationDate: null,
|
InsuranceAuthorizationDate: null,
|
||||||
DateOpened: job.date_open && moment(job.date_open).format(AhDateFormat),
|
DateOpened:
|
||||||
|
(job.date_open && moment(job.date_open).format(AhDateFormat)) || "",
|
||||||
ScheduledArrivalDate:
|
ScheduledArrivalDate:
|
||||||
job.scheduled_in && moment(job.scheduled_in).format(AhDateFormat),
|
(job.scheduled_in && moment(job.scheduled_in).format(AhDateFormat)) ||
|
||||||
CarinShop: job.actual_in && moment(job.actual_in).format(AhDateFormat),
|
"",
|
||||||
|
CarinShop:
|
||||||
|
(job.actual_in && moment(job.actual_in).format(AhDateFormat)) || "",
|
||||||
InsInspDate: null,
|
InsInspDate: null,
|
||||||
StartDate: job.actual_in && moment(job.actual_in).format(AhDateFormat),
|
StartDate:
|
||||||
|
(job.actual_in && moment(job.actual_in).format(AhDateFormat)) || "",
|
||||||
PartsOrder: null,
|
PartsOrder: null,
|
||||||
TeardownHold: null,
|
TeardownHold: null,
|
||||||
SupplementSubmittedDate: null,
|
SupplementSubmittedDate: null,
|
||||||
@@ -268,22 +274,28 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
AssntoPaint: null,
|
AssntoPaint: null,
|
||||||
AssntoDetail: null,
|
AssntoDetail: null,
|
||||||
PromiseDate:
|
PromiseDate:
|
||||||
job.scheduled_completion &&
|
(job.scheduled_completion &&
|
||||||
moment(job.scheduled_completion).format(AhDateFormat),
|
moment(job.scheduled_completion).format(AhDateFormat)) ||
|
||||||
InsuranceTargetOut: null,
|
"",
|
||||||
|
//InsuranceTargetOut: null,
|
||||||
CarComplete:
|
CarComplete:
|
||||||
job.actual_completion &&
|
(job.actual_completion &&
|
||||||
moment(job.actual_completion).format(AhDateFormat),
|
moment(job.actual_completion).format(AhDateFormat)) ||
|
||||||
|
"",
|
||||||
DeliveryAppointmentDate:
|
DeliveryAppointmentDate:
|
||||||
job.scheduled_delivery &&
|
(job.scheduled_delivery &&
|
||||||
moment(job.scheduled_delivery).format(AhDateFormat),
|
moment(job.scheduled_delivery).format(AhDateFormat)) ||
|
||||||
|
"",
|
||||||
DateClosed:
|
DateClosed:
|
||||||
job.date_invoiced && moment(job.date_invoiced).format(AhDateFormat),
|
(job.date_invoiced &&
|
||||||
|
moment(job.date_invoiced).format(AhDateFormat)) ||
|
||||||
|
"",
|
||||||
CustomerPaidInFullDate: null,
|
CustomerPaidInFullDate: null,
|
||||||
InsurancePaidInFullDate: null,
|
InsurancePaidInFullDate: null,
|
||||||
CustPickup:
|
CustPickup:
|
||||||
job.actual_delivery &&
|
(job.actual_delivery &&
|
||||||
moment(job.actual_delivery).format(AhDateFormat),
|
moment(job.actual_delivery).format(AhDateFormat)) ||
|
||||||
|
"",
|
||||||
AccountPostedDate:
|
AccountPostedDate:
|
||||||
job.date_exported && moment(job.date_exported).format(AhDateFormat),
|
job.date_exported && moment(job.date_exported).format(AhDateFormat),
|
||||||
CSIProcessedDate: null,
|
CSIProcessedDate: null,
|
||||||
@@ -291,85 +303,86 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
AdditionalFollowUpDate: null,
|
AdditionalFollowUpDate: null,
|
||||||
},
|
},
|
||||||
Rates: {
|
Rates: {
|
||||||
BodyRate: job.rate_lab,
|
BodyRate: job.rate_lab || 0,
|
||||||
RefinishRate: job.rate_lar,
|
RefinishRate: job.rate_lar || 0,
|
||||||
MechanicalRate: job.rate_lam,
|
MechanicalRate: job.rate_lam || 0,
|
||||||
StructuralRate: job.rate_las,
|
StructuralRate: job.rate_las || 0,
|
||||||
PMRate: job.rate_mapa,
|
PMRate: job.rate_mapa || 0,
|
||||||
BMRate: job.rate_mash,
|
BMRate: job.rate_mash || 0,
|
||||||
TaxRate:
|
TaxRate:
|
||||||
job.parts_tax_rates &&
|
(job.parts_tax_rates &&
|
||||||
job.parts_tax_rates.PAN &&
|
job.parts_tax_rates.PAN &&
|
||||||
job.parts_tax_rates.PAN.prt_tax_rt,
|
job.parts_tax_rates.PAN.prt_tax_rt) ||
|
||||||
StorageRateperDay: null,
|
0,
|
||||||
DaysStored: null,
|
StorageRateperDay: 0,
|
||||||
},
|
DaysStored: 0,
|
||||||
EstimateTotals: {
|
|
||||||
BodyHours: null,
|
|
||||||
RefinishHours: null,
|
|
||||||
MechanicalHours: null,
|
|
||||||
StructuralHours: null,
|
|
||||||
PartsTotal: null,
|
|
||||||
PartsOEM: null,
|
|
||||||
PartsAM: null,
|
|
||||||
PartsReconditioned: null,
|
|
||||||
PartsRecycled: null,
|
|
||||||
PartsOther: null,
|
|
||||||
SubletTotal: null,
|
|
||||||
BodyLaborTotal: null,
|
|
||||||
RefinishLaborTotal: null,
|
|
||||||
MechanicalLaborTotal: null,
|
|
||||||
StructuralLaborTotal: null,
|
|
||||||
MiscellaneousChargeTotal: null,
|
|
||||||
PMTotal: null,
|
|
||||||
BMTotal: null,
|
|
||||||
MiscTotal: null,
|
|
||||||
TowingTotal: null,
|
|
||||||
StorageTotal: null,
|
|
||||||
DetailTotal: null,
|
|
||||||
SalesTaxTotal: null,
|
|
||||||
GrossTotal: null,
|
|
||||||
DeductibleTotal: null,
|
|
||||||
DepreciationTotal: null,
|
|
||||||
Discount: null,
|
|
||||||
CustomerPay: null,
|
|
||||||
InsurancePay: null,
|
|
||||||
Deposit: null,
|
|
||||||
AmountDue: null,
|
|
||||||
},
|
|
||||||
SupplementTotals: {
|
|
||||||
BodyHours: null,
|
|
||||||
RefinishHours: null,
|
|
||||||
MechanicalHours: null,
|
|
||||||
StructuralHours: null,
|
|
||||||
PartsTotal: null,
|
|
||||||
PartsOEM: null,
|
|
||||||
PartsAM: null,
|
|
||||||
PartsReconditioned: null,
|
|
||||||
PartsRecycled: null,
|
|
||||||
PartsOther: null,
|
|
||||||
SubletTotal: null,
|
|
||||||
BodyLaborTotal: null,
|
|
||||||
RefinishLaborTotal: null,
|
|
||||||
MechanicalLaborTotal: null,
|
|
||||||
StructuralLaborTotal: null,
|
|
||||||
MiscellaneousChargeTotal: null,
|
|
||||||
PMTotal: null,
|
|
||||||
BMTotal: null,
|
|
||||||
MiscTotal: null,
|
|
||||||
TowingTotal: null,
|
|
||||||
StorageTotal: null,
|
|
||||||
DetailTotal: null,
|
|
||||||
SalesTaxTotal: null,
|
|
||||||
GrossTotal: null,
|
|
||||||
DeductibleTotal: null,
|
|
||||||
DepreciationTotal: null,
|
|
||||||
Discount: null,
|
|
||||||
CustomerPay: null,
|
|
||||||
InsurancePay: null,
|
|
||||||
Deposit: null,
|
|
||||||
AmountDue: null,
|
|
||||||
},
|
},
|
||||||
|
// EstimateTotals: {
|
||||||
|
// BodyHours: null,
|
||||||
|
// RefinishHours: null,
|
||||||
|
// MechanicalHours: null,
|
||||||
|
// StructuralHours: null,
|
||||||
|
// PartsTotal: null,
|
||||||
|
// PartsOEM: null,
|
||||||
|
// PartsAM: null,
|
||||||
|
// PartsReconditioned: null,
|
||||||
|
// PartsRecycled: null,
|
||||||
|
// PartsOther: null,
|
||||||
|
// SubletTotal: null,
|
||||||
|
// BodyLaborTotal: null,
|
||||||
|
// RefinishLaborTotal: null,
|
||||||
|
// MechanicalLaborTotal: null,
|
||||||
|
// StructuralLaborTotal: null,
|
||||||
|
// MiscellaneousChargeTotal: null,
|
||||||
|
// PMTotal: null,
|
||||||
|
// BMTotal: null,
|
||||||
|
// MiscTotal: null,
|
||||||
|
// TowingTotal: null,
|
||||||
|
// StorageTotal: null,
|
||||||
|
// DetailTotal: null,
|
||||||
|
// SalesTaxTotal: null,
|
||||||
|
// GrossTotal: null,
|
||||||
|
// DeductibleTotal: null,
|
||||||
|
// DepreciationTotal: null,
|
||||||
|
// Discount: null,
|
||||||
|
// CustomerPay: null,
|
||||||
|
// InsurancePay: null,
|
||||||
|
// Deposit: null,
|
||||||
|
// AmountDue: null,
|
||||||
|
// },
|
||||||
|
// SupplementTotals: {
|
||||||
|
// BodyHours: null,
|
||||||
|
// RefinishHours: null,
|
||||||
|
// MechanicalHours: null,
|
||||||
|
// StructuralHours: null,
|
||||||
|
// PartsTotal: null,
|
||||||
|
// PartsOEM: null,
|
||||||
|
// PartsAM: null,
|
||||||
|
// PartsReconditioned: null,
|
||||||
|
// PartsRecycled: null,
|
||||||
|
// PartsOther: null,
|
||||||
|
// SubletTotal: null,
|
||||||
|
// BodyLaborTotal: null,
|
||||||
|
// RefinishLaborTotal: null,
|
||||||
|
// MechanicalLaborTotal: null,
|
||||||
|
// StructuralLaborTotal: null,
|
||||||
|
// MiscellaneousChargeTotal: null,
|
||||||
|
// PMTotal: null,
|
||||||
|
// BMTotal: null,
|
||||||
|
// MiscTotal: null,
|
||||||
|
// TowingTotal: null,
|
||||||
|
// StorageTotal: null,
|
||||||
|
// DetailTotal: null,
|
||||||
|
// SalesTaxTotal: null,
|
||||||
|
// GrossTotal: null,
|
||||||
|
// DeductibleTotal: null,
|
||||||
|
// DepreciationTotal: null,
|
||||||
|
// Discount: null,
|
||||||
|
// CustomerPay: null,
|
||||||
|
// InsurancePay: null,
|
||||||
|
// Deposit: null,
|
||||||
|
// AmountDue: null,
|
||||||
|
// },
|
||||||
RevisedTotals: {
|
RevisedTotals: {
|
||||||
BodyHours: job.job_totals.rates.lab.hours,
|
BodyHours: job.job_totals.rates.lab.hours,
|
||||||
BodyRepairHours: job.joblines
|
BodyRepairHours: job.joblines
|
||||||
@@ -441,8 +454,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
),
|
),
|
||||||
StructuralLaborTotalCost:
|
StructuralLaborTotalCost:
|
||||||
repairCosts.StructuralLaborTotalCost.toFormat(AHDineroFormat),
|
repairCosts.StructuralLaborTotalCost.toFormat(AHDineroFormat),
|
||||||
MiscellaneousChargeTotal: null,
|
MiscellaneousChargeTotal: 0,
|
||||||
MiscellaneousChargeTotalCost: null,
|
MiscellaneousChargeTotalCost: 0,
|
||||||
PMTotal: Dinero(job.job_totals.rates.mapa.total).toFormat(
|
PMTotal: Dinero(job.job_totals.rates.mapa.total).toFormat(
|
||||||
AHDineroFormat
|
AHDineroFormat
|
||||||
),
|
),
|
||||||
@@ -461,17 +474,17 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
AHDineroFormat
|
AHDineroFormat
|
||||||
),
|
),
|
||||||
StorageTotalCost: repairCosts.StorageTotalCost.toFormat(AHDineroFormat),
|
StorageTotalCost: repairCosts.StorageTotalCost.toFormat(AHDineroFormat),
|
||||||
DetailTotal: null,
|
DetailTotal: 0,
|
||||||
DetailTotalCost: null,
|
DetailTotalCost: 0,
|
||||||
SalesTaxTotal: Dinero(job.job_totals.totals.local_tax)
|
SalesTaxTotal: Dinero(job.job_totals.totals.local_tax)
|
||||||
.add(Dinero(job.job_totals.totals.state_tax))
|
.add(Dinero(job.job_totals.totals.state_tax))
|
||||||
.add(Dinero(job.job_totals.totals.federal_tax))
|
.add(Dinero(job.job_totals.totals.federal_tax))
|
||||||
.toFormat(AHDineroFormat),
|
.toFormat(AHDineroFormat),
|
||||||
SalesTaxTotalCost: null,
|
SalesTaxTotalCost: 0,
|
||||||
GrossTotal: Dinero(job.job_totals.totals.net_repairs).toFormat(
|
GrossTotal: Dinero(job.job_totals.totals.net_repairs).toFormat(
|
||||||
AHDineroFormat
|
AHDineroFormat
|
||||||
),
|
),
|
||||||
DeductibleTotal: job.ded_amt,
|
DeductibleTotal: job.ded_amt || 0,
|
||||||
DepreciationTotal: Dinero(
|
DepreciationTotal: Dinero(
|
||||||
job.job_totals.totals.custPayable.dep_taxes
|
job.job_totals.totals.custPayable.dep_taxes
|
||||||
).toFormat(AHDineroFormat),
|
).toFormat(AHDineroFormat),
|
||||||
@@ -496,8 +509,10 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
InsScreenCommentsLine2: null,
|
InsScreenCommentsLine2: null,
|
||||||
AssignmentCaller: null,
|
AssignmentCaller: null,
|
||||||
AssignmentDivision: null,
|
AssignmentDivision: null,
|
||||||
LocationofPrimaryImpact: "12",
|
LocationofPrimaryImpact:
|
||||||
LocationofSecondaryImpact: null,
|
(job.area_of_damage && job.area_of_damage.impact1) || 0,
|
||||||
|
LocationofSecondaryImpact:
|
||||||
|
(job.area_of_damage && job.area_of_damage.impact2) || 0,
|
||||||
PaintTechID: null,
|
PaintTechID: null,
|
||||||
PaintTechName: null,
|
PaintTechName: null,
|
||||||
ImportType: null,
|
ImportType: null,
|
||||||
@@ -517,7 +532,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
RentalDueDate: null,
|
RentalDueDate: null,
|
||||||
RentalActRetDate: null,
|
RentalActRetDate: null,
|
||||||
RentalCompanyID: null,
|
RentalCompanyID: null,
|
||||||
CSIID: null,
|
// CSIID: null,
|
||||||
InsGroupCode: null,
|
InsGroupCode: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -669,36 +684,39 @@ const GenerateDetailLines = (line, statuses) => {
|
|||||||
const ret = {
|
const ret = {
|
||||||
BackOrdered: line.status === statuses.default_bo ? "1" : "0",
|
BackOrdered: line.status === statuses.default_bo ? "1" : "0",
|
||||||
Cost:
|
Cost:
|
||||||
line.billlines[0] &&
|
(line.billlines[0] &&
|
||||||
(line.billlines[0].actual_cost * line.billlines[0].quantity).toFixed(2),
|
(line.billlines[0].actual_cost * line.billlines[0].quantity).toFixed(
|
||||||
Critical: null,
|
2
|
||||||
Description: line.line_desc,
|
)) ||
|
||||||
DiscountMarkup: null,
|
0,
|
||||||
|
//Critical: null,
|
||||||
|
Description: line.line_desc || "",
|
||||||
|
DiscountMarkup: line.prt_dsmk_m || "",
|
||||||
InvoiceNumber: line.billlines[0] && line.billlines[0].bill.invoice_number,
|
InvoiceNumber: line.billlines[0] && line.billlines[0].bill.invoice_number,
|
||||||
IOUPart: null,
|
IOUPart: 0,
|
||||||
LineNumber: line.line_no,
|
LineNumber: line.line_no || 0,
|
||||||
MarkUp: null,
|
MarkUp: null,
|
||||||
OrderedOn: null,
|
OrderedOn: null,
|
||||||
OriginalCost: null,
|
OriginalCost: null,
|
||||||
OriginalInvoiceNumber: null,
|
OriginalInvoiceNumber: null,
|
||||||
PriceEach: line.billlines[0] && line.billlines[0].actual_cost,
|
PriceEach: (line.billlines[0] && line.billlines[0].actual_cost) || 0,
|
||||||
PartNumber: _.escape(line.oem_partno),
|
PartNumber: _.escape(line.oem_partno),
|
||||||
ProfitPercent: null,
|
ProfitPercent: null,
|
||||||
PurchaseOrderNumber: null,
|
PurchaseOrderNumber: null,
|
||||||
Qty: line.part_qty,
|
Qty: line.part_qty || 0,
|
||||||
Status: line.status,
|
Status: line.status || "",
|
||||||
SupplementNumber: null,
|
SupplementNumber: line.line_ind || "",
|
||||||
Type: line.part_type,
|
Type: line.part_type || "",
|
||||||
Vendor: line.billlines[0] && line.billlines[0].bill.vendor.name,
|
Vendor: (line.billlines[0] && line.billlines[0].bill.vendor.name) || "",
|
||||||
VendorPaid: null,
|
VendorPaid: null,
|
||||||
VendorPrice: line.billlines[0] && line.billlines[0].actual_price,
|
VendorPrice: (line.billlines[0] && line.billlines[0].actual_price) || 0,
|
||||||
Deleted: null,
|
Deleted: null,
|
||||||
ExpectedOn: null,
|
ExpectedOn: null,
|
||||||
ReceivedOn: null,
|
ReceivedOn: null,
|
||||||
OrderedBy: null,
|
OrderedBy: null,
|
||||||
ShipVia: null,
|
ShipVia: null,
|
||||||
VendorContact: null,
|
VendorContact: null,
|
||||||
EstimateAmount: line.act_price,
|
EstimateAmount: line.act_price || 0,
|
||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
@@ -709,9 +727,9 @@ const generateNullDetailLine = () => {
|
|||||||
Cost: 0,
|
Cost: 0,
|
||||||
Critical: null,
|
Critical: null,
|
||||||
Description: "No Lines on Estimate",
|
Description: "No Lines on Estimate",
|
||||||
DiscountMarkup: null,
|
DiscountMarkup: 0,
|
||||||
InvoiceNumber: null,
|
InvoiceNumber: null,
|
||||||
IOUPart: null,
|
IOUPart: 0,
|
||||||
LineNumber: 0,
|
LineNumber: 0,
|
||||||
MarkUp: null,
|
MarkUp: null,
|
||||||
OrderedOn: null,
|
OrderedOn: null,
|
||||||
@@ -722,12 +740,12 @@ const generateNullDetailLine = () => {
|
|||||||
ProfitPercent: null,
|
ProfitPercent: null,
|
||||||
PurchaseOrderNumber: null,
|
PurchaseOrderNumber: null,
|
||||||
Qty: 0,
|
Qty: 0,
|
||||||
Status: null,
|
Status: "",
|
||||||
SupplementNumber: null,
|
SupplementNumber: 0,
|
||||||
Type: null,
|
Type: "",
|
||||||
Vendor: null,
|
Vendor: "",
|
||||||
VendorPaid: null,
|
VendorPaid: null,
|
||||||
VendorPrice: null,
|
VendorPrice: 0,
|
||||||
Deleted: null,
|
Deleted: null,
|
||||||
ExpectedOn: null,
|
ExpectedOn: null,
|
||||||
ReceivedOn: null,
|
ReceivedOn: null,
|
||||||
|
|||||||
@@ -74,31 +74,48 @@ exports.updateUser = (req, res) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.sendNotification = (req, res) => {
|
exports.sendNotification = async (req, res) => {
|
||||||
var registrationToken =
|
setTimeout(() => {
|
||||||
"fqIWg8ENDFyrRrMWJ1sItR:APA91bHirdZ05Zo66flMlvala97SMXoiQGwP4oCvMwd-vVrSauD_WoNim3kXHGqyP-bzENjkXwA5icyUAReFbeHn6dIaPcbpcsXuY73-eJAXvZiu1gIsrd1BOsnj3dEMT7Q4F6mTPth1";
|
// Send a message to the device corresponding to the provided
|
||||||
var message = {
|
// registration token.
|
||||||
notification: { title: "The Title", body: "The Body" },
|
admin
|
||||||
data: {
|
.messaging()
|
||||||
jobid: "1234",
|
.send({
|
||||||
},
|
topic: "PRD_PATRICK-messaging",
|
||||||
token: registrationToken,
|
notification: {
|
||||||
};
|
title: `ImEX Online Message - +16049992002`,
|
||||||
|
body: "Test Noti.",
|
||||||
|
//imageUrl: "https://thinkimex.com/img/io-fcm.png",
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: "messaging-inbound",
|
||||||
|
conversationid: "e0eb17c3-3a78-4e3f-b932-55ef35aa2297",
|
||||||
|
text: "Hello. ",
|
||||||
|
image_path: "",
|
||||||
|
phone_num: "+16049992002",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
// Response is a message ID string.
|
||||||
|
console.log("Successfully sent message:", response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log("Error sending message:", error);
|
||||||
|
});
|
||||||
|
|
||||||
// Send a message to the device corresponding to the provided
|
res.sendStatus(200);
|
||||||
// registration token.
|
}, 500);
|
||||||
admin
|
};
|
||||||
|
|
||||||
|
exports.subscribe = async (req, res) => {
|
||||||
|
const result = await admin
|
||||||
.messaging()
|
.messaging()
|
||||||
.send(message)
|
.subscribeToTopic(
|
||||||
.then((response) => {
|
req.body.fcm_tokens,
|
||||||
// Response is a message ID string.
|
`${req.body.imexshopid}-${req.body.type}`
|
||||||
console.log("Successfully sent message:", response);
|
);
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log("Error sending message:", error);
|
|
||||||
});
|
|
||||||
|
|
||||||
res.sendStatus(200);
|
res.json(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.validateFirebaseIdToken = async (req, res, next) => {
|
exports.validateFirebaseIdToken = async (req, res, next) => {
|
||||||
|
|||||||
@@ -22,48 +22,74 @@ mutation UNARCHIVE_CONVERSATION($id: uuid!) {
|
|||||||
|
|
||||||
exports.RECEIVE_MESSAGE = `
|
exports.RECEIVE_MESSAGE = `
|
||||||
mutation RECEIVE_MESSAGE($msg: [messages_insert_input!]!) {
|
mutation RECEIVE_MESSAGE($msg: [messages_insert_input!]!) {
|
||||||
|
insert_messages(objects: $msg) {
|
||||||
insert_messages(objects: $msg) {
|
|
||||||
returning {
|
returning {
|
||||||
conversation {
|
conversation {
|
||||||
id
|
id
|
||||||
archived
|
archived
|
||||||
bodyshop {
|
bodyshop{
|
||||||
associations(where: {active: {_eq: true}}) {
|
imexshopid
|
||||||
user {
|
|
||||||
fcmtokens
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
|
unreadcnt
|
||||||
|
phone_num
|
||||||
}
|
}
|
||||||
|
conversationid
|
||||||
|
created_at
|
||||||
|
id
|
||||||
|
image_path
|
||||||
|
image
|
||||||
|
isoutbound
|
||||||
|
msid
|
||||||
|
read
|
||||||
|
text
|
||||||
|
updated_at
|
||||||
|
status
|
||||||
|
userid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports.INSERT_MESSAGE = `
|
exports.INSERT_MESSAGE = `
|
||||||
mutation INSERT_MESSAGE($msg: [messages_insert_input!]!, $conversationid: uuid!) {
|
mutation INSERT_MESSAGE($msg: [messages_insert_input!]!, $conversationid: uuid!) {
|
||||||
update_conversations_by_pk(pk_columns: {id: $conversationid}, _set: {archived: false}) {
|
update_conversations_by_pk(pk_columns: {id: $conversationid}, _set: {archived: false}) {
|
||||||
id
|
id
|
||||||
|
archived
|
||||||
}
|
}
|
||||||
insert_messages(objects: $msg) {
|
insert_messages(objects: $msg) {
|
||||||
returning {
|
returning {
|
||||||
conversation {
|
conversation {
|
||||||
id
|
id
|
||||||
archived
|
archived
|
||||||
bodyshop {
|
bodyshop{
|
||||||
associations(where: {active: {_eq: true}}) {
|
imexshopid
|
||||||
user {
|
|
||||||
fcmtokens
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
|
unreadcnt
|
||||||
|
phone_num
|
||||||
}
|
}
|
||||||
|
conversationid
|
||||||
|
created_at
|
||||||
|
id
|
||||||
|
image_path
|
||||||
|
image
|
||||||
|
isoutbound
|
||||||
|
msid
|
||||||
|
read
|
||||||
|
text
|
||||||
|
updated_at
|
||||||
|
status
|
||||||
|
userid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports.UPDATE_MESSAGE_STATUS = `
|
exports.UPDATE_MESSAGE_STATUS = `
|
||||||
@@ -499,7 +525,7 @@ exports.QUERY_EMPLOYEE_PIN = `query QUERY_EMPLOYEE_PIN($shopId: uuid!, $employee
|
|||||||
}`;
|
}`;
|
||||||
|
|
||||||
exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshopid: uuid!) {
|
exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshopid: uuid!) {
|
||||||
jobs(where: {_and: [{converted :{_eq: true}},{updated_at: {_gt: $start}}, {shopid: {_eq: $bodyshopid}}]}) {
|
jobs(where: {_and: [{converted: {_eq: true}}, {updated_at: {_gt: $start}}, {shopid: {_eq: $bodyshopid}}]}, limit: 50) {
|
||||||
id
|
id
|
||||||
ro_number
|
ro_number
|
||||||
status
|
status
|
||||||
@@ -507,6 +533,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
|
|||||||
est_ct_ln
|
est_ct_ln
|
||||||
ownr_zip
|
ownr_zip
|
||||||
referral_source
|
referral_source
|
||||||
|
loss_type
|
||||||
v_model_yr
|
v_model_yr
|
||||||
v_model_desc
|
v_model_desc
|
||||||
v_make_desc
|
v_make_desc
|
||||||
@@ -581,6 +608,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
|
|||||||
joblines(where: {removed: {_eq: false}}) {
|
joblines(where: {removed: {_eq: false}}) {
|
||||||
id
|
id
|
||||||
line_no
|
line_no
|
||||||
|
line_ind
|
||||||
status
|
status
|
||||||
line_ind
|
line_ind
|
||||||
db_price
|
db_price
|
||||||
@@ -650,6 +678,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports.ENTEGRAL_EXPORT = `
|
exports.ENTEGRAL_EXPORT = `
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ require("dotenv").config({
|
|||||||
const client = require("../graphql-client/graphql-client").client;
|
const client = require("../graphql-client/graphql-client").client;
|
||||||
const queries = require("../graphql-client/queries");
|
const queries = require("../graphql-client/queries");
|
||||||
const { phone } = require("phone");
|
const { phone } = require("phone");
|
||||||
const admin = require("../firebase/firebase-handler").admin;
|
const { admin } = require("../firebase/firebase-handler");
|
||||||
const logger = require("../utils/logger");
|
const logger = require("../utils/logger");
|
||||||
exports.receive = async (req, res) => {
|
exports.receive = async (req, res) => {
|
||||||
//Perform request validation
|
//Perform request validation
|
||||||
@@ -78,21 +78,43 @@ exports.receive = async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
let insertresp;
|
||||||
if (response.bodyshops[0].conversations[0]) {
|
if (response.bodyshops[0].conversations[0]) {
|
||||||
const r3 = await client.request(queries.INSERT_MESSAGE, {
|
insertresp = await client.request(queries.INSERT_MESSAGE, {
|
||||||
msg: newMessage,
|
msg: newMessage,
|
||||||
conversationid:
|
conversationid:
|
||||||
response.bodyshops[0].conversations[0] &&
|
response.bodyshops[0].conversations[0] &&
|
||||||
response.bodyshops[0].conversations[0].id,
|
response.bodyshops[0].conversations[0].id,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const r2 = await client.request(queries.RECEIVE_MESSAGE, {
|
insertresp = await client.request(queries.RECEIVE_MESSAGE, {
|
||||||
msg: newMessage,
|
msg: newMessage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const message = insertresp.insert_messages.returning[0];
|
||||||
|
const data = {
|
||||||
|
type: "messaging-inbound",
|
||||||
|
conversationid: message.conversationid || "",
|
||||||
|
text: message.text || "",
|
||||||
|
image_path: message.image_path || "",
|
||||||
|
image: (message.image && message.image.toString()) || "",
|
||||||
|
messageid: message.id || "",
|
||||||
|
phone_num: message.conversation.phone_num || "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const fcmresp = await admin.messaging().send({
|
||||||
|
topic: `${message.conversation.bodyshop.imexshopid}-messaging`,
|
||||||
|
notification: {
|
||||||
|
title: `ImEX Online Message - ${data.phone_num}`,
|
||||||
|
body: message.image_path ? `Image ${message.text}` : message.text,
|
||||||
|
//imageUrl: "https://thinkimex.com/img/io-fcm.png",
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
logger.log("sms-inbound-success", "DEBUG", "api", null, {
|
logger.log("sms-inbound-success", "DEBUG", "api", null, {
|
||||||
newMessage,
|
newMessage,
|
||||||
|
fcmresp,
|
||||||
});
|
});
|
||||||
res.status(200).send("");
|
res.status(200).send("");
|
||||||
} catch (e2) {
|
} catch (e2) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const client = require("../graphql-client/graphql-client").client;
|
|||||||
const queries = require("../graphql-client/queries");
|
const queries = require("../graphql-client/queries");
|
||||||
const { phone } = require("phone");
|
const { phone } = require("phone");
|
||||||
const logger = require("../utils/logger");
|
const logger = require("../utils/logger");
|
||||||
|
const { admin } = require("../firebase/firebase-handler");
|
||||||
|
|
||||||
exports.status = (req, res) => {
|
exports.status = (req, res) => {
|
||||||
const { SmsSid, SmsStatus } = req.body;
|
const { SmsSid, SmsStatus } = req.body;
|
||||||
@@ -34,6 +35,23 @@ exports.status = (req, res) => {
|
|||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.markConversationRead = async (req, res) => {
|
||||||
|
const { conversationid, imexshopid } = req.body;
|
||||||
|
admin.messaging().send({
|
||||||
|
topic: `${imexshopid}-messaging`,
|
||||||
|
// notification: {
|
||||||
|
// title: `ImEX Online Message - ${data.phone_num}`,
|
||||||
|
// body: message.image_path ? `Image ${message.text}` : message.text,
|
||||||
|
// imageUrl: "https://thinkimex.com/img/logo512.png",
|
||||||
|
// },
|
||||||
|
data: {
|
||||||
|
type: "messaging-mark-conversation-read",
|
||||||
|
conversationid: conversationid || "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
res.send(200);
|
||||||
|
};
|
||||||
|
|
||||||
// Inbound Sample
|
// Inbound Sample
|
||||||
// {
|
// {
|
||||||
// "SmsSid": "SM5205ea340e06437799d9345e7283457c",
|
// "SmsSid": "SM5205ea340e06437799d9345e7283457c",
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ const logger = new graylog2.graylog({
|
|||||||
});
|
});
|
||||||
|
|
||||||
function log(message, type, user, record, object) {
|
function log(message, type, user, record, object) {
|
||||||
console.log(message, {
|
if (type !== "ioevent")
|
||||||
type,
|
console.log(message, {
|
||||||
env: process.env.NODE_ENV || "development",
|
type,
|
||||||
user,
|
env: process.env.NODE_ENV || "development",
|
||||||
record,
|
user,
|
||||||
...object,
|
record,
|
||||||
});
|
...object,
|
||||||
|
});
|
||||||
logger.log(message, {
|
logger.log(message, {
|
||||||
type,
|
type,
|
||||||
env: process.env.NODE_ENV || "development",
|
env: process.env.NODE_ENV || "development",
|
||||||
|
|||||||
Reference in New Issue
Block a user