diff --git a/.circleci/config.yml b/.circleci/config.yml index b1451e587..8d0860a22 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,31 +42,22 @@ jobs: app-build: docker: - image: cimg/node:16.15.0 - + resource_class: large working_directory: ~/repo/client steps: - checkout: path: ~/repo - - - restore_cache: - name: Restore Yarn Package Cache - keys: - - yarn-packages-{{ checksum "yarn.lock" }} - run: name: Install Dependencies - command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn - - save_cache: - name: Save Yarn Package Cache - key: yarn-packages-{{ checksum "yarn.lock" }} - paths: - - ~/.cache/yarn + command: npm i - - run: yarn run build + - run: npm run build - aws-s3/sync: from: build to: "s3://imex-online-production/" + arguments: "--exclude '*.map'" - jira/notify rome-api-deploy: @@ -245,31 +236,22 @@ jobs: test-app-build: docker: - image: cimg/node:16.15.0 - + resource_class: large working_directory: ~/repo/client steps: - checkout: path: ~/repo - - - restore_cache: - name: Restore Yarn Package Cache - keys: - - yarn-packages-{{ checksum "yarn.lock" }} - run: name: Install Dependencies - command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn - - save_cache: - name: Save Yarn Package Cache - key: yarn-packages-{{ checksum "yarn.lock" }} - paths: - - ~/.cache/yarn + command: npm i - - run: yarn run build:test + - run: npm run build:test - aws-s3/sync: from: build to: "s3://imex-online-test/" + arguments: "--exclude '*.map'" - jira/notify test-app-beta-build: @@ -411,4 +393,4 @@ workflows: #- admin-app-build: #filters: #branches: - #only: master \ No newline at end of file + #only: master diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 000000000..387b4257c --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,3 @@ + +# Sentry Config File +.sentryclirc diff --git a/client/craco.config.js b/client/craco.config.js index b8967bc1e..a4f0ee09f 100644 --- a/client/craco.config.js +++ b/client/craco.config.js @@ -1,25 +1,25 @@ // craco.config.js const TerserPlugin = require("terser-webpack-plugin"); const CracoLessPlugin = require("craco-less"); -const SentryWebpackPlugin = require("@sentry/webpack-plugin"); +//const SentryWebpackPlugin = require("@sentry/webpack-plugin"); module.exports = { plugins: [ - { - plugin: SentryWebpackPlugin, - options: { - // sentry-cli configuration - authToken: - "6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260", - org: "snapt-software", - project: "rome-online", - release: process.env.REACT_APP_GIT_SHA, + // { + // 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"], - }, - }, + // // webpack-specific configuration + // include: ".", + // ignore: ["node_modules", "webpack.config.js"], + // }, + // }, { plugin: CracoLessPlugin, options: { diff --git a/client/package-lock.json b/client/package-lock.json index 9a868698e..25a526f60 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -13,12 +13,14 @@ "@craco/craco": "^7.0.0", "@fingerprintjs/fingerprintjs": "^3.4.2", "@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", "@splitsoftware/splitio-react": "^1.8.1", "@tanem/react-nprogress": "^5.0.8", "antd": "^4.24.8", "apollo-link-logger": "^2.0.1", + "apollo-link-sentry": "^3.3.0", "axios": "^1.3.4", "craco-less": "^2.0.0", "dinero.js": "^1.9.1", @@ -4214,40 +4216,195 @@ "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", "license": "MIT" }, - "node_modules/@sentry/browser": { - "version": "7.40.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.40.0.tgz", - "integrity": "sha512-07rZ+cTcpmYB1r84/oZtmSPJJvLCxW8yIh/5s4MdKRyZpqIDKhOz6cCS/4j+l1V+MeLcNLZBjFtNdKA2eocTpg==", - "license": "MIT", + "node_modules/@sentry-internal/feedback": { + "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.40.0", - "@sentry/replay": "7.40.0", - "@sentry/types": "7.40.0", - "@sentry/utils": "7.40.0", - "tslib": "^1.9.3" + "@sentry/core": "7.99.0", + "@sentry/types": "7.99.0", + "@sentry/utils": "7.99.0" + }, + "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": { "node": ">=8" } }, - "node_modules/@sentry/browser/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" + "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.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": { - "version": "1.74.6", - "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.74.6.tgz", - "integrity": "sha512-pJ7JJgozyjKZSTjOGi86chIngZMLUlYt2HOog+OJn+WGvqEkVymu8m462j1DiXAnex9NspB4zLLNuZ/R6rTQHg==", - "dev": true, + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.27.0.tgz", + "integrity": "sha512-pc0opd71W8lGhYvmB1keQtJkarxzCS9f9ErKYv6TfXOOX6drvwkyA6vD/6xEnpzyvqGAuGRU4T4sEeLD3irwUQ==", "hasInstallScript": true, - "license": "BSD-3-Clause", "dependencies": { "https-proxy-agent": "^5.0.0", - "mkdirp": "^0.5.5", "node-fetch": "^2.6.7", - "npmlog": "^4.1.2", "progress": "^2.0.3", "proxy-from-env": "^1.1.0", "which": "^2.0.2" @@ -4256,7 +4413,124 @@ "sentry-cli": "bin/sentry-cli" }, "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": { @@ -4280,16 +4554,15 @@ "license": "0BSD" }, "node_modules/@sentry/react": { - "version": "7.40.0", - "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.40.0.tgz", - "integrity": "sha512-7yYagpOCdsXnVTtLL8Y7wAf2xXgsk2ncuju3O/G4kEckkLewZWmQeoknOSGFlAgVdGNhTaXc2WGzgOiBMOkhug==", - "license": "MIT", + "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.40.0", - "@sentry/types": "7.40.0", - "@sentry/utils": "7.40.0", - "hoist-non-react-statics": "^3.3.2", - "tslib": "^1.9.3" + "@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": { "node": ">=8" @@ -4298,26 +4571,82 @@ "react": "15.x || 16.x || 17.x || 18.x" } }, - "node_modules/@sentry/react/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" + "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/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.40.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.40.0.tgz", - "integrity": "sha512-Y9Kvo9jKouUdrHQhHVv5SmWZClF5o7BFI6oVpLlv4zXORPQlyoZONM/9sxiMvvH73alDSpxzCoxyhlypAOH4ww==", - "license": "MIT", + "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/core": "7.40.0", - "@sentry/types": "7.40.0", - "@sentry/utils": "7.40.0" + "@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/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.40.0", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.40.0.tgz", @@ -4381,6 +4710,27 @@ "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": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", @@ -6112,12 +6462,21 @@ "@apollo/client": "^3.0.0" } }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true, - "license": "ISC" + "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", @@ -6140,17 +6499,6 @@ ], "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": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -7571,16 +7919,6 @@ "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": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", @@ -7743,13 +8081,6 @@ "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": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -8911,13 +9242,6 @@ "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": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", @@ -9218,6 +9542,28 @@ "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": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", @@ -11285,74 +11631,6 @@ "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": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -11755,13 +12033,6 @@ "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": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -15746,19 +16017,6 @@ "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": { "version": "2.1.1", "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" } }, - "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": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz", @@ -17796,7 +18044,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -20528,13 +20775,6 @@ "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": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -23069,16 +23309,6 @@ "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": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", diff --git a/client/package.json b/client/package.json index 602f30e12..a93f91fac 100644 --- a/client/package.json +++ b/client/package.json @@ -9,12 +9,14 @@ "@craco/craco": "^7.0.0", "@fingerprintjs/fingerprintjs": "^3.4.2", "@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", "@splitsoftware/splitio-react": "^1.8.1", "@tanem/react-nprogress": "^5.0.8", "antd": "^4.24.8", "apollo-link-logger": "^2.0.1", + "apollo-link-sentry": "^3.3.0", "axios": "^1.3.4", "craco-less": "^2.0.0", "dinero.js": "^1.9.1", @@ -88,13 +90,14 @@ "scripts": { "analyze": "source-map-explorer 'build/static/js/*.js'", "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-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", "test": "cypress open", "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": { "extends": [ diff --git a/client/src/App/App.container.jsx b/client/src/App/App.container.jsx index c58ddd7e4..206b2016d 100644 --- a/client/src/App/App.container.jsx +++ b/client/src/App/App.container.jsx @@ -8,6 +8,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"; moment.locale("en-US"); @@ -18,7 +19,7 @@ export const factory = SplitSdk({ }, }); -export default function AppContainer() { +function AppContainer() { const { t } = useTranslation(); return ( @@ -48,3 +49,5 @@ export default function AppContainer() { ); } + +export default Sentry.withProfiler(AppContainer); diff --git a/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx b/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx index 90a14a5d1..b0c8c22da 100644 --- a/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx +++ b/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx @@ -105,6 +105,7 @@ function BillEnterModalContainer({ location, outstanding_returns, inventory, + federal_tax_exempt, ...remainingValues } = values; diff --git a/client/src/components/csi-response-form/csi-response-form.container.jsx b/client/src/components/csi-response-form/csi-response-form.container.jsx index a1882b09a..d7a565608 100644 --- a/client/src/components/csi-response-form/csi-response-form.container.jsx +++ b/client/src/components/csi-response-form/csi-response-form.container.jsx @@ -5,6 +5,7 @@ import React, { useEffect } from "react"; import { useTranslation } from "react-i18next"; import { useLocation } from "react-router-dom"; import { QUERY_CSI_RESPONSE_BY_PK } from "../../graphql/csi.queries"; +import { DateFormatter } from "../../utils/DateFormatter"; import AlertComponent from "../alert/alert.component"; import ConfigFormComponents from "../config-form-components/config-form-components.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component"; @@ -44,6 +45,13 @@ export default function CsiResponseFormContainer() { readOnly componentList={data.csi_by_pk.csiquestion.config} /> + {data.csi_by_pk.validuntil ? ( + <> + {t("csi.fields.validuntil")} + {": "} + {data.csi_by_pk.validuntil} + + ) : null} ); diff --git a/client/src/components/csi-response-list-paginated/csi-response-list-paginated.component.jsx b/client/src/components/csi-response-list-paginated/csi-response-list-paginated.component.jsx index f73e35512..3d1a3b864 100644 --- a/client/src/components/csi-response-list-paginated/csi-response-list-paginated.component.jsx +++ b/client/src/components/csi-response-list-paginated/csi-response-list-paginated.component.jsx @@ -5,9 +5,11 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useHistory, useLocation } from "react-router-dom"; import { DateFormatter } from "../../utils/DateFormatter"; -import { alphaSort } from "../../utils/sorters"; -import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; -import {pageLimit} from "../../utils/config"; +import { pageLimit } from "../../utils/config"; +import { alphaSort, dateSort } from "../../utils/sorters"; +import OwnerNameDisplay, { + OwnerNameDisplayFunction, +} from "../owner-name-display/owner-name-display.component"; export default function CsiResponseListPaginated({ refetch, @@ -16,23 +18,23 @@ export default function CsiResponseListPaginated({ total, }) { const search = queryString.parse(useLocation().search); - const { responseid, page, sortcolumn, sortorder } = search; + const { responseid } = search; const history = useHistory(); + const { t } = useTranslation(); const [state, setState] = useState({ sortedInfo: {}, filteredInfo: { text: "" }, + page: "", }); - const { t } = useTranslation(); const columns = [ { title: t("jobs.fields.ro_number"), dataIndex: "ro_number", key: "ro_number", - width: "8%", 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) => ( {record.job.ro_number || t("general.labels.na")} @@ -41,15 +43,18 @@ export default function CsiResponseListPaginated({ }, { title: t("jobs.fields.owner"), - dataIndex: "owner", - key: "owner", - ellipsis: true, - sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln), - width: "25%", - sortOrder: sortcolumn === "owner" && sortorder, + dataIndex: "owner_name", + key: "owner_name", + sorter: (a, b) => + alphaSort( + OwnerNameDisplayFunction(a.job), + OwnerNameDisplayFunction(b.job) + ), + sortOrder: + state.sortedInfo.columnKey === "owner_name" && state.sortedInfo.order, render: (text, record) => { - return record.job.owner ? ( - + return record.job.ownerid ? ( + ) : ( @@ -64,9 +69,9 @@ export default function CsiResponseListPaginated({ dataIndex: "completedon", key: "completedon", ellipsis: true, - sorter: (a, b) => a.completedon - b.completedon, - width: "25%", - sortOrder: sortcolumn === "completedon" && sortorder, + sorter: (a, b) => dateSort(a.completedon, b.completedon), + sortOrder: + state.sortedInfo.columnKey === "completedon" && state.sortedInfo.order, render: (text, record) => { return record.completedon ? ( {record.completedon} @@ -76,11 +81,12 @@ export default function CsiResponseListPaginated({ ]; const handleTableChange = (pagination, filters, sorter) => { - setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); - search.page = pagination.current; - search.sortcolumn = sorter.columnKey; - search.sortorder = sorter.order; - history.push({ search: queryString.stringify(search) }); + setState({ + ...state, + filteredInfo: filters, + sortedInfo: sorter, + page: pagination.current, + }); }; const handleOnRowClick = (record) => { @@ -108,7 +114,7 @@ export default function CsiResponseListPaginated({ pagination={{ position: "top", pageSize: pageLimit, - current: parseInt(page || 1), + current: parseInt(state.page || 1), total: total, }} columns={columns} @@ -122,13 +128,6 @@ export default function CsiResponseListPaginated({ selectedRowKeys: [responseid], type: "radio", }} - onRow={(record, rowIndex) => { - return { - onClick: (event) => { - handleOnRowClick(record); - }, // click row - }; - }} /> ); diff --git a/client/src/components/error-boundary/error-boundary.component.jsx b/client/src/components/error-boundary/error-boundary.component.jsx index cc464c7dd..d1be31d1c 100644 --- a/client/src/components/error-boundary/error-boundary.component.jsx +++ b/client/src/components/error-boundary/error-boundary.component.jsx @@ -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)) +); diff --git a/client/src/components/jobs-list/jobs-list.component.jsx b/client/src/components/jobs-list/jobs-list.component.jsx index 7761c0ed9..68b3f3425 100644 --- a/client/src/components/jobs-list/jobs-list.component.jsx +++ b/client/src/components/jobs-list/jobs-list.component.jsx @@ -1,8 +1,8 @@ import { - BranchesOutlined, - ExclamationCircleFilled, - PauseCircleOutlined, - SyncOutlined, + BranchesOutlined, + ExclamationCircleFilled, + PauseCircleOutlined, + SyncOutlined, } from "@ant-design/icons"; import { useQuery } from "@apollo/client"; import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd"; diff --git a/client/src/components/parts-queue-card/parts-queue-card.component.jsx b/client/src/components/parts-queue-card/parts-queue-card.component.jsx new file mode 100644 index 000000000..c703b7616 --- /dev/null +++ b/client/src/components/parts-queue-card/parts-queue-card.component.jsx @@ -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 ( + + {loading ? : null} + {error ? : null} + {data ? ( + + {data.jobs_by_pk.ro_number || t("general.labels.na")} + + } + > + + + + + ) : null} + + ); +} diff --git a/client/src/components/parts-queue-card/parts-queue-job-lines.component.jsx b/client/src/components/parts-queue-card/parts-queue-job-lines.component.jsx new file mode 100644 index 000000000..d680c1bef --- /dev/null +++ b/client/src/components/parts-queue-card/parts-queue-job-lines.component.jsx @@ -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) => ( + + {record.db_ref === "900510" || record.db_ref === "900511" + ? record.prt_dsmk_m + : record.act_price} + + ), + }, + { + 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 ( + + + + ); +} +export default connect( + mapStateToProps, + mapDispatchToProps +)(PartsQueueJobLinesComponent); diff --git a/client/src/pages/parts-queue/parts-queue.page.component.jsx b/client/src/components/parts-queue-list/parts-queue.list.component.jsx similarity index 76% rename from client/src/pages/parts-queue/parts-queue.page.component.jsx rename to client/src/components/parts-queue-list/parts-queue.list.component.jsx index 62970d788..44d5368ce 100644 --- a/client/src/pages/parts-queue/parts-queue.page.component.jsx +++ b/client/src/components/parts-queue-list/parts-queue.list.component.jsx @@ -8,30 +8,28 @@ import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { Link, useHistory, useLocation } from "react-router-dom"; 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 { selectBodyshop } from "../../redux/user/user.selectors"; import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter"; +import { onlyUnique } from "../../utils/arrayHelper"; +import { pageLimit } from "../../utils/config"; import { alphaSort, dateSort } from "../../utils/sorters"; 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({ bodyshop: selectBodyshop, }); -export function PartsQueuePageComponent({ bodyshop }) { +export function PartsQueueListComponent({ bodyshop }) { const searchParams = queryString.parse(useLocation().search); - const { - //page, - sortcolumn, - sortorder, - statusFilters, - } = searchParams; + const { selected, sortcolumn, sortorder, statusFilters } = searchParams; const history = useHistory(); const [filter, setFilter] = useLocalStorage("filter_parts_queue", null); @@ -39,19 +37,8 @@ export function PartsQueuePageComponent({ bodyshop }) { fetchPolicy: "network-only", nextFetchPolicy: "network-only", variables: { - // offset: page ? (page - 1) * 25 : 0, - // limit: 25, statuses: (statusFilters && JSON.parse(statusFilters)) || 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) }); }; + const handleOnRowClick = (record) => { + if (record) { + if (record.id) { + history.push({ + search: queryString.stringify({ + ...searchParams, + selected: record.id, + }), + }); + } + } + }; + const columns = [ { title: t("jobs.fields.ro_number"), @@ -125,7 +125,8 @@ export function PartsQueuePageComponent({ bodyshop }) { title: t("jobs.fields.owner"), dataIndex: "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, render: (text, record) => { 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 ? ( + + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + + ) : ( + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + ); + }, + }, + { + 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"), dataIndex: "status", @@ -170,23 +221,16 @@ export function PartsQueuePageComponent({ bodyshop }) { ), }, { - title: t("jobs.fields.vehicle"), - dataIndex: "vehicle", - key: "vehicle", + title: t("jobs.fields.scheduled_completion"), + dataIndex: "scheduled_completion", + key: "scheduled_completion", ellipsis: true, - render: (text, record) => { - return record.vehicleid ? ( - - {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ - record.v_model_desc || "" - }`} - - ) : ( - {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ - record.v_model_desc || "" - }`} - ); - }, + sorter: (a, b) => + dateSort(a.scheduled_completion, b.scheduled_completion), + sortOrder: sortcolumn === "scheduled_completion" && sortorder, + render: (text, record) => ( + {record.scheduled_completion} + ), }, // { // title: t("vehicles.fields.plate_no"), @@ -198,14 +242,6 @@ export function PartsQueuePageComponent({ bodyshop }) { // 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"), // dataIndex: "clm_total", @@ -307,9 +343,16 @@ export function PartsQueuePageComponent({ bodyshop }) { style={{ height: "100%" }} scroll={{ x: true }} onChange={handleTableChange} + rowSelection={{ + onSelect: (record) => { + handleOnRowClick(record); + }, + selectedRowKeys: [selected], + type: "radio", + }} /> ); } -export default connect(mapStateToProps, null)(PartsQueuePageComponent); +export default connect(mapStateToProps, null)(PartsQueueListComponent); diff --git a/client/src/components/production-board-kanban/production-board-kanban.component.jsx b/client/src/components/production-board-kanban/production-board-kanban.component.jsx index b1061f973..9421de918 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.component.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.component.jsx @@ -115,7 +115,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({ @@ -153,7 +153,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 diff --git a/client/src/components/shop-csi-config-form/shop-csi-config-form.component.jsx b/client/src/components/shop-csi-config-form/shop-csi-config-form.component.jsx index 4ed0c6033..9818bf896 100644 --- a/client/src/components/shop-csi-config-form/shop-csi-config-form.component.jsx +++ b/client/src/components/shop-csi-config-form/shop-csi-config-form.component.jsx @@ -1,5 +1,5 @@ -import React from "react"; import { Form } from "antd"; +import React from "react"; import ConfigFormComponents from "../config-form-components/config-form-components.component"; export default function ShopCsiConfigForm({ selectedCsi }) { @@ -9,7 +9,7 @@ export default function ShopCsiConfigForm({ selectedCsi }) { return (
- The Config Form {readOnly} + {readOnly} {selectedCsi && (
; return (
- The Config Form
- + + diff --git a/client/src/graphql/apollo-error-handling.js b/client/src/graphql/apollo-error-handling.js index 1ee2c1d8e..1d3f4e857 100644 --- a/client/src/graphql/apollo-error-handling.js +++ b/client/src/graphql/apollo-error-handling.js @@ -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); } ); diff --git a/client/src/graphql/csi.queries.js b/client/src/graphql/csi.queries.js index f835f1d56..d2b62af43 100644 --- a/client/src/graphql/csi.queries.js +++ b/client/src/graphql/csi.queries.js @@ -57,19 +57,15 @@ export const INSERT_CSI = gql` `; export const QUERY_CSI_RESPONSE_PAGINATED = gql` - query QUERY_CSI_RESPONSE_PAGINATED( - $offset: Int - $limit: Int - $order: [csi_order_by!]! - ) { - csi(offset: $offset, limit: $limit, order_by: $order) { + query QUERY_CSI_RESPONSE_PAGINATED { + csi(order_by: { completedon: desc_nulls_last }) { id completedon job { ownr_fn ownr_ln + ownerid ro_number - id } } @@ -83,6 +79,7 @@ export const QUERY_CSI_RESPONSE_PAGINATED = gql` export const QUERY_CSI_RESPONSE_BY_PK = gql` query QUERY_CSI_RESPONSE_BY_PK($id: uuid!) { csi_by_pk(id: $id) { + completedon relateddata valid validuntil diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index 419171248..c1a2d901b 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -108,12 +108,7 @@ export const QUERY_ALL_ACTIVE_JOBS = gql` `; export const QUERY_PARTS_QUEUE = gql` - query QUERY_PARTS_QUEUE( - $statuses: [String!]! - $offset: Int - $limit: Int - $order: [jobs_order_by!] - ) { + query QUERY_PARTS_QUEUE($statuses: [String!]!, $offset: Int, $limit: Int) { jobs_aggregate(where: { _and: [{ status: { _in: $statuses } }] }) { aggregate { count(distinct: true) @@ -125,7 +120,7 @@ export const QUERY_PARTS_QUEUE = gql` } offset: $offset limit: $limit - order_by: $order + order_by: { ro_number: desc } ) { ownr_fn ownr_ln @@ -142,7 +137,9 @@ export const QUERY_PARTS_QUEUE = gql` v_color vehicleid scheduled_in + scheduled_completion id + ins_co_nm clm_no ro_number 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` query QUERY_COMPLETED_TASKS($jobid: uuid!) { jobs_by_pk(id: $jobid) { diff --git a/client/src/index.js b/client/src/index.js index 896aadca2..aa33f842a 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -14,6 +14,7 @@ import { persistor, store } from "./redux/store"; import reportWebVitals from "./reportWebVitals"; import "./translations/i18n"; import "./utils/CleanAxios"; + //import { BrowserTracing } from "@sentry/tracing"; // Dinero.defaultCurrency = "CAD"; @@ -23,27 +24,25 @@ Dinero.globalRoundingMode = "HALF_EVEN"; if (process.env.NODE_ENV !== "development") { Sentry.init({ dsn: "https://a6acc91c073e414196014b8484627a61@o492140.ingest.sentry.io/4504561071161344", - ignoreErrors: [ + 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, + tracePropagationTargets: [ + "api.romeonline.io", + "api.test.romeonline.io", + "db.romeonline.io", + ], + tracesSampleRate: 1.0, environment: process.env.NODE_ENV, - // tracesSampleRate: 0.2, - // We recommend adjusting this value in production, or using tracesSampler - // for finer control - // tracesSampleRate: 0.5, }); } diff --git a/client/src/pages/csi/csi.container.page.jsx b/client/src/pages/csi/csi.container.page.jsx index c6f34ab30..74113ae20 100644 --- a/client/src/pages/csi/csi.container.page.jsx +++ b/client/src/pages/csi/csi.container.page.jsx @@ -1,88 +1,67 @@ -import { useQuery, useMutation } from "@apollo/client"; -import { Form, Layout, Typography, Button, Result } from "antd"; -import React, { useState } from "react"; +// import { useMutation, useQuery } from "@apollo/client"; +import { Button, Form, Layout, Result, Typography } from "antd"; +import axios from "axios"; +import React, { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; import { useParams } from "react-router-dom"; +import { createStructuredSelector } from "reselect"; import AlertComponent from "../../components/alert/alert.component"; import ConfigFormComponents from "../../components/config-form-components/config-form-components.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 { DateTimeFormat } from "./../../utils/DateFormatter"; const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser, }); -const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) -}); +const mapDispatchToProps = (dispatch) => ({}); + export default connect(mapStateToProps, mapDispatchToProps)(CsiContainerPage); export function CsiContainerPage({ currentUser }) { const { surveyId } = useParams(); const [form] = Form.useForm(); + const [axiosResponse, setAxiosResponse] = useState(null); const [submitting, setSubmitting] = useState({ loading: false, submitted: false, }); - - const { loading, error, data } = useQuery(QUERY_SURVEY, { - variables: { surveyId }, - fetchPolicy: "network-only", - nextFetchPolicy: "network-only", - }); - const { t } = useTranslation(); - const [completeSurvey] = useMutation(COMPLETE_SURVEY); - if (loading) return ; - if (error || !!!data.csi_by_pk) - return ( -
- - {error ? ( -
ERROR: {error.graphQLErrors.map((e) => e.message)}
- ) : null} -
-
- ); - - 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, + const getAxiosData = useCallback(async () => { + try { + try { + window.$crisp.push(["do", "chat:hide"]); + } catch { + console.log("Unable to attach to crisp instance. "); + } + setSubmitting((prevSubmitting) => ({ ...prevSubmitting, loading: true })); + const response = await axios.post("/csi/lookup", { surveyId }); + setSubmitting((prevSubmitting) => ({ + ...prevSubmitting, 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 { - relateddata: { bodyshop, job }, - csiquestion: { config: csiquestions }, - } = data.csi_by_pk; + useEffect(() => { + getAxiosData().catch((err) => + console.error( + `Something went wrong fetching axios data: ${err.message || ""}` + ) + ); + }, [getAxiosData]); - if (currentUser && currentUser.authorized) + // Return if authorized + if (currentUser && currentUser.authorized) { return ( ); + } - return ( - -
-
- {bodyshop.logo_img_path && bodyshop.logo_img_path.src ? ( - Logo - ) : null} -
- {bodyshop.shopname || ""} -
{`${bodyshop.address1 || ""}`}
-
{`${bodyshop.address2 || ""}`}
-
{`${bodyshop.city || ""} ${bodyshop.state || ""} ${ - bodyshop.zip_post || "" - }`}
+ if (submitting.loading) return ; + + const handleFinish = async (values) => { + try { + setSubmitting({ ...submitting, loading: true, submitting: true }); + const result = await axios.post("/csi/submit", { surveyId, values }); + console.log("result", result); + if (!!!result.errors && result.data.update_csi.affected_rows > 0) { + setSubmitting({ ...submitting, loading: false, submitted: true }); + } + } catch (error) { + console.error(`Something went wrong...: ${error.message}`); + console.dir({ + stack: error?.stack, + message: error?.message, + }); + } + }; + + if (!axiosResponse || axiosResponse.csi_by_pk === null) { + // Do something here , this is where you would return a loading box or something + return ( + <> + + + + + + + + {t("csi.labels.copyright")}{" "} + {t("csi.fields.surveyid", { surveyId: surveyId })} + + + + ); + } else { + const { + relateddata: { bodyshop, job }, + csiquestion: { config: csiquestions }, + } = axiosResponse.csi_by_pk; + + return ( + +
+
+ {bodyshop.logo_img_path && bodyshop.logo_img_path.src ? ( + {bodyshop.shopname.concat(" + ) : null} +
+ + {bodyshop.shopname || ""} + + + {`${bodyshop.address1 || ""}${bodyshop.address2 ? ", " : ""}${ + bodyshop.address2 || "" + }`.trim()} + + + {`${bodyshop.city || ""}${ + bodyshop.city && bodyshop.state ? ", " : "" + }${bodyshop.state || ""} ${bodyshop.zip_post || ""}`.trim()} + +
+ {t("csi.labels.title")} + + {t("csi.labels.greeting", { + name: job.ownr_co_nm || job.ownr_fn || "", + })} + + + {t("csi.labels.intro", { shopname: bodyshop.shopname || "" })} +
- {t("csi.labels.title")} - {`Hi ${job.ownr_co_nm || job.ownr_fn || ""}!`} - - {`At ${ - bodyshop.shopname || "" - }, we value your feedback. We would love to - hear what you have to say. Please fill out the form below.`} - -
- {submitting.error ? ( - - ) : null} + {submitting.error ? ( + + ) : null} - {submitting.submitted ? ( - - - - ) : ( - -
- - - -
- )} - - - {`Survey ID: ${surveyId}`} - - - ); + {submitting.submitted ? ( + + + + ) : ( + +
+ {axiosResponse.csi_by_pk.valid ? ( + <> + + + + ) : ( + <> + + + {t("csi.successes.submittedsub")} + + + )} + +
+ )} + + {t("csi.labels.copyright")}{" "} + {t("csi.fields.surveyid", { surveyId: surveyId })} + + + ); + } } diff --git a/client/src/pages/parts-queue/parts-queue.page.container.jsx b/client/src/pages/parts-queue/parts-queue.page.container.jsx index 5f5d8d617..3f78e9b92 100644 --- a/client/src/pages/parts-queue/parts-queue.page.container.jsx +++ b/client/src/pages/parts-queue/parts-queue.page.container.jsx @@ -1,12 +1,13 @@ import React, { useEffect } from "react"; import { useTranslation } from "react-i18next"; 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 { setBreadcrumbs, setSelectedHeader, } from "../../redux/application/application.actions"; -import PartsQueuePage from "./parts-queue.page.component"; const mapDispatchToProps = (dispatch) => ({ setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), @@ -26,7 +27,8 @@ export function PartsQueuePageContainer({ setBreadcrumbs, setSelectedHeader }) { return ( - + + ); } diff --git a/client/src/pages/shop-csi/shop-csi.container.page.jsx b/client/src/pages/shop-csi/shop-csi.container.page.jsx index f0c295685..e9d304b53 100644 --- a/client/src/pages/shop-csi/shop-csi.container.page.jsx +++ b/client/src/pages/shop-csi/shop-csi.container.page.jsx @@ -1,22 +1,20 @@ -import { Row, Col } from "antd"; import { useQuery } from "@apollo/client"; -import queryString from "query-string"; +import { Col, Row } from "antd"; import React, { useEffect } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; -import { useLocation } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import AlertComponent from "../../components/alert/alert.component"; 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 RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import { QUERY_CSI_RESPONSE_PAGINATED } from "../../graphql/csi.queries"; import { setBreadcrumbs, setSelectedHeader, } from "../../redux/application/application.actions"; import { selectBodyshop } from "../../redux/user/user.selectors"; -import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; -import {pageLimit} from "../../utils/config"; + const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, }); @@ -33,28 +31,11 @@ export function ShopCsiContainer({ }) { const { t } = useTranslation(); - const searchParams = queryString.parse(useLocation().search); - const { page, sortcolumn, sortorder } = searchParams; - const { loading, error, data, refetch } = useQuery( QUERY_CSI_RESPONSE_PAGINATED, { fetchPolicy: "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 ; return ( - - // } - > +
{ 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 (process.env.NODE_ENV === "development") { } middlewares.push( - roundTripLink.concat( - retryLink.concat(errorLink.concat(authLink.concat(link))) + new SentryLink().concat( + roundTripLink.concat( + retryLink.concat(errorLink.concat(authLink.concat(link))) + ) ) ); diff --git a/server.js b/server.js index cfec97d59..1a2c3b3c8 100644 --- a/server.js +++ b/server.js @@ -73,6 +73,7 @@ app.use('/tech', require("./server/routes/techRoutes")); app.use('/intellipay', require("./server/routes/intellipayRoutes")); app.use('/cdk', require("./server/routes/cdkRoutes")); app.use('/payroll', require("./server/routes/payrollRoutes")); +app.use('/csi', require("./server/routes/csiRoutes")); // Default route for forbidden access app.get("/", (req, res) => { diff --git a/server/csi/csi.js b/server/csi/csi.js new file mode 100644 index 000000000..819a9ebc7 --- /dev/null +++ b/server/csi/csi.js @@ -0,0 +1,2 @@ +exports.lookup = require("./lookup").default; +exports.submit = require("./submit").default; \ No newline at end of file diff --git a/server/csi/lookup.js b/server/csi/lookup.js new file mode 100644 index 000000000..81a44c2b8 --- /dev/null +++ b/server/csi/lookup.js @@ -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); + } +}; diff --git a/server/csi/submit.js b/server/csi/submit.js new file mode 100644 index 000000000..a232425ae --- /dev/null +++ b/server/csi/submit.js @@ -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); + } +}; diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 80c21cf4e..697752548 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -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 + } + }`; \ No newline at end of file diff --git a/server/routes/csiRoutes.js b/server/routes/csiRoutes.js new file mode 100644 index 000000000..8f47a2b2d --- /dev/null +++ b/server/routes/csiRoutes.js @@ -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;