- Merge and update

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-01-31 11:58:17 -05:00
93 changed files with 52899 additions and 3579 deletions

View File

@@ -1,4 +1,4 @@
GENERATE_SOURCEMAP=false
GENERATE_SOURCEMAP=true
REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql
REACT_APP_GA_CODE=231103507

3
client/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# Sentry Config File
.sentryclirc

View File

@@ -1,7 +1,6 @@
// craco.config.js
const TerserPlugin = require("terser-webpack-plugin");
const CracoLessPlugin = require("craco-less");
const SentryWebpackPlugin = require("@sentry/webpack-plugin");
const {convertLegacyToken} = require('@ant-design/compatible/lib');
const {theme} = require('antd/lib');
@@ -14,21 +13,21 @@ const v4Token = convertLegacyToken(mapToken);
module.exports = {
plugins: [
{
plugin: SentryWebpackPlugin,
options: {
// sentry-cli configuration
authToken:
"6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260",
org: "snapt-software",
project: "imexonline",
release: process.env.REACT_APP_GIT_SHA,
// webpack-specific configuration
include: ".",
ignore: ["node_modules", "webpack.config.js"],
},
},
// {
// plugin: SentryWebpackPlugin,
// options: {
// // sentry-cli configuration
// authToken:
// "6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260",
// org: "snapt-software",
// project: "imexonline",
// release: process.env.REACT_APP_GIT_SHA,
//
// // webpack-specific configuration
// include: ".",
// ignore: ["node_modules", "webpack.config.js"],
// },
// },
{
plugin: CracoLessPlugin,
options: {

592
client/package-lock.json generated
View File

@@ -10,14 +10,15 @@
"dependencies": {
"@ant-design/compatible": "^5.1.2",
"@ant-design/pro-layout": "^7.17.16",
"@apollo/client": "^3.8.10",
"@apollo/client": "^3.9.0",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^7.1.0",
"@fingerprintjs/fingerprintjs": "^4.2.1",
"@fingerprintjs/fingerprintjs": "^4.2.2",
"@jsreport/browser-client": "^3.1.0",
"@reduxjs/toolkit": "^2.1.0",
"@sentry/react": "^7.95.0",
"@sentry/tracing": "^7.95.0",
"@sentry/cli": "^2.27.0",
"@sentry/react": "^7.99.0",
"@sentry/tracing": "^7.99.0",
"@splitsoftware/splitio-react": "^1.11.0",
"@tanem/react-nprogress": "^5.0.51",
"@vitejs/plugin-legacy": "^5.2.0",
@@ -26,25 +27,26 @@
"@vitejs/plugin-react-swc": "^3.5.0",
"antd": "^5.13.2",
"apollo-link-logger": "^2.0.1",
"apollo-link-sentry": "^3.3.0",
"axios": "^1.6.5",
"consola": "^3.2.3",
"dayjs": "^1.11.10",
"dayjs-business-days2": "^1.2.2",
"dinero.js": "^1.9.1",
"dotenv": "^16.4.0",
"dotenv": "^16.4.1",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"esbuild": "^0.19.11",
"exifr": "^7.1.3",
"firebase": "^10.7.2",
"graphql": "^16.6.0",
"i18next": "^23.7.18",
"i18next": "^23.8.1",
"i18next-browser-languagedetector": "^7.0.2",
"jsoneditor": "^10.0.0",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.10.54",
"logrocket": "^7.0.0",
"markerjs2": "^2.31.4",
"markerjs2": "^2.32.0",
"normalize-url": "^8.0.0",
"phone": "^3.1.42",
"preval.macro": "^5.0.0",
@@ -53,9 +55,9 @@
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6",
"react": "^18.2.0",
"react-big-calendar": "^1.8.6",
"react-big-calendar": "^1.8.7",
"react-color": "^2.19.3",
"react-cookie": "^7.0.1",
"react-cookie": "^7.0.2",
"react-dom": "^18.2.0",
"react-drag-listview": "^2.0.0",
"react-grid-gallery": "^1.0.0",
@@ -78,13 +80,13 @@
"redux-saga": "^1.3.0",
"redux-state-sync": "^3.1.4",
"reselect": "^5.1.0",
"sass": "^1.69.7",
"sass": "^1.70.0",
"socket.io-client": "^4.7.4",
"styled-components": "^6.1.8",
"subscriptions-transport-ws": "^0.11.0",
"terser-webpack-plugin": "^5.3.10",
"vite-plugin-svgr": "^4.2.0",
"web-vitals": "^3.5.1",
"web-vitals": "^3.5.2",
"workbox-core": "^7.0.0",
"workbox-expiration": "^7.0.0",
"workbox-navigation-preload": "^7.0.0",
@@ -98,7 +100,7 @@
"@babel/preset-react": "^7.23.3",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/react": "^11.11.3",
"@sentry/webpack-plugin": "^2.10.2",
"@sentry/webpack-plugin": "^2.10.3",
"@swc/core": "^1.3.104",
"@swc/plugin-styled-components": "^1.5.108",
"@testing-library/cypress": "^10.0.1",
@@ -106,7 +108,7 @@
"browserslist-to-esbuild": "^2.1.1",
"craco-less": "^3.0.1",
"cross-env": "^7.0.3",
"cypress": "^13.6.2",
"cypress": "^13.6.4",
"eslint-plugin-cypress": "^2.15.1",
"memfs": "^4.6.0",
"os-browserify": "^0.3.0",
@@ -294,17 +296,19 @@
}
},
"node_modules/@apollo/client": {
"version": "3.8.10",
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.8.10.tgz",
"integrity": "sha512-p/22RZ8ehHyvySnC20EHPPe0gdu8Xp6ZCiXOfdEe1ZORw5cUteD/TLc66tfKv8qu8NLIfbiWoa+6s70XnKvxqg==",
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.9.1.tgz",
"integrity": "sha512-gpnIwMrEZ4n811gy2Ksz51Ax4zjAR5O5Tg1vlKs70Eig5Qj7kGGfnWfH5B37LSCIXanRyuMgVbQx+Aty5WruBA==",
"dependencies": {
"@graphql-typed-document-node/core": "^3.1.1",
"@wry/caches": "^1.0.0",
"@wry/equality": "^0.5.6",
"@wry/trie": "^0.5.0",
"graphql-tag": "^2.12.6",
"hoist-non-react-statics": "^3.3.2",
"optimism": "^0.18.0",
"prop-types": "^15.7.2",
"rehackt": "0.0.3",
"response-iterator": "^0.2.6",
"symbol-observable": "^4.0.0",
"ts-invariant": "^0.10.3",
@@ -312,7 +316,7 @@
"zen-observable-ts": "^1.2.5"
},
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0",
"graphql": "^15.0.0 || ^16.0.0",
"graphql-ws": "^5.5.5",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
@@ -2878,9 +2882,9 @@
}
},
"node_modules/@fingerprintjs/fingerprintjs": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.2.1.tgz",
"integrity": "sha512-uW+GVUNTgCXbVPEbgnbf5Aor22e1dyYR0JRwdUiZBaikfxr7KlhV9y0aahA1FB99fEeQVvhCEvTcPIFSYTy9Pw==",
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.2.2.tgz",
"integrity": "sha512-scD+pDgNZW78LuFAr7ms2yxmDx2NWC4+K5iiOjPT2ZlTlHFbLsORUzLJI2rcKicxxLtHbvf3A7BU1drVr4iHGg==",
"dependencies": {
"tslib": "^2.4.1"
}
@@ -4086,36 +4090,99 @@
]
},
"node_modules/@sentry-internal/feedback": {
"version": "7.95.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.95.0.tgz",
"integrity": "sha512-UWNUUg+OrhV58/ChQNY0yLCCYdEI2cIYVwq1riZ+AFU9Z2ZyCxEoSRVLh7c5tnCPLbKAPR+VvlEsSjMqucAKnA==",
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.99.0.tgz",
"integrity": "sha512-exIO1o+bE0MW4z30FxC0cYzJ4ZHSMlDPMHCBDPzU+MWGQc/fb8s58QUrx5Dnm6HTh9G3H+YlroCxIo9u0GSwGQ==",
"dependencies": {
"@sentry/core": "7.95.0",
"@sentry/types": "7.95.0",
"@sentry/utils": "7.95.0"
"@sentry/core": "7.99.0",
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@sentry-internal/replay-canvas": {
"version": "7.95.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.95.0.tgz",
"integrity": "sha512-nK+VYLnhlxLnnEUPMnzN8BSCFXPy2dQ5OVFO+fiTNPlGCbxsq8O9SnkX6E+u1oPLSA/151s2B3VxIasR8bXJAQ==",
"node_modules/@sentry-internal/feedback/node_modules/@sentry/core": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
"dependencies": {
"@sentry/core": "7.95.0",
"@sentry/replay": "7.95.0",
"@sentry/types": "7.95.0",
"@sentry/utils": "7.95.0"
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry-internal/feedback/node_modules/@sentry/types": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry-internal/feedback/node_modules/@sentry/utils": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
"dependencies": {
"@sentry/types": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry-internal/replay-canvas": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.99.0.tgz",
"integrity": "sha512-PoIkfusToDq0snfl2M6HJx/1KJYtXxYhQplrn11kYadO04SdG0XGXf4h7wBTMEQ7LDEAtQyvsOu4nEQtTO3YjQ==",
"dependencies": {
"@sentry/core": "7.99.0",
"@sentry/replay": "7.99.0",
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/core": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
"dependencies": {
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/types": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/utils": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
"dependencies": {
"@sentry/types": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry-internal/tracing": {
"version": "7.95.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.95.0.tgz",
"integrity": "sha512-YKiLPMnEgTsTh7u/W1Zep9HtV1rJqAetqJ4ekaIxyUUB6ppi6V00MacSjb01o++fwlNNDYFxNpJlgQqNPqsCNA==",
"dev": true,
"dependencies": {
"@sentry/core": "7.95.0",
"@sentry/types": "7.95.0",
@@ -4126,17 +4193,61 @@
}
},
"node_modules/@sentry/browser": {
"version": "7.95.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.95.0.tgz",
"integrity": "sha512-J+bAryc/7StRZSeaJByLTnEslWu7uXaZ1zERQF17sZbRxvbyaYbAO7cqSUKZ1gK3p9QfrkqzNl7xA8thg4FtHQ==",
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.99.0.tgz",
"integrity": "sha512-bgfoUv3wkwwLgN5YUOe0ibB3y268ZCnamZh6nLFqnY/UBKC1+FXWFdvzVON/XKUm62LF8wlpCybOf08ebNj2yg==",
"dependencies": {
"@sentry-internal/feedback": "7.95.0",
"@sentry-internal/replay-canvas": "7.95.0",
"@sentry-internal/tracing": "7.95.0",
"@sentry/core": "7.95.0",
"@sentry/replay": "7.95.0",
"@sentry/types": "7.95.0",
"@sentry/utils": "7.95.0"
"@sentry-internal/feedback": "7.99.0",
"@sentry-internal/replay-canvas": "7.99.0",
"@sentry-internal/tracing": "7.99.0",
"@sentry/core": "7.99.0",
"@sentry/replay": "7.99.0",
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/browser/node_modules/@sentry-internal/tracing": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.99.0.tgz",
"integrity": "sha512-z3JQhHjoM1KdM20qrHwRClKJrNLr2CcKtCluq7xevLtXHJWNAQQbafnWD+Aoj85EWXBzKt9yJMv2ltcXJ+at+w==",
"dependencies": {
"@sentry/core": "7.99.0",
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/browser/node_modules/@sentry/core": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
"dependencies": {
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/browser/node_modules/@sentry/types": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/browser/node_modules/@sentry/utils": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
"dependencies": {
"@sentry/types": "7.99.0"
},
"engines": {
"node": ">=8"
@@ -4162,10 +4273,9 @@
}
},
"node_modules/@sentry/cli": {
"version": "2.26.0",
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.26.0.tgz",
"integrity": "sha512-WRrY9nkjLLUvyo+l8KE0x0Q+0NtCd2U8HYJzh3kyJHyyfKWiSH7ZhExcsb2MoSIjlzbKjjrIJzxhklZABkidDw==",
"dev": true,
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.27.0.tgz",
"integrity": "sha512-pc0opd71W8lGhYvmB1keQtJkarxzCS9f9ErKYv6TfXOOX6drvwkyA6vD/6xEnpzyvqGAuGRU4T4sEeLD3irwUQ==",
"hasInstallScript": true,
"dependencies": {
"https-proxy-agent": "^5.0.0",
@@ -4181,23 +4291,115 @@
"node": ">= 10"
},
"optionalDependencies": {
"@sentry/cli-darwin": "2.26.0",
"@sentry/cli-linux-arm": "2.26.0",
"@sentry/cli-linux-arm64": "2.26.0",
"@sentry/cli-linux-i686": "2.26.0",
"@sentry/cli-linux-x64": "2.26.0",
"@sentry/cli-win32-i686": "2.26.0",
"@sentry/cli-win32-x64": "2.26.0"
"@sentry/cli-darwin": "2.27.0",
"@sentry/cli-linux-arm": "2.27.0",
"@sentry/cli-linux-arm64": "2.27.0",
"@sentry/cli-linux-i686": "2.27.0",
"@sentry/cli-linux-x64": "2.27.0",
"@sentry/cli-win32-i686": "2.27.0",
"@sentry/cli-win32-x64": "2.27.0"
}
},
"node_modules/@sentry/cli-win32-x64": {
"version": "2.26.0",
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.26.0.tgz",
"integrity": "sha512-r3ZaxdHGC6OyJhOxO5ADzAitpGcgT/PkqQzOzKXBOebHj5jzwY27JWjdNhpT6sJZDII13HxqwISRedVWftZgRw==",
"node_modules/@sentry/cli-darwin": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.27.0.tgz",
"integrity": "sha512-/DOZlN5rK19g7YP2OaVNauQhUrRfJ88RDr6qURFiqdxYHDc3isPFGHZJmeZBTwOnDDepyZb4XLaOyfwvAOxHig==",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@sentry/cli-linux-arm": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.27.0.tgz",
"integrity": "sha512-JmMQ9zgFhkZUEN5WIYuJisu4Jif/ThRHDjbsbXBRbUkkgRn88hgUfg299djMvlZZxjpl3K9AEua+1TIUeQd0Sg==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux",
"freebsd"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@sentry/cli-linux-arm64": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.27.0.tgz",
"integrity": "sha512-f+zuB9XGfB8pNamNgSDhqsavuLuzi6saZxbr3uQf30bA5AESI5hspOd1zPcidOORCVZxiPzQe3+T7avBI1XLuw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux",
"freebsd"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@sentry/cli-linux-i686": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.27.0.tgz",
"integrity": "sha512-/4eyz7jnYp20mZqNtpvCEBkxFW0nEjEZRo2BiASQ5/7K8CmoJRe1vhpDA0WOfzi1zTFIfpdE1/RZm2CjHS6DHQ==",
"cpu": [
"x86",
"ia32"
],
"optional": true,
"os": [
"linux",
"freebsd"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@sentry/cli-linux-x64": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.27.0.tgz",
"integrity": "sha512-ptu7wXecnYssihzHlxEOaqbFHWmNEfbepBKGXTdWK2kC+D51+7yHsR9xRdThwVID1bisFgjAveKmBQjmKuXjHQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux",
"freebsd"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@sentry/cli-win32-i686": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.27.0.tgz",
"integrity": "sha512-Db4/xmdE5qV4Aq7Yc8vRw22Y46JJdGMdsMsl5jIf0GVSQPgO23O/2uTiDGpPOdeq91K9EtvpH1zQfDLIfLMaXw==",
"cpu": [
"x86",
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@sentry/cli-win32-x64": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.27.0.tgz",
"integrity": "sha512-q7y/BH4iGfs0TD5PXh2Q8oqnTbOIufoT1NWJcKqvZcOiqCLK3PNUiq7xUeX1PMTrFYAh3Bm6EekOnMavqvbGmg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
@@ -4210,6 +4412,7 @@
"version": "7.95.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.95.0.tgz",
"integrity": "sha512-z+ffO6jK/ZUxnRbBGmnj5sOouKZ4mvRY0KJa33kbyqcmeiJKrN81M7Ecj1IJUCamo/6RqX0GCwDDxgUPZZZBwA==",
"dev": true,
"dependencies": {
"@sentry/types": "7.95.0",
"@sentry/utils": "7.95.0"
@@ -4234,14 +4437,14 @@
}
},
"node_modules/@sentry/react": {
"version": "7.95.0",
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.95.0.tgz",
"integrity": "sha512-L1WVoRJ14uUBhnpVURVO3G7szuA2hk5pzNWMGjNhE7I+ERdfYjgRS5FMnjcsZsnyNtELe/56jLCBirKqwWZJIQ==",
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.99.0.tgz",
"integrity": "sha512-RtHwgzMHJhzJfSQpVG0SDPQYMTGDX3Q37/YWI59S4ALMbSW4/F6n/eQAvGVYZKbh2UCSqgFuRWaXOYkSZT17wA==",
"dependencies": {
"@sentry/browser": "7.95.0",
"@sentry/core": "7.95.0",
"@sentry/types": "7.95.0",
"@sentry/utils": "7.95.0",
"@sentry/browser": "7.99.0",
"@sentry/core": "7.99.0",
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0",
"hoist-non-react-statics": "^3.3.2"
},
"engines": {
@@ -4251,26 +4454,145 @@
"react": "15.x || 16.x || 17.x || 18.x"
}
},
"node_modules/@sentry/replay": {
"version": "7.95.0",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.95.0.tgz",
"integrity": "sha512-WAAEvWCXoNC7hfPI1fNoxODB9UzUYhXtscrWIm89Mn3AAi8haayVNNiUWKVj6+Vpt87C+/sb9UsERwdPWpl/Sw==",
"node_modules/@sentry/react/node_modules/@sentry/core": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
"dependencies": {
"@sentry-internal/tracing": "7.95.0",
"@sentry/core": "7.95.0",
"@sentry/types": "7.95.0",
"@sentry/utils": "7.95.0"
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/react/node_modules/@sentry/types": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/react/node_modules/@sentry/utils": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
"dependencies": {
"@sentry/types": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/replay": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.99.0.tgz",
"integrity": "sha512-gyN/I2WpQrLAZDT+rScB/0jnFL2knEVBo8U8/OVt8gNP20Pq8T/rDZKO/TG0cBfvULDUbJj2P4CJryn2p/O2rA==",
"dependencies": {
"@sentry-internal/tracing": "7.99.0",
"@sentry/core": "7.99.0",
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@sentry/tracing": {
"version": "7.95.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.95.0.tgz",
"integrity": "sha512-0vhTwd2j1Il+stpPH/cwcVuqzMlW9Ixzm5IYMKwjyoauHJIaG9b71NIG7xMmO7gOfcdxh14NbebHgqdyqDrCGw==",
"node_modules/@sentry/replay/node_modules/@sentry-internal/tracing": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.99.0.tgz",
"integrity": "sha512-z3JQhHjoM1KdM20qrHwRClKJrNLr2CcKtCluq7xevLtXHJWNAQQbafnWD+Aoj85EWXBzKt9yJMv2ltcXJ+at+w==",
"dependencies": {
"@sentry-internal/tracing": "7.95.0"
"@sentry/core": "7.99.0",
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/replay/node_modules/@sentry/core": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
"dependencies": {
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/replay/node_modules/@sentry/types": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/replay/node_modules/@sentry/utils": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
"dependencies": {
"@sentry/types": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/tracing": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.99.0.tgz",
"integrity": "sha512-Cf622gSeamiSsi0JEj3PTXnq019OymaCrGf91x1d6OPyJ5jAXdlNuhw7NkqCEw8euIhhULuS81l5nGfBrgjj9Q==",
"dependencies": {
"@sentry-internal/tracing": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/tracing/node_modules/@sentry-internal/tracing": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.99.0.tgz",
"integrity": "sha512-z3JQhHjoM1KdM20qrHwRClKJrNLr2CcKtCluq7xevLtXHJWNAQQbafnWD+Aoj85EWXBzKt9yJMv2ltcXJ+at+w==",
"dependencies": {
"@sentry/core": "7.99.0",
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/tracing/node_modules/@sentry/core": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
"integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
"dependencies": {
"@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/tracing/node_modules/@sentry/types": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
"integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/tracing/node_modules/@sentry/utils": {
"version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.99.0.tgz",
"integrity": "sha512-cYZy5WNTkWs5GgggGnjfGqC44CWir0pAv4GVVSx0fsup4D4pMKBJPrtub15f9uC+QkUf3vVkqwpBqeFxtmJQTQ==",
"dependencies": {
"@sentry/types": "7.99.0"
},
"engines": {
"node": ">=8"
@@ -4280,6 +4602,7 @@
"version": "7.95.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.95.0.tgz",
"integrity": "sha512-ouU7NsEcrwmcnXHMNBGmKZEmKMzmgPGoBydZn1gukCI67Ci71fAYpPNrbtmjai6+jtsY21o45rVLqExru2sdfw==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -4288,6 +4611,7 @@
"version": "7.95.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.95.0.tgz",
"integrity": "sha512-0zget8AOaQWLIEA9cTx/qiQQYpx2x0UfnaW5xRmQg12QGTSngo/cUm9O04zuHw5gpBBGG0ocMDHxwwr+UCCBiw==",
"dev": true,
"dependencies": {
"@sentry/types": "7.95.0"
},
@@ -5123,7 +5447,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"dependencies": {
"debug": "4"
},
@@ -5311,6 +5634,22 @@
"@apollo/client": "^3.0.0"
}
},
"node_modules/apollo-link-sentry": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/apollo-link-sentry/-/apollo-link-sentry-3.3.0.tgz",
"integrity": "sha512-wLffWmo5sRw3rHN1Ck6azM0oxObvtaBBf3AC8cLX4SxhyjmkRIagGDji6CFkyAhxupPz0b9/H1u4Ocx+63lNug==",
"dependencies": {
"deepmerge": "^4.2.2",
"dot-prop": "^6.0.0",
"tslib": "^2.0.3",
"zen-observable-ts": "^1.2.5"
},
"peerDependencies": {
"@apollo/client": "^3.2.3",
"@sentry/browser": "^7.41.0",
"graphql": "15 - 16"
}
},
"node_modules/arch": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
@@ -6669,9 +7008,9 @@
"integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw=="
},
"node_modules/cypress": {
"version": "13.6.3",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.3.tgz",
"integrity": "sha512-d/pZvgwjAyZsoyJ3FOsJT5lDsqnxQ/clMqnNc++rkHjbkkiF2h9s0JsZSyyH4QXhVFW3zPFg82jD25roFLOdZA==",
"version": "13.6.4",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.4.tgz",
"integrity": "sha512-pYJjCfDYB+hoOoZuhysbbYhEmNW7DEDsqn+ToCLwuVowxUXppIWRr7qk4TVRIU471ksfzyZcH+mkoF0CQUKnpw==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
@@ -7003,6 +7342,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/define-data-property": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
@@ -7159,10 +7506,24 @@
"tslib": "^2.0.3"
}
},
"node_modules/dot-prop": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
"integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
"dependencies": {
"is-obj": "^2.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/dotenv": {
"version": "16.4.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.0.tgz",
"integrity": "sha512-WvImr5kpN5NGNn7KaDjJnLTh5rDVLZiDf/YLA8T1ZEZEBZNEDOE+mnkS0PVjPax8ZxBP5zC5SLMB3/9VV5de9g==",
"version": "16.4.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz",
"integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==",
"engines": {
"node": ">=12"
},
@@ -8263,7 +8624,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dev": true,
"dependencies": {
"agent-base": "6",
"debug": "4"
@@ -8291,9 +8651,9 @@
}
},
"node_modules/i18next": {
"version": "23.7.18",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.7.18.tgz",
"integrity": "sha512-b9N2KjRCYQNlUvE1Kc83g8knyUkL5NiZQOp9BsTR/v/LXk6Fzz+doOzTg2/826XK28mCgBkYLNAtixjE58qpCw==",
"version": "23.8.2",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.8.2.tgz",
"integrity": "sha512-Z84zyEangrlERm0ZugVy4bIt485e/H8VecGUZkZWrH7BDePG6jT73QdL9EA1tRTTVVMpry/MgWIP1FjEn0DRXA==",
"funding": [
{
"type": "individual",
@@ -8790,6 +9150,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
"engines": {
"node": ">=8"
}
},
"node_modules/is-path-inside": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
@@ -9636,9 +10004,9 @@
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
"node_modules/markerjs2": {
"version": "2.31.5",
"resolved": "https://registry.npmjs.org/markerjs2/-/markerjs2-2.31.5.tgz",
"integrity": "sha512-fOk0IPfqmwdmKlcaf9qfD8SnBBEzJtgl6NVcDOQpUJAcyyk6/wkA4m410NQ8mD7B42WdjkAzAGj2BMxHsSYthQ=="
"version": "2.32.0",
"resolved": "https://registry.npmjs.org/markerjs2/-/markerjs2-2.32.0.tgz",
"integrity": "sha512-B9bk63dGS5NvP/68yPAK2F44bo3un3fpN96+0ZwCzQPzu1ZdMG7Ns6QYx0iTpx3QsmdwIr3D1lZTwsmiiVUfug=="
},
"node_modules/material-colors": {
"version": "1.2.6",
@@ -11105,7 +11473,6 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
@@ -12033,9 +12400,9 @@
}
},
"node_modules/react-big-calendar": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.8.6.tgz",
"integrity": "sha512-/weukU7kfaowMgC634Vtzc3eNAEpTPxmK9aVEpRQ6gIrTUlL8E+xpgWZ4ouc8HdwRDD0nJD14po5NGbTOFwbtg==",
"version": "1.8.7",
"resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.8.7.tgz",
"integrity": "sha512-qgxQyPIYoWM+TLbGNlEUIEyNE6USAAc7LEAhtUKzJgoP4XsgpnqXJ46Ub298mRzMRM6crRweh3AywDjl5GbHnQ==",
"dependencies": {
"@babel/runtime": "^7.20.7",
"clsx": "^1.2.1",
@@ -12082,9 +12449,9 @@
}
},
"node_modules/react-cookie": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.0.1.tgz",
"integrity": "sha512-SmDjy2TFp+vS6BGOqW7HyaWKyJzVmIH74uP3mxq6kswlwLJEBtIbhkrioozdvQL9r81yprHYFQkSmcO4HiXPdA==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.0.2.tgz",
"integrity": "sha512-UnW1rZw1VibRdTvV8Ksr0BKKZoajeUxYLE89sIygDeyQgtz6ik89RHOM+3kib36G9M7HxheORggPoLk5DxAK7Q==",
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.5",
"hoist-non-react-statics": "^3.3.2",
@@ -12691,6 +13058,23 @@
"jsesc": "bin/jsesc"
}
},
"node_modules/rehackt": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.0.3.tgz",
"integrity": "sha512-aBRHudKhOWwsTvCbSoinzq+Lej/7R8e8UoPvLZo5HirZIIBLGAgdG7SL9QpdcBoQ7+3QYPi3lRLknAzXBlhZ7g==",
"peerDependencies": {
"@types/react": "*",
"react": "*"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"react": {
"optional": true
}
}
},
"node_modules/remark-parse": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
@@ -15169,9 +15553,9 @@
}
},
"node_modules/web-vitals": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.5.1.tgz",
"integrity": "sha512-xQ9lvIpfLxUj0eSmT79ZjRoU5wIRfIr7pNukL7ZE4EcWZSmfZQqOlhuAGfkVa3EFmzPHZhWhXfm2i5ys+THVPg=="
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.5.2.tgz",
"integrity": "sha512-c0rhqNcHXRkY/ogGDJQxZ9Im9D19hDihbzSQJrsioex+KnFgmMzBiy57Z1EjkhX/+OjyBpclDCzz2ITtjokFmg=="
},
"node_modules/webpack-merge": {
"version": "5.10.0",

View File

@@ -4,20 +4,23 @@
"engines": {
"node": "18.18.2"
},
"type": "commonjs",
"private": true,
"proxy": "http://localhost:4000",
"dependencies": {
"@ant-design/compatible": "^5.1.2",
"@ant-design/pro-layout": "^7.17.16",
"@apollo/client": "^3.8.10",
"@apollo/client": "^3.9.0",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^7.1.0",
"@fingerprintjs/fingerprintjs": "^4.2.1",
"@fingerprintjs/fingerprintjs": "^4.2.2",
"@jsreport/browser-client": "^3.1.0",
"@reduxjs/toolkit": "^2.1.0",
"@sentry/react": "^7.95.0",
"@sentry/tracing": "^7.95.0",
"@sentry/cli": "^2.27.0",
"@sentry/react": "^7.99.0",
"@sentry/tracing": "^7.99.0",
"@splitsoftware/splitio-react": "^1.11.0",
"apollo-link-sentry": "^3.3.0",
"@tanem/react-nprogress": "^5.0.51",
"@vitejs/plugin-legacy": "^5.2.0",
"@vitejs/plugin-react": "^4.2.1",
@@ -30,20 +33,20 @@
"dayjs": "^1.11.10",
"dayjs-business-days2": "^1.2.2",
"dinero.js": "^1.9.1",
"dotenv": "^16.4.0",
"dotenv": "^16.4.1",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"esbuild": "^0.19.11",
"exifr": "^7.1.3",
"firebase": "^10.7.2",
"graphql": "^16.6.0",
"i18next": "^23.7.18",
"i18next": "^23.8.1",
"i18next-browser-languagedetector": "^7.0.2",
"jsoneditor": "^10.0.0",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.10.54",
"logrocket": "^7.0.0",
"markerjs2": "^2.31.4",
"markerjs2": "^2.32.0",
"normalize-url": "^8.0.0",
"phone": "^3.1.42",
"preval.macro": "^5.0.0",
@@ -52,9 +55,9 @@
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6",
"react": "^18.2.0",
"react-big-calendar": "^1.8.6",
"react-big-calendar": "^1.8.7",
"react-color": "^2.19.3",
"react-cookie": "^7.0.1",
"react-cookie": "^7.0.2",
"react-dom": "^18.2.0",
"react-drag-listview": "^2.0.0",
"react-grid-gallery": "^1.0.0",
@@ -77,13 +80,13 @@
"redux-saga": "^1.3.0",
"redux-state-sync": "^3.1.4",
"reselect": "^5.1.0",
"sass": "^1.69.7",
"sass": "^1.70.0",
"socket.io-client": "^4.7.4",
"styled-components": "^6.1.8",
"subscriptions-transport-ws": "^0.11.0",
"terser-webpack-plugin": "^5.3.10",
"vite-plugin-svgr": "^4.2.0",
"web-vitals": "^3.5.1",
"web-vitals": "^3.5.2",
"workbox-core": "^7.0.0",
"workbox-expiration": "^7.0.0",
"workbox-navigation-preload": "^7.0.0",
@@ -95,14 +98,15 @@
"scripts": {
"analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "vite",
"build": "cross-env-shell VITE_APP_GIT_SHA=\\\"`git rev-parse --short HEAD`\\\" vite build",
"build": "cross-env-shell VITE_APP_GIT_SHA=\\\"`git rev-parse --short HEAD`\\\" vite build && npm run sentry:sourcemaps",
"build:test": "env-cmd -f .env.test npm run build",
"build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
"buildcra": "cross-env-shell VITE_APP_GIT_SHA=\\\"`git rev-parse --short HEAD`\\\" vite build",
"test": "cypress open",
"eject": "react-scripts eject",
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular .",
"eulaize": "node src/utils/eulaize.js",
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular ."
"sentry:sourcemaps": "sentry-cli sourcemaps inject --org imex --project imexonline ./build && sentry-cli sourcemaps upload --org imex --project imexonline ./build"
},
"eslintConfig": {
"extends": [
@@ -139,7 +143,7 @@
"browserslist-to-esbuild": "^2.1.1",
"craco-less": "^3.0.1",
"cross-env": "^7.0.3",
"cypress": "^13.6.2",
"cypress": "^13.6.4",
"eslint-plugin-cypress": "^2.15.1",
"memfs": "^4.6.0",
"os-browserify": "^0.3.0",

View File

@@ -9,6 +9,7 @@ import {useTranslation} from "react-i18next";
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
import client from "../utils/GraphQLClient";
import App from "./App";
import * as Sentry from "@sentry/react";
import themeProvider from "./themeProvider";
@@ -23,7 +24,7 @@ const config = {
export const factory = SplitSdk(config);
export default function AppContainer() {
function AppContainer() {
const {t} = useTranslation();
return (
@@ -48,3 +49,5 @@ export default function AppContainer() {
</ApolloProvider>
);
}
export default Sentry.withProfiler(AppContainer);

View File

@@ -17,7 +17,7 @@ import TechPageContainer from "../pages/tech/tech.page.container";
import {setOnline} from "../redux/application/application.actions";
import {selectOnline} from "../redux/application/application.selectors";
import {checkUserSession} from "../redux/user/user.actions";
import {selectBodyshop, selectCurrentUser,} from "../redux/user/user.selectors";
import {selectBodyshop, selectCurrentEula, selectCurrentUser,} from "../redux/user/user.selectors";
import PrivateRoute from "../components/PrivateRoute";
import "./App.styles.scss";
import handleBeta from "../utils/betaHandler";
@@ -37,13 +37,14 @@ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
online: selectOnline,
bodyshop: selectBodyshop,
currentEula: selectCurrentEula
});
const mapDispatchToProps = (dispatch) => ({
checkUserSession: () => dispatch(checkUserSession()),
setOnline: (isOnline) => dispatch(setOnline(isOnline)),
});
export function App({bodyshop, checkUserSession, currentUser, online, setOnline}) {
export function App({bodyshop, checkUserSession, currentUser, online, setOnline, currentEula}) {
const client = useSplitClient().client;
const [listenersAdded, setListenersAdded] = useState(false)
const {t} = useTranslation();
@@ -122,7 +123,7 @@ export function App({bodyshop, checkUserSession, currentUser, online, setOnline}
/>
);
if (!currentUser.eulaIsAccepted) {
if (currentEula && !currentUser.eulaIsAccepted) {
return <Eula/>
}

View File

@@ -2,7 +2,7 @@ import { Button, Col, Collapse, Result, Row, Space } from "antd";
import React from "react";
import { withTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import * as Sentry from "@sentry/react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
@@ -138,7 +138,6 @@ class ErrorBoundary extends React.Component {
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(withTranslation()(ErrorBoundary));
export default Sentry.withErrorBoundary(
connect(mapStateToProps, mapDispatchToProps)(withTranslation()(ErrorBoundary))
);

View File

@@ -1,36 +1,42 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import {Button, Card, Checkbox, Col, Form, Input, Modal, notification, Row, Space} from "antd";
import React, {useCallback, useEffect, useRef, useState} from "react";
import {Button, Card, Checkbox, Col, Form, Input, Modal, notification, Row} from "antd";
import Markdown from "react-markdown";
import { createStructuredSelector } from "reselect";
import { selectCurrentEula, selectCurrentUser } from "../../redux/user/user.selectors";
import { connect } from "react-redux";
import { FormDatePicker } from "../form-date-picker/form-date-picker.component";
import { INSERT_EULA_ACCEPTANCE } from "../../graphql/user.queries";
import { useMutation } from "@apollo/client";
import { acceptEula } from "../../redux/user/user.actions";
import { useTranslation } from "react-i18next";
import {createStructuredSelector} from "reselect";
import {selectCurrentEula, selectCurrentUser} from "../../redux/user/user.selectors";
import {connect} from "react-redux";
import {FormDatePicker} from "../form-date-picker/form-date-picker.component";
import {INSERT_EULA_ACCEPTANCE} from "../../graphql/user.queries";
import {useMutation} from "@apollo/client";
import {acceptEula} from "../../redux/user/user.actions";
import {useTranslation} from "react-i18next";
import day from '../../utils/day';
import './eula.styles.scss';
const Eula = ({ currentEula, currentUser, acceptEula }) => {
const Eula = ({currentEula, currentUser, acceptEula}) => {
const [formReady, setFormReady] = useState(false);
const [hasEverScrolledToBottom, setHasEverScrolledToBottom] = useState(false);
const [insertEulaAcceptance] = useMutation(INSERT_EULA_ACCEPTANCE);
const [form] = Form.useForm();
const markdownCardRef = useRef(null);
const { t } = useTranslation();
const {t} = useTranslation();
const [api, contextHolder] = notification.useNotification();
const handleScroll = (e) => {
const handleScroll = useCallback((e) => {
const bottom = e.target.scrollHeight - 100 <= e.target.scrollTop + e.target.clientHeight;
if (bottom && !hasEverScrolledToBottom) {
setHasEverScrolledToBottom(true);
} else if (e.target.scrollHeight <= e.target.clientHeight && !hasEverScrolledToBottom) {
setHasEverScrolledToBottom(true);
}
};
}, [hasEverScrolledToBottom, setHasEverScrolledToBottom]);
useEffect(() => {
handleScroll({target: markdownCardRef.current});
}, [handleScroll]);
const handleChange = useCallback(() => {
form.validateFields({ validateOnly: true })
form.validateFields({validateOnly: true})
.then(() => setFormReady(hasEverScrolledToBottom))
.catch(() => setFormReady(false));
}, [form, hasEverScrolledToBottom]);
@@ -39,25 +45,33 @@ const Eula = ({ currentEula, currentUser, acceptEula }) => {
handleChange();
}, [handleChange, hasEverScrolledToBottom, form]);
const onFinish = async ({ acceptTerms, ...formValues }) => {
const onFinish = async ({acceptTerms, ...formValues}) => {
const eulaId = currentEula.id;
const useremail = currentUser.email;
try {
const { accepted_terms, ...otherFormValues } = formValues;
const {accepted_terms, ...otherFormValues} = formValues;
// Trim the values of the fields before submitting
const trimmedFormValues = Object.entries(otherFormValues).reduce((acc, [key, value]) => {
acc[key] = typeof value === 'string' ? value.trim() : value;
return acc;
}, {});
await insertEulaAcceptance({
variables: {
eulaAcceptance: {
eulaid: eulaId,
useremail,
...otherFormValues,
...trimmedFormValues,
date_accepted: new Date(),
}
}
});
acceptEula();
} catch (err) {
api.error({
api.error({
message: t('eula.errors.acceptance.message'),
description: t('eula.errors.acceptance.description'),
placement: 'bottomRight',
@@ -93,46 +107,54 @@ const Eula = ({ currentEula, currentUser, acceptEula }) => {
)}
closable={false}
>
<Space direction='vertical'>
<Card type='inner' className='eula-markdown-card' onScroll={handleScroll} ref={markdownCardRef}>
<div id='markdowndiv' className='eula-markdown-div'>
<Markdown children={currentEula?.content?.replace(/\\n/g, '\n')} />
</div>
<Card type='inner' className='eula-markdown-card' onScroll={handleScroll} ref={markdownCardRef}>
<div id='markdowndiv' className='eula-markdown-div'>
<Markdown children={currentEula?.content?.replace(/\\n|\\r|\\n\\r|\\r\\n/g, '\n')}/>
</div>
</Card>
<EulaFormComponent form={form} handleChange={handleChange} onFinish={onFinish} t={t}/>
{!hasEverScrolledToBottom && (
<Card className='eula-never-scrolled' type='inner'>
<h3>{t('eula.content.never_scrolled')}</h3>
</Card>
<EulaFormComponent form={form} handleChange={handleChange} onFinish={onFinish} t={t} />
{!hasEverScrolledToBottom && (
<Card className='eula-never-scrolled' type='inner'>
<h3>{t('eula.content.never_scrolled')}</h3>
</Card>
)}
</Space>
)}
</Modal>
</>
)
}
const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
<Card type='inner' title={t('eula.titles.upper_card')}>
const EulaFormComponent = ({form, handleChange, onFinish, t}) => (
<Card type='inner' title={t('eula.titles.upper_card')} style={{marginTop: '10px'}}>
<Form id='tosForm' onChange={handleChange} onFinish={onFinish} form={form}>
<Row gutter={24}>
<Col span={12}>
<Form.Item
label={t('eula.labels.first_name')}
name="first_name"
rules={[{ required: true, message: t('eula.messages.first_name') }]}
rules={[{
required: true,
validator: (_, value) =>
value.trim() !== '' ? Promise.resolve() : Promise.reject(new Error(t('eula.messages.first_name'))),
},]}
>
<Input placeholder={t('eula.labels.first_name')}
aria-label={t('eula.labels.first_name')} />
aria-label={t('eula.labels.first_name')}/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label={t('eula.labels.last_name')}
name="last_name"
rules={[{ required: true, message: t('eula.messages.last_name') }]}
rules={[{
required: true,
validator: (_, value) =>
value.trim() !== '' ? Promise.resolve() : Promise.reject(new Error(t('eula.messages.last_name'))),
}]}
>
<Input placeholder={t('eula.labels.last_name')}
aria-label={t('eula.labels.last_name')} />
aria-label={t('eula.labels.last_name')}/>
</Form.Item>
</Col>
</Row>
@@ -141,10 +163,13 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
<Form.Item
label={t('eula.labels.business_name')}
name="business_name"
rules={[{ required: true, message: t('eula.messages.business_name') }]}
rules={[{
required: true,
validator: (_, value) =>
value.trim() !== '' ? Promise.resolve() : Promise.reject(new Error(t('eula.messages.business_name'))),
}]}
>
<Input placeholder={t('eula.labels.business_name')}
aria-label={t('eula.labels.business_name')} />
<Input placeholder={t('eula.labels.business_name')} aria-label={t('eula.labels.business_name')}/>
</Form.Item>
</Col>
<Col span={12}>
@@ -159,7 +184,7 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
]}
>
<Input placeholder={t('eula.labels.phone_number')}
aria-label={t('eula.labels.phone_number')} />
aria-label={t('eula.labels.phone_number')}/>
</Form.Item>
</Col>
</Row>
@@ -169,7 +194,7 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
label={t('eula.labels.address')}
name="address"
>
<Input placeholder={t('eula.labels.address')} aria-label={t('eula.labels.address')} />
<Input placeholder={t('eula.labels.address')} aria-label={t('eula.labels.address')}/>
</Form.Item>
</Col>
<Col span={12}>
@@ -177,8 +202,8 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
label={t('eula.labels.date_accepted')}
name="date_accepted"
rules={[
{ required: true },
{
required: true,
validator: (_, value) => {
if (day(value).isSame(day(), 'day')) {
return Promise.resolve();
@@ -188,8 +213,7 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
},
]}
>
<FormDatePicker onChange={handleChange} onlyToday
aria-label={t('eula.labels.date_accepted')} />
<FormDatePicker onChange={handleChange} onlyToday aria-label={t('eula.labels.date_accepted')}/>
</Form.Item>
</Col>
</Row>
@@ -200,13 +224,13 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
valuePropName="checked"
rules={[
{
required: true,
validator: (_, value) =>
value ? Promise.resolve() : Promise.reject(new Error(t('eula.messages.accepted_terms'))),
},
]}
>
<Checkbox
aria-label={t('eula.labels.accepted_terms')}>{t('eula.labels.accepted_terms')}</Checkbox>
<Checkbox aria-label={t('eula.labels.accepted_terms')}>{t('eula.labels.accepted_terms')}</Checkbox>
</Form.Item>
</Col>
</Row>

View File

@@ -0,0 +1,246 @@
import React, {useCallback, useEffect, useState} from 'react';
import day from '../../utils/day';
import axios from 'axios';
import {Badge, Card, Space, Table, Tag} from 'antd';
import {gql, useQuery} from "@apollo/client";
import {DateTimeFormatterFunction} from "../../utils/DateFormatter";
import {isEmpty} from "lodash";
import {useTranslation} from "react-i18next";
require('./job-lifecycle.styles.scss');
// show text on bar if text can fit
export function JobLifecycleComponent({job, statuses, ...rest}) {
const [loading, setLoading] = useState(true);
const [lifecycleData, setLifecycleData] = useState(null);
const {t} = useTranslation(); // Used for tracking external state changes.
const {data} = useQuery(gql`
query get_job_test($id: uuid!){
jobs_by_pk(id:$id){
id
status
}
}
`, {
variables: {
id: job.id
},
fetchPolicy: 'cache-only'
});
/**
* Gets the lifecycle data for the job.
* @returns {Promise<void>}
*/
const getLifecycleData = useCallback(async () => {
if (job && job.id && statuses && statuses.statuses) {
try {
setLoading(true);
const response = await axios.post("/job/lifecycle", {
jobids: job.id,
statuses: statuses.statuses
});
const data = response.data.transition[job.id];
setLifecycleData(data);
} catch (err) {
console.error(`${t('job_lifecycle.errors.fetch')}: ${err.message}`);
} finally {
setLoading(false);
}
}
}, [job, statuses, t]);
useEffect(() => {
if (!data) return;
setTimeout(() => {
getLifecycleData().catch(err => console.error(`${t('job_lifecycle.errors.fetch')}: ${err.message}`));
}, 500);
}, [data, getLifecycleData, t]);
const columns = [
{
title: t('job_lifecycle.columns.value'),
dataIndex: 'value',
key: 'value',
},
{
title: t('job_lifecycle.columns.start'),
dataIndex: 'start',
key: 'start',
render: (text) => DateTimeFormatterFunction(text),
sorter: (a, b) => day(a.start).unix() - day(b.start).unix(),
},
{
title: t('job_lifecycle.columns.relative_start'),
dataIndex: 'start_readable',
key: 'start_readable',
},
{
title: t('job_lifecycle.columns.end'),
dataIndex: 'end',
key: 'end',
sorter: (a, b) => {
if (isEmpty(a.end) || isEmpty(b.end)) {
if (isEmpty(a.end) && isEmpty(b.end)) {
return 0;
}
return isEmpty(a.end) ? 1 : -1;
}
return day(a.end).unix() - day(b.end).unix();
},
render: (text) => isEmpty(text) ? t('job_lifecycle.content.not_available') : DateTimeFormatterFunction(text)
},
{
title: t('job_lifecycle.columns.relative_end'),
dataIndex: 'end_readable',
key: 'end_readable',
},
{
title: t('job_lifecycle.columns.duration'),
dataIndex: 'duration_readable',
key: 'duration_readable',
sorter: (a, b) => a.duration - b.duration,
},
];
return (
<Card loading={loading} title={t('job_lifecycle.content.title')}>
{!loading ? (
lifecycleData && lifecycleData.lifecycle && lifecycleData.durations ? (
<Space direction='vertical' style={{width: '100%'}}>
<Card
type='inner'
title={(
<Space direction='horizontal' size='small'>
<Badge status='processing' count={lifecycleData.durations.totalStatuses}/>
{t('job_lifecycle.content.title_durations')}
</Space>
)}
style={{width: '100%'}}
>
<div id="bar-container" style={{
display: 'flex',
width: '100%',
height: '100px',
textAlign: 'center',
borderRadius: '5px',
borderWidth: '5px',
borderStyle: 'solid',
borderColor: '#f0f2f5',
margin: 0,
padding: 0
}}>
{lifecycleData.durations.summations.map((key, index, array) => {
const isFirst = index === 0;
const isLast = index === array.length - 1;
return (
<div key={key.status} style={{
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
margin: 0,
padding: 0,
borderTop: '1px solid #f0f2f5',
borderBottom: '1px solid #f0f2f5',
borderLeft: isFirst ? '1px solid #f0f2f5' : undefined,
borderRight: isLast ? '1px solid #f0f2f5' : undefined,
backgroundColor: key.color,
width: `${key.percentage}%`
}}
aria-label={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
title={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
>
{key.percentage > 15 ?
<>
<div>{key.roundedPercentage}</div>
<div style={{
backgroundColor: '#f0f2f5',
borderRadius: '5px',
paddingRight: '2px',
paddingLeft: '2px',
fontSize: '0.8rem',
}}>
{key.status}
</div>
</>
: null}
</div>
);
})}
</div>
<Card type='inner' title={t('job_lifecycle.content.legend_title')}
style={{marginTop: '10px'}}>
<div>
{lifecycleData.durations.summations.map((key) => (
<Tag key={key.status} color={key.color} style={{width: '13vh', padding: '4px', margin: '4px'}}>
<div
aria-label={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
title={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
style={{
backgroundColor: '#f0f2f5',
color: '#000',
padding: '4px',
textAlign: 'center'
}}>
{key.status} ({key.roundedPercentage})
</div>
</Tag>
))}
</div>
</Card>
{(lifecycleData?.durations?.humanReadableTotal) ||
(lifecycleData.lifecycle[0] && lifecycleData.lifecycle[0].value && lifecycleData?.durations?.totalCurrentStatusDuration?.humanReadable) ?
<Card style={{marginTop: '10px'}}>
<ul>
{lifecycleData.durations && lifecycleData.durations.humanReadableTotal &&
<li>
<span
style={{fontWeight: 'bold'}}>{t('job_lifecycle.content.previous_status_accumulated_time')}:</span> {lifecycleData.durations.humanReadableTotal}
</li>
}
{lifecycleData.lifecycle[0] && lifecycleData.lifecycle[0].value && lifecycleData?.durations?.totalCurrentStatusDuration?.humanReadable &&
<li>
<span
style={{fontWeight: 'bold'}}>{t('job_lifecycle.content.current_status_accumulated_time')} ({lifecycleData.lifecycle[0].value}):</span> {lifecycleData.durations.totalCurrentStatusDuration.humanReadable}
</li>
}
</ul>
</Card>
: null}
</Card>
<Card type='inner' title={(
<>
<Space direction="horizontal" size="small">
<Badge status='processing' count={lifecycleData.lifecycle.length}/>
{t('job_lifecycle.content.title_transitions')}
</Space>
</>
)}>
<Table style={{
overflow: 'auto',
width: '100%',
}} columns={columns} dataSource={lifecycleData.lifecycle}/>
</Card>
</Space>
) : (
<Card type='inner' style={{textAlign: 'center'}}>
{t('job_lifecycle.content.data_unavailable')}
</Card>
)
) : (
<Card type='inner' title={t('job_lifecycle.content.title_loading')}>
{t('job_lifecycle.content.loading')}
</Card>
)}
</Card>
);
}
export default JobLifecycleComponent;

View File

@@ -51,12 +51,14 @@ export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) {
}
return (
<Dropdown menu={statusMenu} trigger={["click"]} key="changestatus">
<Button shape="round">
<span>{job.status}</span>
<>
<Dropdown menu={statusMenu} trigger={["click"]} key="changestatus">
<Button shape="round">
<span>{job.status}</span>
<DownCircleFilled />
</Button>
</Dropdown>
<DownCircleFilled />
</Button>
</Dropdown>
</>
);
}

View File

@@ -1,34 +1,18 @@
import { useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import { gql } from "@apollo/client";
import { Button, Space, notification } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import {
DELETE_DELIVERY_CHECKLIST,
DELETE_INTAKE_CHECKLIST,
} from "../../graphql/jobs.queries";
export default function JobAdminDeleteIntake({ job }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [deleteIntake] = useMutation(gql`
mutation DELETE_INTAKE($jobId: uuid!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { intakechecklist: null }
) {
id
intakechecklist
}
}
`);
const [DELETE_DELIVERY] = useMutation(gql`
mutation DELETE_DELIVERY($jobId: uuid!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { deliverchecklist: null }
) {
id
deliverchecklist
}
}
`);
const [deleteIntake] = useMutation(DELETE_INTAKE_CHECKLIST);
const [deleteDelivery] = useMutation(DELETE_DELIVERY_CHECKLIST);
const handleDelete = async (values) => {
setLoading(true);
@@ -50,7 +34,7 @@ export default function JobAdminDeleteIntake({ job }) {
const handleDeleteDelivery = async (values) => {
setLoading(true);
const result = await DELETE_DELIVERY({
const result = await deleteDelivery({
variables: { jobId: job.id },
});
@@ -68,12 +52,22 @@ export default function JobAdminDeleteIntake({ job }) {
return (
<>
<Button loading={loading} onClick={handleDelete}>
{t("jobs.labels.deleteintake")}
</Button>
<Button loading={loading} onClick={handleDeleteDelivery}>
{t("jobs.labels.deletedelivery")}
</Button>
<Space wrap>
<Button
loading={loading}
onClick={handleDelete}
disabled={!job.intakechecklist}
>
{t("jobs.labels.deleteintake")}
</Button>
<Button
loading={loading}
onClick={handleDeleteDelivery}
disabled={!job.deliverychecklist}
>
{t("jobs.labels.deletedelivery")}
</Button>
</Space>
</>
);
}

View File

@@ -1,5 +1,5 @@
import { gql, useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import { useMutation } from "@apollo/client";
import { Button, Space, notification } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -7,6 +7,11 @@ import dayjs from "../../utils/day";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import {
MARK_JOB_AS_EXPORTED,
MARK_JOB_AS_UNINVOICED,
MARK_JOB_FOR_REEXPORT,
} from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import {
selectBodyshop,
@@ -35,58 +40,18 @@ export function JobAdminMarkReexport({
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [markJobForReexport] = useMutation(gql`
mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { date_exported: null
status: "${bodyshop.md_ro_statuses.default_invoiced}"
}
) {
id
date_exported
status
date_invoiced
}
}
`);
const [markJobExported] = useMutation(gql`
mutation MARK_JOB_AS_EXPORTED($jobId: uuid!, $date_exported: timestamptz!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { date_exported: $date_exported
status: "${bodyshop.md_ro_statuses.default_exported}"
}
) {
id
date_exported
date_invoiced
status
}
}
`);
const [markJobUninvoiced] = useMutation(gql`
mutation MARK_JOB_AS_UNINVOICED($jobId: uuid!, ) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { date_exported: null
date_invoiced: null
status: "${bodyshop.md_ro_statuses.default_delivered}"
}
) {
id
date_exported
date_invoiced
status
}
}
`);
const [markJobForReexport] = useMutation(MARK_JOB_FOR_REEXPORT);
const [markJobExported] = useMutation(MARK_JOB_AS_EXPORTED);
const [markJobUninvoiced] = useMutation(MARK_JOB_AS_UNINVOICED);
const handleMarkForExport = async () => {
setLoading(true);
const result = await markJobForReexport({
variables: { jobId: job.id },
variables: {
jobId: job.id,
default_invoiced: bodyshop.md_ro_statuses.default_invoiced,
},
});
if (!result.errors) {
@@ -108,7 +73,11 @@ export function JobAdminMarkReexport({
const handleMarkExported = async () => {
setLoading(true);
const result = await markJobExported({
variables: { jobId: job.id, date_exported: dayjs() },
variables: {
jobId: job.id,
date_exported: dayjs(),
default_exported: bodyshop.md_ro_statuses.default_exported,
},
});
await insertExportLog({
@@ -144,7 +113,10 @@ export function JobAdminMarkReexport({
const handleUninvoice = async () => {
setLoading(true);
const result = await markJobUninvoiced({
variables: { jobId: job.id },
variables: {
jobId: job.id,
default_delivered: bodyshop.md_ro_statuses.default_delivered,
},
});
if (!result.errors) {
@@ -165,27 +137,29 @@ export function JobAdminMarkReexport({
return (
<>
<Button
loading={loading}
disabled={!job.date_exported}
onClick={handleMarkForExport}
>
{t("jobs.labels.markforreexport")}
</Button>
<Button
loading={loading}
disabled={job.date_exported}
onClick={handleMarkExported}
>
{t("jobs.actions.markasexported")}
</Button>
<Button
loading={loading}
disabled={!job.date_invoiced || job.date_exported}
onClick={handleUninvoice}
>
{t("jobs.actions.uninvoice")}
</Button>
<Space wrap>
<Button
loading={loading}
disabled={!job.date_exported}
onClick={handleMarkForExport}
>
{t("jobs.labels.markforreexport")}
</Button>
<Button
loading={loading}
disabled={job.date_exported}
onClick={handleMarkExported}
>
{t("jobs.actions.markasexported")}
</Button>
<Button
loading={loading}
disabled={!job.date_invoiced || job.date_exported}
onClick={handleUninvoice}
>
{t("jobs.actions.uninvoice")}
</Button>
</Space>
</>
);
}

View File

@@ -0,0 +1,65 @@
import { useMutation } from "@apollo/client";
import { Switch, notification } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_REMOVE_FROM_AR } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminRemoveAR);
export function JobsAdminRemoveAR({ insertAuditTrail, job }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [switchValue, setSwitchValue] = useState(job.remove_from_ar);
const [mutationUpdateRemoveFromAR] = useMutation(UPDATE_REMOVE_FROM_AR);
const handleChange = async (value) => {
setLoading(true);
const result = await mutationUpdateRemoveFromAR({
variables: { jobId: job.id, remove_from_ar: value },
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_job_remove_from_ar(value),
});
setSwitchValue(value);
} else {
notification["error"]({
message: t("jobs.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false);
};
return (
<>
<div style={{ display: "flex", alignItems: "center" }}>
<div style={{ marginRight: "10px" }}>
{t("jobs.labels.remove_from_ar")}:
</div>
<div>
<Switch
checked={switchValue}
loading={loading}
onChange={handleChange}
/>
</div>
</div>
</>
);
}

View File

@@ -1,9 +1,10 @@
import { gql, useMutation } from "@apollo/client";
import { useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UNVOID_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import {
selectBodyshop,
@@ -29,66 +30,17 @@ export function JobsAdminUnvoid({
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [updateJob] = useMutation(gql`
mutation UNVOID_JOB($jobId: uuid!) {
update_jobs_by_pk(pk_columns: {id: $jobId}, _set: {voided: false, status: "${
bodyshop.md_ro_statuses.default_imported
}", date_void: null}) {
id
date_void
voided
status
}
insert_notes(objects: {jobid: $jobId, audit: true, created_by: "${
currentUser.email
}", text: "${t("jobs.labels.unvoidnote")}"}) {
returning {
id
}
}
}
`);
// const result = await voidJob({
// variables: {
// jobId: job.id,
// job: {
// status: bodyshop.md_ro_statuses.default_void,
// voided: true,
// },
// note: [
// {
// jobid: job.id,
// created_by: currentUser.email,
// audit: true,
// text: t("jobs.labels.voidnote", {
// date: dayjs().format("MM/DD/yyy"),
// time: dayjs().format("hh:mm a"),
// }),
// },
// ],
// },
// });
// if (!!!result.errors) {
// notification["success"]({
// message: t("jobs.successes.voided"),
// });
// //go back to jobs list.
// history.push(`/manage/`);
// } else {
// notification["error"]({
// message: t("jobs.errors.voiding", {
// error: JSON.stringify(result.errors),
// }),
// });
// }
const [mutationUnvoidJob] = useMutation(UNVOID_JOB);
const handleUpdate = async (values) => {
setLoading(true);
const result = await updateJob({
variables: { jobId: job.id },
const result = await mutationUnvoidJob({
variables: {
jobId: job.id,
default_imported: bodyshop.md_ro_statuses.default_imported,
currentUserEmail: currentUser.email,
text: t("jobs.labels.unvoidnote"),
},
});
if (!result.errors) {
@@ -110,8 +62,10 @@ mutation UNVOID_JOB($jobId: uuid!) {
};
return (
<Button loading={loading} disabled={!job.voided} onClick={handleUpdate}>
{t("jobs.actions.unvoid")}
</Button>
<>
<Button loading={loading} disabled={!job.voided} onClick={handleUpdate}>
{t("jobs.actions.unvoid")}
</Button>
</>
);
}

View File

@@ -624,7 +624,7 @@ export function JobsDetailHeaderActions({
{
key: 'cancelallappointments',
onClick: () => {
if ( job.status !== bodyshop.md_ro_statuses.default_scheduled) {
if (job.status !== bodyshop.md_ro_statuses.default_scheduled) {
return;
}
showCancelScheduleModal()
@@ -713,12 +713,7 @@ export function JobsDetailHeaderActions({
menuItems.push({
key: 'cccontract',
disabled: jobRO || !job.converted,
label: <Link
to={{
pathname: "/manage/courtesycars/contracts/new",
state: {jobId: job.id},
}}
>
label: <Link state={{jobId: job.id}} to='/manage/courtesycars/contracts/new'>
{t("menus.jobsactions.newcccontract")}
</Link>
});
@@ -993,10 +988,11 @@ export function JobsDetailHeaderActions({
onOk={handleCancelScheduleOK}
onCancel={handleCancelScheduleModalCancel}
footer={[
<Button form="cancelScheduleForm" key="back" onClick={handleCancelScheduleModalCancel}>
<Button form="cancelScheduleForm" key="back" onClick={handleCancelScheduleModalCancel}>
{t("general.actions.cancel")}
</Button>,
<Button form="cancelScheduleForm" htmlType="submit" key="submit" type="primary" loading={loading} onClick={handleCancelScheduleOK}>
<Button form="cancelScheduleForm" htmlType="submit" key="submit" type="primary" loading={loading}
onClick={handleCancelScheduleOK}>
{t("appointments.actions.cancel")}
</Button>,
]}
@@ -1004,7 +1000,7 @@ export function JobsDetailHeaderActions({
<Form
layout="vertical"
id="cancelScheduleForm"
onFinish={s =>{
onFinish={s => {
console.log(s);
handleLostSaleFinish(s);
}}

View File

@@ -225,7 +225,7 @@ export function PayableExportAll({
if (bodyshop.pbs_serialnumber)
return (
<Link to={{ state: { billids }, pathname: `/manage/dmsap` }}>
<Link to='/manage/dmsap' state={{ billids }}>
<Button>{t("jobs.actions.export")}</Button>
</Link>
);

View File

@@ -218,7 +218,7 @@ export function PayableExportButton({
if (bodyshop.pbs_serialnumber)
return (
<Link to={{ state: { billids: [billId] }, pathname: `/manage/dmsap` }}>
<Link to='/manage/dmsap' state={{billids: [billId]}}>
<Button>{t("jobs.actions.export")}</Button>
</Link>
);

View File

@@ -116,7 +116,7 @@ export function ProductionBoardKanbanComponent({
// console.log("==> New Card is somewhere in the middle");
movedCardNewKanbanParent = newChildCard.kanbanparent;
} else {
throw new Error("==> !!!!!!Couldn't find a parent.!!!! <==");
console.log("==> !!!!!!Couldn't find a parent.!!!! <==");
}
const newChildCardNewParent = newChildCard ? card.id : null;
const update = await client.mutate({
@@ -154,7 +154,7 @@ export function ProductionBoardKanbanComponent({
0
)
.toFixed(1);
const totalLAB = data
const totalLAB = data
.reduce(
(acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0

View File

@@ -68,8 +68,8 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
const handleFinish = async (values) => {
setLoading(true);
const start = values.dates[0];
const end = values.dates[1];
const start = values.dates ? values.dates[0] : null;
const end = values.dates ? values.dates[1] : null;
const { id } = values;
await GenerateDocument(
@@ -239,20 +239,30 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
else return null;
}}
</Form.Item>
<Form.Item
name="dates"
label={t("reportcenter.labels.dates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<DatePicker.RangePicker
format="MM/DD/YYYY"
presets={DatePIckerRanges}
/>
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
const datedisable = Templates[key] && Templates[key].datedisable;
if (datedisable !== true) {
return (
<Form.Item
name="dates"
label={t("reportcenter.labels.dates")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<DatePicker.RangePicker
format="MM/DD/YYYY"
ranges={DatePIckerRanges}
/>
</Form.Item>
);
} else return null;
}}
</Form.Item>
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
{() => {

View File

@@ -1,13 +1,13 @@
import { useQuery } from "@apollo/client";
import { Col, Row } from "antd";
import {useQuery} from "@apollo/client";
import {Col, Row} from "antd";
import _ from "lodash";
import dayjs from "../../utils/day";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_TIME_TICKETS_IN_RANGE_SB } from "../../graphql/timetickets.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import React, {useMemo} from "react";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import {createStructuredSelector} from "reselect";
import {QUERY_TIME_TICKETS_IN_RANGE_SB} from "../../graphql/timetickets.queries";
import {selectBodyshop} from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
@@ -16,396 +16,397 @@ import ScoreboardTicketsStats from "./scoreboard-timetickets.stats.component";
import ScoreboardTimeticketsTargetsTable from "./scoreboard-timetickets.targets-table.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(ScoreboardTimeTicketsStats);
export function ScoreboardTimeTicketsStats({ bodyshop }) {
const { t } = useTranslation();
const startDate = dayjs().startOf("month")
const endDate = dayjs().endOf("month");
export function ScoreboardTimeTicketsStats({bodyshop}) {
const {t} = useTranslation();
const startDate = dayjs().startOf("month")
const endDate = dayjs().endOf("month");
const fixedPeriods = useMemo(() => {
const endOfThisMonth = dayjs().endOf("month");
const startofthisMonth = dayjs().startOf("month");
const fixedPeriods = useMemo(() => {
const endOfThisMonth = dayjs().endOf("month");
const startofthisMonth = dayjs().startOf("month");
const endOfLastmonth = dayjs().subtract(1, "month").endOf("month");
const startOfLastmonth = dayjs().subtract(1, "month").startOf("month");
const endOfLastmonth = dayjs().subtract(1, "month").endOf("month");
const startOfLastmonth = dayjs().subtract(1, "month").startOf("month");
const endOfThisWeek = dayjs().endOf("week");
const startOfThisWeek = dayjs().startOf("week");
const endOfThisWeek = dayjs().endOf("week");
const startOfThisWeek = dayjs().startOf("week");
const endOfLastWeek = dayjs().subtract(1, "week").endOf("week");
const startOfLastWeek = dayjs().subtract(1, "week").startOf("week");
const endOfLastWeek = dayjs().subtract(1, "week").endOf("week");
const startOfLastWeek = dayjs().subtract(1, "week").startOf("week");
const endOfPriorWeek = dayjs().subtract(2, "week").endOf("week");
const startOfPriorWeek = dayjs().subtract(2, "week").startOf("week");
const endOfPriorWeek = dayjs().subtract(2, "week").endOf("week");
const startOfPriorWeek = dayjs().subtract(2, "week").startOf("week");
const allDates = [
endOfThisMonth,
startofthisMonth,
endOfLastmonth,
startOfLastmonth,
endOfThisWeek,
startOfThisWeek,
endOfLastWeek,
startOfLastWeek,
endOfPriorWeek,
startOfPriorWeek,
];
const start = dayjs.min(allDates);
const end = dayjs.max(allDates);
return {
start,
end,
endOfThisMonth,
startofthisMonth,
endOfLastmonth,
startOfLastmonth,
endOfThisWeek,
startOfThisWeek,
endOfLastWeek,
startOfLastWeek,
endOfPriorWeek,
startOfPriorWeek,
};
}, []);
const allDates = [
endOfThisMonth,
startofthisMonth,
endOfLastmonth,
startOfLastmonth,
endOfThisWeek,
startOfThisWeek,
endOfLastWeek,
startOfLastWeek,
endOfPriorWeek,
startOfPriorWeek,
];
const start = dayjs.min(allDates);
const end = dayjs.max(allDates);
return {
start,
end,
endOfThisMonth,
startofthisMonth,
endOfLastmonth,
startOfLastmonth,
endOfThisWeek,
startOfThisWeek,
endOfLastWeek,
startOfLastWeek,
endOfPriorWeek,
startOfPriorWeek,
};
}, []);
const { loading, error, data } = useQuery(QUERY_TIME_TICKETS_IN_RANGE_SB, {
variables: {
start: startDate.format("YYYY-MM-DD"),
end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
jobStart: startDate,
jobEnd: endDate,
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
pollInterval: 60000,
skip: !fixedPeriods,
});
const calculatedData = useMemo(() => {
if (!data) return [];
const ret = {
totalThisWeek: 0,
totalThisWeekLAB: 0,
totalThisWeekLAR: 0,
totalLastWeek: 0,
totalLastWeekLAB: 0,
totalLastWeekLAR: 0,
totalPriorWeek: 0,
totalPriorWeekLAB: 0,
totalPriorWeekLAR: 0,
totalThisMonth: 0,
totalThisMonthLAB: 0,
totalThisMonthLAR: 0,
totalLastMonth: 0,
totalLastMonthLAB: 0,
totalLastMonthLAR: 0,
actualTotalOverPeriod: 0,
actualTotalOverPeriodLAB: 0,
actualTotalOverPeriodLAR: 0,
totalEffieciencyOverPeriod: 0,
totalEffieciencyOverPeriodLAB: 0,
totalEffieciencyOverPeriodLAR: 0,
seperatedThisWeek: {
sunday: {
total: 0,
lab: 0,
lar: 0,
const {loading, error, data} = useQuery(QUERY_TIME_TICKETS_IN_RANGE_SB, {
variables: {
start: startDate.format("YYYY-MM-DD"),
end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
jobStart: startDate,
jobEnd: endDate,
},
monday: {
total: 0,
lab: 0,
lar: 0,
},
tuesday: {
total: 0,
lab: 0,
lar: 0,
},
wednesday: {
total: 0,
lab: 0,
lar: 0,
},
thursday: {
total: 0,
lab: 0,
lar: 0,
},
friday: {
total: 0,
lab: 0,
lar: 0,
},
saturday: {
total: 0,
lab: 0,
lar: 0,
},
},
};
data.fixedperiod.forEach((ticket) => {
const ticketDate = dayjs(ticket.date);
if (
ticketDate.isBetween(
fixedPeriods.startOfThisWeek,
fixedPeriods.endOfThisWeek,
undefined,
"[]"
)
) {
ret.totalThisWeek = ret.totalThisWeek + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalThisWeekLAB = ret.totalThisWeekLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalThisWeekLAR = ret.totalThisWeekLAR + ticket.productivehrs;
//Seperate out to Day of Week
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].total =
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].total + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].lab =
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].lab + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].lar =
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].lar + ticket.productivehrs;
} else if (
ticketDate.isBetween(
fixedPeriods.startOfLastWeek,
fixedPeriods.endOfLastWeek,
undefined,
"[]"
)
) {
ret.totalLastWeek = ret.totalLastWeek + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalLastWeekLAB = ret.totalLastWeekLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalLastWeekLAR = ret.totalLastWeekLAR + ticket.productivehrs;
} else if (
ticketDate.isBetween(
fixedPeriods.startOfPriorWeek,
fixedPeriods.endOfPriorWeek,
undefined,
"[]"
)
) {
ret.totalPriorWeek = ret.totalPriorWeek + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalPriorWeekLAB = ret.totalPriorWeekLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalPriorWeekLAR = ret.totalPriorWeekLAR + ticket.productivehrs;
}
if (
ticketDate.isBetween(
fixedPeriods.startofthisMonth,
fixedPeriods.endOfThisMonth,
undefined,
"[]"
)
) {
ret.totalThisMonth = ret.totalThisMonth + ticket.productivehrs;
ret.actualTotalOverPeriod =
ret.actualTotalOverPeriod + (ticket.actualhrs || 0);
if (ticket.ciecacode !== "LAR") {
ret.totalThisMonthLAB = ret.totalThisMonthLAB + ticket.productivehrs;
ret.actualTotalOverPeriodLAB =
ret.actualTotalOverPeriodLAB + (ticket.actualhrs || 0);
}
if (ticket.ciecacode === "LAR") {
ret.totalThisMonthLAR = ret.totalThisMonthLAR + ticket.productivehrs;
ret.actualTotalOverPeriodLAR =
ret.actualTotalOverPeriodLAR + (ticket.actualhrs || 0);
}
} else if (
ticketDate.isBetween(
fixedPeriods.startOfLastmonth,
fixedPeriods.endOfLastmonth,
undefined,
"[]"
)
) {
ret.totalLastMonth = ret.totalLastMonth + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalLastMonthLAB = ret.totalLastMonthLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalLastMonthLAR = ret.totalLastMonthLAR + ticket.productivehrs;
}
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
pollInterval: 60000,
skip: !fixedPeriods,
});
ret.totalEffieciencyOverPeriod = ret.actualTotalOverPeriod
? (ret.totalThisMonth / ret.actualTotalOverPeriod) * 100
: 0;
ret.totalEffieciencyOverPeriodLAB = ret.actualTotalOverPeriodLAB
? (ret.totalThisMonthLAB / ret.actualTotalOverPeriodLAB) * 100
: 0;
ret.totalEffieciencyOverPeriodLAR = ret.actualTotalOverPeriodLAR
? (ret.totalThisMonthLAR / ret.actualTotalOverPeriodLAR) * 100
: 0;
const calculatedData = useMemo(() => {
if (!data) return [];
const ret = {
totalThisWeek: 0,
totalThisWeekLAB: 0,
totalThisWeekLAR: 0,
totalLastWeek: 0,
totalLastWeekLAB: 0,
totalLastWeekLAR: 0,
totalPriorWeek: 0,
totalPriorWeekLAB: 0,
totalPriorWeekLAR: 0,
totalThisMonth: 0,
totalThisMonthLAB: 0,
totalThisMonthLAR: 0,
totalLastMonth: 0,
totalLastMonthLAB: 0,
totalLastMonthLAR: 0,
actualTotalOverPeriod: 0,
actualTotalOverPeriodLAB: 0,
actualTotalOverPeriodLAR: 0,
totalEffieciencyOverPeriod: 0,
totalEffieciencyOverPeriodLAB: 0,
totalEffieciencyOverPeriodLAR: 0,
seperatedThisWeek: {
sunday: {
total: 0,
lab: 0,
lar: 0,
},
monday: {
total: 0,
lab: 0,
lar: 0,
},
tuesday: {
total: 0,
lab: 0,
lar: 0,
},
wednesday: {
total: 0,
lab: 0,
lar: 0,
},
thursday: {
total: 0,
lab: 0,
lar: 0,
},
friday: {
total: 0,
lab: 0,
lar: 0,
},
saturday: {
total: 0,
lab: 0,
lar: 0,
},
},
};
roundObject(ret);
data.fixedperiod.forEach((ticket) => {
const ticketDate = dayjs(ticket.date);
if (
ticketDate.isBetween(
fixedPeriods.startOfThisWeek,
fixedPeriods.endOfThisWeek,
undefined,
"[]"
)
) {
ret.totalThisWeek = ret.totalThisWeek + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalThisWeekLAB = ret.totalThisWeekLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalThisWeekLAR = ret.totalThisWeekLAR + ticket.productivehrs;
const ticketsGroupedByDate = _.groupBy(data.timetickets, "date");
const listOfDays = Utils.ListOfDaysInCurrentMonth();
const combinedData = [],
labData = [],
larData = [];
var acc_comb = 0;
var acc_lab = 0;
var acc_lar = 0;
listOfDays.forEach((day) => {
const r = {
date: dayjs(day).format("MM/DD"),
actualhrs: 0,
productivehrs: 0,
};
const combined = {
accTargetHrs: _.round(
Utils.AsOfDateTargetHours(
bodyshop.scoreboard_target.dailyBodyTarget +
bodyshop.scoreboard_target.dailyPaintTarget,
day
) +
(bodyshop.scoreboard_target.dailyBodyTarget +
bodyshop.scoreboard_target.dailyPaintTarget),
1
),
accHrs: 0,
};
const lab = {
accTargetHrs: _.round(
Utils.AsOfDateTargetHours(
bodyshop.scoreboard_target.dailyBodyTarget,
day
) + bodyshop.scoreboard_target.dailyBodyTarget,
1
),
accHrs: 0,
};
const lar = {
accTargetHrs: _.round(
Utils.AsOfDateTargetHours(
bodyshop.scoreboard_target.dailyPaintTarget,
day
) + bodyshop.scoreboard_target.dailyPaintTarget,
1
),
accHrs: 0,
};
if (ticketsGroupedByDate[day]) {
ticketsGroupedByDate[day].forEach((ticket) => {
r.actualhrs = r.actualhrs + ticket.actualhrs;
r.productivehrs = r.productivehrs + ticket.productivehrs;
acc_comb = acc_comb + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
acc_lab = acc_lab + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
acc_lar = acc_lar + ticket.productivehrs;
//Seperate out to Day of Week
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].total =
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].total + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].lab =
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].lab + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].lar =
ret.seperatedThisWeek[
dayjs(ticket.date).format("dddd").toLowerCase()
].lar + ticket.productivehrs;
} else if (
ticketDate.isBetween(
fixedPeriods.startOfLastWeek,
fixedPeriods.endOfLastWeek,
undefined,
"[]"
)
) {
ret.totalLastWeek = ret.totalLastWeek + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalLastWeekLAB = ret.totalLastWeekLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalLastWeekLAR = ret.totalLastWeekLAR + ticket.productivehrs;
} else if (
ticketDate.isBetween(
fixedPeriods.startOfPriorWeek,
fixedPeriods.endOfPriorWeek,
undefined,
"[]"
)
) {
ret.totalPriorWeek = ret.totalPriorWeek + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalPriorWeekLAB = ret.totalPriorWeekLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalPriorWeekLAR = ret.totalPriorWeekLAR + ticket.productivehrs;
}
if (
ticketDate.isBetween(
fixedPeriods.startofthisMonth,
fixedPeriods.endOfThisMonth,
undefined,
"[]"
)
) {
ret.totalThisMonth = ret.totalThisMonth + ticket.productivehrs;
ret.actualTotalOverPeriod =
ret.actualTotalOverPeriod + (ticket.actualhrs || 0);
if (ticket.ciecacode !== "LAR") {
ret.totalThisMonthLAB = ret.totalThisMonthLAB + ticket.productivehrs;
ret.actualTotalOverPeriodLAB =
ret.actualTotalOverPeriodLAB + (ticket.actualhrs || 0);
}
if (ticket.ciecacode === "LAR") {
ret.totalThisMonthLAR = ret.totalThisMonthLAR + ticket.productivehrs;
ret.actualTotalOverPeriodLAR =
ret.actualTotalOverPeriodLAR + (ticket.actualhrs || 0);
}
} else if (
ticketDate.isBetween(
fixedPeriods.startOfLastmonth,
fixedPeriods.endOfLastmonth,
undefined,
"[]"
)
) {
ret.totalLastMonth = ret.totalLastMonth + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalLastMonthLAB = ret.totalLastMonthLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalLastMonthLAR = ret.totalLastMonthLAR + ticket.productivehrs;
}
});
}
combined.accHrs = acc_comb;
lab.accHrs = acc_lab;
lar.accHrs = acc_lar;
combinedData.push({ ...r, ...combined });
labData.push({ ...r, ...lab });
larData.push({ ...r, ...lar });
});
ret.totalEffieciencyOverPeriod = ret.actualTotalOverPeriod
? (ret.totalThisMonth / ret.actualTotalOverPeriod) * 100
: 0;
ret.totalEffieciencyOverPeriodLAB = ret.actualTotalOverPeriodLAB
? (ret.totalThisMonthLAB / ret.actualTotalOverPeriodLAB) * 100
: 0;
ret.totalEffieciencyOverPeriodLAR = ret.actualTotalOverPeriodLAR
? (ret.totalThisMonthLAR / ret.actualTotalOverPeriodLAR) * 100
: 0;
const jobData = {};
roundObject(ret);
data.jobs.forEach((job) => {
job.tthrs = job.joblines.reduce((acc, val) => acc + val.mod_lb_hrs, 0);
});
const ticketsGroupedByDate = _.groupBy(data.timetickets, "date");
jobData.tthrs = data.jobs
.reduce((acc, val) => acc + val.tthrs, 0)
.toFixed(1);
const listOfDays = Utils.ListOfDaysInCurrentMonth();
jobData.count = data.jobs.length.toFixed(0);
const combinedData = [],
labData = [],
larData = [];
var acc_comb = 0;
var acc_lab = 0;
var acc_lar = 0;
return {
fixed: ret,
combinedData: combinedData,
labData: labData,
larData: larData,
jobData: jobData,
};
}, [fixedPeriods, data, bodyshop]);
listOfDays.forEach((day) => {
const r = {
date: dayjs(day).format("MM/DD"),
actualhrs: 0,
productivehrs: 0,
};
if (error) return <AlertComponent message={error.message} type="error" />;
if (loading) return <LoadingSpinner />;
return (
<Row gutter={[16, 16]}>
<Col span={24}>
<ScoreboardTimeticketsTargetsTable />
</Col>
<Col span={24}>
<ScoreboardTicketsStats
data={calculatedData.fixed}
jobData={calculatedData.jobData}
/>
</Col>
<Col span={24}>
<ScoreboardTimeTicketsChart
data={calculatedData.combinedData}
chartTitle={t("scoreboard.labels.combinedcharttitle")}
/>
</Col>
<Col span={12}>
<ScoreboardTimeTicketsChart
data={calculatedData.labData}
chartTitle={t("scoreboard.labels.bodycharttitle")}
/>
</Col>
<Col span={12}>
<ScoreboardTimeTicketsChart
data={calculatedData.larData}
chartTitle={t("scoreboard.labels.refinishcharttitle")}
/>
</Col>
</Row>
);
const combined = {
accTargetHrs: _.round(
Utils.AsOfDateTargetHours(
bodyshop.scoreboard_target.dailyBodyTarget +
bodyshop.scoreboard_target.dailyPaintTarget,
day
) +
(bodyshop.scoreboard_target.dailyBodyTarget +
bodyshop.scoreboard_target.dailyPaintTarget),
1
),
accHrs: 0,
};
const lab = {
accTargetHrs: _.round(
Utils.AsOfDateTargetHours(
bodyshop.scoreboard_target.dailyBodyTarget,
day
) + bodyshop.scoreboard_target.dailyBodyTarget,
1
),
accHrs: 0,
};
const lar = {
accTargetHrs: _.round(
Utils.AsOfDateTargetHours(
bodyshop.scoreboard_target.dailyPaintTarget,
day
) + bodyshop.scoreboard_target.dailyPaintTarget,
1
),
accHrs: 0,
};
if (ticketsGroupedByDate[day]) {
ticketsGroupedByDate[day].forEach((ticket) => {
r.actualhrs = r.actualhrs + ticket.actualhrs;
r.productivehrs = r.productivehrs + ticket.productivehrs;
acc_comb = acc_comb + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
acc_lab = acc_lab + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
acc_lar = acc_lar + ticket.productivehrs;
});
}
combined.accHrs = acc_comb;
lab.accHrs = acc_lab;
lar.accHrs = acc_lar;
combinedData.push({...r, ...combined});
labData.push({...r, ...lab});
larData.push({...r, ...lar});
});
const jobData = {};
const dataJobs = data.jobs.map((job) => ({
...job,
tthrs: job.joblines.reduce((acc, val) => acc + val.mod_lb_hrs, 0)
}));
jobData.tthrs = dataJobs
.reduce((acc, val) => acc + val.tthrs, 0)
.toFixed(1);
jobData.count = dataJobs.length.toFixed(0);
return {
fixed: ret,
combinedData: combinedData,
labData: labData,
larData: larData,
jobData: jobData,
};
}, [fixedPeriods, data, bodyshop]);
if (error) return <AlertComponent message={error.message} type="error"/>;
if (loading) return <LoadingSpinner/>;
return (
<Row gutter={[16, 16]}>
<Col span={24}>
<ScoreboardTimeticketsTargetsTable/>
</Col>
<Col span={24}>
<ScoreboardTicketsStats
data={calculatedData.fixed}
jobData={calculatedData.jobData}
/>
</Col>
<Col span={24}>
<ScoreboardTimeTicketsChart
data={calculatedData.combinedData}
chartTitle={t("scoreboard.labels.combinedcharttitle")}
/>
</Col>
<Col span={12}>
<ScoreboardTimeTicketsChart
data={calculatedData.labData}
chartTitle={t("scoreboard.labels.bodycharttitle")}
/>
</Col>
<Col span={12}>
<ScoreboardTimeTicketsChart
data={calculatedData.larData}
chartTitle={t("scoreboard.labels.refinishcharttitle")}
/>
</Col>
</Row>
);
}
function roundObject(inputObj) {
for (var key of Object.keys(inputObj)) {
if (typeof inputObj[key] === "number") {
inputObj[key] = inputObj[key].toFixed(1);
} else if (Array.isArray(inputObj[key])) {
inputObj[key].forEach((item) => roundObject(item));
} else if (typeof inputObj[key] === "object") {
roundObject(inputObj[key]);
for (var key of Object.keys(inputObj)) {
if (typeof inputObj[key] === "number") {
inputObj[key] = inputObj[key].toFixed(1);
} else if (Array.isArray(inputObj[key])) {
inputObj[key].forEach((item) => roundObject(item));
} else if (typeof inputObj[key] === "object") {
roundObject(inputObj[key]);
}
}
}
}

View File

@@ -1,17 +1,21 @@
import { onError } from "@apollo/client/link/error";
//https://stackoverflow.com/questions/57163454/refreshing-a-token-with-apollo-client-firebase-auth
import * as Sentry from "@sentry/react";
const errorLink = onError(
({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) => {
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
);
Sentry.captureException({ message, locations, path });
});
}
if (networkError)
console.log(`[Network error]: ${JSON.stringify(networkError)}`);
console.log(operation.getContext());
return forward(operation);
}
);

View File

@@ -545,147 +545,166 @@ export const QUERY_JOB_COSTING_DETAILS = gql`
export const GET_JOB_BY_PK = gql`
query GET_JOB_BY_PK($id: uuid!) {
jobs_by_pk(id: $id) {
updated_at
actual_completion
actual_delivery
actual_in
adjustment_bottom_line
area_of_damage
auto_add_ats
available_jobs {
id
}
alt_transport
ca_bc_pvrt
ca_customer_gst
ca_gst_registrant
category
cccontracts {
agreementnumber
courtesycar {
fleetnumber
id
make
model
plate
year
}
id
scheduledreturn
start
status
}
cieca_ttl
class
clm_no
clm_total
comment
converted
csiinvites {
completedon
id
}
date_estimated
date_exported
date_invoiced
date_last_contacted
date_lost_sale
date_next_contact
date_open
date_rentalresp
date_repairstarted
date_scheduled
date_towin
date_void
ded_amt
ded_note
ded_status
deliverchecklist
depreciation_taxes
driveable
employee_body
employee_body_rel {
id
first_name
last_name
}
employee_refinish_rel {
id
first_name
last_name
}
employee_prep_rel {
id
first_name
last_name
}
employee_csr
employee_csr_rel {
id
first_name
last_name
}
employee_csr
employee_prep
employee_prep_rel {
id
first_name
last_name
}
employee_refinish
employee_body
alt_transport
intakechecklist
invoice_final_note
comment
loss_desc
kmin
kmout
referral_source
referral_source_extra
unit_number
po_number
special_coverage_policy
scheduled_delivery
converted
lbr_adjustments
ro_number
po_number
clm_total
employee_refinish_rel {
id
first_name
last_name
}
est_co_nm
est_ct_fn
est_ct_ln
est_ea
est_ph1
federal_tax_rate
id
inproduction
vehicleid
plate_no
plate_st
v_vin
v_model_yr
v_model_desc
v_make_desc
v_color
vehicleid
driveable
towin
loss_of_use
lost_sale_reason
vehicle {
id
plate_no
plate_st
v_vin
v_model_yr
v_model_desc
v_make_desc
v_color
notes
v_paint_codes
jobs {
id
ro_number
status
clm_no
}
}
available_jobs {
id
}
ins_co_id
policy_no
loss_date
clm_no
area_of_damage
ins_co_nm
ins_addr1
ins_city
ins_co_id
ins_co_nm
ins_ct_ln
ins_ct_fn
ins_ea
ins_ph1
est_co_nm
est_ct_fn
est_ct_ln
est_ph1
est_ea
selling_dealer
servicing_dealer
selling_dealer_contact
servicing_dealer_contact
regie_number
scheduled_completion
id
ded_amt
ded_status
depreciation_taxes
other_amount_payable
towing_payable
storage_payable
adjustment_bottom_line
federal_tax_rate
state_tax_rate
local_tax_rate
tax_tow_rt
tax_str_rt
tax_paint_mat_rt
tax_shop_mat_rt
tax_sub_rt
tax_lbr_rt
tax_levies_rt
parts_tax_rates
job_totals
ownr_fn
ownr_ln
ownr_co_nm
ownr_ea
ownr_addr1
ownr_addr2
ownr_city
ownr_st
ownr_zip
ownr_ctry
ownr_ph1
ownr_ph2
production_vars
ca_gst_registrant
ownerid
ded_note
materials
auto_add_ats
rate_ats
intakechecklist
invoice_final_note
iouparent
job_totals
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
act_price
ah_detail_line
alt_partm
alt_partno
billlines(limit: 1, order_by: { bill: { date: desc } }) {
actual_cost
actual_price
bill {
id
invoice_number
vendor {
id
name
}
}
joblineid
id
quantity
}
convertedtolbr
critical
db_hrs
db_price
db_ref
id
ioucreated
lbr_amt
lbr_op
line_desc
line_ind
line_no
line_ref
location
manual_line
mod_lb_hrs
mod_lbr_ty
notes
oem_partno
op_code_desc
part_qty
part_type
prt_dsmk_m
prt_dsmk_p
status
tax_part
unq_seq
}
kmin
kmout
labor_rate_desc
lbr_adjustments
local_tax_rate
loss_date
loss_desc
loss_of_use
lost_sale_reason
materials
other_amount_payable
owner {
id
ownr_fn
@@ -702,7 +721,40 @@ export const GET_JOB_BY_PK = gql`
ownr_ph2
tax_number
}
labor_rate_desc
owner_owing
ownerid
ownr_addr1
ownr_addr2
ownr_ctry
ownr_city
ownr_co_nm
ownr_ea
ownr_fn
ownr_ln
ownr_ph1
ownr_ph2
ownr_st
ownr_zip
parts_tax_rates
payments {
amount
created_at
date
exportedat
id
jobid
memo
payer
paymentnum
transactionid
type
}
plate_no
plate_st
po_number
policy_no
production_vars
rate_ats
rate_la1
rate_la2
rate_la3
@@ -726,121 +778,64 @@ export const GET_JOB_BY_PK = gql`
rate_mapa
rate_mash
rate_matd
actual_in
federal_tax_rate
local_tax_rate
state_tax_rate
regie_number
referral_source
referral_source_extra
remove_from_ar
ro_number
scheduled_completion
scheduled_in
actual_completion
scheduled_delivery
actual_delivery
date_estimated
date_open
date_scheduled
date_invoiced
date_last_contacted
date_lost_sale
date_next_contact
date_towin
date_rentalresp
date_exported
date_repairstarted
date_void
scheduled_in
selling_dealer
servicing_dealer
selling_dealer_contact
servicing_dealer_contact
special_coverage_policy
state_tax_rate
status
owner_owing
tax_registration_number
class
category
deliverchecklist
voided
ca_bc_pvrt
ca_customer_gst
storage_payable
suspended
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
tax_lbr_rt
tax_levies_rt
tax_paint_mat_rt
tax_registration_number
tax_shop_mat_rt
tax_str_rt
tax_sub_rt
tax_tow_rt
towin
towing_payable
unit_number
updated_at
v_vin
v_model_yr
v_model_desc
v_make_desc
v_color
vehicleid
vehicle {
id
alt_partm
line_no
unq_seq
line_ind
line_desc
line_ref
part_type
oem_partno
alt_partno
db_price
act_price
part_qty
mod_lbr_ty
db_hrs
mod_lb_hrs
lbr_op
lbr_amt
op_code_desc
status
jobs {
clm_no
id
ro_number
status
}
notes
location
tax_part
db_ref
manual_line
prt_dsmk_p
prt_dsmk_m
ioucreated
convertedtolbr
ah_detail_line
critical
billlines(limit: 1, order_by: { bill: { date: desc } }) {
id
quantity
actual_cost
actual_price
joblineid
bill {
id
invoice_number
vendor {
id
name
}
}
}
}
payments {
id
jobid
amount
payer
paymentnum
created_at
transactionid
memo
date
type
exportedat
}
cccontracts {
id
status
start
scheduledreturn
agreementnumber
courtesycar {
id
make
model
year
plate
fleetnumber
}
}
cieca_ttl
csiinvites {
id
completedon
plate_no
plate_st
v_color
v_make_desc
v_model_desc
v_model_yr
v_paint_codes
v_vin
}
voided
}
}
`;
export const GET_JOB_RECONCILIATION_BY_PK = gql`
query GET_JOB_RECONCILIATION_BY_PK($id: uuid!) {
bills(where: { jobid: { _eq: $id } }) {
@@ -905,6 +900,7 @@ export const GET_JOB_RECONCILIATION_BY_PK = gql`
}
}
`;
export const QUERY_JOB_CARD_DETAILS = gql`
query QUERY_JOB_CARD_DETAILS($id: uuid!) {
jobs_by_pk(id: $id) {
@@ -2225,3 +2221,120 @@ export const GET_JOB_LINE_ORDERS = gql`
}
}
`;
export const UPDATE_REMOVE_FROM_AR = gql`
mutation UPDATE_REMOVE_FROM_AR($jobId: uuid!, $remove_from_ar: Boolean!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { remove_from_ar: $remove_from_ar }
) {
id
remove_from_ar
}
}
`;
export const UNVOID_JOB = gql`
mutation UNVOID_JOB(
$jobId: uuid!
$default_imported: String!
$currentUserEmail: String!
$text: String!
) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { voided: false, status: $default_imported, date_void: null }
) {
id
date_void
voided
status
}
insert_notes(
objects: {
jobid: $jobId
audit: true
created_by: $currentUserEmail
text: $text
}
) {
returning {
id
}
}
}
`;
export const DELETE_INTAKE_CHECKLIST = gql`
mutation DELETE_INTAKE($jobId: uuid!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { intakechecklist: null }
) {
id
intakechecklist
}
}
`;
export const DELETE_DELIVERY_CHECKLIST = gql`
mutation DELETE_DELIVERY($jobId: uuid!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { deliverchecklist: null }
) {
id
deliverchecklist
}
}
`;
export const MARK_JOB_FOR_REEXPORT = gql`
mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!, $default_invoiced: String!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { date_exported: null, status: $default_invoiced }
) {
id
date_exported
status
date_invoiced
}
}
`;
export const MARK_JOB_AS_EXPORTED = gql`
mutation MARK_JOB_AS_EXPORTED(
$jobId: uuid!
$date_exported: timestamptz!
$default_exported: String!
) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { date_exported: $date_exported, status: $default_exported }
) {
id
date_exported
date_invoiced
status
}
}
`;
export const MARK_JOB_AS_UNINVOICED = gql`
mutation MARK_JOB_AS_UNINVOICED($jobId: uuid!, $default_delivered: String!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: {
date_exported: null
date_invoiced: null
status: $default_delivered
}
) {
id
date_exported
date_invoiced
status
}
}
`;

View File

@@ -13,6 +13,7 @@ import reportWebVitals from "./reportWebVitals";
import "./translations/i18n";
import "./utils/CleanAxios";
import { ConfigProvider } from "antd";
//import { BrowserTracing } from "@sentry/tracing";
//import "antd/dist/antd.css";
// import "antd/dist/antd.less";
@@ -25,24 +26,20 @@ if (import.meta.env.PROD) {
Sentry.init({
dsn: "https://fd7e89369b6b4bdc9c6c4c9f22fa4ee4@o492140.ingest.sentry.io/5651027",
ignoreErrors: [
"ResizeObserver loop",
"Module specifier, 'fs' does not start",
"Module specifier, 'zlib' does not start with",
],
integrations: [
// new BrowserTracing(),
// new Sentry.Integrations.Breadcrumbs({ console: true }),
// new Sentry.Replay(),
Sentry.replayIntegration({
maskAllText: false,
blockAllMedia: true,
}),
new Sentry.BrowserTracing(),
],
// This sets the sample rate to be 10%. You may want this to be 100% while
// in development and sample at a lower rate in production
// replaysSessionSampleRate: 0.1,
// // If the entire session is not sampled, use the below sample rate to sample
// // sessions when an error occurs.
// replaysOnErrorSampleRate: 1.0,
tracesSampleRate: 1.0,
replaysOnErrorSampleRate: 1.0,
environment: import.meta.env.MODE,
// tracesSampleRate: 0.2,
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
// tracesSampleRate: 0.5,
});
}

View File

@@ -7,16 +7,16 @@ import { useParams } from "react-router-dom";
import AlertComponent from "../../components/alert/alert.component";
import JobCalculateTotals from "../../components/job-calculate-totals/job-calculate-totals.component";
import ScoreboardAddButton from "../../components/job-scoreboard-add-button/job-scoreboard-add-button.component";
import JobsAdminStatus from "../../components/jobs-admin-change-status/jobs-admin-change.status.component";
import JobsAdminClass from "../../components/jobs-admin-class/jobs-admin-class.component";
import JobsAdminDatesChange from "../../components/jobs-admin-dates/jobs-admin-dates.component";
import JobsAdminDeleteIntake from "../../components/jobs-admin-delete-intake/jobs-admin-delete-intake.component";
import JobsAdminMarkReexport from "../../components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component";
import JobAdminOwnerReassociate from "../../components/jobs-admin-owner-reassociate/jobs-admin-owner-reassociate.component";
import JobsAdminRemoveAR from "../../components/jobs-admin-remove-ar/jobs-admin-remove-ar.component";
import JobsAdminUnvoid from "../../components/jobs-admin-unvoid/jobs-admin-unvoid.component";
import JobAdminVehicleReassociate from "../../components/jobs-admin-vehicle-reassociate/jobs-admin-vehicle-reassociate.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import JobsAdminStatus from "../../components/jobs-admin-change-status/jobs-admin-change.status.component";
import NotFound from "../../components/not-found/not-found.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import { GET_JOB_BY_PK } from "../../graphql/jobs.queries";
@@ -104,6 +104,7 @@ export function JobsCloseContainer({ setBreadcrumbs, setSelectedHeader }) {
<JobsAdminMarkReexport job={data ? data.jobs_by_pk : {}} />
<JobsAdminUnvoid job={data ? data.jobs_by_pk : {}} />
<JobsAdminStatus job={data ? data.jobs_by_pk : {}} />
<JobsAdminRemoveAR job={data ? data.jobs_by_pk : {}} />
</Space>
</Card>
</Col>

View File

@@ -55,6 +55,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import UndefinedToNull from "../../utils/undefinedtonull";
import { DateTimeFormat } from "./../../utils/DateFormatter";
import JobLifecycleComponent from "../../components/job-lifecycle/job-lifecycle.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -305,6 +306,12 @@ export function JobsDetailPage({
label: t("menus.jobsdetail.labor"),
children: <JobsDetailLaborContainer job={job} jobId={job.id} />,
},
{
key: 'lifecycle',
icon: <BarsOutlined />,
label: t('menus.jobsdetail.lifecycle'),
children: <JobLifecycleComponent job={job} statuses={bodyshop.md_ro_statuses} />,
},
{
key: "dates",
icon: <CalendarFilled />,

View File

@@ -99,6 +99,7 @@
},
"audit_trail": {
"messages": {
"admin_job_remove_from_ar": "ADMIN: Remove from AR updated to: {{status}}",
"admin_jobmarkexported": "ADMIN: Job marked as exported.",
"admin_jobmarkforreexport": "ADMIN: Job marked for re-export.",
"admin_jobuninvoice": "ADMIN: Job has been uninvoiced.",
@@ -112,11 +113,11 @@
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
"jobinvoiced": "Job has been invoiced.",
"jobconverted": "Job converted and assigned number {{ro_number}}.",
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
"jobimported": "Job imported.",
"jobinproductionchange": "Job production status set to {{inproduction}}",
"jobinvoiced": "Job has been invoiced.",
"jobioucreated": "IOU Created.",
"jobmodifylbradj": "Labor adjustments modified {{mod_lbr_ty}} / {{hours}}.",
"jobnoteadded": "Note added to Job.",
@@ -254,7 +255,6 @@
"saving": "Error encountered while saving. {{message}}"
},
"fields": {
"ReceivableCustomField": "QBO Receivable Custom Field {{number}}",
"address1": "Address 1",
"address2": "Address 2",
"appt_alt_transport": "Appointment Alternative Transportation Options",
@@ -331,6 +331,9 @@
"md_ded_notes": "Deductible Notes",
"md_email_cc": "Auto Email CC: $t(printcenter.subjects.jobs.{{template}})",
"md_from_emails": "Additional From Emails",
"md_functionality_toggles": {
"parts_queue_toggle": "Auto Add Imported/Supplemented Jobs to Parts Queue"
},
"md_hour_split": {
"paint": "Paint Hour Split",
"prep": "Prep Hour Split"
@@ -353,9 +356,6 @@
},
"md_payment_types": "Payment Types",
"md_referral_sources": "Referral Sources",
"md_functionality_toggles": {
"parts_queue_toggle": "Auto Add Imported/Supplemented Jobs to Parts Queue"
},
"md_tasks_presets": {
"hourstype": "",
"memo": "",
@@ -473,6 +473,7 @@
"editaccess": "Users -> Edit access"
}
},
"ReceivableCustomField": "QBO Receivable Custom Field {{number}}",
"responsibilitycenter": "Responsibility Center",
"responsibilitycenter_accountdesc": "Account Description",
"responsibilitycenter_accountitem": "Item",
@@ -814,6 +815,10 @@
"usage": "Usage",
"vehicle": "Vehicle Description"
},
"readiness": {
"notready": "Not Ready",
"ready": "Ready"
},
"status": {
"in": "Available",
"inservice": "In Service",
@@ -823,10 +828,6 @@
},
"successes": {
"saved": "Courtesy Car saved successfully."
},
"readiness": {
"notready": "Not Ready",
"ready": "Ready"
}
},
"csi": {
@@ -1248,6 +1249,31 @@
"updated": "Inventory line updated."
}
},
"job_lifecycle": {
"columns": {
"duration": "Duration",
"end": "End",
"relative_end": "Relative End",
"relative_start": "Relative Start",
"start": "Start",
"value": "Value"
},
"content": {
"current_status_accumulated_time": "Current Status Accumulated Time",
"data_unavailable": " There is currently no Lifecycle data for this Job.",
"legend_title": "Legend",
"loading": "Loading Job Timelines....",
"not_available": "N/A",
"previous_status_accumulated_time": "Previous Status Accumulated Time",
"title": "Job Lifecycle Component",
"title_durations": "Historical Status Durations",
"title_loading": "Loading",
"title_transitions": "Transitions"
},
"errors": {
"fetch": "Error getting Job Lifecycle Data"
}
},
"job_payments": {
"buttons": {
"goback": "Go Back",
@@ -1868,6 +1894,7 @@
},
"reconciliationheader": "Parts & Sublet Reconciliation",
"relatedros": "Related ROs",
"remove_from_ar": "Remove from AR",
"returntotals": "Return Totals",
"rosaletotal": "RO Parts Total",
"sale_additional": "Sales - Additional",
@@ -2047,6 +2074,7 @@
"general": "General",
"insurance": "Insurance Information",
"labor": "Labor",
"lifecycle": "Lifecycle",
"partssublet": "Parts & Bills",
"rates": "Rates",
"repairdata": "Repair Data",
@@ -2598,6 +2626,7 @@
},
"templates": {
"anticipated_revenue": "Anticipated Revenue",
"ar_aging": "AR Aging",
"attendance_detail": "Attendance (All Employees)",
"attendance_employee": "Employee Attendance",
"attendance_summary": "Attendance Summary (All Employees)",
@@ -2954,8 +2983,8 @@
"shop-templates": "Shop Templates | $t(titles.app)",
"shop_vendors": "Vendors | $t(titles.app)",
"techconsole": "Technician Console | $t(titles.app)",
"techjoblookup": "Technician Job Lookup | $t(titles.app)",
"techjobclock": "Technician Job Clock | $t(titles.app)",
"techjoblookup": "Technician Job Lookup | $t(titles.app)",
"techshiftclock": "Technician Shift Clock | $t(titles.app)",
"temporarydocs": "Temporary Documents | $t(titles.app)",
"timetickets": "Time Tickets | $t(titles.app)",

View File

@@ -99,6 +99,7 @@
},
"audit_trail": {
"messages": {
"admin_job_remove_from_ar": "",
"admin_jobmarkexported": "",
"admin_jobmarkforreexport": "",
"admin_jobuninvoice": "",
@@ -112,11 +113,11 @@
"jobassignmentchange": "",
"jobassignmentremoved": "",
"jobchecklist": "",
"jobinvoiced": "",
"jobconverted": "",
"jobfieldchanged": "",
"jobimported": "",
"jobinproductionchange": "",
"jobinvoiced": "",
"jobioucreated": "",
"jobmodifylbradj": "",
"jobnoteadded": "",
@@ -254,13 +255,9 @@
"saving": ""
},
"fields": {
"ReceivableCustomField": "",
"address1": "",
"address2": "",
"appt_alt_transport": "",
"md_functionality_toggles": {
"parts_queue_toggle": ""
},
"appt_colors": {
"color": "",
"label": ""
@@ -334,6 +331,9 @@
"md_ded_notes": "",
"md_email_cc": "",
"md_from_emails": "",
"md_functionality_toggles": {
"parts_queue_toggle": ""
},
"md_hour_split": {
"paint": "",
"prep": ""
@@ -473,6 +473,7 @@
"editaccess": ""
}
},
"ReceivableCustomField": "",
"responsibilitycenter": "",
"responsibilitycenter_accountdesc": "",
"responsibilitycenter_accountitem": "",
@@ -814,6 +815,10 @@
"usage": "",
"vehicle": ""
},
"readiness": {
"notready": "",
"ready": ""
},
"status": {
"in": "",
"inservice": "",
@@ -823,10 +828,6 @@
},
"successes": {
"saved": ""
},
"readiness": {
"notready": "",
"ready": ""
}
},
"csi": {
@@ -1248,6 +1249,31 @@
"updated": ""
}
},
"job_lifecycle": {
"columns": {
"duration": "",
"end": "",
"relative_end": "",
"relative_start": "",
"start": "",
"value": ""
},
"content": {
"current_status_accumulated_time": "",
"data_unavailable": "",
"legend_title": "",
"loading": "",
"not_available": "",
"previous_status_accumulated_time": "",
"title": "",
"title_durations": "",
"title_loading": "",
"title_transitions": ""
},
"errors": {
"fetch": "Error al obtener los datos del ciclo de vida del trabajo"
}
},
"job_payments": {
"buttons": {
"goback": "",
@@ -1868,6 +1894,7 @@
},
"reconciliationheader": "",
"relatedros": "",
"remove_from_ar": "",
"returntotals": "",
"rosaletotal": "",
"sale_additional": "",
@@ -2047,6 +2074,7 @@
"general": "",
"insurance": "",
"labor": "Labor",
"lifecycle": "",
"partssublet": "Piezas / Subarrendamiento",
"rates": "",
"repairdata": "Datos de reparación",
@@ -2598,6 +2626,7 @@
},
"templates": {
"anticipated_revenue": "",
"ar_aging": "",
"attendance_detail": "",
"attendance_employee": "",
"attendance_summary": "",
@@ -2954,8 +2983,8 @@
"shop-templates": "",
"shop_vendors": "Vendedores | $t(titles.app)",
"techconsole": "$t(titles.app)",
"techjoblookup": "$t(titles.app)",
"techjobclock": "$t(titles.app)",
"techjoblookup": "$t(titles.app)",
"techshiftclock": "$t(titles.app)",
"temporarydocs": "",
"timetickets": "",

View File

@@ -99,6 +99,7 @@
},
"audit_trail": {
"messages": {
"admin_job_remove_from_ar": "",
"admin_jobmarkexported": "",
"admin_jobmarkforreexport": "",
"admin_jobuninvoice": "",
@@ -112,11 +113,11 @@
"jobassignmentchange": "",
"jobassignmentremoved": "",
"jobchecklist": "",
"jobinvoiced": "",
"jobconverted": "",
"jobfieldchanged": "",
"jobimported": "",
"jobinproductionchange": "",
"jobinvoiced": "",
"jobioucreated": "",
"jobmodifylbradj": "",
"jobnoteadded": "",
@@ -254,7 +255,6 @@
"saving": ""
},
"fields": {
"ReceivableCustomField": "",
"address1": "",
"address2": "",
"appt_alt_transport": "",
@@ -331,13 +331,13 @@
"md_ded_notes": "",
"md_email_cc": "",
"md_from_emails": "",
"md_functionality_toggles": {
"parts_queue_toggle": ""
},
"md_hour_split": {
"paint": "",
"prep": ""
},
"md_functionality_toggles": {
"parts_queue_toggle": ""
},
"md_ins_co": {
"city": "",
"name": "",
@@ -473,6 +473,7 @@
"editaccess": ""
}
},
"ReceivableCustomField": "",
"responsibilitycenter": "",
"responsibilitycenter_accountdesc": "",
"responsibilitycenter_accountitem": "",
@@ -814,6 +815,10 @@
"usage": "",
"vehicle": ""
},
"readiness": {
"notready": "",
"ready": ""
},
"status": {
"in": "",
"inservice": "",
@@ -823,10 +828,6 @@
},
"successes": {
"saved": ""
},
"readiness": {
"notready": "",
"ready": ""
}
},
"csi": {
@@ -1248,6 +1249,31 @@
"updated": ""
}
},
"job_lifecycle": {
"columns": {
"duration": "",
"end": "",
"relative_end": "",
"relative_start": "",
"start": "",
"value": ""
},
"content": {
"current_status_accumulated_time": "",
"data_unavailable": "",
"legend_title": "",
"loading": "",
"not_available": "",
"previous_status_accumulated_time": "",
"title": "",
"title_durations": "",
"title_loading": "",
"title_transitions": ""
},
"errors": {
"fetch": "Erreur lors de l'obtention des données du cycle de vie des tâches"
}
},
"job_payments": {
"buttons": {
"goback": "",
@@ -1868,6 +1894,7 @@
},
"reconciliationheader": "",
"relatedros": "",
"remove_from_ar": "",
"returntotals": "",
"rosaletotal": "",
"sale_additional": "",
@@ -2047,6 +2074,7 @@
"general": "",
"insurance": "",
"labor": "La main d'oeuvre",
"lifecycle": "",
"partssublet": "Pièces / Sous-location",
"rates": "",
"repairdata": "Données de réparation",
@@ -2598,6 +2626,7 @@
},
"templates": {
"anticipated_revenue": "",
"ar_aging": "",
"attendance_detail": "",
"attendance_employee": "",
"attendance_summary": "",
@@ -2954,8 +2983,8 @@
"shop-templates": "",
"shop_vendors": "Vendeurs | $t(titles.app)",
"techconsole": "$t(titles.app)",
"techjoblookup": "$t(titles.app)",
"techjobclock": "$t(titles.app)",
"techjoblookup": "$t(titles.app)",
"techshiftclock": "$t(titles.app)",
"temporarydocs": "",
"timetickets": "",

View File

@@ -1,54 +1,56 @@
import i18n from "i18next";
const AuditTrailMapping = {
alertToggle: (status) => i18n.t("audit_trail.messages.alerttoggle", { status }),
admin_job_remove_from_ar: (status) =>
i18n.t("audit_trail.messages.admin_job_remove_from_ar", { status }),
admin_jobfieldchange: (field, value) =>
"ADMIN: " +
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
admin_jobmarkexported: () =>
i18n.t("audit_trail.messages.admin_jobmarkexported"),
admin_jobmarkforreexport: () =>
i18n.t("audit_trail.messages.admin_jobmarkforreexport"),
admin_jobstatuschange: (status) =>
"ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }),
admin_jobuninvoice: () => i18n.t("audit_trail.messages.admin_jobuninvoice"),
admin_jobunvoid: () => i18n.t("audit_trail.messages.admin_jobunvoid"),
alertToggle: (status) =>
i18n.t("audit_trail.messages.alerttoggle", { status }),
appointmentcancel: (lost_sale_reason) =>
i18n.t("audit_trail.messages.appointmentcancel", { lost_sale_reason }),
appointmentinsert: (start) =>
i18n.t("audit_trail.messages.appointmentinsert", { start }),
jobstatuschange: (status) =>
i18n.t("audit_trail.messages.jobstatuschange", { status }),
admin_jobstatuschange: (status) =>
"ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }),
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
jobinvoiced: () =>
i18n.t("audit_trail.messages.jobinvoiced"),
jobconverted: (ro_number) =>
i18n.t("audit_trail.messages.jobconverted", { ro_number }),
jobfieldchange: (field, value) =>
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
admin_jobfieldchange: (field, value) =>
"ADMIN: " +
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
jobspartsorder: (order_number) =>
i18n.t("audit_trail.messages.jobspartsorder", { order_number }),
jobspartsreturn: (order_number) =>
i18n.t("audit_trail.messages.jobspartsreturn", { order_number }),
jobmodifylbradj: ({ mod_lbr_ty, hours }) =>
i18n.t("audit_trail.messages.jobmodifylbradj", { mod_lbr_ty, hours }),
billposted: (invoice_number) =>
i18n.t("audit_trail.messages.billposted", { invoice_number }),
billupdated: (invoice_number) =>
i18n.t("audit_trail.messages.billupdated", { invoice_number }),
failedpayment: () => i18n.t("audit_trail.messages.failedpayment"),
jobassignmentchange: (operation, name) =>
i18n.t("audit_trail.messages.jobassignmentchange", { operation, name }),
jobassignmentremoved: (operation) =>
i18n.t("audit_trail.messages.jobassignmentremoved", { operation }),
jobinproductionchange: (inproduction) =>
i18n.t("audit_trail.messages.jobinproductionchange", { inproduction }),
jobchecklist: (type, inproduction, status) =>
i18n.t("audit_trail.messages.jobchecklist", { type, inproduction, status }),
jobconverted: (ro_number) =>
i18n.t("audit_trail.messages.jobconverted", { ro_number }),
jobfieldchange: (field, value) =>
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
jobinproductionchange: (inproduction) =>
i18n.t("audit_trail.messages.jobinproductionchange", { inproduction }),
jobinvoiced: () => i18n.t("audit_trail.messages.jobinvoiced"),
jobmodifylbradj: ({ mod_lbr_ty, hours }) =>
i18n.t("audit_trail.messages.jobmodifylbradj", { mod_lbr_ty, hours }),
jobnoteadded: () => i18n.t("audit_trail.messages.jobnoteadded"),
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
jobnotedeleted: () => i18n.t("audit_trail.messages.jobnotedeleted"),
admin_jobunvoid: () => i18n.t("audit_trail.messages.admin_jobunvoid"),
admin_jobuninvoice: () => i18n.t("audit_trail.messages.admin_jobuninvoice"),
admin_jobmarkforreexport: () =>
i18n.t("audit_trail.messages.admin_jobmarkforreexport"),
admin_jobmarkexported: () =>
i18n.t("audit_trail.messages.admin_jobmarkexported"),
failedpayment: () => i18n.t("audit_trail.messages.failedpayment"),
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
jobspartsorder: (order_number) =>
i18n.t("audit_trail.messages.jobspartsorder", { order_number }),
jobspartsreturn: (order_number) =>
i18n.t("audit_trail.messages.jobspartsreturn", { order_number }),
jobstatuschange: (status) =>
i18n.t("audit_trail.messages.jobstatuschange", { status }),
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
};
export default AuditTrailMapping;

View File

@@ -17,6 +17,9 @@ export function DateTimeFormatter(props) {
)
: null;
}
export function DateTimeFormatterFunction(date) {
return dayjs(date).format("MM/DD/YYYY hh:mm a");
}
export function TimeFormatter(props) {
return props.children
? dayjs(props.children).format(props.format ? props.format : "hh:mm a")

View File

@@ -12,6 +12,8 @@ import apolloLogger from "apollo-link-logger";
//import axios from "axios";
import { auth } from "../firebase/firebase.utils";
import errorLink from "../graphql/apollo-error-handling";
import { SentryLink } from "apollo-link-sentry";
//import { store } from "../redux/store";
const httpLink = new HttpLink({
uri: import.meta.env.VITE_APP_GRAPHQL_ENDPOINT,
@@ -105,18 +107,30 @@ const link = split(
const authLink = setContext((_, { headers }) => {
return (
auth.currentUser &&
auth.currentUser.getIdToken().then((token) => {
if (token) {
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
} else {
auth.currentUser
.getIdToken()
.then((token) => {
if (token) {
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
} else {
console.error(
"Authentication error. Unable to add authorization token because it was empty."
);
return { headers };
}
})
.catch((error) => {
console.error(
"Authentication error. Unable to add authorization token.",
error.message
);
return { headers };
}
})
})
);
});
@@ -138,8 +152,10 @@ if (import.meta.env.DEV) {
}
middlewares.push(
roundTripLink.concat(
retryLink.concat(errorLink.concat(authLink.concat(link)))
new SentryLink().concat(
roundTripLink.concat(
retryLink.concat(errorLink.concat(authLink.concat(link)))
)
)
);

View File

@@ -2039,6 +2039,15 @@ export const TemplateList = (type, context) => {
},
group: "jobs",
},
ar_aging: {
title: i18n.t("reportcenter.templates.ar_aging"),
subject: i18n.t("reportcenter.templates.ar_aging"),
key: "ar_aging",
//idtype: "vendor",
disabled: false,
datedisable: true,
group: "customers",
},
}
: {}),
...(!type || type === "courtesycarcontract"