Merge branch 'rome/release/2024-02-09' into rome/master

This commit is contained in:
Patrick Fic
2024-02-12 13:25:15 -08:00
34 changed files with 1451 additions and 566 deletions

View File

@@ -42,31 +42,22 @@ jobs:
app-build: app-build:
docker: docker:
- image: cimg/node:16.15.0 - image: cimg/node:16.15.0
resource_class: large
working_directory: ~/repo/client working_directory: ~/repo/client
steps: steps:
- checkout: - checkout:
path: ~/repo path: ~/repo
- restore_cache:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run: - run:
name: Install Dependencies name: Install Dependencies
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn command: npm i
- save_cache:
name: Save Yarn Package Cache
key: yarn-packages-{{ checksum "yarn.lock" }}
paths:
- ~/.cache/yarn
- run: yarn run build - run: npm run build
- aws-s3/sync: - aws-s3/sync:
from: build from: build
to: "s3://imex-online-production/" to: "s3://imex-online-production/"
arguments: "--exclude '*.map'"
- jira/notify - jira/notify
rome-api-deploy: rome-api-deploy:
@@ -245,31 +236,22 @@ jobs:
test-app-build: test-app-build:
docker: docker:
- image: cimg/node:16.15.0 - image: cimg/node:16.15.0
resource_class: large
working_directory: ~/repo/client working_directory: ~/repo/client
steps: steps:
- checkout: - checkout:
path: ~/repo path: ~/repo
- restore_cache:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run: - run:
name: Install Dependencies name: Install Dependencies
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn command: npm i
- save_cache:
name: Save Yarn Package Cache
key: yarn-packages-{{ checksum "yarn.lock" }}
paths:
- ~/.cache/yarn
- run: yarn run build:test - run: npm run build:test
- aws-s3/sync: - aws-s3/sync:
from: build from: build
to: "s3://imex-online-test/" to: "s3://imex-online-test/"
arguments: "--exclude '*.map'"
- jira/notify - jira/notify
test-app-beta-build: test-app-beta-build:
@@ -411,4 +393,4 @@ workflows:
#- admin-app-build: #- admin-app-build:
#filters: #filters:
#branches: #branches:
#only: master #only: master

3
client/.gitignore vendored Normal file
View File

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

View File

@@ -1,25 +1,25 @@
// craco.config.js // craco.config.js
const TerserPlugin = require("terser-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin");
const CracoLessPlugin = require("craco-less"); const CracoLessPlugin = require("craco-less");
const SentryWebpackPlugin = require("@sentry/webpack-plugin"); //const SentryWebpackPlugin = require("@sentry/webpack-plugin");
module.exports = { module.exports = {
plugins: [ plugins: [
{ // {
plugin: SentryWebpackPlugin, // plugin: SentryWebpackPlugin,
options: { // options: {
// sentry-cli configuration // // sentry-cli configuration
authToken: // authToken:
"6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260", // "6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260",
org: "snapt-software", // org: "snapt-software",
project: "rome-online", // project: "imexonline",
release: process.env.REACT_APP_GIT_SHA, // release: process.env.REACT_APP_GIT_SHA,
// webpack-specific configuration // // webpack-specific configuration
include: ".", // include: ".",
ignore: ["node_modules", "webpack.config.js"], // ignore: ["node_modules", "webpack.config.js"],
}, // },
}, // },
{ {
plugin: CracoLessPlugin, plugin: CracoLessPlugin,
options: { options: {

634
client/package-lock.json generated
View File

@@ -13,12 +13,14 @@
"@craco/craco": "^7.0.0", "@craco/craco": "^7.0.0",
"@fingerprintjs/fingerprintjs": "^3.4.2", "@fingerprintjs/fingerprintjs": "^3.4.2",
"@jsreport/browser-client": "^3.1.0", "@jsreport/browser-client": "^3.1.0",
"@sentry/react": "^7.40.0", "@sentry/cli": "^2.27.0",
"@sentry/react": "^7.99.0",
"@sentry/tracing": "^7.40.0", "@sentry/tracing": "^7.40.0",
"@splitsoftware/splitio-react": "^1.8.1", "@splitsoftware/splitio-react": "^1.8.1",
"@tanem/react-nprogress": "^5.0.8", "@tanem/react-nprogress": "^5.0.8",
"antd": "^4.24.8", "antd": "^4.24.8",
"apollo-link-logger": "^2.0.1", "apollo-link-logger": "^2.0.1",
"apollo-link-sentry": "^3.3.0",
"axios": "^1.3.4", "axios": "^1.3.4",
"craco-less": "^2.0.0", "craco-less": "^2.0.0",
"dinero.js": "^1.9.1", "dinero.js": "^1.9.1",
@@ -4214,40 +4216,195 @@
"integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@sentry/browser": { "node_modules/@sentry-internal/feedback": {
"version": "7.40.0", "version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.40.0.tgz", "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.99.0.tgz",
"integrity": "sha512-07rZ+cTcpmYB1r84/oZtmSPJJvLCxW8yIh/5s4MdKRyZpqIDKhOz6cCS/4j+l1V+MeLcNLZBjFtNdKA2eocTpg==", "integrity": "sha512-exIO1o+bE0MW4z30FxC0cYzJ4ZHSMlDPMHCBDPzU+MWGQc/fb8s58QUrx5Dnm6HTh9G3H+YlroCxIo9u0GSwGQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@sentry/core": "7.40.0", "@sentry/core": "7.99.0",
"@sentry/replay": "7.40.0", "@sentry/types": "7.99.0",
"@sentry/types": "7.40.0", "@sentry/utils": "7.99.0"
"@sentry/utils": "7.40.0", },
"tslib": "^1.9.3" "engines": {
"node": ">=12"
}
},
"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/types": "7.99.0",
"@sentry/utils": "7.99.0"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/@sentry/browser/node_modules/tslib": { "node_modules/@sentry-internal/feedback/node_modules/@sentry/types": {
"version": "1.14.1", "version": "7.99.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.99.0.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "integrity": "sha512-94qwOw4w40sAs5mCmzcGyj8ZUu/KhnWnuMZARRq96k+SjRW/tHFAOlIdnFSrt3BLPvSOK7R3bVAskZQ0N4FTmA==",
"license": "0BSD" "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.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-internal/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-internal/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-internal/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"
}
},
"node_modules/@sentry/browser": {
"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.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/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"
}
}, },
"node_modules/@sentry/cli": { "node_modules/@sentry/cli": {
"version": "1.74.6", "version": "2.27.0",
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.74.6.tgz", "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.27.0.tgz",
"integrity": "sha512-pJ7JJgozyjKZSTjOGi86chIngZMLUlYt2HOog+OJn+WGvqEkVymu8m462j1DiXAnex9NspB4zLLNuZ/R6rTQHg==", "integrity": "sha512-pc0opd71W8lGhYvmB1keQtJkarxzCS9f9ErKYv6TfXOOX6drvwkyA6vD/6xEnpzyvqGAuGRU4T4sEeLD3irwUQ==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"https-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0",
"mkdirp": "^0.5.5",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"npmlog": "^4.1.2",
"progress": "^2.0.3", "progress": "^2.0.3",
"proxy-from-env": "^1.1.0", "proxy-from-env": "^1.1.0",
"which": "^2.0.2" "which": "^2.0.2"
@@ -4256,7 +4413,124 @@
"sentry-cli": "bin/sentry-cli" "sentry-cli": "bin/sentry-cli"
}, },
"engines": { "engines": {
"node": ">= 8" "node": ">= 10"
},
"optionalDependencies": {
"@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-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"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
} }
}, },
"node_modules/@sentry/core": { "node_modules/@sentry/core": {
@@ -4280,16 +4554,15 @@
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/@sentry/react": { "node_modules/@sentry/react": {
"version": "7.40.0", "version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.40.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.99.0.tgz",
"integrity": "sha512-7yYagpOCdsXnVTtLL8Y7wAf2xXgsk2ncuju3O/G4kEckkLewZWmQeoknOSGFlAgVdGNhTaXc2WGzgOiBMOkhug==", "integrity": "sha512-RtHwgzMHJhzJfSQpVG0SDPQYMTGDX3Q37/YWI59S4ALMbSW4/F6n/eQAvGVYZKbh2UCSqgFuRWaXOYkSZT17wA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@sentry/browser": "7.40.0", "@sentry/browser": "7.99.0",
"@sentry/types": "7.40.0", "@sentry/core": "7.99.0",
"@sentry/utils": "7.40.0", "@sentry/types": "7.99.0",
"hoist-non-react-statics": "^3.3.2", "@sentry/utils": "7.99.0",
"tslib": "^1.9.3" "hoist-non-react-statics": "^3.3.2"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@@ -4298,26 +4571,82 @@
"react": "15.x || 16.x || 17.x || 18.x" "react": "15.x || 16.x || 17.x || 18.x"
} }
}, },
"node_modules/@sentry/react/node_modules/tslib": { "node_modules/@sentry/react/node_modules/@sentry/core": {
"version": "1.14.1", "version": "7.99.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.99.0.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "integrity": "sha512-vOAtzcAXEUtS/oW7wi3wMkZ3hsb5Ch96gKyrrj/mXdOp2zrcwdNV6N9/pawq2E9P/7Pw8AXw4CeDZztZrjQLuA==",
"license": "0BSD" "dependencies": {
"@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": { "node_modules/@sentry/replay": {
"version": "7.40.0", "version": "7.99.0",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.40.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.99.0.tgz",
"integrity": "sha512-Y9Kvo9jKouUdrHQhHVv5SmWZClF5o7BFI6oVpLlv4zXORPQlyoZONM/9sxiMvvH73alDSpxzCoxyhlypAOH4ww==", "integrity": "sha512-gyN/I2WpQrLAZDT+rScB/0jnFL2knEVBo8U8/OVt8gNP20Pq8T/rDZKO/TG0cBfvULDUbJj2P4CJryn2p/O2rA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@sentry/core": "7.40.0", "@sentry-internal/tracing": "7.99.0",
"@sentry/types": "7.40.0", "@sentry/core": "7.99.0",
"@sentry/utils": "7.40.0" "@sentry/types": "7.99.0",
"@sentry/utils": "7.99.0"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
}, },
"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": { "node_modules/@sentry/tracing": {
"version": "7.40.0", "version": "7.40.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.40.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.40.0.tgz",
@@ -4381,6 +4710,27 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@sentry/webpack-plugin/node_modules/@sentry/cli": {
"version": "1.77.3",
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.77.3.tgz",
"integrity": "sha512-c3eDqcDRmy4TFz2bFU5Y6QatlpoBPPa8cxBooaS4aMQpnIdLYPF1xhyyiW0LQlDUNc3rRjNF7oN5qKoaRoMTQQ==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"https-proxy-agent": "^5.0.0",
"mkdirp": "^0.5.5",
"node-fetch": "^2.6.7",
"progress": "^2.0.3",
"proxy-from-env": "^1.1.0",
"which": "^2.0.2"
},
"bin": {
"sentry-cli": "bin/sentry-cli"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/@sinclair/typebox": { "node_modules/@sinclair/typebox": {
"version": "0.24.51", "version": "0.24.51",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz",
@@ -6112,12 +6462,21 @@
"@apollo/client": "^3.0.0" "@apollo/client": "^3.0.0"
} }
}, },
"node_modules/aproba": { "node_modules/apollo-link-sentry": {
"version": "1.2.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "resolved": "https://registry.npmjs.org/apollo-link-sentry/-/apollo-link-sentry-3.3.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "integrity": "sha512-wLffWmo5sRw3rHN1Ck6azM0oxObvtaBBf3AC8cLX4SxhyjmkRIagGDji6CFkyAhxupPz0b9/H1u4Ocx+63lNug==",
"dev": true, "dependencies": {
"license": "ISC" "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": { "node_modules/arch": {
"version": "2.2.0", "version": "2.2.0",
@@ -6140,17 +6499,6 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/are-we-there-yet": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
"integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
"dev": true,
"license": "ISC",
"dependencies": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
}
},
"node_modules/arg": { "node_modules/arg": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
@@ -7571,16 +7919,6 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/collect-v8-coverage": { "node_modules/collect-v8-coverage": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
@@ -7743,13 +8081,6 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
"dev": true,
"license": "ISC"
},
"node_modules/content-disposition": { "node_modules/content-disposition": {
"version": "0.5.4", "version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -8911,13 +9242,6 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/denque": { "node_modules/denque": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
@@ -9218,6 +9542,28 @@
"tslib": "^2.0.3" "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/dot-prop/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/dotenv": { "node_modules/dotenv": {
"version": "16.0.1", "version": "16.0.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
@@ -11285,74 +11631,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
"dev": true,
"license": "ISC",
"dependencies": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.0",
"object-assign": "^4.1.0",
"signal-exit": "^3.0.0",
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wide-align": "^1.1.0"
}
},
"node_modules/gauge/node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/gauge/node_modules/is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"number-is-nan": "^1.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/gauge/node_modules/string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
"dev": true,
"license": "MIT",
"dependencies": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/gauge/node_modules/strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/gensync": { "node_modules/gensync": {
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -11755,13 +12033,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
"dev": true,
"license": "ISC"
},
"node_modules/he": { "node_modules/he": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -15746,19 +16017,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dev": true,
"license": "ISC",
"dependencies": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
"gauge": "~2.7.3",
"set-blocking": "~2.0.0"
}
},
"node_modules/nth-check": { "node_modules/nth-check": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -15771,16 +16029,6 @@
"url": "https://github.com/fb55/nth-check?sponsor=1" "url": "https://github.com/fb55/nth-check?sponsor=1"
} }
}, },
"node_modules/number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/nwsapi": { "node_modules/nwsapi": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz",
@@ -17796,7 +18044,6 @@
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.4.0" "node": ">=0.4.0"
@@ -20528,13 +20775,6 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true,
"license": "ISC"
},
"node_modules/setprototypeof": { "node_modules/setprototypeof": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -23069,16 +23309,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
"node_modules/wildcard": { "node_modules/wildcard": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",

View File

@@ -9,12 +9,14 @@
"@craco/craco": "^7.0.0", "@craco/craco": "^7.0.0",
"@fingerprintjs/fingerprintjs": "^3.4.2", "@fingerprintjs/fingerprintjs": "^3.4.2",
"@jsreport/browser-client": "^3.1.0", "@jsreport/browser-client": "^3.1.0",
"@sentry/react": "^7.40.0", "@sentry/cli": "^2.27.0",
"@sentry/react": "^7.99.0",
"@sentry/tracing": "^7.40.0", "@sentry/tracing": "^7.40.0",
"@splitsoftware/splitio-react": "^1.8.1", "@splitsoftware/splitio-react": "^1.8.1",
"@tanem/react-nprogress": "^5.0.8", "@tanem/react-nprogress": "^5.0.8",
"antd": "^4.24.8", "antd": "^4.24.8",
"apollo-link-logger": "^2.0.1", "apollo-link-logger": "^2.0.1",
"apollo-link-sentry": "^3.3.0",
"axios": "^1.3.4", "axios": "^1.3.4",
"craco-less": "^2.0.0", "craco-less": "^2.0.0",
"dinero.js": "^1.9.1", "dinero.js": "^1.9.1",
@@ -88,13 +90,14 @@
"scripts": { "scripts": {
"analyze": "source-map-explorer 'build/static/js/*.js'", "analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "craco start", "start": "craco start",
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build", "build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build && npm run sentry:sourcemaps",
"build:test": "env-cmd -f .env.test npm run build", "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!'", "build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build", "buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
"test": "cypress open", "test": "cypress open",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular ." "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": { "eslintConfig": {
"extends": [ "extends": [

View File

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

View File

@@ -105,6 +105,7 @@ function BillEnterModalContainer({
location, location,
outstanding_returns, outstanding_returns,
inventory, inventory,
federal_tax_exempt,
...remainingValues ...remainingValues
} = values; } = values;

View File

@@ -5,6 +5,7 @@ import React, { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import { QUERY_CSI_RESPONSE_BY_PK } from "../../graphql/csi.queries"; import { QUERY_CSI_RESPONSE_BY_PK } from "../../graphql/csi.queries";
import { DateFormatter } from "../../utils/DateFormatter";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import ConfigFormComponents from "../config-form-components/config-form-components.component"; import ConfigFormComponents from "../config-form-components/config-form-components.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component";
@@ -44,6 +45,13 @@ export default function CsiResponseFormContainer() {
readOnly readOnly
componentList={data.csi_by_pk.csiquestion.config} componentList={data.csi_by_pk.csiquestion.config}
/> />
{data.csi_by_pk.validuntil ? (
<>
{t("csi.fields.validuntil")}
{": "}
<DateFormatter>{data.csi_by_pk.validuntil}</DateFormatter>
</>
) : null}
</Form> </Form>
</Card> </Card>
); );

View File

@@ -5,9 +5,11 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link, useHistory, useLocation } from "react-router-dom"; import { Link, useHistory, useLocation } from "react-router-dom";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters"; import { pageLimit } from "../../utils/config";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import { alphaSort, dateSort } from "../../utils/sorters";
import {pageLimit} from "../../utils/config"; import OwnerNameDisplay, {
OwnerNameDisplayFunction,
} from "../owner-name-display/owner-name-display.component";
export default function CsiResponseListPaginated({ export default function CsiResponseListPaginated({
refetch, refetch,
@@ -16,23 +18,23 @@ export default function CsiResponseListPaginated({
total, total,
}) { }) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const { responseid, page, sortcolumn, sortorder } = search; const { responseid } = search;
const history = useHistory(); const history = useHistory();
const { t } = useTranslation();
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
filteredInfo: { text: "" }, filteredInfo: { text: "" },
page: "",
}); });
const { t } = useTranslation();
const columns = [ const columns = [
{ {
title: t("jobs.fields.ro_number"), title: t("jobs.fields.ro_number"),
dataIndex: "ro_number", dataIndex: "ro_number",
key: "ro_number", key: "ro_number",
width: "8%",
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number), sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
sortOrder: sortcolumn === "ro_number" && sortorder, sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
render: (text, record) => ( render: (text, record) => (
<Link to={"/manage/jobs/" + record.job.id}> <Link to={"/manage/jobs/" + record.job.id}>
{record.job.ro_number || t("general.labels.na")} {record.job.ro_number || t("general.labels.na")}
@@ -41,15 +43,18 @@ export default function CsiResponseListPaginated({
}, },
{ {
title: t("jobs.fields.owner"), title: t("jobs.fields.owner"),
dataIndex: "owner", dataIndex: "owner_name",
key: "owner", key: "owner_name",
ellipsis: true, sorter: (a, b) =>
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln), alphaSort(
width: "25%", OwnerNameDisplayFunction(a.job),
sortOrder: sortcolumn === "owner" && sortorder, OwnerNameDisplayFunction(b.job)
),
sortOrder:
state.sortedInfo.columnKey === "owner_name" && state.sortedInfo.order,
render: (text, record) => { render: (text, record) => {
return record.job.owner ? ( return record.job.ownerid ? (
<Link to={"/manage/owners/" + record.job.owner.id}> <Link to={"/manage/owners/" + record.job.ownerid}>
<OwnerNameDisplay ownerObject={record.job} /> <OwnerNameDisplay ownerObject={record.job} />
</Link> </Link>
) : ( ) : (
@@ -64,9 +69,9 @@ export default function CsiResponseListPaginated({
dataIndex: "completedon", dataIndex: "completedon",
key: "completedon", key: "completedon",
ellipsis: true, ellipsis: true,
sorter: (a, b) => a.completedon - b.completedon, sorter: (a, b) => dateSort(a.completedon, b.completedon),
width: "25%", sortOrder:
sortOrder: sortcolumn === "completedon" && sortorder, state.sortedInfo.columnKey === "completedon" && state.sortedInfo.order,
render: (text, record) => { render: (text, record) => {
return record.completedon ? ( return record.completedon ? (
<DateFormatter>{record.completedon}</DateFormatter> <DateFormatter>{record.completedon}</DateFormatter>
@@ -76,11 +81,12 @@ export default function CsiResponseListPaginated({
]; ];
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); setState({
search.page = pagination.current; ...state,
search.sortcolumn = sorter.columnKey; filteredInfo: filters,
search.sortorder = sorter.order; sortedInfo: sorter,
history.push({ search: queryString.stringify(search) }); page: pagination.current,
});
}; };
const handleOnRowClick = (record) => { const handleOnRowClick = (record) => {
@@ -108,7 +114,7 @@ export default function CsiResponseListPaginated({
pagination={{ pagination={{
position: "top", position: "top",
pageSize: pageLimit, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(state.page || 1),
total: total, total: total,
}} }}
columns={columns} columns={columns}
@@ -122,13 +128,6 @@ export default function CsiResponseListPaginated({
selectedRowKeys: [responseid], selectedRowKeys: [responseid],
type: "radio", type: "radio",
}} }}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleOnRowClick(record);
}, // click row
};
}}
/> />
</Card> </Card>
); );

View File

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

View File

@@ -1,8 +1,8 @@
import { import {
BranchesOutlined, BranchesOutlined,
ExclamationCircleFilled, ExclamationCircleFilled,
PauseCircleOutlined, PauseCircleOutlined,
SyncOutlined, SyncOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { useQuery } from "@apollo/client"; import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd"; import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";

View File

@@ -0,0 +1,77 @@
import { useQuery } from "@apollo/client";
import { Card, Divider, Drawer, Grid } from "antd";
import queryString from "query-string";
import React from "react";
import { useTranslation } from "react-i18next";
import { Link, useHistory, useLocation } from "react-router-dom";
import { QUERY_PARTS_QUEUE_CARD_DETAILS } from "../../graphql/jobs.queries";
import AlertComponent from "../alert/alert.component";
import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import PartsQueueJobLinesComponent from "./parts-queue-job-lines.component";
export default function PartsQueueDetailCard() {
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const bpoints = {
xs: "100%",
sm: "100%",
md: "100%",
lg: "75%",
xl: "75%",
xxl: "60%",
};
const drawerPercentage = selectedBreakpoint
? bpoints[selectedBreakpoint[0]]
: "100%";
const searchParams = queryString.parse(useLocation().search);
const { selected } = searchParams;
const history = useHistory();
const { loading, error, data } = useQuery(QUERY_PARTS_QUEUE_CARD_DETAILS, {
variables: { id: selected },
skip: !selected,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const { t } = useTranslation();
const handleDrawerClose = () => {
delete searchParams.selected;
history.push({
search: queryString.stringify({
...searchParams,
}),
});
};
return (
<Drawer
visible={!!selected}
destroyOnClose
width={drawerPercentage}
placement="right"
onClose={handleDrawerClose}
>
{loading ? <LoadingSpinner /> : null}
{error ? <AlertComponent message={error.message} type="error" /> : null}
{data ? (
<Card
title={
<Link to={`/manage/jobs/${data.jobs_by_pk.id}`}>
{data.jobs_by_pk.ro_number || t("general.labels.na")}
</Link>
}
>
<JobsDetailHeader job={data ? data.jobs_by_pk : null} />
<Divider type="horizontal" />
<PartsQueueJobLinesComponent
jobLines={data.jobs_by_pk ? data.jobs_by_pk.joblines : null}
/>
</Card>
) : null}
</Drawer>
);
}

View File

@@ -0,0 +1,208 @@
import { Card, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { onlyUnique } from "../../utils/arrayHelper";
import { alphaSort } from "../../utils/sorters";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
});
const mapDispatchToProps = (dispatch) => ({});
export function PartsQueueJobLinesComponent({ jobRO, loading, jobLines }) {
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: {},
});
const { t } = useTranslation();
const columns = [
{
title: "#",
dataIndex: "line_no",
key: "line_no",
sorter: (a, b) => a.line_no - b.line_no,
sortOrder:
state.sortedInfo.columnKey === "line_no" && state.sortedInfo.order,
},
{
title: t("joblines.fields.line_desc"),
dataIndex: "line_desc",
key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
onCell: (record) => ({
className: record.manual_line && "job-line-manual",
style: {
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
},
}),
sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
ellipsis: true,
},
{
title: t("joblines.fields.oem_partno"),
dataIndex: "oem_partno",
key: "oem_partno",
sorter: (a, b) => alphaSort(a.oem_partno, b.oem_partno),
sortOrder:
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
ellipsis: true,
render: (text, record) =>
`${record.oem_partno || ""} ${
record.alt_partno ? `(${record.alt_partno})` : ""
}`.trim(),
},
{
title: t("joblines.fields.part_type"),
dataIndex: "part_type",
key: "part_type",
filteredValue: state.filteredInfo.part_type || null,
sorter: (a, b) => alphaSort(a.part_type, b.part_type),
sortOrder:
state.sortedInfo.columnKey === "part_type" && state.sortedInfo.order,
filters: [
{
text: t("jobs.labels.partsfilter"),
value: [
"PAN",
"PAC",
"PAR",
"PAL",
"PAA",
"PAM",
"PAP",
"PAS",
"PASL",
"PAG",
],
},
{
text: t("joblines.fields.part_types.PAN"),
value: ["PAN"],
},
{
text: t("joblines.fields.part_types.PAP"),
value: ["PAP"],
},
{
text: t("joblines.fields.part_types.PAL"),
value: ["PAL"],
},
{
text: t("joblines.fields.part_types.PAA"),
value: ["PAA"],
},
{
text: t("joblines.fields.part_types.PAG"),
value: ["PAG"],
},
{
text: t("joblines.fields.part_types.PAS"),
value: ["PAS"],
},
{
text: t("joblines.fields.part_types.PASL"),
value: ["PASL"],
},
{
text: t("joblines.fields.part_types.PAC"),
value: ["PAC"],
},
{
text: t("joblines.fields.part_types.PAR"),
value: ["PAR"],
},
{
text: t("joblines.fields.part_types.PAM"),
value: ["PAM"],
},
],
onFilter: (value, record) => value.includes(record.part_type),
render: (text, record) =>
record.part_type
? t(`joblines.fields.part_types.${record.part_type}`)
: null,
},
{
title: t("joblines.fields.part_qty"),
dataIndex: "part_qty",
key: "part_qty",
},
{
title: t("joblines.fields.act_price"),
dataIndex: "act_price",
key: "act_price",
sorter: (a, b) => a.act_price - b.act_price,
sortOrder:
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
ellipsis: true,
render: (text, record) => (
<CurrencyFormatter>
{record.db_ref === "900510" || record.db_ref === "900511"
? record.prt_dsmk_m
: record.act_price}
</CurrencyFormatter>
),
},
{
title: t("joblines.fields.location"),
dataIndex: "location",
key: "location",
},
{
title: t("joblines.fields.status"),
dataIndex: "status",
key: "status",
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
filteredValue: state.filteredInfo.status || null,
filters:
(jobLines &&
jobLines
.map((l) => l.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status),
},
];
const handleTableChange = (pagination, filters, sorter) => {
setState((state) => ({
...state,
filteredInfo: filters,
sortedInfo: sorter,
}));
};
return (
<Card title={t("jobs.labels.parts_lines")}>
<Table
columns={columns}
rowKey="id"
loading={loading}
pagination={false}
dataSource={jobLines}
onChange={handleTableChange}
/>
</Card>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(PartsQueueJobLinesComponent);

View File

@@ -8,30 +8,28 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom"; import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import AlertComponent from "../../components/alert/alert.component";
import JobPartsQueueCount from "../../components/job-parts-queue-count/job-parts-queue-count.component";
import JobRemoveFromPartsQueue from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component";
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
import ProductionListColumnComment from "../../components/production-list-columns/production-list-columns.comment.component";
import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries"; import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter"; import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter";
import { onlyUnique } from "../../utils/arrayHelper";
import { pageLimit } from "../../utils/config";
import { alphaSort, dateSort } from "../../utils/sorters"; import { alphaSort, dateSort } from "../../utils/sorters";
import useLocalStorage from "../../utils/useLocalStorage"; import useLocalStorage from "../../utils/useLocalStorage";
import {pageLimit} from "../../utils/config"; import AlertComponent from "../alert/alert.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
import JobRemoveFromPartsQueue from "../job-remove-from-parst-queue/job-remove-from-parts-queue.component";
import OwnerNameDisplay, {
OwnerNameDisplayFunction,
} from "../owner-name-display/owner-name-display.component";
import ProductionListColumnComment from "../production-list-columns/production-list-columns.comment.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function PartsQueuePageComponent({ bodyshop }) { export function PartsQueueListComponent({ bodyshop }) {
const searchParams = queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
const { const { selected, sortcolumn, sortorder, statusFilters } = searchParams;
//page,
sortcolumn,
sortorder,
statusFilters,
} = searchParams;
const history = useHistory(); const history = useHistory();
const [filter, setFilter] = useLocalStorage("filter_parts_queue", null); const [filter, setFilter] = useLocalStorage("filter_parts_queue", null);
@@ -39,19 +37,8 @@ export function PartsQueuePageComponent({ bodyshop }) {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
// offset: page ? (page - 1) * 25 : 0,
// limit: 25,
statuses: (statusFilters && JSON.parse(statusFilters)) || statuses: (statusFilters && JSON.parse(statusFilters)) ||
bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"], bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
order: [
{
[sortcolumn || "ro_number"]: sortorder
? sortorder === "descend"
? "desc"
: "asc"
: "desc",
},
],
}, },
}); });
@@ -107,6 +94,19 @@ export function PartsQueuePageComponent({ bodyshop }) {
history.push({ search: queryString.stringify(searchParams) }); history.push({ search: queryString.stringify(searchParams) });
}; };
const handleOnRowClick = (record) => {
if (record) {
if (record.id) {
history.push({
search: queryString.stringify({
...searchParams,
selected: record.id,
}),
});
}
}
};
const columns = [ const columns = [
{ {
title: t("jobs.fields.ro_number"), title: t("jobs.fields.ro_number"),
@@ -125,7 +125,8 @@ export function PartsQueuePageComponent({ bodyshop }) {
title: t("jobs.fields.owner"), title: t("jobs.fields.owner"),
dataIndex: "ownr_ln", dataIndex: "ownr_ln",
key: "ownr_ln", key: "ownr_ln",
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), sorter: (a, b) =>
alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)),
sortOrder: sortcolumn === "ownr_ln" && sortorder, sortOrder: sortcolumn === "ownr_ln" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.ownerid ? ( return record.ownerid ? (
@@ -139,6 +140,56 @@ export function PartsQueuePageComponent({ bodyshop }) {
); );
}, },
}, },
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
sorter: (a, b) =>
alphaSort(
`${a.v_model_yr || ""} ${a.v_make_desc || ""} ${
a.v_model_desc || ""
}`,
`${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}`
),
sortOrder: sortcolumn === "vehicle" && sortorder,
render: (text, record) => {
return record.vehicleid ? (
<Link to={"/manage/vehicles/" + record.vehicleid}>
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}
</Link>
) : (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}</span>
);
},
},
{
title: t("jobs.fields.ins_co_nm_short"),
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
sortOrder: sortcolumn === "ins_co_nm" && sortorder,
filteredValue: filter?.ins_co_nm || null,
filters:
(jobs &&
jobs
.map((j) => j.ins_co_nm)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Ins. Co.*",
value: [s],
};
})
.sort((a, b) => alphaSort(a.text, b.text))) ||
[],
onFilter: (value, record) => value.includes(record.ins_co_nm),
},
{ {
title: t("jobs.fields.status"), title: t("jobs.fields.status"),
dataIndex: "status", dataIndex: "status",
@@ -170,23 +221,16 @@ export function PartsQueuePageComponent({ bodyshop }) {
), ),
}, },
{ {
title: t("jobs.fields.vehicle"), title: t("jobs.fields.scheduled_completion"),
dataIndex: "vehicle", dataIndex: "scheduled_completion",
key: "vehicle", key: "scheduled_completion",
ellipsis: true, ellipsis: true,
render: (text, record) => { sorter: (a, b) =>
return record.vehicleid ? ( dateSort(a.scheduled_completion, b.scheduled_completion),
<Link to={"/manage/vehicles/" + record.vehicleid}> sortOrder: sortcolumn === "scheduled_completion" && sortorder,
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ render: (text, record) => (
record.v_model_desc || "" <DateTimeFormatter>{record.scheduled_completion}</DateTimeFormatter>
}`} ),
</Link>
) : (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}</span>
);
},
}, },
// { // {
// title: t("vehicles.fields.plate_no"), // title: t("vehicles.fields.plate_no"),
@@ -198,14 +242,6 @@ export function PartsQueuePageComponent({ bodyshop }) {
// return record.plate_no ? record.plate_no : ""; // return record.plate_no ? record.plate_no : "";
// }, // },
// }, // },
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
ellipsis: true,
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder: sortcolumn === "clm_no" && sortorder,
},
// { // {
// title: t("jobs.fields.clm_total"), // title: t("jobs.fields.clm_total"),
// dataIndex: "clm_total", // dataIndex: "clm_total",
@@ -307,9 +343,16 @@ export function PartsQueuePageComponent({ bodyshop }) {
style={{ height: "100%" }} style={{ height: "100%" }}
scroll={{ x: true }} scroll={{ x: true }}
onChange={handleTableChange} onChange={handleTableChange}
rowSelection={{
onSelect: (record) => {
handleOnRowClick(record);
},
selectedRowKeys: [selected],
type: "radio",
}}
/> />
</Card> </Card>
); );
} }
export default connect(mapStateToProps, null)(PartsQueuePageComponent); export default connect(mapStateToProps, null)(PartsQueueListComponent);

View File

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

View File

@@ -1,5 +1,5 @@
import React from "react";
import { Form } from "antd"; import { Form } from "antd";
import React from "react";
import ConfigFormComponents from "../config-form-components/config-form-components.component"; import ConfigFormComponents from "../config-form-components/config-form-components.component";
export default function ShopCsiConfigForm({ selectedCsi }) { export default function ShopCsiConfigForm({ selectedCsi }) {
@@ -9,7 +9,7 @@ export default function ShopCsiConfigForm({ selectedCsi }) {
return ( return (
<div> <div>
The Config Form {readOnly} {readOnly}
{selectedCsi && ( {selectedCsi && (
<Form form={form} onFinish={handleFinish}> <Form form={form} onFinish={handleFinish}>
<ConfigFormComponents <ConfigFormComponents

View File

@@ -1,7 +1,7 @@
import { CheckCircleFilled } from "@ant-design/icons"; import { CheckCircleFilled } from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Col, List, Row } from "antd"; import { Button, Col, List, Row } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useQuery } from "@apollo/client";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { GET_ALL_QUESTION_SETS } from "../../graphql/csi.queries"; import { GET_ALL_QUESTION_SETS } from "../../graphql/csi.queries";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
@@ -21,7 +21,6 @@ export default function ShopCsiConfig() {
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
return ( return (
<div> <div>
The Config Form
<Row> <Row>
<Col span={3}> <Col span={3}>
<List <List
@@ -42,7 +41,8 @@ export default function ShopCsiConfig() {
)} )}
/> />
</Col> </Col>
<Col span={21}> <Col span={1} />
<Col span={20}>
<ShopCsiConfigForm selectedCsi={selectedCsi} /> <ShopCsiConfigForm selectedCsi={selectedCsi} />
</Col> </Col>
</Row> </Row>

View File

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

View File

@@ -57,19 +57,15 @@ export const INSERT_CSI = gql`
`; `;
export const QUERY_CSI_RESPONSE_PAGINATED = gql` export const QUERY_CSI_RESPONSE_PAGINATED = gql`
query QUERY_CSI_RESPONSE_PAGINATED( query QUERY_CSI_RESPONSE_PAGINATED {
$offset: Int csi(order_by: { completedon: desc_nulls_last }) {
$limit: Int
$order: [csi_order_by!]!
) {
csi(offset: $offset, limit: $limit, order_by: $order) {
id id
completedon completedon
job { job {
ownr_fn ownr_fn
ownr_ln ownr_ln
ownerid
ro_number ro_number
id id
} }
} }
@@ -83,6 +79,7 @@ export const QUERY_CSI_RESPONSE_PAGINATED = gql`
export const QUERY_CSI_RESPONSE_BY_PK = gql` export const QUERY_CSI_RESPONSE_BY_PK = gql`
query QUERY_CSI_RESPONSE_BY_PK($id: uuid!) { query QUERY_CSI_RESPONSE_BY_PK($id: uuid!) {
csi_by_pk(id: $id) { csi_by_pk(id: $id) {
completedon
relateddata relateddata
valid valid
validuntil validuntil

View File

@@ -108,12 +108,7 @@ export const QUERY_ALL_ACTIVE_JOBS = gql`
`; `;
export const QUERY_PARTS_QUEUE = gql` export const QUERY_PARTS_QUEUE = gql`
query QUERY_PARTS_QUEUE( query QUERY_PARTS_QUEUE($statuses: [String!]!, $offset: Int, $limit: Int) {
$statuses: [String!]!
$offset: Int
$limit: Int
$order: [jobs_order_by!]
) {
jobs_aggregate(where: { _and: [{ status: { _in: $statuses } }] }) { jobs_aggregate(where: { _and: [{ status: { _in: $statuses } }] }) {
aggregate { aggregate {
count(distinct: true) count(distinct: true)
@@ -125,7 +120,7 @@ export const QUERY_PARTS_QUEUE = gql`
} }
offset: $offset offset: $offset
limit: $limit limit: $limit
order_by: $order order_by: { ro_number: desc }
) { ) {
ownr_fn ownr_fn
ownr_ln ownr_ln
@@ -142,7 +137,9 @@ export const QUERY_PARTS_QUEUE = gql`
v_color v_color
vehicleid vehicleid
scheduled_in scheduled_in
scheduled_completion
id id
ins_co_nm
clm_no clm_no
ro_number ro_number
status status
@@ -2407,6 +2404,166 @@ export const MARK_JOB_AS_UNINVOICED = gql`
} }
`; `;
export const QUERY_PARTS_QUEUE_CARD_DETAILS = gql`
query QUERY_JOB_CARD_DETAILS($id: uuid!) {
jobs_by_pk(id: $id) {
actual_completion
actual_delivery
actual_in
alt_transport
available_jobs {
id
}
area_of_damage
ca_gst_registrant
cccontracts {
agreementnumber
courtesycar {
id
make
model
year
plate
fleetnumber
}
id
scheduledreturn
start
status
}
clm_no
clm_total
comment
date_estimated
date_exported
date_invoiced
date_last_contacted
date_next_contact
date_open
date_repairstarted
date_scheduled
ded_amt
employee_body
employee_body_rel {
id
first_name
last_name
}
employee_csr
employee_csr_rel {
id
first_name
last_name
}
employee_prep
employee_prep_rel {
id
first_name
last_name
}
employee_refinish
employee_refinish_rel {
id
first_name
last_name
}
est_co_nm
est_ct_fn
est_ct_ln
est_ea
est_ph1
id
ins_co_nm
ins_ct_fn
ins_ct_ln
ins_ea
ins_ph1
inproduction
job_totals
joblines(
order_by: { line_no: asc }
where: {
part_type: {
_in: [
"PAN"
"PAC"
"PAR"
"PAL"
"PAA"
"PAM"
"PAP"
"PAG"
]
}
removed: { _eq: false }
}
) {
act_price
alt_partno
db_ref
id
line_desc
line_no
location
mod_lbr_ty
mod_lb_hrs
oem_partno
part_qty
part_type
prt_dsmk_m
status
}
lbr_adjustments
ownr_co_nm
ownr_ea
ownr_fn
ownr_ln
ownr_ph1
ownr_ph2
owner {
id
allow_text_message
preferred_contact
tax_number
}
owner_owing
plate_no
plate_st
po_number
production_vars
ro_number
scheduled_completion
scheduled_delivery
scheduled_in
special_coverage_policy
status
suspended
updated_at
vehicle {
id
jobs {
id
clm_no
ro_number
}
notes
plate_no
v_color
v_make_desc
v_model_desc
v_model_yr
}
vehicleid
v_color
v_make_desc
v_model_desc
v_model_yr
v_vin
voided
}
}
`;
export const QUERY_COMPLETED_TASKS = gql` export const QUERY_COMPLETED_TASKS = gql`
query QUERY_COMPLETED_TASKS($jobid: uuid!) { query QUERY_COMPLETED_TASKS($jobid: uuid!) {
jobs_by_pk(id: $jobid) { jobs_by_pk(id: $jobid) {

View File

@@ -14,6 +14,7 @@ import { persistor, store } from "./redux/store";
import reportWebVitals from "./reportWebVitals"; import reportWebVitals from "./reportWebVitals";
import "./translations/i18n"; import "./translations/i18n";
import "./utils/CleanAxios"; import "./utils/CleanAxios";
//import { BrowserTracing } from "@sentry/tracing"; //import { BrowserTracing } from "@sentry/tracing";
// Dinero.defaultCurrency = "CAD"; // Dinero.defaultCurrency = "CAD";
@@ -23,27 +24,25 @@ Dinero.globalRoundingMode = "HALF_EVEN";
if (process.env.NODE_ENV !== "development") { if (process.env.NODE_ENV !== "development") {
Sentry.init({ Sentry.init({
dsn: "https://a6acc91c073e414196014b8484627a61@o492140.ingest.sentry.io/4504561071161344", dsn: "https://a6acc91c073e414196014b8484627a61@o492140.ingest.sentry.io/4504561071161344",
ignoreErrors: [ ignoreErrors: [
"ResizeObserver loop", "ResizeObserver loop",
"Module specifier, 'fs' does not start", "Module specifier, 'fs' does not start",
"Module specifier, 'zlib' does not start with", "Module specifier, 'zlib' does not start with",
], ],
integrations: [ integrations: [
// new BrowserTracing(), Sentry.replayIntegration({
// new Sentry.Integrations.Breadcrumbs({ console: true }), maskAllText: false,
// new Sentry.Replay(), blockAllMedia: true,
}),
new Sentry.BrowserTracing({}),
], ],
// This sets the sample rate to be 10%. You may want this to be 100% while tracePropagationTargets: [
// in development and sample at a lower rate in production "api.romeonline.io",
// replaysSessionSampleRate: 0.1, "api.test.romeonline.io",
// // If the entire session is not sampled, use the below sample rate to sample "db.romeonline.io",
// // sessions when an error occurs. ],
// replaysOnErrorSampleRate: 1.0, tracesSampleRate: 1.0,
environment: process.env.NODE_ENV, environment: process.env.NODE_ENV,
// tracesSampleRate: 0.2,
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
// tracesSampleRate: 0.5,
}); });
} }

View File

@@ -1,88 +1,67 @@
import { useQuery, useMutation } from "@apollo/client"; // import { useMutation, useQuery } from "@apollo/client";
import { Form, Layout, Typography, Button, Result } from "antd"; import { Button, Form, Layout, Result, Typography } from "antd";
import React, { useState } from "react"; import axios from "axios";
import React, { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import AlertComponent from "../../components/alert/alert.component"; import AlertComponent from "../../components/alert/alert.component";
import ConfigFormComponents from "../../components/config-form-components/config-form-components.component"; import ConfigFormComponents from "../../components/config-form-components/config-form-components.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import { QUERY_SURVEY, COMPLETE_SURVEY } from "../../graphql/csi.queries";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectCurrentUser } from "../../redux/user/user.selectors"; import { selectCurrentUser } from "../../redux/user/user.selectors";
import { DateTimeFormat } from "./../../utils/DateFormatter";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({});
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(CsiContainerPage); export default connect(mapStateToProps, mapDispatchToProps)(CsiContainerPage);
export function CsiContainerPage({ currentUser }) { export function CsiContainerPage({ currentUser }) {
const { surveyId } = useParams(); const { surveyId } = useParams();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [axiosResponse, setAxiosResponse] = useState(null);
const [submitting, setSubmitting] = useState({ const [submitting, setSubmitting] = useState({
loading: false, loading: false,
submitted: false, submitted: false,
}); });
const { loading, error, data } = useQuery(QUERY_SURVEY, {
variables: { surveyId },
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const { t } = useTranslation(); const { t } = useTranslation();
const [completeSurvey] = useMutation(COMPLETE_SURVEY);
if (loading) return <LoadingSpinner />;
if (error || !!!data.csi_by_pk) const getAxiosData = useCallback(async () => {
return ( try {
<div> try {
<Result window.$crisp.push(["do", "chat:hide"]);
status="error" } catch {
title={t("csi.errors.notfoundtitle")} console.log("Unable to attach to crisp instance. ");
subTitle={t("csi.errors.notfoundsubtitle")} }
> setSubmitting((prevSubmitting) => ({ ...prevSubmitting, loading: true }));
{error ? ( const response = await axios.post("/csi/lookup", { surveyId });
<div>ERROR: {error.graphQLErrors.map((e) => e.message)}</div> setSubmitting((prevSubmitting) => ({
) : null} ...prevSubmitting,
</Result>
</div>
);
const handleFinish = async (values) => {
setSubmitting({ ...submitting, loading: true });
const result = await completeSurvey({
variables: {
surveyId,
survey: {
response: values,
valid: false,
completedon: new Date(),
},
},
});
if (!!!result.errors) {
setSubmitting({ ...submitting, loading: false, submitted: true });
} else {
setSubmitting({
...submitting,
loading: false, loading: false,
error: JSON.stringify(result.errors), }));
setAxiosResponse(response.data);
} catch (error) {
console.error(`Something went wrong...: ${error.message}`);
console.dir({
stack: error?.stack,
message: error?.message,
}); });
} }
}; }, [setAxiosResponse, surveyId]);
const { useEffect(() => {
relateddata: { bodyshop, job }, getAxiosData().catch((err) =>
csiquestion: { config: csiquestions }, console.error(
} = data.csi_by_pk; `Something went wrong fetching axios data: ${err.message || ""}`
)
);
}, [getAxiosData]);
if (currentUser && currentUser.authorized) // Return if authorized
if (currentUser && currentUser.authorized) {
return ( return (
<Layout <Layout
style={{ height: "100vh", display: "flex", flexDirection: "column" }} style={{ height: "100vh", display: "flex", flexDirection: "column" }}
@@ -94,85 +73,176 @@ export function CsiContainerPage({ currentUser }) {
/> />
</Layout> </Layout>
); );
}
return ( if (submitting.loading) return <LoadingSpinner />;
<Layout
style={{ height: "100vh", display: "flex", flexDirection: "column" }} const handleFinish = async (values) => {
> try {
<div setSubmitting({ ...submitting, loading: true, submitting: true });
style={{ const result = await axios.post("/csi/submit", { surveyId, values });
display: "flex", console.log("result", result);
flexDirection: "column", if (!!!result.errors && result.data.update_csi.affected_rows > 0) {
alignItems: "center", setSubmitting({ ...submitting, loading: false, submitted: true });
}} }
> } catch (error) {
<div style={{ display: "flex", alignItems: "center", margin: "2em" }}> console.error(`Something went wrong...: ${error.message}`);
{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? ( console.dir({
<img src={bodyshop.logo_img_path.src} alt="Logo" /> stack: error?.stack,
) : null} message: error?.message,
<div style={{ margin: "2em" }}> });
<strong>{bodyshop.shopname || ""}</strong> }
<div>{`${bodyshop.address1 || ""}`}</div> };
<div>{`${bodyshop.address2 || ""}`}</div>
<div>{`${bodyshop.city || ""} ${bodyshop.state || ""} ${ if (!axiosResponse || axiosResponse.csi_by_pk === null) {
bodyshop.zip_post || "" // Do something here , this is where you would return a loading box or something
}`}</div> return (
<>
<Layout style={{ display: "flex", flexDirection: "column" }}>
<Layout.Content
style={{
backgroundColor: "#fff",
margin: "2em 4em",
padding: "2em",
overflowY: "auto",
textAlign: "center",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<Form>
<Result
status="error"
title={t("csi.errors.notfoundtitle")}
subTitle={t("csi.errors.notfoundsubtitle")}
/>
</Form>
</Layout.Content>
<Layout.Footer>
{t("csi.labels.copyright")}{" "}
{t("csi.fields.surveyid", { surveyId: surveyId })}
</Layout.Footer>
</Layout>
</>
);
} else {
const {
relateddata: { bodyshop, job },
csiquestion: { config: csiquestions },
} = axiosResponse.csi_by_pk;
return (
<Layout style={{ display: "flex", flexDirection: "column" }}>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<div style={{ display: "flex", alignItems: "center", margin: "2em" }}>
{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? (
<img
src={bodyshop.logo_img_path.src}
alt={bodyshop.shopname.concat(" Logo")}
height={bodyshop.logo_img_path.height}
width={bodyshop.logo_img_path.width}
/>
) : null}
<div style={{ margin: "2em", verticalAlign: "middle" }}>
<Typography.Title level={4} style={{ margin: 0 }}>
{bodyshop.shopname || ""}
</Typography.Title>
<Typography.Paragraph style={{ margin: 0 }}>
{`${bodyshop.address1 || ""}${bodyshop.address2 ? ", " : ""}${
bodyshop.address2 || ""
}`.trim()}
</Typography.Paragraph>
<Typography.Paragraph style={{ margin: 0 }}>
{`${bodyshop.city || ""}${
bodyshop.city && bodyshop.state ? ", " : ""
}${bodyshop.state || ""} ${bodyshop.zip_post || ""}`.trim()}
</Typography.Paragraph>
</div>
</div> </div>
<Typography.Title>{t("csi.labels.title")}</Typography.Title>
<strong>
{t("csi.labels.greeting", {
name: job.ownr_co_nm || job.ownr_fn || "",
})}
</strong>
<Typography.Paragraph>
{t("csi.labels.intro", { shopname: bodyshop.shopname || "" })}
</Typography.Paragraph>
</div> </div>
<Typography.Title>{t("csi.labels.title")}</Typography.Title>
<strong>{`Hi ${job.ownr_co_nm || job.ownr_fn || ""}!`}</strong>
<Typography.Paragraph>
{`At ${
bodyshop.shopname || ""
}, we value your feedback. We would love to
hear what you have to say. Please fill out the form below.`}
</Typography.Paragraph>
</div>
{submitting.error ? ( {submitting.error ? (
<AlertComponent message={submitting.error} type="error" /> <AlertComponent message={submitting.error} type="error" />
) : null} ) : null}
{submitting.submitted ? ( {submitting.submitted ? (
<Layout.Content <Layout.Content
style={{ style={{
backgroundColor: "#fff", backgroundColor: "#fff",
margin: "2em 4em", margin: "2em 4em",
padding: "2em", padding: "2em",
overflowY: "auto", overflowY: "auto",
}} }}
> >
<Result <Result
status="success" status="success"
title={t("csi.successes.submitted")} title={t("csi.successes.submitted")}
subTitle={t("csi.successes.submittedsub")} subTitle={t("csi.successes.submittedsub")}
/> />
</Layout.Content> </Layout.Content>
) : ( ) : (
<Layout.Content <Layout.Content
style={{ style={{
backgroundColor: "#fff", backgroundColor: "#fff",
margin: "2em 4em", margin: "2em 4em",
padding: "2em", padding: "2em",
overflowY: "auto", overflowY: "auto",
}} }}
> >
<Form form={form} onFinish={handleFinish}> <Form form={form} onFinish={handleFinish}>
<ConfigFormComponents componentList={csiquestions} /> {axiosResponse.csi_by_pk.valid ? (
<Button <>
loading={submitting.loading} <ConfigFormComponents componentList={csiquestions} />
type="primary" <Button
htmlType="submit" loading={submitting.loading}
> type="primary"
{t("general.actions.submit")} htmlType="submit"
</Button> style={{ float: "right" }}
</Form> >
</Layout.Content> {t("general.actions.submit")}
)} </Button>
</>
<Layout.Footer> ) : (
{`Survey ID: ${surveyId}`} <>
</Layout.Footer> <Result
</Layout> title={t("csi.errors.surveycompletetitle")}
); status="warning"
subTitle={t("csi.errors.surveycompletesubtitle", {
date: DateTimeFormat(axiosResponse.csi_by_pk.completedon),
})}
/>
<Typography.Paragraph
type="secondary"
style={{ textAlign: "center" }}
>
{t("csi.successes.submittedsub")}
</Typography.Paragraph>
</>
)}
</Form>
</Layout.Content>
)}
<Layout.Footer>
{t("csi.labels.copyright")}{" "}
{t("csi.fields.surveyid", { surveyId: surveyId })}
</Layout.Footer>
</Layout>
);
}
} }

View File

@@ -1,12 +1,13 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import PartsQueueDetailCard from "../../components/parts-queue-card/parts-queue-card.component";
import PartsQueueList from "../../components/parts-queue-list/parts-queue.list.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import { import {
setBreadcrumbs, setBreadcrumbs,
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import PartsQueuePage from "./parts-queue.page.component";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
@@ -26,7 +27,8 @@ export function PartsQueuePageContainer({ setBreadcrumbs, setSelectedHeader }) {
return ( return (
<RbacWrapper action="jobs:partsqueue"> <RbacWrapper action="jobs:partsqueue">
<PartsQueuePage /> <PartsQueueList />
<PartsQueueDetailCard />
</RbacWrapper> </RbacWrapper>
); );
} }

View File

@@ -1,22 +1,20 @@
import { Row, Col } from "antd";
import { useQuery } from "@apollo/client"; import { useQuery } from "@apollo/client";
import queryString from "query-string"; import { Col, Row } from "antd";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import AlertComponent from "../../components/alert/alert.component"; import AlertComponent from "../../components/alert/alert.component";
import CsiResponseFormContainer from "../../components/csi-response-form/csi-response-form.container"; import CsiResponseFormContainer from "../../components/csi-response-form/csi-response-form.container";
import CsiResponseListPaginated from "../../components/csi-response-list-paginated/csi-response-list-paginated.component"; import CsiResponseListPaginated from "../../components/csi-response-list-paginated/csi-response-list-paginated.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import { QUERY_CSI_RESPONSE_PAGINATED } from "../../graphql/csi.queries"; import { QUERY_CSI_RESPONSE_PAGINATED } from "../../graphql/csi.queries";
import { import {
setBreadcrumbs, setBreadcrumbs,
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
@@ -33,28 +31,11 @@ export function ShopCsiContainer({
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const searchParams = queryString.parse(useLocation().search);
const { page, sortcolumn, sortorder } = searchParams;
const { loading, error, data, refetch } = useQuery( const { loading, error, data, refetch } = useQuery(
QUERY_CSI_RESPONSE_PAGINATED, QUERY_CSI_RESPONSE_PAGINATED,
{ {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: {
//search: search || "",
offset: page ? (page - 1) * pageLimit : 0,
limit: pageLimit,
order: [
{
[sortcolumn || "completedon"]: sortorder
? sortorder === "descend"
? "desc_nulls_last"
: "asc"
: "desc_nulls_last",
},
],
},
} }
); );
@@ -73,12 +54,7 @@ export function ShopCsiContainer({
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
return ( return (
<RbacWrapper <RbacWrapper action="csi:page">
action="csi:page"
// noauth={
// <AlertComponent message="You don't have acess to see this screen." />
// }
>
<Row gutter={16}> <Row gutter={16}>
<Col span={10}> <Col span={10}>
<CsiResponseListPaginated <CsiResponseListPaginated

View File

@@ -851,20 +851,27 @@
"creating": "Error creating survey {{message}}", "creating": "Error creating survey {{message}}",
"notconfigured": "You do not have any current CSI Question Sets configured.", "notconfigured": "You do not have any current CSI Question Sets configured.",
"notfoundsubtitle": "We were unable to find a survey using the link you provided. Please ensure the URL is correct or reach out to your shop for more help.", "notfoundsubtitle": "We were unable to find a survey using the link you provided. Please ensure the URL is correct or reach out to your shop for more help.",
"notfoundtitle": "No survey found." "notfoundtitle": "No survey found.",
"surveycompletetitle": "Survey previously completed",
"surveycompletesubtitle": "This survey was already completed on {{date}}."
}, },
"fields": { "fields": {
"completedon": "Completed On", "completedon": "Completed On",
"created_at": "Created At" "created_at": "Created At",
"surveyid": "Survey ID {{surveyId}}",
"validuntil": "Valid Until"
}, },
"labels": { "labels": {
"nologgedinuser": "Please log out of $t(titles.app)", "nologgedinuser": "Please log out of $t(titles.app)",
"nologgedinuser_sub": "Users of $t(titles.app) cannot complete CSI surveys while logged in. Please log out and try again.", "nologgedinuser_sub": "Users of $t(titles.app) cannot complete CSI surveys while logged in. Please log out and try again.",
"noneselected": "No response selected.", "noneselected": "No response selected.",
"title": "Customer Satisfaction Survey" "title": "Customer Satisfaction Survey",
"greeting": "Hi {{name}}!",
"intro": "At {{shopname}}, we value your feedback. We would love to hear what you have to say. Please fill out the form below.",
"copyright": "Copyright © $t(titles.app). All Rights Reserved."
}, },
"successes": { "successes": {
"created": "CSI created successfully. ", "created": "CSI created successfully.",
"submitted": "Your responses have been submitted successfully.", "submitted": "Your responses have been submitted successfully.",
"submittedsub": "Your input is highly appreciated." "submittedsub": "Your input is highly appreciated."
} }
@@ -1888,6 +1895,7 @@
"override_header": "Override estimate header on import?", "override_header": "Override estimate header on import?",
"ownerassociation": "Owner Association", "ownerassociation": "Owner Association",
"parts": "Parts", "parts": "Parts",
"parts_lines": "Parts Lines",
"parts_received": "Parts Rec.", "parts_received": "Parts Rec.",
"parts_tax_rates": "Parts Tax rates", "parts_tax_rates": "Parts Tax rates",
"partsfilter": "Parts Only", "partsfilter": "Parts Only",

View File

@@ -851,17 +851,24 @@
"creating": "", "creating": "",
"notconfigured": "", "notconfigured": "",
"notfoundsubtitle": "", "notfoundsubtitle": "",
"notfoundtitle": "" "notfoundtitle": "",
"surveycompletetitle": "",
"surveycompletesubtitle": ""
}, },
"fields": { "fields": {
"completedon": "", "completedon": "",
"created_at": "" "created_at": "",
"surveyid": "",
"validuntil": ""
}, },
"labels": { "labels": {
"nologgedinuser": "", "nologgedinuser": "",
"nologgedinuser_sub": "", "nologgedinuser_sub": "",
"noneselected": "", "noneselected": "",
"title": "" "title": "",
"greeting": "",
"intro": "",
"copyright": ""
}, },
"successes": { "successes": {
"created": "", "created": "",
@@ -1888,6 +1895,7 @@
"override_header": "¿Anular encabezado estimado al importar?", "override_header": "¿Anular encabezado estimado al importar?",
"ownerassociation": "", "ownerassociation": "",
"parts": "Partes", "parts": "Partes",
"parts_lines": "",
"parts_received": "", "parts_received": "",
"parts_tax_rates": "", "parts_tax_rates": "",
"partsfilter": "", "partsfilter": "",

View File

@@ -851,17 +851,24 @@
"creating": "", "creating": "",
"notconfigured": "", "notconfigured": "",
"notfoundsubtitle": "", "notfoundsubtitle": "",
"notfoundtitle": "" "notfoundtitle": "",
"surveycompletetitle": "",
"surveycompletesubtitle": ""
}, },
"fields": { "fields": {
"completedon": "", "completedon": "",
"created_at": "" "created_at": "",
"surveyid": "",
"validuntil": ""
}, },
"labels": { "labels": {
"nologgedinuser": "", "nologgedinuser": "",
"nologgedinuser_sub": "", "nologgedinuser_sub": "",
"noneselected": "", "noneselected": "",
"title": "" "title": "",
"greeting": "",
"intro": "",
"copyright": ""
}, },
"successes": { "successes": {
"created": "", "created": "",
@@ -1888,6 +1895,7 @@
"override_header": "Remplacer l'en-tête d'estimation à l'importation?", "override_header": "Remplacer l'en-tête d'estimation à l'importation?",
"ownerassociation": "", "ownerassociation": "",
"parts": "les pièces", "parts": "les pièces",
"parts_lines": "",
"parts_received": "", "parts_received": "",
"parts_tax_rates": "", "parts_tax_rates": "",
"partsfilter": "", "partsfilter": "",

View File

@@ -12,6 +12,8 @@ import apolloLogger from "apollo-link-logger";
//import axios from "axios"; //import axios from "axios";
import { auth } from "../firebase/firebase.utils"; import { auth } from "../firebase/firebase.utils";
import errorLink from "../graphql/apollo-error-handling"; import errorLink from "../graphql/apollo-error-handling";
import { SentryLink } from "apollo-link-sentry";
//import { store } from "../redux/store"; //import { store } from "../redux/store";
const httpLink = new HttpLink({ const httpLink = new HttpLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT, uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
@@ -105,18 +107,30 @@ const link = split(
const authLink = setContext((_, { headers }) => { const authLink = setContext((_, { headers }) => {
return ( return (
auth.currentUser && auth.currentUser &&
auth.currentUser.getIdToken().then((token) => { auth.currentUser
if (token) { .getIdToken()
return { .then((token) => {
headers: { if (token) {
...headers, return {
authorization: token ? `Bearer ${token}` : "", headers: {
}, ...headers,
}; authorization: token ? `Bearer ${token}` : "",
} else { },
};
} 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 }; return { headers };
} })
})
); );
}); });
@@ -138,8 +152,10 @@ if (process.env.NODE_ENV === "development") {
} }
middlewares.push( middlewares.push(
roundTripLink.concat( new SentryLink().concat(
retryLink.concat(errorLink.concat(authLink.concat(link))) roundTripLink.concat(
retryLink.concat(errorLink.concat(authLink.concat(link)))
)
) )
); );

View File

@@ -73,6 +73,7 @@ app.use('/tech', require("./server/routes/techRoutes"));
app.use('/intellipay', require("./server/routes/intellipayRoutes")); app.use('/intellipay', require("./server/routes/intellipayRoutes"));
app.use('/cdk', require("./server/routes/cdkRoutes")); app.use('/cdk', require("./server/routes/cdkRoutes"));
app.use('/payroll', require("./server/routes/payrollRoutes")); app.use('/payroll', require("./server/routes/payrollRoutes"));
app.use('/csi', require("./server/routes/csiRoutes"));
// Default route for forbidden access // Default route for forbidden access
app.get("/", (req, res) => { app.get("/", (req, res) => {

2
server/csi/csi.js Normal file
View File

@@ -0,0 +1,2 @@
exports.lookup = require("./lookup").default;
exports.submit = require("./submit").default;

24
server/csi/lookup.js Normal file
View File

@@ -0,0 +1,24 @@
const path = require("path");
const queries = require("../graphql-client/queries");
const logger = require("../utils/logger");
require("dotenv").config({
path: path.resolve(
process.cwd(),
`.env.${process.env.NODE_ENV || "development"}`
),
});
const client = require("../graphql-client/graphql-client").client;
exports.default = async (req, res) => {
try {
logger.log("csi-surveyID-lookup", "DEBUG", "csi", req.body.surveyId, null);
const gql_response = await client.request(queries.QUERY_SURVEY, {
surveyId: req.body.surveyId,
});
res.status(200).json(gql_response);
} catch (error) {
logger.log("csi-surveyID-lookup", "ERROR", "csi", req.body.surveyId, error);
res.status(400).json(error);
}
};

29
server/csi/submit.js Normal file
View File

@@ -0,0 +1,29 @@
const path = require("path");
const queries = require("../graphql-client/queries");
const logger = require("../utils/logger");
require("dotenv").config({
path: path.resolve(
process.cwd(),
`.env.${process.env.NODE_ENV || "development"}`
),
});
const client = require("../graphql-client/graphql-client").client;
exports.default = async (req, res) => {
try {
logger.log("csi-surveyID-submit", "DEBUG", "csi", req.body.surveyId, null);
const gql_response = await client.request(queries.COMPLETE_SURVEY, {
surveyId: req.body.surveyId,
survey: {
response: req.body.values,
valid: false,
completedon: new Date(),
},
});
res.status(200).json(gql_response);
} catch (error) {
logger.log("csi-surveyID-submit", "ERROR", "csi", req.body.surveyId, error);
res.status(400).json(error);
}
};

View File

@@ -2271,3 +2271,23 @@ exports.INSERT_TIME_TICKETS = `mutation INSERT_TIMETICKETS($timetickets: [timeti
} }
} }
`; `;
exports.QUERY_SURVEY = `query QUERY_SURVEY($surveyId: uuid!) {
csi_by_pk(id: $surveyId) {
completedon
csiquestion {
id
config
}
id
relateddata
valid
validuntil
}
}`;
exports.COMPLETE_SURVEY = `mutation COMPLETE_SURVEY($surveyId: uuid!, $survey: csi_set_input) {
update_csi(where: { id: { _eq: $surveyId } }, _set: $survey) {
affected_rows
}
}`;

View File

@@ -0,0 +1,8 @@
const express = require("express");
const router = express.Router();
const { lookup, submit } = require("../csi/csi");
router.post("/lookup", lookup);
router.post("/submit", submit);
module.exports = router;