Created
February 13, 2024 04:22
-
-
Save jay-babu/feb3c93d34c50d67963c74c1afccc962 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/.github/ISSUE_TEMPLATE/pull_request_template.md b/.github/ISSUE_TEMPLATE/pull_request_template.md | |
new file mode 100644 | |
index 0000000..a4210a8 | |
--- /dev/null | |
+++ b/.github/ISSUE_TEMPLATE/pull_request_template.md | |
@@ -0,0 +1,32 @@ | |
+### All Submissions: | |
+ | |
+- [ ] Have you added authorization guards based on permission levels? | |
+- [ ] Is developer-facing telemtry added? | |
+ | |
+### New Feature Submissions: | |
+ | |
+- [ ] Describe how you tested this feature | |
+- [ ] Have you written tests? | |
+- [ ] Are customer-exposed audit logs added? | |
+ | |
+### Changes to Core Features: | |
+ | |
+- [ ] Have you written new tests for your core changes, as applicable? | |
+- [ ] Have you successfully ran tests with your changes locally? | |
+- [ ] Does this feature have any impact on transactions or the reporting of transactions? | |
+- [ ] If so, have you tested the following? | |
+ - [ ] Add item via barcode | |
+ - [ ] Add item via search | |
+ - [ ] Add item with qty multiplier | |
+ - [ ] Add item with keyboard hot keys | |
+ - [ ] Add item that does not exist and see that the modal cannot be closed | |
+ - [ ] Add item with parent item and verfify correct qty changing | |
+ - [ ] Add variant item | |
+ - [ ] Add tax except item | |
+ - [ ] Add dicounted item (during sale) | |
+ - [ ] Add promotional item | |
+ - [ ] Add multiple promotional items | |
+ - [ ] Add items from holds | |
+ - [ ] Checkout with credit | |
+ - [ ] Checkout with mutiple credit cards | |
+ | |
diff --git a/POSServiceModel b/POSServiceModel | |
index 38b2d15..af64a57 160000 | |
--- a/POSServiceModel | |
+++ b/POSServiceModel | |
@@ -1 +1 @@ | |
-Subproject commit 38b2d151cc6ddbbbaffa73403d818050d352f5ff | |
+Subproject commit af64a571f142ca2c2a33a1f31d520e4ca6d68e05 | |
diff --git a/package-lock.json b/package-lock.json | |
index e9c87c2..6259aeb 100644 | |
--- a/package-lock.json | |
+++ b/package-lock.json | |
@@ -42,12 +42,13 @@ | |
"react-apexcharts": "^1.4.1", | |
"react-dom": "^18.2.0", | |
"react-hook-form": "^7.47.0", | |
+ "react-hook-form-chakra": "^1.0.2", | |
"react-icons": "^4.12.0", | |
"react-infinite-scroll-component": "^6.1.0", | |
"react-json-view-lite": "^1.2.1", | |
"react-router-dom": "^6.14.0", | |
"react-scripts": "5.0.1", | |
- "swagger-typescript-api": "^12.0.4", | |
+ "swagger-typescript-api": "^13.0.3", | |
"typescript": "^4.9.5", | |
"use-debounce": "^10.0.0", | |
"use-query-params": "^2.2.1", | |
@@ -10233,6 +10234,17 @@ | |
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", | |
"integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" | |
}, | |
+ "node_modules/@sindresorhus/is": { | |
+ "version": "3.1.2", | |
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.2.tgz", | |
+ "integrity": "sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==", | |
+ "engines": { | |
+ "node": ">=10" | |
+ }, | |
+ "funding": { | |
+ "url": "https://github.com/sindresorhus/is?sponsor=1" | |
+ } | |
+ }, | |
"node_modules/@sinonjs/commons": { | |
"version": "3.0.0", | |
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", | |
@@ -10250,30 +10262,41 @@ | |
} | |
}, | |
"node_modules/@stoplight/http-spec": { | |
- "version": "5.9.6", | |
- "resolved": "https://registry.npmjs.org/@stoplight/http-spec/-/http-spec-5.9.6.tgz", | |
- "integrity": "sha512-3BSNYLwUw/O8wXAeLalyNC6tMeDP7OffX3jiLBzxNKTqGiQJAbnRWdD6wcDqL2EtZLt6FBamHTI5vw9lNvUbew==", | |
+ "version": "7.0.2", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/http-spec/-/http-spec-7.0.2.tgz", | |
+ "integrity": "sha512-4DvT0w5goAhLxVbHfdzkMqGcTdi9bU4LmBrYNrZBOCFV4JPAHRERSBdI7F7n/MfgVvzxWb3Vftrh6pCgTd/+Jg==", | |
"dev": true, | |
"dependencies": { | |
"@stoplight/json": "^3.18.1", | |
"@stoplight/json-schema-generator": "1.0.2", | |
- "@stoplight/types": "^13.15.0", | |
+ "@stoplight/types": "14.1.0", | |
"@types/json-schema": "7.0.11", | |
"@types/swagger-schema-official": "~2.0.22", | |
"@types/type-is": "^1.6.3", | |
"fnv-plus": "^1.3.1", | |
- "lodash.isequalwith": "^4.4.0", | |
- "lodash.pick": "^4.4.0", | |
- "lodash.pickby": "^4.6.0", | |
+ "lodash": "^4.17.21", | |
"openapi3-ts": "^2.0.2", | |
"postman-collection": "^4.1.2", | |
- "tslib": "^2.3.1", | |
+ "tslib": "^2.6.2", | |
"type-is": "^1.6.18" | |
}, | |
"engines": { | |
"node": ">=14.13" | |
} | |
}, | |
+ "node_modules/@stoplight/http-spec/node_modules/@stoplight/types": { | |
+ "version": "14.1.0", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.0.tgz", | |
+ "integrity": "sha512-fL8Nzw03+diALw91xHEHA5Q0WCGeW9WpPgZQjodNUWogAgJ56aJs03P9YzsQ1J6fT7/XjDqHMgn7/RlsBzB/SQ==", | |
+ "dev": true, | |
+ "dependencies": { | |
+ "@types/json-schema": "^7.0.4", | |
+ "utility-types": "^3.10.0" | |
+ }, | |
+ "engines": { | |
+ "node": "^12.20 || >=14.13" | |
+ } | |
+ }, | |
"node_modules/@stoplight/http-spec/node_modules/@types/json-schema": { | |
"version": "7.0.11", | |
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", | |
@@ -10331,9 +10354,9 @@ | |
} | |
}, | |
"node_modules/@stoplight/json-schema-ref-parser": { | |
- "version": "9.2.5", | |
- "resolved": "https://registry.npmjs.org/@stoplight/json-schema-ref-parser/-/json-schema-ref-parser-9.2.5.tgz", | |
- "integrity": "sha512-7UI3pX5oyGzAdGPah001CyPnIsJZJW+38sGjvx862zXQFidBe0sxFO5MUety61Zr/RaygCQ2RU/KfD7hSfOLxg==", | |
+ "version": "9.2.7", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/json-schema-ref-parser/-/json-schema-ref-parser-9.2.7.tgz", | |
+ "integrity": "sha512-1vNzJ7iSrFTAFNbZHPyhI6GiJJw74+WaV61bARUQEDR4Jm80f9s0Tq9uCvGoMYwIFmWDJAoTiyegnUs6SvVxDw==", | |
"dev": true, | |
"dependencies": { | |
"@jsdevtools/ono": "^7.1.3", | |
@@ -10346,19 +10369,32 @@ | |
} | |
}, | |
"node_modules/@stoplight/json-schema-sampler": { | |
- "version": "0.2.2", | |
- "resolved": "https://registry.npmjs.org/@stoplight/json-schema-sampler/-/json-schema-sampler-0.2.2.tgz", | |
- "integrity": "sha512-QP4ZwXh3dEn5wHZs2361kdf4BmaKiiP+pxIImAuVTLmulv9sBTB+ETG7Y5z9u4DOUQu2GNxfUY10iSwuBQMXrg==", | |
+ "version": "0.3.0", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/json-schema-sampler/-/json-schema-sampler-0.3.0.tgz", | |
+ "integrity": "sha512-G7QImi2xr9+8iPEg0D9YUi1BWhIiiEm19aMb91oWBSdxuhezOAqqRP3XNY6wczHV9jLWW18f+KkghTy9AG0BQA==", | |
"dev": true, | |
"dependencies": { | |
"@types/json-schema": "^7.0.7", | |
"json-pointer": "^0.6.1" | |
} | |
}, | |
+ "node_modules/@stoplight/json/node_modules/@stoplight/types": { | |
+ "version": "13.20.0", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.20.0.tgz", | |
+ "integrity": "sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA==", | |
+ "dev": true, | |
+ "dependencies": { | |
+ "@types/json-schema": "^7.0.4", | |
+ "utility-types": "^3.10.0" | |
+ }, | |
+ "engines": { | |
+ "node": "^12.20 || >=14.13" | |
+ } | |
+ }, | |
"node_modules/@stoplight/ordered-object-literal": { | |
- "version": "1.0.4", | |
- "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.4.tgz", | |
- "integrity": "sha512-OF8uib1jjDs5/cCU+iOVy+GJjU3X7vk/qJIkIJFqwmlJKrrtijFmqwbu8XToXrwTYLQTP+Hebws5gtZEmk9jag==", | |
+ "version": "1.0.5", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.5.tgz", | |
+ "integrity": "sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==", | |
"dev": true, | |
"engines": { | |
"node": ">=8" | |
@@ -10374,22 +10410,22 @@ | |
} | |
}, | |
"node_modules/@stoplight/prism-cli": { | |
- "version": "5.0.1", | |
- "resolved": "https://registry.npmjs.org/@stoplight/prism-cli/-/prism-cli-5.0.1.tgz", | |
- "integrity": "sha512-A13olRGUOeAxWWdv2lJ7JaufRmsvdVRNCYcyOjg17CTyIkZF46I0y3mvlHtICDDBH/85GjOk3OXI7a/UkQsSDA==", | |
+ "version": "5.5.4", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/prism-cli/-/prism-cli-5.5.4.tgz", | |
+ "integrity": "sha512-MvJUQcd8Fb3cuJuggjWinclz/JlHBeqZd5cYJtyJYs1Cz/woXjYF+0KeDviTdUTXEcTLhFguXS8vUK9vxqZ01g==", | |
"dev": true, | |
"dependencies": { | |
- "@stoplight/http-spec": "^5.9.2", | |
+ "@stoplight/http-spec": "^7.0.2", | |
"@stoplight/json": "^3.18.1", | |
- "@stoplight/json-schema-ref-parser": "9.2.5", | |
- "@stoplight/prism-core": "^5.0.1", | |
- "@stoplight/prism-http": "^5.0.1", | |
- "@stoplight/prism-http-server": "^5.0.1", | |
- "@stoplight/types": "^13.15.0", | |
+ "@stoplight/json-schema-ref-parser": "9.2.7", | |
+ "@stoplight/prism-core": "^5.5.4", | |
+ "@stoplight/prism-http": "^5.5.4", | |
+ "@stoplight/prism-http-server": "^5.5.4", | |
+ "@stoplight/types": "^14.1.0", | |
"chalk": "^4.1.2", | |
"chokidar": "^3.5.2", | |
"fp-ts": "^2.11.5", | |
- "json-schema-faker": "0.5.0-rcv.40", | |
+ "json-schema-faker": "0.5.3", | |
"lodash": "^4.17.21", | |
"node-fetch": "^2.6.5", | |
"pino": "^6.13.3", | |
@@ -10498,9 +10534,9 @@ | |
} | |
}, | |
"node_modules/@stoplight/prism-core": { | |
- "version": "5.0.1", | |
- "resolved": "https://registry.npmjs.org/@stoplight/prism-core/-/prism-core-5.0.1.tgz", | |
- "integrity": "sha512-rHdhmDrhBDg7yYipLJWlD/jRyECif5bAqDMVfobx1F6mzw+Yfc1YgXQbTgc+6oPwk8SgEr7+WQBq5jCi0ezHYw==", | |
+ "version": "5.5.4", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/prism-core/-/prism-core-5.5.4.tgz", | |
+ "integrity": "sha512-P/I1UD2HwP02EocTRFtjRJe3BeQbJASIGMbE1/rT5fHlYeOiMb+IerFfrTp+/AGO5a193UEDut3XBXR3dBuQcw==", | |
"dev": true, | |
"dependencies": { | |
"fp-ts": "^2.11.5", | |
@@ -10513,17 +10549,17 @@ | |
} | |
}, | |
"node_modules/@stoplight/prism-http": { | |
- "version": "5.0.1", | |
- "resolved": "https://registry.npmjs.org/@stoplight/prism-http/-/prism-http-5.0.1.tgz", | |
- "integrity": "sha512-/esXjNBbXjxZPtl3WyuvkgHUH7l3eDgOwIOP26qLH10BE3jlKR7IHF1gwZgEKdtBhQKQ0/2tB4fPJSAPaU2Blg==", | |
+ "version": "5.5.4", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/prism-http/-/prism-http-5.5.4.tgz", | |
+ "integrity": "sha512-W7NV3W09iAbAWj4ft6tqcBC2kdXpgArWSHO15glxQwRu1Y7I/U1Nr6EUhqQwyBgF5DWB5WFW/mnj30fZuyxGzw==", | |
"dev": true, | |
"dependencies": { | |
"@faker-js/faker": "^6.0.0", | |
"@stoplight/json": "^3.18.1", | |
"@stoplight/json-schema-merge-allof": "0.7.8", | |
- "@stoplight/json-schema-sampler": "0.2.2", | |
- "@stoplight/prism-core": "^5.0.1", | |
- "@stoplight/types": "^13.15.0", | |
+ "@stoplight/json-schema-sampler": "0.3.0", | |
+ "@stoplight/prism-core": "^5.5.4", | |
+ "@stoplight/types": "^14.1.0", | |
"@stoplight/yaml": "^4.2.3", | |
"abstract-logging": "^2.0.1", | |
"accepts": "^1.3.7", | |
@@ -10535,27 +10571,29 @@ | |
"fp-ts": "^2.11.5", | |
"http-proxy-agent": "^5.0.0", | |
"https-proxy-agent": "^5.0.0", | |
- "json-schema-faker": "0.5.0-rcv.40", | |
+ "json-schema-faker": "0.5.3", | |
"lodash": "^4.17.21", | |
"node-fetch": "^2.6.5", | |
+ "parse-multipart-data": "^1.5.0", | |
"pino": "^6.13.3", | |
"tslib": "^2.3.1", | |
"type-is": "^1.6.18", | |
- "uri-template-lite": "^22.9.0" | |
+ "uri-template-lite": "^22.9.0", | |
+ "whatwg-mimetype": "^3.0.0" | |
}, | |
"engines": { | |
"node": ">=16" | |
} | |
}, | |
"node_modules/@stoplight/prism-http-server": { | |
- "version": "5.0.1", | |
- "resolved": "https://registry.npmjs.org/@stoplight/prism-http-server/-/prism-http-server-5.0.1.tgz", | |
- "integrity": "sha512-VeDvw35JvEluyO1h5VEcxTlYwGLrk3GILLZNOB9oRoy31kwXyPrP4mdaFoAVyqfqa+wMkXmWn/6HdPDBsZTb+w==", | |
+ "version": "5.5.4", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/prism-http-server/-/prism-http-server-5.5.4.tgz", | |
+ "integrity": "sha512-2k/hWfxq+P7HQkoFpB9Bb18s+JDiiIZADXIQLYvk/bx6LO/cVxoZst9kqtM7UxugYrfkemYPP9/7/z0v73+ifg==", | |
"dev": true, | |
"dependencies": { | |
- "@stoplight/prism-core": "^5.0.1", | |
- "@stoplight/prism-http": "^5.0.1", | |
- "@stoplight/types": "^13.15.0", | |
+ "@stoplight/prism-core": "^5.5.4", | |
+ "@stoplight/prism-http": "^5.5.4", | |
+ "@stoplight/types": "^14.1.0", | |
"fast-xml-parser": "^4.2.0", | |
"fp-ts": "^2.11.5", | |
"io-ts": "^2.2.16", | |
@@ -10571,9 +10609,9 @@ | |
} | |
}, | |
"node_modules/@stoplight/prism-http-server/node_modules/node-fetch": { | |
- "version": "2.6.12", | |
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", | |
- "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", | |
+ "version": "2.7.0", | |
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", | |
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", | |
"dev": true, | |
"dependencies": { | |
"whatwg-url": "^5.0.0" | |
@@ -10694,9 +10732,9 @@ | |
"dev": true | |
}, | |
"node_modules/@stoplight/prism-http/node_modules/node-fetch": { | |
- "version": "2.6.12", | |
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", | |
- "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", | |
+ "version": "2.7.0", | |
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", | |
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", | |
"dev": true, | |
"dependencies": { | |
"whatwg-url": "^5.0.0" | |
@@ -10725,10 +10763,19 @@ | |
"node": ">=8" | |
} | |
}, | |
+ "node_modules/@stoplight/prism-http/node_modules/whatwg-mimetype": { | |
+ "version": "3.0.0", | |
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", | |
+ "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", | |
+ "dev": true, | |
+ "engines": { | |
+ "node": ">=12" | |
+ } | |
+ }, | |
"node_modules/@stoplight/types": { | |
- "version": "13.15.0", | |
- "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.15.0.tgz", | |
- "integrity": "sha512-pBLjVRrWGVd+KzTbL3qrmufSKIEp0UfziDBdt/nrTHPKrlrtVwaHdrrQMcpM23yJDU1Wcg4cHvhIuGtKCT5OmA==", | |
+ "version": "14.1.1", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-14.1.1.tgz", | |
+ "integrity": "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==", | |
"dev": true, | |
"dependencies": { | |
"@types/json-schema": "^7.0.4", | |
@@ -10759,6 +10806,19 @@ | |
"integrity": "sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==", | |
"dev": true | |
}, | |
+ "node_modules/@stoplight/yaml/node_modules/@stoplight/types": { | |
+ "version": "13.20.0", | |
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.20.0.tgz", | |
+ "integrity": "sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA==", | |
+ "dev": true, | |
+ "dependencies": { | |
+ "@types/json-schema": "^7.0.4", | |
+ "utility-types": "^3.10.0" | |
+ }, | |
+ "engines": { | |
+ "node": "^12.20 || >=14.13" | |
+ } | |
+ }, | |
"node_modules/@storybook/addon-actions": { | |
"version": "7.6.7", | |
"resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.7.tgz", | |
@@ -13012,14 +13072,14 @@ | |
} | |
}, | |
"node_modules/@storybook/preset-create-react-app": { | |
- "version": "7.6.7", | |
- "resolved": "https://registry.npmjs.org/@storybook/preset-create-react-app/-/preset-create-react-app-7.6.7.tgz", | |
- "integrity": "sha512-49m7yeyo1DiRoMqNk87UFg179C4+MYFPAy935K0WUwAlGKZ3/69ipYi8xYbtdAaBCXX5V86BI8HypEMLujWBVw==", | |
+ "version": "7.6.14", | |
+ "resolved": "https://registry.npmjs.org/@storybook/preset-create-react-app/-/preset-create-react-app-7.6.14.tgz", | |
+ "integrity": "sha512-XYSgBnLcLv/P+xV8QbIxgsaVF3BQwTXeEnkEjvU0Iyaiuw7HAPbFb3FRo+JSXU8QkFC3q5JTAUPtJ8KSKFEHsg==", | |
"dev": true, | |
"dependencies": { | |
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.1", | |
"@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0", | |
- "@storybook/types": "7.6.7", | |
+ "@storybook/types": "7.6.14", | |
"@types/babel__core": "^7.1.7", | |
"@types/semver": "^7.3.4", | |
"pnp-webpack-plugin": "^1.7.0", | |
@@ -13034,6 +13094,66 @@ | |
"react-scripts": ">=5.0.0" | |
} | |
}, | |
+ "node_modules/@storybook/preset-create-react-app/node_modules/@storybook/channels": { | |
+ "version": "7.6.14", | |
+ "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.14.tgz", | |
+ "integrity": "sha512-tyrnnXTh7Ca6HbtzYtZGZmbUkC+eYPdot41+YDERMxXCnejd18BnsH/pyGW66GwgY079Q7uhdDFyM63ynZrt/A==", | |
+ "dev": true, | |
+ "dependencies": { | |
+ "@storybook/client-logger": "7.6.14", | |
+ "@storybook/core-events": "7.6.14", | |
+ "@storybook/global": "^5.0.0", | |
+ "qs": "^6.10.0", | |
+ "telejson": "^7.2.0", | |
+ "tiny-invariant": "^1.3.1" | |
+ }, | |
+ "funding": { | |
+ "type": "opencollective", | |
+ "url": "https://opencollective.com/storybook" | |
+ } | |
+ }, | |
+ "node_modules/@storybook/preset-create-react-app/node_modules/@storybook/client-logger": { | |
+ "version": "7.6.14", | |
+ "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.14.tgz", | |
+ "integrity": "sha512-rHa2hLU+80BN5E58Shf1g09YS6QEEOk5hwMuJ4WJfAypMDYPjnIsOYUboHClkCA9TDCH/iVhyRSPy83NWN2MZg==", | |
+ "dev": true, | |
+ "dependencies": { | |
+ "@storybook/global": "^5.0.0" | |
+ }, | |
+ "funding": { | |
+ "type": "opencollective", | |
+ "url": "https://opencollective.com/storybook" | |
+ } | |
+ }, | |
+ "node_modules/@storybook/preset-create-react-app/node_modules/@storybook/core-events": { | |
+ "version": "7.6.14", | |
+ "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.14.tgz", | |
+ "integrity": "sha512-zuSMjOgju7WLFL+okTXVvOKKNzwqVGRVp5UhXeSikT4aXuVdpfepCfikkjntn12G1ybL7mfFCsBU2DV1lwwp6Q==", | |
+ "dev": true, | |
+ "dependencies": { | |
+ "ts-dedent": "^2.0.0" | |
+ }, | |
+ "funding": { | |
+ "type": "opencollective", | |
+ "url": "https://opencollective.com/storybook" | |
+ } | |
+ }, | |
+ "node_modules/@storybook/preset-create-react-app/node_modules/@storybook/types": { | |
+ "version": "7.6.14", | |
+ "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.14.tgz", | |
+ "integrity": "sha512-sJ3qn45M2XLXlOi+wkhXK5xsXbSVzi8YGrusux//DttI3s8wCP3BQSnEgZkBiEktloxPferINHT1er8/9UK7Xw==", | |
+ "dev": true, | |
+ "dependencies": { | |
+ "@storybook/channels": "7.6.14", | |
+ "@types/babel__core": "^7.0.0", | |
+ "@types/express": "^4.7.0", | |
+ "file-system-cache": "2.3.0" | |
+ }, | |
+ "funding": { | |
+ "type": "opencollective", | |
+ "url": "https://opencollective.com/storybook" | |
+ } | |
+ }, | |
"node_modules/@storybook/preset-react-webpack": { | |
"version": "7.6.7", | |
"resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-7.6.7.tgz", | |
@@ -15074,9 +15194,9 @@ | |
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" | |
}, | |
"node_modules/@types/type-is": { | |
- "version": "1.6.3", | |
- "resolved": "https://registry.npmjs.org/@types/type-is/-/type-is-1.6.3.tgz", | |
- "integrity": "sha512-PNs5wHaNcBgCQG5nAeeZ7OvosrEsI9O4W2jAOO9BCCg4ux9ZZvH2+0iSCOIDBiKuQsiNS8CBlmfX9f5YBQ22cA==", | |
+ "version": "1.6.6", | |
+ "resolved": "https://registry.npmjs.org/@types/type-is/-/type-is-1.6.6.tgz", | |
+ "integrity": "sha512-fs1KHv/f9OvmTMsu4sBNaUu32oyda9Y9uK25naJG8gayxNrfqGIjPQsbLIYyfe7xFkppnPlJB+BuTldOaX9bXw==", | |
"dev": true, | |
"dependencies": { | |
"@types/node": "*" | |
@@ -16322,11 +16442,11 @@ | |
} | |
}, | |
"node_modules/axios": { | |
- "version": "1.5.0", | |
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", | |
- "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", | |
+ "version": "1.6.7", | |
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", | |
+ "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", | |
"dependencies": { | |
- "follow-redirects": "^1.15.0", | |
+ "follow-redirects": "^1.15.4", | |
"form-data": "^4.0.0", | |
"proxy-from-env": "^1.1.0" | |
} | |
@@ -18258,9 +18378,9 @@ | |
} | |
}, | |
"node_modules/cross-fetch/node_modules/node-fetch": { | |
- "version": "2.6.12", | |
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", | |
- "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", | |
+ "version": "2.7.0", | |
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", | |
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", | |
"dev": true, | |
"dependencies": { | |
"whatwg-url": "^5.0.0" | |
@@ -19512,6 +19632,11 @@ | |
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", | |
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" | |
}, | |
+ "node_modules/emojilib": { | |
+ "version": "2.4.0", | |
+ "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", | |
+ "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==" | |
+ }, | |
"node_modules/emojis-list": { | |
"version": "3.0.0", | |
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", | |
@@ -21113,9 +21238,9 @@ | |
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" | |
}, | |
"node_modules/fast-redact": { | |
- "version": "3.2.0", | |
- "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.2.0.tgz", | |
- "integrity": "sha512-zaTadChr+NekyzallAMXATXLOR8MNx3zqpZ0MUF2aGf4EathnG0f32VLODNlY8IuGY3HoRO2L6/6fSzNsLaHIw==", | |
+ "version": "3.3.0", | |
+ "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz", | |
+ "integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==", | |
"dev": true, | |
"engines": { | |
"node": ">=6" | |
@@ -21491,9 +21616,9 @@ | |
} | |
}, | |
"node_modules/follow-redirects": { | |
- "version": "1.15.2", | |
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", | |
- "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", | |
+ "version": "1.15.5", | |
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", | |
+ "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", | |
"funding": [ | |
{ | |
"type": "individual", | |
@@ -21791,9 +21916,9 @@ | |
} | |
}, | |
"node_modules/fp-ts": { | |
- "version": "2.16.0", | |
- "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.0.tgz", | |
- "integrity": "sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ==", | |
+ "version": "2.16.2", | |
+ "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.2.tgz", | |
+ "integrity": "sha512-CkqAjnIKFqvo3sCyoBTqgJvF+bHrSik584S9nhTjtBESLx26cbtVMR/T9a6ApChOcSDAaM3JydDmWDUn4EEXng==", | |
"dev": true | |
}, | |
"node_modules/fraction.js": { | |
@@ -23004,9 +23129,9 @@ | |
} | |
}, | |
"node_modules/io-ts": { | |
- "version": "2.2.20", | |
- "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.2.20.tgz", | |
- "integrity": "sha512-Rq2BsYmtwS5vVttie4rqrOCIfHCS9TgpRLFpKQCM1wZBBRY9nWVGmEvm2FnDbSE2un1UE39DvFpTR5UL47YDcA==", | |
+ "version": "2.2.21", | |
+ "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.2.21.tgz", | |
+ "integrity": "sha512-zz2Z69v9ZIC3mMLYWIeoUcwWD6f+O7yP92FMVVaXEOSZH1jnVBmET/urd/uoarD1WGBY4rCj8TAyMPzsGNzMFQ==", | |
"dev": true, | |
"peerDependencies": { | |
"fp-ts": "^2.5.0" | |
@@ -23615,9 +23740,9 @@ | |
} | |
}, | |
"node_modules/isomorphic-fetch/node_modules/node-fetch": { | |
- "version": "2.6.12", | |
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", | |
- "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", | |
+ "version": "2.7.0", | |
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", | |
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", | |
"dev": true, | |
"dependencies": { | |
"whatwg-url": "^5.0.0" | |
@@ -28218,16 +28343,16 @@ | |
} | |
}, | |
"node_modules/json-schema-faker": { | |
- "version": "0.5.0-rcv.40", | |
- "resolved": "https://registry.npmjs.org/json-schema-faker/-/json-schema-faker-0.5.0-rcv.40.tgz", | |
- "integrity": "sha512-BczZvu03jKrGh3ovCWrHusiX6MwiaKK2WZeyomKBNA8Nm/n7aBYz0mub1CnONB6cgxOZTNxx4afNmLblbUmZbA==", | |
+ "version": "0.5.3", | |
+ "resolved": "https://registry.npmjs.org/json-schema-faker/-/json-schema-faker-0.5.3.tgz", | |
+ "integrity": "sha512-BeIrR0+YSrTbAR9dOMnjbFl1MvHyXnq+Wpdw1FpWZDHWKLzK229hZ5huyPcmzFUfVq1ODwf40WdGVoE266UBUg==", | |
"dev": true, | |
"dependencies": { | |
"json-schema-ref-parser": "^6.1.0", | |
- "jsonpath-plus": "^5.1.0" | |
+ "jsonpath-plus": "^7.2.0" | |
}, | |
"bin": { | |
- "jsf": "bin/gen.js" | |
+ "jsf": "bin/gen.cjs" | |
} | |
}, | |
"node_modules/json-schema-ref-parser": { | |
@@ -28281,12 +28406,12 @@ | |
} | |
}, | |
"node_modules/jsonpath-plus": { | |
- "version": "5.1.0", | |
- "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-5.1.0.tgz", | |
- "integrity": "sha512-890w2Pjtj0iswAxalRlt2kHthi6HKrXEfZcn+ZNZptv7F3rUGIeDuZo+C+h4vXBHLEsVjJrHeCm35nYeZLzSBQ==", | |
+ "version": "7.2.0", | |
+ "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", | |
+ "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==", | |
"dev": true, | |
"engines": { | |
- "node": ">=10.0.0" | |
+ "node": ">=12.0.0" | |
} | |
}, | |
"node_modules/jsonpointer": { | |
@@ -28810,12 +28935,6 @@ | |
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", | |
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" | |
}, | |
- "node_modules/lodash.isequalwith": { | |
- "version": "4.4.0", | |
- "resolved": "https://registry.npmjs.org/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz", | |
- "integrity": "sha512-dcZON0IalGBpRmJBmMkaoV7d3I80R2O+FrzsZyHdNSFrANq/cgDqKQNmAHE8UEj4+QYWwwhkQOVdLHiAopzlsQ==", | |
- "dev": true | |
- }, | |
"node_modules/lodash.memoize": { | |
"version": "4.1.2", | |
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", | |
@@ -28831,18 +28950,6 @@ | |
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", | |
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" | |
}, | |
- "node_modules/lodash.pick": { | |
- "version": "4.4.0", | |
- "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", | |
- "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", | |
- "dev": true | |
- }, | |
- "node_modules/lodash.pickby": { | |
- "version": "4.6.0", | |
- "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", | |
- "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", | |
- "dev": true | |
- }, | |
"node_modules/lodash.sortby": { | |
"version": "4.7.0", | |
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", | |
@@ -30642,11 +30749,14 @@ | |
} | |
}, | |
"node_modules/node-emoji": { | |
- "version": "1.11.0", | |
- "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", | |
- "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", | |
+ "version": "2.1.0", | |
+ "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.0.tgz", | |
+ "integrity": "sha512-tcsBm9C6FmPN5Wo7OjFi9lgMyJjvkAeirmjR/ax8Ttfqy4N8PoFic26uqFTIgayHPNI5FH4ltUvfh9kHzwcK9A==", | |
"dependencies": { | |
- "lodash": "^4.17.21" | |
+ "@sindresorhus/is": "^3.1.2", | |
+ "char-regex": "^1.0.2", | |
+ "emojilib": "^2.4.0", | |
+ "skin-tone": "^2.0.0" | |
} | |
}, | |
"node_modules/node-fetch": { | |
@@ -31631,6 +31741,12 @@ | |
"url": "https://github.com/sponsors/sindresorhus" | |
} | |
}, | |
+ "node_modules/parse-multipart-data": { | |
+ "version": "1.5.0", | |
+ "resolved": "https://registry.npmjs.org/parse-multipart-data/-/parse-multipart-data-1.5.0.tgz", | |
+ "integrity": "sha512-ck5zaMF0ydjGfejNMnlo5YU2oJ+pT+80Jb1y4ybanT27j+zbVP/jkYmCrUGsEln0Ox/hZmuvgy8Ra7AxbXP2Mw==", | |
+ "dev": true | |
+ }, | |
"node_modules/parse-prefer-header": { | |
"version": "1.0.0", | |
"resolved": "https://registry.npmjs.org/parse-prefer-header/-/parse-prefer-header-1.0.0.tgz", | |
@@ -33296,9 +33412,9 @@ | |
} | |
}, | |
"node_modules/postman-collection": { | |
- "version": "4.2.1", | |
- "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.2.1.tgz", | |
- "integrity": "sha512-DFLt3/yu8+ldtOTIzmBUctoupKJBOVK4NZO0t68K2lIir9smQg7OdQTBjOXYy+PDh7u0pSDvD66tm93eBHEPHA==", | |
+ "version": "4.3.0", | |
+ "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-4.3.0.tgz", | |
+ "integrity": "sha512-QpmNOw1JhAVQTFWRz443/qpKs4/3T1MFrKqDZ84RS1akxOzhXXr15kD8+/+jeA877qyy9rfMsrFgLe2W7aCPjw==", | |
"dev": true, | |
"dependencies": { | |
"@faker-js/faker": "5.5.3", | |
@@ -33365,7 +33481,6 @@ | |
"version": "3.0.0", | |
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", | |
"integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", | |
- "dev": true, | |
"bin": { | |
"prettier": "bin/prettier.cjs" | |
}, | |
@@ -34197,6 +34312,16 @@ | |
"react": "^16.8.0 || ^17 || ^18" | |
} | |
}, | |
+ "node_modules/react-hook-form-chakra": { | |
+ "version": "1.0.2", | |
+ "resolved": "https://registry.npmjs.org/react-hook-form-chakra/-/react-hook-form-chakra-1.0.2.tgz", | |
+ "integrity": "sha512-QWoR9Sh5TXDPhPSX5mR4XZgdRXMBmH/R7iAqTTRRfOqsN3MM0oBvAHDxgPfhsJf68yrxTG3U5S1T3fsOrXeLdQ==", | |
+ "peerDependencies": { | |
+ "@chakra-ui/react": "^2", | |
+ "react": ">=17", | |
+ "react-hook-form": "^7" | |
+ } | |
+ }, | |
"node_modules/react-icons": { | |
"version": "4.12.0", | |
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", | |
@@ -36910,6 +37035,17 @@ | |
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", | |
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" | |
}, | |
+ "node_modules/skin-tone": { | |
+ "version": "2.0.0", | |
+ "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", | |
+ "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", | |
+ "dependencies": { | |
+ "unicode-emoji-modifier-base": "^1.0.0" | |
+ }, | |
+ "engines": { | |
+ "node": ">=8" | |
+ } | |
+ }, | |
"node_modules/slash": { | |
"version": "3.0.0", | |
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", | |
@@ -38099,24 +38235,24 @@ | |
"integrity": "sha512-rCC0NWGKr/IJhtRuPq/t37qvZHI/mH4I4sxflVM+qgVe5Z2uOCivzWaVbuioJaB61kvm5UvB7b49E+oBY0M8jA==" | |
}, | |
"node_modules/swagger-typescript-api": { | |
- "version": "12.0.4", | |
- "resolved": "https://registry.npmjs.org/swagger-typescript-api/-/swagger-typescript-api-12.0.4.tgz", | |
- "integrity": "sha512-04ZxlJzu3g15TupfPhS0Yk0jzV/MM23WU4uuOl2vSi4yHrxEwnkIsoBkP084ec61q4vr2FHcI3DKxC+Mt1u10Q==", | |
+ "version": "13.0.3", | |
+ "resolved": "https://registry.npmjs.org/swagger-typescript-api/-/swagger-typescript-api-13.0.3.tgz", | |
+ "integrity": "sha512-774ndLpGm2FNpUZpDugfoOO2pIcvSW9nlcqwLVSH9ju4YKCi1Gd83jPly7upcljOvZ8KO/edIUx+9eYViDYglg==", | |
"dependencies": { | |
"@types/swagger-schema-official": "2.0.22", | |
- "cosmiconfig": "7.0.1", | |
+ "cosmiconfig": "8.2.0", | |
"didyoumean": "^1.2.2", | |
- "eta": "^2.0.0", | |
+ "eta": "^2.2.0", | |
"js-yaml": "4.1.0", | |
"lodash": "4.17.21", | |
- "make-dir": "3.1.0", | |
- "nanoid": "3.3.4", | |
- "node-emoji": "1.11.0", | |
- "node-fetch": "^3.2.10", | |
- "prettier": "2.7.1", | |
+ "make-dir": "4.0.0", | |
+ "nanoid": "3.3.6", | |
+ "node-emoji": "2.1.0", | |
+ "node-fetch": "^3.3.1", | |
+ "prettier": "3.0.0", | |
"swagger-schema-official": "2.0.0-bab6bed", | |
"swagger2openapi": "7.0.8", | |
- "typescript": "4.8.4" | |
+ "typescript": "5.1.6" | |
}, | |
"bin": { | |
"sta": "index.js", | |
@@ -38129,18 +38265,20 @@ | |
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" | |
}, | |
"node_modules/swagger-typescript-api/node_modules/cosmiconfig": { | |
- "version": "7.0.1", | |
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", | |
- "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", | |
+ "version": "8.2.0", | |
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", | |
+ "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", | |
"dependencies": { | |
- "@types/parse-json": "^4.0.0", | |
"import-fresh": "^3.2.1", | |
+ "js-yaml": "^4.1.0", | |
"parse-json": "^5.0.0", | |
- "path-type": "^4.0.0", | |
- "yaml": "^1.10.0" | |
+ "path-type": "^4.0.0" | |
}, | |
"engines": { | |
- "node": ">=10" | |
+ "node": ">=14" | |
+ }, | |
+ "funding": { | |
+ "url": "https://github.com/sponsors/d-fischer" | |
} | |
}, | |
"node_modules/swagger-typescript-api/node_modules/js-yaml": { | |
@@ -38154,41 +38292,47 @@ | |
"js-yaml": "bin/js-yaml.js" | |
} | |
}, | |
- "node_modules/swagger-typescript-api/node_modules/nanoid": { | |
- "version": "3.3.4", | |
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", | |
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", | |
- "bin": { | |
- "nanoid": "bin/nanoid.cjs" | |
+ "node_modules/swagger-typescript-api/node_modules/make-dir": { | |
+ "version": "4.0.0", | |
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", | |
+ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", | |
+ "dependencies": { | |
+ "semver": "^7.5.3" | |
}, | |
"engines": { | |
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" | |
+ "node": ">=10" | |
+ }, | |
+ "funding": { | |
+ "url": "https://github.com/sponsors/sindresorhus" | |
} | |
}, | |
- "node_modules/swagger-typescript-api/node_modules/prettier": { | |
- "version": "2.7.1", | |
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", | |
- "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", | |
+ "node_modules/swagger-typescript-api/node_modules/nanoid": { | |
+ "version": "3.3.6", | |
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", | |
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", | |
+ "funding": [ | |
+ { | |
+ "type": "github", | |
+ "url": "https://github.com/sponsors/ai" | |
+ } | |
+ ], | |
"bin": { | |
- "prettier": "bin-prettier.js" | |
+ "nanoid": "bin/nanoid.cjs" | |
}, | |
"engines": { | |
- "node": ">=10.13.0" | |
- }, | |
- "funding": { | |
- "url": "https://github.com/prettier/prettier?sponsor=1" | |
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" | |
} | |
}, | |
"node_modules/swagger-typescript-api/node_modules/typescript": { | |
- "version": "4.8.4", | |
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", | |
- "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", | |
+ "version": "5.1.6", | |
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", | |
+ "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", | |
"bin": { | |
"tsc": "bin/tsc", | |
"tsserver": "bin/tsserver" | |
}, | |
"engines": { | |
- "node": ">=4.2.0" | |
+ "node": ">=14.17" | |
} | |
}, | |
"node_modules/swagger2openapi": { | |
@@ -38863,9 +39007,9 @@ | |
} | |
}, | |
"node_modules/tslib": { | |
- "version": "2.5.3", | |
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | |
- "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" | |
+ "version": "2.6.2", | |
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", | |
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" | |
}, | |
"node_modules/tsutils": { | |
"version": "3.21.0", | |
@@ -39052,6 +39196,14 @@ | |
"node": ">=4" | |
} | |
}, | |
+ "node_modules/unicode-emoji-modifier-base": { | |
+ "version": "1.0.0", | |
+ "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", | |
+ "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", | |
+ "engines": { | |
+ "node": ">=4" | |
+ } | |
+ }, | |
"node_modules/unicode-match-property-ecmascript": { | |
"version": "2.0.0", | |
"resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", | |
@@ -39530,9 +39682,9 @@ | |
"integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" | |
}, | |
"node_modules/utility-types": { | |
- "version": "3.10.0", | |
- "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", | |
- "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", | |
+ "version": "3.11.0", | |
+ "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", | |
+ "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", | |
"dev": true, | |
"engines": { | |
"node": ">= 4" | |
diff --git a/package.json b/package.json | |
index e998f17..2524421 100644 | |
--- a/package.json | |
+++ b/package.json | |
@@ -37,12 +37,13 @@ | |
"react-apexcharts": "^1.4.1", | |
"react-dom": "^18.2.0", | |
"react-hook-form": "^7.47.0", | |
+ "react-hook-form-chakra": "^1.0.2", | |
"react-icons": "^4.12.0", | |
"react-infinite-scroll-component": "^6.1.0", | |
"react-json-view-lite": "^1.2.1", | |
"react-router-dom": "^6.14.0", | |
"react-scripts": "5.0.1", | |
- "swagger-typescript-api": "^12.0.4", | |
+ "swagger-typescript-api": "^13.0.3", | |
"typescript": "^4.9.5", | |
"use-debounce": "^10.0.0", | |
"use-query-params": "^2.2.1", | |
diff --git a/src/App.tsx b/src/App.tsx | |
index 3fea800..a55a763 100644 | |
--- a/src/App.tsx | |
+++ b/src/App.tsx | |
@@ -1,4 +1,5 @@ | |
-import { lazy, Suspense } from "react"; | |
+import { useColorMode } from "@chakra-ui/react"; | |
+import { lazy, Suspense, useEffect } from "react"; | |
import { createBrowserRouter, Outlet, RouterProvider } from "react-router-dom"; | |
import { QueryParamProvider } from "use-query-params"; | |
import { ReactRouter6Adapter } from "use-query-params/adapters/react-router-6"; | |
@@ -395,6 +396,14 @@ const router = createBrowserRouter([ | |
]); | |
export const App = () => { | |
+ const colorMode = useColorMode(); | |
+ useEffect(() => { | |
+ if (colorMode.colorMode === "dark") { | |
+ document.body.classList.add("dark"); | |
+ } else { | |
+ document.body.classList.remove("dark"); | |
+ } | |
+ }, []); | |
return ( | |
<AuthContextProvider> | |
<AuthorizationContextProvider> | |
diff --git a/src/components/Auth/AuthenticationCard/AuthenticationEmailSignInForm.tsx b/src/components/Auth/AuthenticationCard/AuthenticationEmailSignInForm.tsx | |
index 2bead40..183531b 100644 | |
--- a/src/components/Auth/AuthenticationCard/AuthenticationEmailSignInForm.tsx | |
+++ b/src/components/Auth/AuthenticationCard/AuthenticationEmailSignInForm.tsx | |
@@ -40,6 +40,7 @@ export const AuthenticationEmailSignInForm: React.FC< | |
<FormLabel htmlFor="email">Email</FormLabel> | |
<Input | |
id="email" | |
+ name="email" | |
type="email" | |
onChange={(event) => { | |
setEmail(event.target.value); | |
diff --git a/src/components/Auth/AuthorizationFlowForms/SelectEmployeeForm.tsx b/src/components/Auth/AuthorizationFlowForms/SelectEmployeeForm.tsx | |
index 3c4de64..8914e90 100644 | |
--- a/src/components/Auth/AuthorizationFlowForms/SelectEmployeeForm.tsx | |
+++ b/src/components/Auth/AuthorizationFlowForms/SelectEmployeeForm.tsx | |
@@ -1,12 +1,20 @@ | |
import { ArrowBackIcon } from "@chakra-ui/icons"; | |
-import { Button, ButtonGroup, Progress, Text, VStack } from "@chakra-ui/react"; | |
+import { | |
+ Button, | |
+ ButtonGroup, | |
+ Center, | |
+ Spinner, | |
+ Text, | |
+ VStack, | |
+} from "@chakra-ui/react"; | |
+import * as Sentry from "@sentry/react"; | |
import { motion } from "framer-motion"; | |
import { useCallback, useEffect, useState } from "react"; | |
import { BiRefresh } from "react-icons/bi"; | |
import { useLocalStorage } from "usehooks-ts"; | |
import { useEntityApi } from "../../../config/ItemsApi"; | |
import { useAuthorization } from "../../../context/AuthorizationContext/AuthorizationContext"; | |
-import { EntityDTO, UserDTO } from "../../../model/data-contracts"; | |
+import { EntityDTO, Role, UserDTO } from "../../../model/data-contracts"; | |
import { SelectableList } from "../../common/SelectableList.tsx/SelectableList"; | |
import { AvatarWithName } from "../AvatarWithName"; | |
@@ -33,11 +41,14 @@ export const SelectEmployeeForm: React.FC<SelectEmployeeListProps> = ({ | |
if (!entity) return; | |
setIsLoading(true); | |
try { | |
- const response = await entityApi?.getUsersByEntity(entity?.id); | |
+ const response = await entityApi?.getUsersByEntity(entity?.id, { | |
+ roles: [Role.OWNER, Role.ADMIN, Role.CASHIER, Role.MANAGER], | |
+ }); | |
if (response) { | |
return response.data; | |
} | |
} catch (error) { | |
+ Sentry.captureException(error); | |
console.log(error); | |
} finally { | |
setIsLoading(false); | |
@@ -69,7 +80,9 @@ export const SelectEmployeeForm: React.FC<SelectEmployeeListProps> = ({ | |
key={"selectEmployeeForm"} | |
> | |
{isLoading ? ( | |
- <Progress isIndeterminate /> | |
+ <Center> | |
+ <Spinner /> | |
+ </Center> | |
) : ( | |
<SelectableList | |
items={users.filter( | |
diff --git a/src/components/Auth/AuthorizationFlowForms/StoreCard.tsx b/src/components/Auth/AuthorizationFlowForms/StoreCard.tsx | |
index 258cec0..c606f89 100644 | |
--- a/src/components/Auth/AuthorizationFlowForms/StoreCard.tsx | |
+++ b/src/components/Auth/AuthorizationFlowForms/StoreCard.tsx | |
@@ -7,7 +7,11 @@ export type StoreCardProps = { | |
export const StoreCard = ({ entity }: StoreCardProps) => { | |
return ( | |
- <Card backgroundColor={"gray.50"} variant={"outline"}> | |
+ <Card | |
+ data-testid={`store-card-${entity.id}`} | |
+ backgroundColor={"gray.50"} | |
+ variant={"outline"} | |
+ > | |
<CardHeader> | |
<Heading size={"md"}>{entity.name}</Heading> | |
<Text>{entity.address}</Text> | |
diff --git a/src/components/Auth/AvatarWithName.tsx b/src/components/Auth/AvatarWithName.tsx | |
index 2093517..856c616 100644 | |
--- a/src/components/Auth/AvatarWithName.tsx | |
+++ b/src/components/Auth/AvatarWithName.tsx | |
@@ -31,7 +31,7 @@ export const AvatarWithName: React.FC<AvatarWithNameProps> = ({ | |
/> | |
<Divider orientation="vertical" /> | |
<Heading size={size}> | |
- {user.firstName} {user.lastName} | |
+ {user.firstName ? `${user.firstName} ${user.lastName}` : user.email} | |
</Heading> | |
</HStack> | |
); | |
diff --git a/src/components/Auth/OverrideButton.tsx b/src/components/Auth/OverrideButton.tsx | |
new file mode 100644 | |
index 0000000..8c1d999 | |
--- /dev/null | |
+++ b/src/components/Auth/OverrideButton.tsx | |
@@ -0,0 +1,19 @@ | |
+import { Button } from "@chakra-ui/react"; | |
+import { useEmployeeOverride } from "../../context/AuthorizationContext/EmployeeOverrideHandler"; | |
+ | |
+export type OverrideButtonProps = { | |
+ children?: React.ReactNode; | |
+}; | |
+ | |
+export const OverrideButton: React.FC<OverrideButtonProps> = ({ | |
+ children, | |
+ ...props | |
+}) => { | |
+ const { requestOverride } = useEmployeeOverride(); | |
+ | |
+ return ( | |
+ <Button colorScheme="red" onClick={() => requestOverride()} {...props}> | |
+ {children ?? "Override"} | |
+ </Button> | |
+ ); | |
+}; | |
diff --git a/src/components/Auth/PermissionedButton.tsx b/src/components/Auth/PermissionedButton.tsx | |
index a903eb9..18cdfa5 100644 | |
--- a/src/components/Auth/PermissionedButton.tsx | |
+++ b/src/components/Auth/PermissionedButton.tsx | |
@@ -8,7 +8,16 @@ export type PermissionedButtonProps = { | |
} & ButtonProps; | |
export const PermissionedButton = forwardRef( | |
- ({ requires, allowOverride, ...props }: PermissionedButtonProps, ref) => { | |
+ ( | |
+ { | |
+ requires, | |
+ allowOverride, | |
+ isDisabled, | |
+ children, | |
+ ...props | |
+ }: PermissionedButtonProps, | |
+ ref, | |
+ ) => { | |
const { hasPermission } = usePermissions(); | |
const isAllowed = Boolean(!requires || hasPermission(requires)); | |
@@ -20,12 +29,8 @@ export const PermissionedButton = forwardRef( | |
} | |
isDisabled={isAllowed} | |
> | |
- <Button | |
- {...props} | |
- isDisabled={!isAllowed || props.isDisabled} | |
- ref={ref} | |
- > | |
- {props.children} | |
+ <Button {...props} isDisabled={!isAllowed || isDisabled} ref={ref}> | |
+ {children} | |
</Button> | |
</Tooltip> | |
); | |
diff --git a/src/components/Auth/PermissionedIconButton.tsx b/src/components/Auth/PermissionedIconButton.tsx | |
new file mode 100644 | |
index 0000000..6c9e114 | |
--- /dev/null | |
+++ b/src/components/Auth/PermissionedIconButton.tsx | |
@@ -0,0 +1,38 @@ | |
+import { | |
+ forwardRef, | |
+ IconButton, | |
+ IconButtonProps, | |
+ Tooltip, | |
+} from "@chakra-ui/react"; | |
+import { usePermissions } from "../../context/PermissionsContext"; | |
+import { Permission } from "../../utils/Permission"; | |
+ | |
+export type PermissionedIconButtonProps = { | |
+ requires?: Permission | string; | |
+ allowOverride?: boolean; | |
+} & IconButtonProps; | |
+ | |
+export const PermissionedIconButton = forwardRef( | |
+ ({ requires, allowOverride, ...props }: PermissionedIconButtonProps, ref) => { | |
+ const { hasPermission } = usePermissions(); | |
+ | |
+ const isAllowed = Boolean(!requires || hasPermission(requires)); | |
+ | |
+ return ( | |
+ <Tooltip | |
+ label={ | |
+ allowOverride ? "Requires Override" : !isAllowed ? "Not allowed" : "" | |
+ } | |
+ isDisabled={isAllowed} | |
+ > | |
+ <IconButton | |
+ {...props} | |
+ isDisabled={!isAllowed || props.isDisabled} | |
+ ref={ref} | |
+ > | |
+ {props.children} | |
+ </IconButton> | |
+ </Tooltip> | |
+ ); | |
+ }, | |
+); | |
diff --git a/src/components/Auth/UnauthorizedCard.tsx b/src/components/Auth/UnauthorizedCard.tsx | |
index e042287..3402219 100644 | |
--- a/src/components/Auth/UnauthorizedCard.tsx | |
+++ b/src/components/Auth/UnauthorizedCard.tsx | |
@@ -1,6 +1,5 @@ | |
import { LockIcon } from "@chakra-ui/icons"; | |
import { | |
- Button, | |
ButtonGroup, | |
Card, | |
Center, | |
@@ -8,9 +7,9 @@ import { | |
Text, | |
VStack, | |
} from "@chakra-ui/react"; | |
-import { useNavigate } from "react-router-dom"; | |
import { useUserAuth } from "../../context/AuthenticationContext/AuthenticationContext"; | |
-import { useEmployeeOverride } from "../../context/AuthorizationContext/EmployeeOverrideHandler"; | |
+import { BackButton } from "../common/BackButton"; | |
+import { OverrideButton } from "./OverrideButton"; | |
export type UnauthorizedCardProps = { | |
overrideAllowed?: boolean; | |
@@ -25,12 +24,10 @@ export const UnauthorizedCard: React.FC<UnauthorizedCardProps> = ({ | |
message = "Please contact your administrator to request access.", | |
children, | |
}) => { | |
- const navigate = useNavigate(); | |
const { overriddenUser, user, storeUser } = useUserAuth(); | |
overrideAllowed = storeUser ? overrideAllowed : false; | |
- const { requestOverride } = useEmployeeOverride(); | |
return ( | |
<Card p={8}> | |
<Center> | |
@@ -40,16 +37,8 @@ export const UnauthorizedCard: React.FC<UnauthorizedCardProps> = ({ | |
<Text textAlign="center">{message}</Text> | |
{children ?? ( | |
<ButtonGroup w="100%" justifyContent="center"> | |
- {showBackButton && ( | |
- <Button variant="ghost" onClick={() => navigate(-1)}> | |
- Back | |
- </Button> | |
- )} | |
- {user && !overriddenUser && overrideAllowed && ( | |
- <Button colorScheme="red" onClick={() => requestOverride()}> | |
- Override | |
- </Button> | |
- )} | |
+ {showBackButton && <BackButton />} | |
+ {user && !overriddenUser && overrideAllowed && <OverrideButton />} | |
</ButtonGroup> | |
)} | |
</VStack> | |
diff --git a/src/components/CashDrawer.tsx b/src/components/CashDrawer.tsx | |
index 7be9f53..2992d5d 100644 | |
--- a/src/components/CashDrawer.tsx | |
+++ b/src/components/CashDrawer.tsx | |
@@ -1,3 +1,4 @@ | |
+import * as Sentry from "@sentry/react"; | |
import { useCallback, useEffect, useState } from "react"; | |
const BAUD_RATE = 9600; | |
@@ -33,7 +34,9 @@ const useCashDrawer: () => [ | |
); | |
setDevice(deviceT); | |
}) | |
- .catch(() => undefined); | |
+ .catch((err) => { | |
+ Sentry.captureException(err); | |
+ }); | |
} | |
}, []); | |
diff --git a/src/components/DepartmentSummary.tsx b/src/components/DepartmentSummary.tsx | |
index cd65cff..83cf73a 100644 | |
--- a/src/components/DepartmentSummary.tsx | |
+++ b/src/components/DepartmentSummary.tsx | |
@@ -55,7 +55,7 @@ const DepartmentSummary = ({ | |
})} | |
<Tr> | |
<Td>Total</Td> | |
- <Td> | |
+ <Td data-testid="department-summary-total-amount"> | |
{USDollar.format( | |
departmentSummary.reduce( | |
(partialSum, summary) => partialSum + summary.totalPrice, | |
@@ -66,11 +66,15 @@ const DepartmentSummary = ({ | |
</Tr> | |
<Tr> | |
<Td>Total TAXABLE plus NON TAXABLE Sale</Td> | |
- <Td>{USDollar.format(taxNonTax)}</Td> | |
+ <Td data-testid="department-summary-total-taxable-non-taxable-sale"> | |
+ {USDollar.format(taxNonTax)} | |
+ </Td> | |
</Tr> | |
<Tr> | |
<Td>Total Bottle Deposit+Environment fees Sale</Td> | |
- <Td>{USDollar.format(itemFees)}</Td> | |
+ <Td data-testid="department-summary-total-bottle-deposit-environment-fees"> | |
+ {USDollar.format(itemFees)} | |
+ </Td> | |
</Tr> | |
</Tbody> | |
</Table> | |
diff --git a/src/components/Events/EventTypeTag.tsx b/src/components/Events/EventTypeTag.tsx | |
index a49d9b8..6ec1663 100644 | |
--- a/src/components/Events/EventTypeTag.tsx | |
+++ b/src/components/Events/EventTypeTag.tsx | |
@@ -4,7 +4,7 @@ export interface EventTypeTagProps { | |
type: string; | |
} | |
-const EventType = { | |
+export const EventType = { | |
ITEM_UPDATED: { | |
name: "Item Updated", | |
color: "blue", | |
diff --git a/src/components/ItemDetailComponents/EditableItemTable/EditableItemRow1.tsx b/src/components/ItemDetailComponents/EditableItemTable/EditableItemRow1.tsx | |
index 4dbf5b9..d80826f 100644 | |
--- a/src/components/ItemDetailComponents/EditableItemTable/EditableItemRow1.tsx | |
+++ b/src/components/ItemDetailComponents/EditableItemTable/EditableItemRow1.tsx | |
@@ -160,7 +160,6 @@ export const EditableItemRow1: React.FC<EditableItemRow1Props> = ({ | |
</Editable> | |
<ItemSizeUnitSelect | |
minW="70px" | |
- variant="flushed" | |
value={row.original.sizeUnit ?? ItemSizeUnit.ML} | |
onChange={(sizeUnit: ItemSizeUnit) => { | |
onItemDetailsChange({ | |
@@ -201,7 +200,7 @@ export const EditableItemRow1: React.FC<EditableItemRow1Props> = ({ | |
header: "Department", | |
cell: ({ getValue, row }) => ( | |
<Select | |
- variant={"flushed"} | |
+ w="200px" | |
placeholder={getValue().name} | |
onChange={(select) => { | |
onItemDetailsChange({ | |
diff --git a/src/components/ItemDetailComponents/EditableItemTable/EditableItemRow3.tsx b/src/components/ItemDetailComponents/EditableItemTable/EditableItemRow3.tsx | |
index 0656d44..e82066b 100644 | |
--- a/src/components/ItemDetailComponents/EditableItemTable/EditableItemRow3.tsx | |
+++ b/src/components/ItemDetailComponents/EditableItemTable/EditableItemRow3.tsx | |
@@ -88,7 +88,7 @@ export const EditableItemRow3: React.FC<EditableItemRow3Props> = ({ | |
header: "Last Vendor", | |
cell: ({ getValue, row }) => ( | |
<Select | |
- variant={"flushed"} | |
+ w="200px" | |
placeholder={ | |
getValue() | |
? allVendors.find( | |
diff --git a/src/components/ItemSearchComponents/ItemSearchBox.tsx b/src/components/ItemSearchComponents/ItemSearchBox.tsx | |
index 5ca9c3e..931c1df 100644 | |
--- a/src/components/ItemSearchComponents/ItemSearchBox.tsx | |
+++ b/src/components/ItemSearchComponents/ItemSearchBox.tsx | |
@@ -81,10 +81,10 @@ const customComponents: Partial<SelectComponent> = { | |
...props | |
}: MultiValueGenericProps<ItemDetails>) => { | |
return ( | |
- <Tooltip label={itemLabelFormat(props.data)}> | |
+ <Tooltip label={itemLabelFormat(props.data.value)}> | |
<chakra.span display="inline-block" overflow="hidden"> | |
<chakraComponents.MultiValueLabel {...props}> | |
- {itemLabelFormat(props.data)} | |
+ {itemLabelFormat(props.data.value)} | |
</chakraComponents.MultiValueLabel> | |
</chakra.span> | |
</Tooltip> | |
@@ -118,7 +118,7 @@ const customComponents: Partial<SelectComponent> = { | |
const ItemSearchBox: React.FC<ItemSearchBoxProps> = ({ | |
isMulti, | |
placeholder, | |
- onSearchSelected, | |
+ onSearchSelected = () => undefined, | |
value, | |
persistSelection, | |
containerProps, | |
@@ -162,7 +162,7 @@ const ItemSearchBox: React.FC<ItemSearchBoxProps> = ({ | |
); | |
}); | |
return input; | |
- }, 500), | |
+ }, 300), | |
[api, entity?.id, filter], | |
); | |
@@ -175,7 +175,13 @@ const ItemSearchBox: React.FC<ItemSearchBoxProps> = ({ | |
menuPortalTarget={document.body} | |
useBasicStyles | |
autoFocus={autoFocus} | |
- value={value} | |
+ value={ | |
+ Array.isArray(value) | |
+ ? value.map((v) => ({ label: v.name, value: v })) | |
+ : value | |
+ ? { label: value.name, value } | |
+ : undefined | |
+ } | |
styles={{ | |
// container: (base) => ({ | |
// ...base, | |
@@ -200,11 +206,11 @@ const ItemSearchBox: React.FC<ItemSearchBoxProps> = ({ | |
closeMenuOnSelect={closeMenuOnSelect} | |
blurInputOnSelect={closeMenuOnSelect} //https://stackoverflow.com/questions/65036191/react-select-component-closemenuonselect-false-still-closing | |
isDisabled={isDisabled} | |
- inputValue={inputValue} | |
- onInputChange={(input, { action }) => { | |
- if (action !== "set-value") setInputValue(input); | |
- return input; | |
- }} | |
+ // inputValue={inputValue} | |
+ // onInputChange={(input, { action }) => { | |
+ // if (action !== "set-value") setInputValue(input); | |
+ // return input; | |
+ // }} | |
onChange={(val: SingleValue<any> | MultiValue<any>) => { | |
if (isMulti) { | |
if (val.length === 0) { | |
@@ -212,8 +218,8 @@ const ItemSearchBox: React.FC<ItemSearchBoxProps> = ({ | |
setSelected(""); | |
return; | |
} | |
+ // @ts-ignore | |
onSearchSelected?.([ | |
- ...(value ?? []), | |
...val.map((v: { value: ItemDetails }) => v.value), | |
]); | |
return; | |
@@ -222,6 +228,7 @@ const ItemSearchBox: React.FC<ItemSearchBoxProps> = ({ | |
setSelected(""); | |
return; | |
} | |
+ // @ts-ignore | |
onSearchSelected?.(val.value as ItemDetails); | |
setSelected(val.label); | |
}} | |
diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx | |
index 05e3813..423636b 100644 | |
--- a/src/components/Loading.tsx | |
+++ b/src/components/Loading.tsx | |
@@ -1,11 +1,29 @@ | |
-import { Modal, ModalContent, Progress, Stack } from "@chakra-ui/react"; | |
+import { | |
+ Center, | |
+ Modal, | |
+ ModalContent, | |
+ Spinner, | |
+ Stack, | |
+ VStack, | |
+} from "@chakra-ui/react"; | |
+import { Logo } from "../Logo"; | |
const Loading = () => { | |
return ( | |
- <Stack background={"gray.100"} height={"100vh"} width={"100vw"}> | |
+ <Stack | |
+ key="loading" | |
+ background={"gray.100"} | |
+ height={"100vh"} | |
+ width={"100vw"} | |
+ > | |
<Modal size={"xl"} isOpen={true} onClose={() => 0}> | |
- <ModalContent> | |
- <Progress size="lg" isIndeterminate /> | |
+ <ModalContent m={0} bg="transparent" boxShadow="none" h="100%"> | |
+ <Center h="100%"> | |
+ <VStack w="100vw" justifyContent="center"> | |
+ <Logo width="50%" /> | |
+ <Spinner size="xl" /> | |
+ </VStack> | |
+ </Center> | |
</ModalContent> | |
</Modal> | |
</Stack> | |
diff --git a/src/components/MenuLinks.tsx b/src/components/MenuLinks.tsx | |
index 364bd52..1ff36d4 100644 | |
--- a/src/components/MenuLinks.tsx | |
+++ b/src/components/MenuLinks.tsx | |
@@ -116,6 +116,7 @@ const MenuLinks = () => { | |
}} | |
> | |
<MenuButton | |
+ data-testid="menu-button" | |
as={IconButton} | |
aria-label="Options" | |
icon={<HamburgerIcon />} | |
@@ -181,7 +182,7 @@ const MenuLinks = () => { | |
</MenuButton> | |
<MenuList> | |
<MenuGroup title="Transaction Reports"> | |
- <MenuItem as={Link} to="/transaction/by/date"> | |
+ <MenuItem as={Link} to="/transaction/by/date/"> | |
Transaction History | |
</MenuItem> | |
<MenuItem as={Link} to="/transaction/by/closing/"> | |
@@ -190,7 +191,7 @@ const MenuLinks = () => { | |
</MenuGroup> | |
<MenuDivider /> | |
<MenuGroup title="Item Reports"> | |
- <MenuItem as={Link} to="/item/sales/report"> | |
+ <MenuItem as={Link} to="/item/sales/report/"> | |
Item Sales | |
</MenuItem> | |
<MenuItem as={Link} to="/item/dead/stock/"> | |
@@ -217,7 +218,7 @@ const MenuLinks = () => { | |
> | |
Provi | |
</MenuItem> | |
- <MenuItem as={Link} to="/employee/time" icon={<TimeIcon />}> | |
+ <MenuItem as={Link} to="/employee/time/" icon={<TimeIcon />}> | |
Employee Time-clock | |
</MenuItem> | |
<MenuItem | |
diff --git a/src/components/PoleDisplay.tsx b/src/components/PoleDisplay.tsx | |
index 4bb4ae4..049779c 100644 | |
--- a/src/components/PoleDisplay.tsx | |
+++ b/src/components/PoleDisplay.tsx | |
@@ -1,3 +1,4 @@ | |
+import * as Sentry from "@sentry/react"; | |
import { useCallback, useState } from "react"; | |
import { useEffectOnce } from "usehooks-ts"; | |
@@ -31,7 +32,9 @@ const usePoleDisplay = () => { | |
); | |
setDevice(deviceT); | |
}) | |
- .catch(() => undefined); | |
+ .catch((err) => { | |
+ Sentry.captureException(err); | |
+ }); | |
} | |
}); | |
@@ -41,7 +44,8 @@ const usePoleDisplay = () => { | |
try { | |
await device.open({ baudRate: BAUD_RATE }); | |
- } catch { | |
+ } catch (e) { | |
+ Sentry.captureException(e); | |
return; | |
} | |
const writer = device.writable.getWriter(); | |
diff --git a/src/components/QtyHotKeys.tsx b/src/components/QtyHotKeys.tsx | |
index 192a98b..3889306 100644 | |
--- a/src/components/QtyHotKeys.tsx | |
+++ b/src/components/QtyHotKeys.tsx | |
@@ -27,7 +27,7 @@ export const QtyHotKeys = ({ | |
return ( | |
<Flex justifyContent="flex-end"> | |
- <ButtonGroup gap="4"> | |
+ <ButtonGroup data-testid="qty-hot-keys" gap="4"> | |
{[2, 3, 4, 5, 6, 7, 8, 10, 12, 24].map((qty) => ( | |
<Button | |
key={qty} | |
diff --git a/src/components/SalesScreenComponents/BottomLine/BottomLine.tsx b/src/components/SalesScreenComponents/BottomLine/BottomLine.tsx | |
index bb6bf63..2d150fa 100644 | |
--- a/src/components/SalesScreenComponents/BottomLine/BottomLine.tsx | |
+++ b/src/components/SalesScreenComponents/BottomLine/BottomLine.tsx | |
@@ -11,8 +11,11 @@ import { BottomLineButtonGroup } from "./BottomLineButtonGroup"; | |
export interface BottomLineProps {} | |
export const BottomLine: React.FC<BottomLineProps> = ({}) => { | |
- const date = new Date(); | |
- date.setFullYear(date.getFullYear() - 21); // 21 years | |
+ const date = useMemo(() => { | |
+ const d = new Date(); | |
+ d.setFullYear(d.getFullYear() - 21); // 21 years | |
+ return d; | |
+ }, []); | |
const { | |
items: itemDetails, | |
transactionTotalPrice, | |
@@ -34,11 +37,13 @@ export const BottomLine: React.FC<BottomLineProps> = ({}) => { | |
isDisabled: false, | |
isTag: false, | |
onClick: () => driversLicenseScannerDisclosure.onOpen(), | |
+ "data-testid": "id-verification", | |
}, | |
{ | |
label: `Legal Age: ${date.toLocaleDateString()}`, | |
colorScheme: "red", | |
isTag: true, | |
+ "data-testid": "legal-age-tag", | |
}, | |
{ | |
label: `Total Quantity: ${[...itemDetails.values()].reduce( | |
@@ -48,16 +53,19 @@ export const BottomLine: React.FC<BottomLineProps> = ({}) => { | |
0, | |
)}`, | |
isTag: true, | |
+ "data-testid": "total-quantity-tag", | |
}, | |
{ | |
label: `Outstanding: ${USDollar.format( | |
transactionTotalPrice() - amountReceived.amountPaid, | |
)}`, | |
isTag: true, | |
+ "data-testid": "outstanding-tag", | |
}, | |
{ | |
label: `Total: ${USDollar.format(transactionTotalPrice())}`, | |
isTag: true, | |
+ "data-testid": "transaction-total-tag", | |
}, | |
], | |
[ | |
diff --git a/src/components/SalesScreenComponents/Modals/CartHoldsModal.tsx b/src/components/SalesScreenComponents/Modals/CartHoldsModal.tsx | |
index 05921a7..c6eb0bd 100644 | |
--- a/src/components/SalesScreenComponents/Modals/CartHoldsModal.tsx | |
+++ b/src/components/SalesScreenComponents/Modals/CartHoldsModal.tsx | |
@@ -21,7 +21,7 @@ import moment from "moment"; | |
import { useCallback } from "react"; | |
import { useForm } from "react-hook-form"; | |
import { useSalesContext } from "../../../context/Sales/SalesContext"; | |
-import { ItemWithQty } from "../../../pages/SalePage/SalePageUtils"; | |
+import { LineItem } from "../../../pages/SalePage/SalePageUtils"; | |
import { StyledInput } from "../../common/StyledInput"; | |
export type CartHoldsModalProps = { | |
@@ -30,7 +30,7 @@ export type CartHoldsModalProps = { | |
}; | |
export type Cart = { | |
- items: ItemWithQty[]; | |
+ items: LineItem[]; | |
taxExempt: boolean; | |
discount: number; | |
savedAt: Date; | |
@@ -42,11 +42,8 @@ export const CartHoldCard = (props: { | |
deleteCart: () => void; | |
closeModal: () => void; | |
}) => { | |
- const { | |
- setItems: setItemDetails, | |
- setDiscount, | |
- setTaxExempt, | |
- } = useSalesContext(); | |
+ const { calculateCartPrice, setItems, setDiscount, setTaxExempt } = | |
+ useSalesContext(); | |
return ( | |
<Tooltip | |
@@ -89,9 +86,11 @@ export const CartHoldCard = (props: { | |
</HStack> | |
<Button | |
onClick={() => { | |
- setItemDetails( | |
- new Map(props.cart.items.map((item) => [item.item.id, item])), | |
+ const newItemDetails = new Map( | |
+ props.cart.items.map((item) => [item.item.id, item]), | |
); | |
+ setItems(newItemDetails); | |
+ calculateCartPrice(newItemDetails); | |
setTaxExempt(props.cart.taxExempt); | |
setDiscount(props.cart.discount); | |
props.deleteCart(); | |
diff --git a/src/components/SalesScreenComponents/Modals/ChangeDueModal.tsx b/src/components/SalesScreenComponents/Modals/ChangeDueModal.tsx | |
index 9090a16..7794e0d 100644 | |
--- a/src/components/SalesScreenComponents/Modals/ChangeDueModal.tsx | |
+++ b/src/components/SalesScreenComponents/Modals/ChangeDueModal.tsx | |
@@ -52,6 +52,7 @@ export const ChangeDueModal: React.FC<ChangeDueModalProps> = ({ | |
<ModalFooter> | |
<Button | |
colorScheme="blue" | |
+ data-testid="paid" | |
mr={3} | |
isLoading={isPersisting} | |
isDisabled={isPersisting} | |
diff --git a/src/components/SalesScreenComponents/Modals/MultipleChoicesModal.tsx b/src/components/SalesScreenComponents/Modals/MultipleChoicesModal.tsx | |
index d497b8a..b07dd44 100644 | |
--- a/src/components/SalesScreenComponents/Modals/MultipleChoicesModal.tsx | |
+++ b/src/components/SalesScreenComponents/Modals/MultipleChoicesModal.tsx | |
@@ -11,26 +11,19 @@ import { | |
} from "@chakra-ui/react"; | |
import { useSalesContext } from "../../../context/Sales/SalesContext"; | |
import { ItemDetails } from "../../../model/data-contracts"; | |
-import { | |
- addSingleItem, | |
- ItemWithQty, | |
-} from "../../../pages/SalePage/SalePageUtils"; | |
-import usePoleDisplay from "../../PoleDisplay"; | |
export type MultipleChoicesModalProps = { | |
multipleChoicesModal: UseDisclosureReturn; | |
transactionTotalPrice: () => number; | |
- multipleChoices: ItemDetails[]; | |
+ multipleChoices: { items: ItemDetails[]; quantity: number }; | |
}; | |
export const MultipleChoicesModal: React.FC<MultipleChoicesModalProps> = ({ | |
multipleChoicesModal, | |
- transactionTotalPrice, | |
multipleChoices, | |
}) => { | |
const { isOpen, onClose } = multipleChoicesModal; | |
- const poleDisplay = usePoleDisplay(); | |
- const { quantity, taxExempt, setItems: setItemDetails } = useSalesContext(); | |
+ const { addSingleItemToCart } = useSalesContext(); | |
return ( | |
<Modal isOpen={isOpen} onClose={onClose}> | |
<ModalOverlay /> | |
@@ -39,7 +32,7 @@ export const MultipleChoicesModal: React.FC<MultipleChoicesModalProps> = ({ | |
<ModalCloseButton /> | |
<ModalBody> | |
<SimpleGrid columns={2} spacing="20px"> | |
- {multipleChoices?.map((choice) => { | |
+ {multipleChoices.items.map((choice) => { | |
return ( | |
<Button | |
key={choice.id} | |
@@ -47,27 +40,7 @@ export const MultipleChoicesModal: React.FC<MultipleChoicesModalProps> = ({ | |
overflowX={"hidden"} | |
variant="ghost" | |
onClick={() => { | |
- setItemDetails((prev) => { | |
- const m = new Map<number, ItemWithQty>(prev); | |
- const item = addSingleItem( | |
- choice, | |
- m, | |
- quantity, | |
- taxExempt, | |
- ); | |
- poleDisplay( | |
- `${item.quantity} ${item.item.name | |
- .trim() | |
- .substring(0, 9)} $${( | |
- item.item.sellPrice - item.discount | |
- ).toFixed(2)}`, | |
- `Grand Total $${( | |
- transactionTotalPrice() + item.totalPrice | |
- ).toFixed(2)}`, | |
- ); | |
- m.set(choice.id, item); | |
- return m; | |
- }); | |
+ addSingleItemToCart(choice, multipleChoices.quantity); | |
onClose(); | |
}} | |
> | |
diff --git a/src/components/SalesScreenComponents/Modals/PendingTransactionsModal.tsx b/src/components/SalesScreenComponents/Modals/PendingTransactionsModal.tsx | |
index 2be75ae..2dab402 100644 | |
--- a/src/components/SalesScreenComponents/Modals/PendingTransactionsModal.tsx | |
+++ b/src/components/SalesScreenComponents/Modals/PendingTransactionsModal.tsx | |
@@ -6,6 +6,7 @@ import { | |
ModalHeader, | |
ModalOverlay, | |
} from "@chakra-ui/react"; | |
+import * as Sentry from "@sentry/react"; | |
import { createColumnHelper } from "@tanstack/react-table"; | |
import { useEffect, useMemo, useState } from "react"; | |
import { useTransactionsApi } from "../../../config/ItemsApi"; | |
@@ -58,17 +59,20 @@ export const PendingTransactionsModal: React.FC< | |
// effect to load data | |
useEffect(() => { | |
if (!entity || !transactionsApi || !isOpen) return; | |
- transactionsApi | |
- .getTransactions({ | |
- entityId: entity.id, | |
- transactionStatus: TransactionStatus.PENDING, | |
- }) | |
- .then((resp) => { | |
+ | |
+ const getPendingTransactions = async () => { | |
+ try { | |
+ const resp = await transactionsApi.getTransactions({ | |
+ entityId: entity.id, | |
+ transactionStatus: TransactionStatus.PENDING, | |
+ }); | |
setPendingTransactions(resp.data); | |
- }) | |
- .catch((err) => { | |
+ } catch (err) { | |
+ Sentry.captureException(err); | |
console.log(err); | |
- }); | |
+ } | |
+ }; | |
+ getPendingTransactions(); | |
}, [transactionsApi, entity, isOpen]); | |
return ( | |
diff --git a/src/components/SalesScreenComponents/Modals/QuickItemEditModal.tsx b/src/components/SalesScreenComponents/Modals/QuickItemEditModal.tsx | |
index 2fbc793..2fb5168 100644 | |
--- a/src/components/SalesScreenComponents/Modals/QuickItemEditModal.tsx | |
+++ b/src/components/SalesScreenComponents/Modals/QuickItemEditModal.tsx | |
@@ -13,7 +13,7 @@ import { | |
ModalOverlay, | |
useToast, | |
} from "@chakra-ui/react"; | |
-import { useCallback, useRef, useState } from "react"; | |
+import { useCallback, useEffect, useRef, useState } from "react"; | |
import { useItemsApi } from "../../../config/ItemsApi"; | |
import { ItemDetails } from "../../../model/data-contracts"; | |
import { PermissionedButton } from "../../Auth/PermissionedButton"; | |
@@ -34,9 +34,15 @@ export const QuickItemEditModal: React.FC<QuickItemEditModalProps> = ({ | |
}) => { | |
const itemsApi = useItemsApi(); | |
const toast = useToast(); | |
- const [itemDetails, setItemDetails] = useState<ItemDetails>(selectedItem); | |
+ const [itemDetails, setItemDetails] = useState<ItemDetails>(); | |
const saveRef = useRef<HTMLButtonElement>(null); | |
+ useEffect(() => { | |
+ itemsApi?.detailsDetail(selectedItem.id).then((res) => { | |
+ setItemDetails(res.data); | |
+ }); | |
+ }, [selectedItem.id, itemsApi]); | |
+ | |
const saveItem = useCallback( | |
(item: ItemDetails) => { | |
if (!itemsApi) return; | |
@@ -61,6 +67,22 @@ export const QuickItemEditModal: React.FC<QuickItemEditModalProps> = ({ | |
[toast, itemsApi, onUpdateSuccess, setItemDetails], | |
); | |
+ if (!itemDetails) | |
+ return ( | |
+ <Modal | |
+ isOpen={isOpen} | |
+ onClose={onClose} | |
+ size="8xl" | |
+ initialFocusRef={saveRef} | |
+ > | |
+ <ModalOverlay /> | |
+ <ModalContent> | |
+ <ModalHeader>Loading...</ModalHeader> | |
+ <ModalCloseButton /> | |
+ </ModalContent> | |
+ </Modal> | |
+ ); | |
+ | |
return ( | |
<Modal | |
isOpen={isOpen} | |
@@ -73,7 +95,12 @@ export const QuickItemEditModal: React.FC<QuickItemEditModalProps> = ({ | |
<ModalHeader> | |
<Editable | |
value={itemDetails.name} | |
- onChange={(name) => setItemDetails((prev) => ({ ...prev, name }))} | |
+ onChange={(name) => | |
+ setItemDetails((prev) => { | |
+ if (!prev) return prev; | |
+ return { ...prev, name }; | |
+ }) | |
+ } | |
> | |
<EditablePreview /> | |
<Input as={EditableInput} /> | |
diff --git a/src/components/SalesScreenComponents/SalesButtonGroups/CashOptions/CashOptions.tsx b/src/components/SalesScreenComponents/SalesButtonGroups/CashOptions/CashOptions.tsx | |
index 238f9d8..2bdbe0e 100644 | |
--- a/src/components/SalesScreenComponents/SalesButtonGroups/CashOptions/CashOptions.tsx | |
+++ b/src/components/SalesScreenComponents/SalesButtonGroups/CashOptions/CashOptions.tsx | |
@@ -20,9 +20,10 @@ export const CashOptions: React.FC<CashOptionsProps> = ({ | |
"$50", | |
`$${transactionTotalPrice().toFixed(2)}`, | |
`$${Math.ceil(transactionTotalPrice()).toFixed(2)}`, | |
- ].map((money) => { | |
+ ].map((money, i) => { | |
return { | |
isDisabled: !transactionItems.length || transactionTotalPrice() < 0, | |
+ "data-testid": `moneys-${i}`, | |
onClick: () => { | |
setAmountPaid((prev) => { | |
const newAmountPaid = prev.amountPaid + +money.replaceAll("$", ""); | |
diff --git a/src/components/SalesScreenComponents/SalesButtonGroups/Miscellaneous/Miscellaneous.tsx b/src/components/SalesScreenComponents/SalesButtonGroups/Miscellaneous/Miscellaneous.tsx | |
index 92d52ae..2eec148 100644 | |
--- a/src/components/SalesScreenComponents/SalesButtonGroups/Miscellaneous/Miscellaneous.tsx | |
+++ b/src/components/SalesScreenComponents/SalesButtonGroups/Miscellaneous/Miscellaneous.tsx | |
@@ -35,8 +35,10 @@ export const Miscellaneous: React.FC<MiscellaneousProps> = ({ deleteAll }) => { | |
() => [ | |
{ | |
label: "Discount All", | |
+ "data-testid": "discount-all", | |
leftIcon: <MdDiscount />, | |
isDisabled: !itemDetails.size, | |
+ disabled: !itemDetails.size, | |
onClick: () => { | |
onOpen(); | |
}, | |
@@ -45,8 +47,10 @@ export const Miscellaneous: React.FC<MiscellaneousProps> = ({ deleteAll }) => { | |
}, | |
{ | |
label: "Delete All", | |
+ "data-testid": "delete-all", | |
leftIcon: <DeleteIcon />, | |
isDisabled: !itemDetails.size, | |
+ disabled: !itemDetails.size, | |
onClick: () => { | |
deleteAll(); | |
}, | |
@@ -54,6 +58,7 @@ export const Miscellaneous: React.FC<MiscellaneousProps> = ({ deleteAll }) => { | |
}, | |
{ | |
label: "Holds", | |
+ "data-testid": "holds", | |
leftIcon: <FaShoppingCart />, | |
onClick: cartHoldsDisclosure.onOpen, | |
}, | |
@@ -68,6 +73,7 @@ export const Miscellaneous: React.FC<MiscellaneousProps> = ({ deleteAll }) => { | |
e.currentTarget.blur(); | |
setTaxExempt(e.currentTarget.checked); | |
}} | |
+ data-testid="tax-exempt-switch" | |
/> | |
</FormControl>, | |
], | |
diff --git a/src/components/SalesScreenComponents/SalesButtonGroups/QuickPicks/QuickPicks.tsx b/src/components/SalesScreenComponents/SalesButtonGroups/QuickPicks/QuickPicks.tsx | |
index ecb7772..c3ae2bc 100644 | |
--- a/src/components/SalesScreenComponents/SalesButtonGroups/QuickPicks/QuickPicks.tsx | |
+++ b/src/components/SalesScreenComponents/SalesButtonGroups/QuickPicks/QuickPicks.tsx | |
@@ -12,17 +12,16 @@ export interface QuickPicksProps { | |
retrieveItemsLikeUpc: (upc: string) => void; | |
} | |
+export const NEW_ITEM_UPC = "NEW ITEM"; | |
+ | |
export const QuickPicks: React.FC<QuickPicksProps> = ({ | |
retrieveItemsLikeUpc, | |
}) => { | |
- const { | |
- setItems: setItemDetails, | |
- taxExempt, | |
- transactionTotalPrice, | |
- } = useSalesContext(); | |
+ const { calculateCartPrice, setItems, taxExempt, transactionTotalPrice } = | |
+ useSalesContext(); | |
const { isOpen, onOpen, onClose } = useDisclosure(); | |
const poleDisplay = usePoleDisplay(); | |
- const [entity] = useEntitySelected(); | |
+ const [entity, , isEntityLoading] = useEntitySelected(); | |
const QUICK_PICKS = useMemo(() => { | |
if (!entity?.id) return []; | |
@@ -53,12 +52,12 @@ export const QuickPicks: React.FC<QuickPicksProps> = ({ | |
}, | |
<NewItem | |
onSubmit={(price, department, bottleFee, envFee) => | |
- setItemDetails((prev) => { | |
+ setItems((prev) => { | |
const m = new Map(prev); | |
- const item = addSingleItem( | |
+ const lineItem = addSingleItem( | |
{ | |
id: Math.floor(Math.random() * 900000), // any random id is fine as new item don't have ids when saved | |
- upc: "NEW ITEM", | |
+ upc: NEW_ITEM_UPC, | |
onHandQuantity: 1, | |
name: "UNKNOWN ITEM", | |
sellPrice: price, | |
@@ -76,14 +75,15 @@ export const QuickPicks: React.FC<QuickPicksProps> = ({ | |
taxExempt, | |
); | |
poleDisplay( | |
- `${item.quantity} ${item.item.name.trim().substring(0, 9)} $${( | |
- item.item.sellPrice - item.discount | |
- ).toFixed(2)}`, | |
+ `${lineItem.quantity} ${lineItem.item.name | |
+ .trim() | |
+ .substring(0, 9)} $${lineItem.item.sellPrice.toFixed(2)}`, | |
`Grand Total $${( | |
- transactionTotalPrice() + item.totalPrice | |
+ transactionTotalPrice() + lineItem.totalPrice | |
).toFixed(2)}`, | |
); | |
- m.set(item.item.id, item); | |
+ m.set(lineItem.item.id, lineItem); | |
+ calculateCartPrice(m); | |
return m; | |
}) | |
} | |
@@ -96,14 +96,17 @@ export const QuickPicks: React.FC<QuickPicksProps> = ({ | |
// }, | |
]; | |
}, [ | |
+ calculateCartPrice, | |
+ QUICK_PICKS, | |
retrieveItemsLikeUpc, | |
- setItemDetails, | |
+ setItems, | |
taxExempt, | |
transactionTotalPrice, | |
poleDisplay, | |
- onOpen, | |
]); | |
+ if (isEntityLoading) return null; | |
+ | |
return ( | |
<> | |
<SalesButtonGroup colorScheme="yellow" elements={elements} /> | |
diff --git a/src/components/SalesScreenComponents/SalesScreenItemsTable.tsx b/src/components/SalesScreenComponents/SalesScreenItemsTable.tsx | |
index 17fac3b..62efc34 100644 | |
--- a/src/components/SalesScreenComponents/SalesScreenItemsTable.tsx | |
+++ b/src/components/SalesScreenComponents/SalesScreenItemsTable.tsx | |
@@ -1,5 +1,6 @@ | |
import { DeleteIcon } from "@chakra-ui/icons"; | |
-import { HStack, Icon, IconButton, Text } from "@chakra-ui/react"; | |
+import { HStack, Icon, IconButton, Text, Tooltip } from "@chakra-ui/react"; | |
+import * as Sentry from "@sentry/react"; | |
import { createColumnHelper } from "@tanstack/react-table"; | |
import { | |
Dispatch, | |
@@ -14,25 +15,28 @@ import { useItemsApi, useTransactionApi } from "../../config/ItemsApi"; | |
import { useEntitySelected } from "../../context/EntityProvider"; | |
import { usePermissions } from "../../context/PermissionsContext"; | |
import { useRegisterProvider } from "../../context/RegisterProvider"; | |
-import { useSalesContext } from "../../context/Sales/SalesContext"; | |
-import { Item, ItemDetails } from "../../model/data-contracts"; | |
import { | |
- calculateTotalPrice, | |
- ItemWithQty, | |
-} from "../../pages/SalePage/SalePageUtils"; | |
+ SalesContextType, | |
+ useSalesContext, | |
+} from "../../context/Sales/SalesContext"; | |
+import { Item, ItemDetails } from "../../model/data-contracts"; | |
+import { USDollar } from "../../pages/SalePage/SalePage"; | |
+import { LineItem } from "../../pages/SalePage/SalePageUtils"; | |
import { NumberInputWithSideSteppers } from "../common/NumberInputWithSideSteppers"; | |
import usePoleDisplay from "../PoleDisplay"; | |
import { CurrencyRow } from "../Table/CurrencyRow"; | |
import { TransformityTable } from "../Table/TransformityTable"; | |
import { QuickItemEditModal } from "./Modals/QuickItemEditModal"; | |
+import { NEW_ITEM_UPC } from "./SalesButtonGroups/QuickPicks/QuickPicks"; | |
export interface SalesScreenItemsTableProps { | |
transactionTotalPrice: () => number; | |
} | |
-type ItemWithQtyProps = ItemWithQty & { | |
+type ItemWithQtyProps = LineItem & { | |
poleDisplay: ReturnType<typeof usePoleDisplay>; | |
- setItemDetails: Dispatch<SetStateAction<Map<number, ItemWithQty>>>; | |
+ setItemDetails: Dispatch<SetStateAction<Map<number, LineItem>>>; | |
+ calculateCartPrice: SalesContextType["calculateCartPrice"]; | |
transactionTotalPrice: () => number; | |
removeItem: (id: number) => void; | |
taxExempt: boolean; | |
@@ -52,20 +56,23 @@ export const SalesScreenItemsTable: React.FC<SalesScreenItemsTableProps> = ({ | |
items: itemDetails, | |
setItems: setItemDetails, | |
taxExempt, | |
+ calculateCartPrice, | |
} = useSalesContext(); | |
const poleDisplay = usePoleDisplay(); | |
const [entity] = useEntitySelected(); | |
const { register } = useRegisterProvider(); | |
const removeItem = useCallback( | |
- (id: number) => { | |
+ async (id: number) => { | |
if (!entity?.id) return; | |
- transactionApi | |
- ?.emptyCart({ | |
+ try { | |
+ await transactionApi?.emptyCart({ | |
entityId: entity?.id, | |
registerNumber: register.registerNumber, | |
itemId: id, | |
- }) | |
- .catch((err) => console.error(err)); | |
+ }); | |
+ } catch (err) { | |
+ Sentry.captureException(err); | |
+ } | |
setItemDetails((prev) => { | |
const m = new Map(prev); | |
const item = m.get(id); | |
@@ -78,6 +85,7 @@ export const SalesScreenItemsTable: React.FC<SalesScreenItemsTableProps> = ({ | |
)}`, | |
); | |
} | |
+ calculateCartPrice(m); | |
return m; | |
}); | |
}, | |
@@ -88,6 +96,7 @@ export const SalesScreenItemsTable: React.FC<SalesScreenItemsTableProps> = ({ | |
setItemDetails, | |
transactionApi, | |
transactionTotalPrice, | |
+ calculateCartPrice, | |
], | |
); | |
@@ -103,16 +112,20 @@ export const SalesScreenItemsTable: React.FC<SalesScreenItemsTableProps> = ({ | |
const columns: any[] = useMemo( | |
() => [ | |
- columnHelper.accessor((_row: ItemWithQty, i: number) => i + 1, { | |
+ columnHelper.accessor((_row: LineItem, i: number) => i + 1, { | |
header: "#", | |
}), | |
columnHelper.accessor("item.name", { | |
header: "Name", | |
cell: ({ getValue, row }) => ( | |
<HStack | |
- cursor={"pointer"} | |
+ cursor={ | |
+ row.original.item.upc !== NEW_ITEM_UPC ? "pointer" : undefined | |
+ } | |
onClick={() => | |
- hasPermission("item/*:read") && setSelectedItem(row.original.item) | |
+ hasPermission("item/*:read") && | |
+ row.original.item.upc !== NEW_ITEM_UPC && | |
+ setSelectedItem(row.original.item) | |
} | |
> | |
<Text>{getValue()}</Text> | |
@@ -122,99 +135,125 @@ export const SalesScreenItemsTable: React.FC<SalesScreenItemsTableProps> = ({ | |
}), | |
columnHelper.accessor("item.sellPrice", { | |
header: "Price", | |
- cell: ({ getValue }) => ( | |
- <CurrencyRow | |
- value={getValue()} | |
- containerProps={{ textAlign: undefined }} | |
- /> | |
- ), | |
- }), | |
- columnHelper.accessor("discount", { | |
- header: "Discount", | |
- cell: ({ getValue }) => ( | |
+ cell: ({ getValue, row }) => ( | |
<CurrencyRow | |
+ data-testid={`price-${row.index}` | |
+ .replaceAll(" ", "-") | |
+ .toLowerCase()} | |
+ maxW={"fit-content"} | |
value={getValue()} | |
- containerProps={{ textAlign: undefined }} | |
+ textAlign={undefined} | |
/> | |
), | |
}), | |
columnHelper.accessor("quantity", { | |
header: "Quantity", | |
- cell: ({ getValue, row: { original: item } }) => ( | |
+ cell: ({ getValue, row: { original: lineItem } }) => ( | |
<NumberInputWithSideSteppers | |
- key={item.item.id} | |
+ key={lineItem.item.id} | |
maxWidth={"190px"} | |
precision={0} | |
step={1} | |
value={getValue()} | |
onChange={(val) => { | |
- item.quantityAsString = val.toString(); | |
+ lineItem.quantityAsString = val.toString(); | |
try { | |
- item.quantity = val; | |
- item.setItemDetails((prev) => { | |
+ lineItem.quantity = val; | |
+ lineItem.setItemDetails((prev) => { | |
const m = new Map(prev); | |
- item.totalPrice = calculateTotalPrice(item, item.taxExempt); | |
- m.set(item.item.id, item); | |
+ | |
+ m.set(lineItem.item.id, lineItem); | |
+ lineItem.calculateCartPrice(m); | |
return m; | |
}); | |
- item.poleDisplay( | |
- `${item.quantity} ${item.item.name | |
+ lineItem.poleDisplay( | |
+ `${lineItem.quantity} ${lineItem.item.name | |
.trim() | |
- .substring(0, 9)} ${( | |
- item.item.sellPrice - item.discount | |
- ).toFixed(2)}`, | |
- `${item.transactionTotalPrice().toFixed(2)}`, | |
+ .substring(0, 9)} ${lineItem.item.sellPrice.toFixed(2)}`, | |
+ `${lineItem.transactionTotalPrice().toFixed(2)}`, | |
); | |
- } catch { | |
+ } catch (err) { | |
// Do nothing | |
+ Sentry.captureException(err); | |
} | |
}} | |
/> | |
), | |
}), | |
- columnHelper.accessor( | |
- (row: ItemWithQty) => | |
- (row.item.sellPrice - row.discount) * row.quantity, | |
- { | |
- header: "Sub-total", | |
- cell: ({ getValue }) => ( | |
- <CurrencyRow | |
- value={getValue()} | |
- containerProps={{ textAlign: undefined }} | |
- /> | |
- ), | |
- }, | |
- ), | |
- columnHelper.accessor( | |
- (row: ItemWithQty) => row.item.department.environmentFee * row.quantity, | |
- { | |
- header: "Env Fee", | |
- cell: ({ getValue }) => ( | |
- <CurrencyRow | |
- value={getValue()} | |
- containerProps={{ textAlign: undefined }} | |
- /> | |
- ), | |
- }, | |
- ), | |
- columnHelper.accessor( | |
- (row: ItemWithQty) => row.item.department.bottleDeposit * row.quantity, | |
- { | |
- header: "Deposit", | |
- cell: ({ getValue }) => ( | |
- <CurrencyRow | |
- value={getValue()} | |
- containerProps={{ textAlign: undefined }} | |
- /> | |
- ), | |
+ columnHelper.accessor("discount", { | |
+ header: "DISCOUNT", | |
+ cell: ({ getValue, row }) => { | |
+ return ( | |
+ <Tooltip | |
+ label={row.original.promotionAmounts?.map((p) => ( | |
+ <p> | |
+ Promotion: {p.promotionName} - {USDollar.format(p.amount)} | |
+ </p> | |
+ ))} | |
+ isDisabled={row.original.promotionAmounts?.length === 0} | |
+ hasArrow | |
+ > | |
+ <CurrencyRow | |
+ data-testid={`discount-${row.index}` | |
+ .replaceAll(" ", "-") | |
+ .toLowerCase()} | |
+ maxW={"fit-content"} | |
+ value={getValue()} | |
+ textAlign={undefined} | |
+ /> | |
+ </Tooltip> | |
+ ); | |
}, | |
- ), | |
+ }), | |
+ columnHelper.accessor((row: LineItem) => row.subtotal, { | |
+ header: "Sub-total", | |
+ cell: ({ getValue, row }) => ( | |
+ <CurrencyRow | |
+ data-testid={`subtotal-${row.index}` | |
+ .replaceAll(" ", "-") | |
+ .toLowerCase()} | |
+ maxW={"fit-content"} | |
+ value={getValue()} | |
+ textAlign={undefined} | |
+ /> | |
+ ), | |
+ }), | |
+ columnHelper.accessor((row: LineItem) => row.environmentFee, { | |
+ header: "Env Fee", | |
+ cell: ({ getValue, row }) => ( | |
+ <CurrencyRow | |
+ data-testid={`env-fee-${row.index}` | |
+ .replaceAll(" ", "-") | |
+ .toLowerCase()} | |
+ maxW={"fit-content"} | |
+ value={getValue()} | |
+ textAlign={undefined} | |
+ /> | |
+ ), | |
+ }), | |
+ columnHelper.accessor((row: LineItem) => row.bottleDeposit, { | |
+ header: "Deposit", | |
+ cell: ({ getValue, row }) => ( | |
+ <CurrencyRow | |
+ data-testid={`deposit-${row.index}` | |
+ .replaceAll(" ", "-") | |
+ .toLowerCase()} | |
+ maxW={"fit-content"} | |
+ value={getValue()} | |
+ textAlign={undefined} | |
+ /> | |
+ ), | |
+ }), | |
columnHelper.accessor("totalPrice", { | |
header: "Item Total", | |
- cell: ({ getValue }) => ( | |
+ cell: ({ getValue, row }) => ( | |
<CurrencyRow | |
+ data-testid={`total-price-${row.index}` | |
+ .replaceAll(" ", "-") | |
+ .toLowerCase()} | |
+ maxW={"fit-content"} | |
value={getValue()} | |
- containerProps={{ textAlign: undefined }} | |
+ textAlign={undefined} | |
/> | |
), | |
}), | |
@@ -227,7 +266,7 @@ export const SalesScreenItemsTable: React.FC<SalesScreenItemsTableProps> = ({ | |
size="sm" | |
colorScheme="red" | |
onFocus={(e) => e.currentTarget.blur()} | |
- onClick={(e) => { | |
+ onClick={() => { | |
item.removeItem(item.item.id); | |
}} | |
aria-label={""} | |
@@ -251,6 +290,7 @@ export const SalesScreenItemsTable: React.FC<SalesScreenItemsTableProps> = ({ | |
setItemDetails, | |
removeItem, | |
taxExempt, | |
+ calculateCartPrice, | |
}))} | |
/> | |
{selectedItemDetails && ( | |
@@ -264,12 +304,9 @@ export const SalesScreenItemsTable: React.FC<SalesScreenItemsTableProps> = ({ | |
const itemDetails = m.get(item.id); | |
if (itemDetails) { | |
itemDetails.item = item; | |
- itemDetails.totalPrice = calculateTotalPrice( | |
- itemDetails, | |
- taxExempt, | |
- ); | |
m.set(item.id, itemDetails); | |
} | |
+ calculateCartPrice(m); | |
return m; | |
}); | |
}} | |
diff --git a/src/components/Table/CurrencyRow.tsx b/src/components/Table/CurrencyRow.tsx | |
index fb5dbd5..a361de2 100644 | |
--- a/src/components/Table/CurrencyRow.tsx | |
+++ b/src/components/Table/CurrencyRow.tsx | |
@@ -1,20 +1,18 @@ | |
-import { Box, BoxProps } from "@chakra-ui/react"; | |
+import { Box, BoxProps, forwardRef } from "@chakra-ui/react"; | |
import { Currency, currencyFormat } from "../../utils/currencyUtils"; | |
-export interface CurrencyRowProps { | |
+export interface CurrencyRowProps extends BoxProps { | |
currency?: Currency; | |
value?: number; | |
- containerProps?: BoxProps; | |
} | |
-export const CurrencyRow: React.FC<CurrencyRowProps> = ({ | |
- currency = "USD", | |
- value, | |
- containerProps, | |
-}) => { | |
+export const CurrencyRow: React.FC<CurrencyRowProps> = forwardRef< | |
+ CurrencyRowProps, | |
+ "div" | |
+>(({ currency = "USD", value, ...containerProps }, ref) => { | |
return ( | |
- <Box w="100%" textAlign="right" {...containerProps}> | |
+ <Box w="100%" textAlign="right" ref={ref} {...containerProps}> | |
{currencyFormat(currency, value)} | |
</Box> | |
); | |
-}; | |
+}); | |
diff --git a/src/components/Table/TransformityTable.tsx b/src/components/Table/TransformityTable.tsx | |
index 6aab01f..b9e13f4 100644 | |
--- a/src/components/Table/TransformityTable.tsx | |
+++ b/src/components/Table/TransformityTable.tsx | |
@@ -103,6 +103,10 @@ export function TransformityTable<T>({ | |
getSortedRowModel: getSortedRowModel(), | |
getExpandedRowModel: getExpandedRowModel(), | |
getSubRows, | |
+ state: { | |
+ sorting: sortingState, | |
+ expanded: expanded, | |
+ }, | |
onExpandedChange: (updaterOrValue) => { | |
if (typeof updaterOrValue === "function") { | |
expanded && setExpanded && setExpanded(updaterOrValue(expanded)); | |
diff --git a/src/components/TransactionByDate.tsx b/src/components/TransactionByDate.tsx | |
index c6bbe62..8c09c0d 100644 | |
--- a/src/components/TransactionByDate.tsx | |
+++ b/src/components/TransactionByDate.tsx | |
@@ -17,6 +17,7 @@ import { FaPrint } from "react-icons/fa"; | |
import { Link } from "react-router-dom"; | |
import { TransactionReportByDateOutput } from "../model/data-contracts"; | |
import { USDollar } from "../pages/SalePage/SalePage"; | |
+import { calculateSubtotal } from "../utils/numberUtils"; | |
export type TransactionByDateProps = { | |
data: TransactionReportByDateOutput; | |
@@ -33,12 +34,12 @@ function itemComp(transactions: TransactionReportByDateOutput["transactions"]) { | |
let environmentFee = 0; | |
let discount = 0; | |
- for (const item of transaction.transactionItems) { | |
- tax += item.tax; | |
- subTotal += item.price * item.quantity; | |
- bottleFee += item.bottleFee; | |
- environmentFee += item.environmentFee; | |
- discount += item.discount; | |
+ for (const transactionItem of transaction.transactionItems) { | |
+ tax += transactionItem.tax; | |
+ subTotal += calculateSubtotal(transactionItem); | |
+ bottleFee += transactionItem.bottleFee; | |
+ environmentFee += transactionItem.environmentFee; | |
+ discount += transactionItem.discount; | |
} | |
items.push( | |
@@ -94,7 +95,7 @@ const TransactionByDate = ({ data }: TransactionByDateProps) => { | |
0, | |
); | |
totalSubtotal += transactionItems.reduce( | |
- (partialSum, a) => partialSum + a.price * a.quantity, | |
+ (partialSum, a) => partialSum + calculateSubtotal(a), | |
0, | |
); | |
totalBottleFee += transactionItems.reduce( | |
diff --git a/src/components/TransactionsComponents/SummaryTable.tsx b/src/components/TransactionsComponents/SummaryTable.tsx | |
index 9afed95..7fb6f48 100644 | |
--- a/src/components/TransactionsComponents/SummaryTable.tsx | |
+++ b/src/components/TransactionsComponents/SummaryTable.tsx | |
@@ -87,7 +87,14 @@ export const SummaryTable = ({ | |
{body.map((b, i) => ( | |
<Tr key={i}> | |
{b.map((j, k) => ( | |
- <Td key={k}>{j}</Td> | |
+ <Td | |
+ data-testid={`${header.at(k)}-${i}-${k}` | |
+ .replaceAll(" ", "-") | |
+ .toLowerCase()} | |
+ key={k} | |
+ > | |
+ {j} | |
+ </Td> | |
))} | |
</Tr> | |
))} | |
diff --git a/src/components/TransactionsComponents/TransactionsReportTable.tsx b/src/components/TransactionsComponents/TransactionsReportTable.tsx | |
index 764b0ff..21001a6 100644 | |
--- a/src/components/TransactionsComponents/TransactionsReportTable.tsx | |
+++ b/src/components/TransactionsComponents/TransactionsReportTable.tsx | |
@@ -45,6 +45,7 @@ export const TransactionsReportTable: React.FC< | |
cell: ({ getValue }) => <CurrencyRow value={getValue()} />, | |
footer: ({ table }) => ( | |
<CurrencyRow | |
+ data-testid="tax-footer" | |
value={table | |
.getRowModel() | |
.rows.reduce((acc, row) => acc + row.original.tax, 0)} | |
@@ -57,6 +58,7 @@ export const TransactionsReportTable: React.FC< | |
cell: ({ getValue }) => <CurrencyRow value={getValue()} />, | |
footer: ({ table }) => ( | |
<CurrencyRow | |
+ data-testid="subtotal-footer" | |
value={table | |
.getRowModel() | |
.rows.reduce((acc, row) => acc + row.original.subtotal, 0)} | |
@@ -69,6 +71,7 @@ export const TransactionsReportTable: React.FC< | |
cell: ({ getValue }) => <CurrencyRow value={getValue()} />, | |
footer: ({ table }) => ( | |
<CurrencyRow | |
+ data-testid="bottle-deposit-footer" | |
value={table | |
.getRowModel() | |
.rows.reduce((acc, row) => acc + row.original.bottleDeposit, 0)} | |
@@ -81,6 +84,7 @@ export const TransactionsReportTable: React.FC< | |
cell: ({ getValue }) => <CurrencyRow value={getValue()} />, | |
footer: ({ table }) => ( | |
<CurrencyRow | |
+ data-testid="environment-fee-footer" | |
value={table | |
.getRowModel() | |
.rows.reduce((acc, row) => acc + row.original.environmentFee, 0)} | |
@@ -93,6 +97,7 @@ export const TransactionsReportTable: React.FC< | |
cell: ({ getValue }) => <CurrencyRow value={getValue()} />, | |
footer: ({ table }) => ( | |
<CurrencyRow | |
+ data-testid="discount-footer" | |
value={table | |
.getRowModel() | |
.rows.reduce((acc, row) => acc + row.original.discount, 0)} | |
@@ -111,6 +116,7 @@ export const TransactionsReportTable: React.FC< | |
(acc, row) => acc + row.original.transactionTotal, | |
0, | |
)} | |
+ data-testid="transaction-total-footer" | |
/> | |
), | |
}), | |
@@ -142,7 +148,7 @@ export const TransactionsReportTable: React.FC< | |
}, | |
}), | |
columnHelper.accessor((tx) => tx.allNames.substring(0, 200), { | |
- header: "Items(s) Sold", | |
+ header: "Item(s) Sold", | |
}), | |
columnHelper.display({ | |
id: "actions", | |
diff --git a/src/components/UnauthenticatedUserOnlyRoute.tsx b/src/components/UnauthenticatedUserOnlyRoute.tsx | |
index 0bc6a75..e9650b6 100644 | |
--- a/src/components/UnauthenticatedUserOnlyRoute.tsx | |
+++ b/src/components/UnauthenticatedUserOnlyRoute.tsx | |
@@ -11,7 +11,7 @@ const UnauthenticatedUserOnlyRoute: React.FC<{}> = () => { | |
} else if (user === null) { | |
return <Outlet />; | |
} else { | |
- return <Navigate to="/sale" />; | |
+ return <Navigate to="/sale/" />; | |
} | |
}; | |
diff --git a/src/components/VoidSale.tsx b/src/components/VoidSale.tsx | |
index 9f0cfdc..1e5e95a 100644 | |
--- a/src/components/VoidSale.tsx | |
+++ b/src/components/VoidSale.tsx | |
@@ -6,12 +6,22 @@ import { | |
AlertDialogHeader, | |
AlertDialogOverlay, | |
Button, | |
+ HStack, | |
Input, | |
+ Modal, | |
+ ModalBody, | |
+ ModalCloseButton, | |
+ ModalContent, | |
+ ModalHeader, | |
+ ModalOverlay, | |
+ PinInput, | |
+ PinInputField, | |
Spacer, | |
Text, | |
useDisclosure, | |
useToast, | |
} from "@chakra-ui/react"; | |
+import * as Sentry from "@sentry/react"; | |
import { useEffect, useRef, useState } from "react"; | |
import { useNavigate } from "react-router-dom"; | |
import { useTransactionApi } from "../config/ItemsApi"; | |
@@ -51,10 +61,13 @@ const VoidSale = ({ txId, isVoided, inputtedItemDetails }: VoidSaleType) => { | |
setTransactionOutput(inputtedItemDetails); | |
}, [inputtedItemDetails]); | |
+ // TEMPORARY FOR BROWNSTONE | |
+ const approvalModal = useDisclosure(); | |
+ | |
useEffect(() => { | |
if (isVoided === true) return; | |
if (inputtedItemDetails) return; | |
- if (!blockingModal.isOpen) return; | |
+ if (!(blockingModal.isOpen || approvalModal.isOpen)) return; | |
transactionApi?.getTransactionById(txId).then((res) => { | |
setTransactionOutput(res.data); | |
@@ -65,6 +78,7 @@ const VoidSale = ({ txId, isVoided, inputtedItemDetails }: VoidSaleType) => { | |
transactionApi, | |
txId, | |
inputtedItemDetails, | |
+ approvalModal.isOpen, | |
]); | |
const VOID_SALE = ["Void"].map((value) => { | |
@@ -74,9 +88,18 @@ const VoidSale = ({ txId, isVoided, inputtedItemDetails }: VoidSaleType) => { | |
allowOverride | |
key={value} | |
colorScheme="red" | |
- isDisabled={transactionOutput?.isVoided || isVoided} | |
+ isDisabled={ | |
+ transactionOutput?.isVoided || | |
+ isVoided || | |
+ !(transactionOutput?.isTransactionOpen ?? true) | |
+ } | |
onClick={() => { | |
if (!txId) return; | |
+ // TEMPORARY FOR BROWNSTONE | |
+ if (entity?.id === 6) { | |
+ approvalModal.onOpen(); | |
+ return; | |
+ } | |
blockingModal.onOpen(); | |
}} | |
> | |
@@ -136,7 +159,9 @@ const VoidSale = ({ txId, isVoided, inputtedItemDetails }: VoidSaleType) => { | |
</Button> | |
<Spacer /> | |
<Button | |
- isDisabled={disableButton} | |
+ isDisabled={ | |
+ disableButton || !transactionOutput.isTransactionOpen | |
+ } | |
isLoading={isVoiding} | |
onClick={async () => { | |
if (!transactionApi) return; | |
@@ -158,6 +183,7 @@ const VoidSale = ({ txId, isVoided, inputtedItemDetails }: VoidSaleType) => { | |
duration: 5000, | |
isClosable: true, | |
}); | |
+ Sentry.captureException(err); | |
} finally { | |
blockingModal.onClose(); | |
} | |
@@ -169,8 +195,64 @@ const VoidSale = ({ txId, isVoided, inputtedItemDetails }: VoidSaleType) => { | |
</AlertDialogContent> | |
</AlertDialogOverlay> | |
</AlertDialog> | |
+ <ManagerPinInputModal | |
+ {...approvalModal} | |
+ onSuccess={() => { | |
+ blockingModal.onOpen(); | |
+ }} | |
+ /> | |
</> | |
); | |
}; | |
+// TEMPORARY FOR BROWNSTONE | |
+type ManagerPinInputModalProps = { | |
+ isOpen: boolean; | |
+ onClose: () => void; | |
+ onSuccess: () => void; | |
+}; | |
+ | |
+const ManagerPinInputModal: React.FC<ManagerPinInputModalProps> = ({ | |
+ isOpen, | |
+ onClose, | |
+ onSuccess, | |
+}) => { | |
+ const [approvalCode, setApprovalCode] = useState<string>(""); | |
+ | |
+ useEffect(() => { | |
+ if (approvalCode === "2285") { | |
+ setApprovalCode(""); | |
+ onSuccess(); | |
+ onClose(); | |
+ } | |
+ }, [approvalCode, onSuccess, onClose]); | |
+ | |
+ return ( | |
+ <Modal isOpen={isOpen} onClose={onClose}> | |
+ <ModalOverlay /> | |
+ <ModalContent> | |
+ <ModalHeader>Requires Manager Approval</ModalHeader> | |
+ <ModalCloseButton /> | |
+ <ModalBody> | |
+ <HStack w="100%" justifyContent="center"> | |
+ <PinInput | |
+ otp | |
+ mask | |
+ size="lg" | |
+ onChange={setApprovalCode} | |
+ value={approvalCode} | |
+ autoFocus={true} | |
+ > | |
+ <PinInputField autoComplete="off" /> | |
+ <PinInputField autoComplete="off" /> | |
+ <PinInputField autoComplete="off" /> | |
+ <PinInputField autoComplete="off" /> | |
+ </PinInput> | |
+ </HStack> | |
+ </ModalBody> | |
+ </ModalContent> | |
+ </Modal> | |
+ ); | |
+}; | |
+ | |
export default VoidSale; | |
diff --git a/src/components/common/BackButton.tsx b/src/components/common/BackButton.tsx | |
new file mode 100644 | |
index 0000000..de0ad1d | |
--- /dev/null | |
+++ b/src/components/common/BackButton.tsx | |
@@ -0,0 +1,18 @@ | |
+import { Button, ButtonProps } from "@chakra-ui/react"; | |
+import { useNavigate } from "react-router-dom"; | |
+ | |
+export type BackButtonProps = ButtonProps & { | |
+ children?: React.ReactNode; | |
+}; | |
+ | |
+export const BackButton: React.FC<BackButtonProps> = ({ | |
+ children, | |
+ ...props | |
+}) => { | |
+ const navigate = useNavigate(); | |
+ return ( | |
+ <Button variant="ghost" onClick={() => navigate(-1)} {...props}> | |
+ {children ?? "Back"} | |
+ </Button> | |
+ ); | |
+}; | |
diff --git a/src/components/common/CardList/CardList.tsx b/src/components/common/CardList/CardList.tsx | |
index 97f1707..fdd190d 100644 | |
--- a/src/components/common/CardList/CardList.tsx | |
+++ b/src/components/common/CardList/CardList.tsx | |
@@ -1,4 +1,4 @@ | |
-import { Center, Skeleton, StackProps, VStack } from "@chakra-ui/react"; | |
+import { Box, Center, Skeleton, StackProps, VStack } from "@chakra-ui/react"; | |
export type CardListProps<T> = { | |
items: T[]; | |
@@ -30,7 +30,7 @@ export function CardList<T>({ | |
justifyContent="space-between" | |
{...containerProps} | |
> | |
- <VStack w="100%" h="100%"> | |
+ <VStack data-testid="card-list" w="100%" h="100%" flexGrow={2}> | |
{isLoading | |
? Array.from({ length: 4 }).map((_, i) => ( | |
<Skeleton key={i} w="100%" h={`${estimatedItemHeight ?? 50}px`} /> | |
@@ -42,7 +42,7 @@ export function CardList<T>({ | |
</Center> | |
)} | |
</VStack> | |
- {children} | |
+ <Box w="100%">{children}</Box> | |
</VStack> | |
); | |
} | |
diff --git a/src/components/common/EditableInput/EditableInput2.tsx b/src/components/common/EditableInput/EditableInput2.tsx | |
index b00a9b7..cf82825 100644 | |
--- a/src/components/common/EditableInput/EditableInput2.tsx | |
+++ b/src/components/common/EditableInput/EditableInput2.tsx | |
@@ -1,4 +1,5 @@ | |
import { Input, InputProps, Text, TextProps } from "@chakra-ui/react"; | |
+import * as Sentry from "@sentry/react"; | |
import { useEffect, useRef, useState } from "react"; | |
import { | |
InputConverter, | |
@@ -127,7 +128,9 @@ export function EditableInput<T extends string | number>({ | |
const val = converter.parse(e.target.value); | |
setDisplayValue(converter.format(val)); | |
onChange?.(e.target.value, isMoney || isNumber ? val : undefined); | |
- } catch (e) {} | |
+ } catch (e) { | |
+ Sentry.captureException(e); | |
+ } | |
}} | |
{...inputProps} | |
/> | |
diff --git a/src/components/common/NumberInputWithSideSteppers.tsx b/src/components/common/NumberInputWithSideSteppers.tsx | |
index 3505f63..55ae98a 100644 | |
--- a/src/components/common/NumberInputWithSideSteppers.tsx | |
+++ b/src/components/common/NumberInputWithSideSteppers.tsx | |
@@ -33,23 +33,26 @@ export const NumberInputWithSideSteppers = ({ | |
icon={<MinusIcon />} | |
variant={"ghost"} | |
onFocus={(e) => e.currentTarget.blur()} | |
- isDisabled={!isEditing || (min !== undefined && props.value <= min)} | |
onClick={() => { | |
- const newValue = props.value - step; | |
- if (min === undefined || newValue >= min) { | |
- props.onChange(newValue); | |
- } | |
+ let value = props.value - (step ?? 1); | |
+ if (value < -1000) value = -1000; | |
+ if (value > 1000) value = 1000; | |
+ return props.onChange(value); | |
}} | |
/> | |
<NumberInput | |
- isDisabled={!isEditing} | |
+ name="quantity" | |
value={props.value} | |
defaultValue={props.value} | |
precision={props.precision} | |
- min={min} | |
+ max={1000} | |
+ min={-1000} | |
width={"auto"} | |
onChange={(valueString) => { | |
- props.onChange(parseInt(valueString)); | |
+ let value = parseInt(valueString); | |
+ if (value > 1000) value = 1000; | |
+ if (value < -1000) value = -1000; | |
+ props.onChange(value); | |
}} | |
> | |
<NumberInputField onFocus={(item) => item.currentTarget.select()} /> | |
@@ -63,10 +66,11 @@ export const NumberInputWithSideSteppers = ({ | |
onFocus={(e) => e.currentTarget.blur()} | |
isDisabled={!isEditing || (max !== undefined && props.value >= max)} | |
onClick={() => { | |
- const newValue = props.value + step; | |
- if (max === undefined || newValue <= max) { | |
- props.onChange(newValue); | |
- } | |
+ let value = props.value + (step ?? 1); | |
+ | |
+ if (value < -1000) value = -1000; | |
+ if (value > 1000) value = 1000; | |
+ return props.onChange(value); | |
}} | |
/> | |
</HStack> | |
diff --git a/src/components/common/RadioCard/RadioCardV1.tsx b/src/components/common/RadioCard/RadioCardV1.tsx | |
new file mode 100644 | |
index 0000000..7f7b38e | |
--- /dev/null | |
+++ b/src/components/common/RadioCard/RadioCardV1.tsx | |
@@ -0,0 +1,40 @@ | |
+import { Box, useRadio, UseRadioProps } from "@chakra-ui/react"; | |
+ | |
+export interface RadioCardV1Props extends UseRadioProps { | |
+ children: React.ReactNode; | |
+} | |
+ | |
+export const RadioCardV1: React.FC<RadioCardV1Props> = ({ | |
+ children, | |
+ ...props | |
+}) => { | |
+ const { getInputProps, getRadioProps } = useRadio(props); | |
+ | |
+ const input = getInputProps(); | |
+ const checkbox = getRadioProps(); | |
+ | |
+ return ( | |
+ <Box as="label"> | |
+ <input {...input} /> | |
+ <Box | |
+ {...checkbox} | |
+ cursor="pointer" | |
+ borderWidth="1px" | |
+ borderRadius="md" | |
+ boxShadow="md" | |
+ _checked={{ | |
+ bg: "blue.600", | |
+ color: "white", | |
+ borderColor: "blue.600", | |
+ }} | |
+ _focus={{ | |
+ boxShadow: "outline", | |
+ }} | |
+ px={5} | |
+ py={1} | |
+ > | |
+ {children} | |
+ </Box> | |
+ </Box> | |
+ ); | |
+}; | |
diff --git a/src/components/common/StyledPaginationControls/PaginationComponents.tsx b/src/components/common/StyledPaginationControls/PaginationComponents.tsx | |
index bc8a6f0..f5ed582 100644 | |
--- a/src/components/common/StyledPaginationControls/PaginationComponents.tsx | |
+++ b/src/components/common/StyledPaginationControls/PaginationComponents.tsx | |
@@ -57,6 +57,7 @@ const PaginationPrevious: React.FC<LinkProps & { isDisabled?: boolean }> = ({ | |
> | |
<IconButton | |
variant={"outline"} | |
+ data-testid="pagination-previous" | |
aria-label="Go to previous page" | |
icon={<ChevronLeftIcon />} | |
isDisabled={isDisabled} | |
@@ -73,6 +74,7 @@ const PaginationNext: React.FC<LinkProps & { isDisabled?: boolean }> = ({ | |
<Link {...props}> | |
<IconButton | |
variant={"outline"} | |
+ data-testid="pagination-next" | |
aria-label="Go to next page" | |
icon={<ChevronRightIcon />} | |
isDisabled={isDisabled} | |
diff --git a/src/components/navbar/PopRegisterButton.tsx b/src/components/navbar/PopRegisterButton.tsx | |
index e1182da..736fafd 100644 | |
--- a/src/components/navbar/PopRegisterButton.tsx | |
+++ b/src/components/navbar/PopRegisterButton.tsx | |
@@ -8,8 +8,9 @@ export const PopRegisterButton = () => { | |
const [device, cashDrawerOnOpen] = useCashDrawer(); | |
const [entity] = useEntitySelected(); | |
+ // TODO: remove Knotty Pine check after RBAC setup | |
// If there isn't a device, return null | |
- if (!device) return null; | |
+ if (!device || entity.id === 5) return null; | |
return ( | |
<Tooltip hasArrow label={"Pop cash drawer"} placement={"bottom"}> | |
diff --git a/src/components/navbar/StoreInfo.tsx b/src/components/navbar/StoreInfo.tsx | |
index 7620370..e165dd4 100644 | |
--- a/src/components/navbar/StoreInfo.tsx | |
+++ b/src/components/navbar/StoreInfo.tsx | |
@@ -9,7 +9,7 @@ export const StoreInfo: React.FC<StoreInfoProps> = ({}) => { | |
const [entity] = useEntitySelected(); | |
return ( | |
- <VStack spacing={0} alignItems="end"> | |
+ <VStack data-testid="store-info" spacing={0} alignItems="end"> | |
<Text> | |
<strong>{entity?.name}</strong> | |
</Text> | |
diff --git a/src/components/navbar/StoreInfoWithSelect.tsx b/src/components/navbar/StoreInfoWithSelect.tsx | |
index 99193bc..d54d7e9 100644 | |
--- a/src/components/navbar/StoreInfoWithSelect.tsx | |
+++ b/src/components/navbar/StoreInfoWithSelect.tsx | |
@@ -1,5 +1,12 @@ | |
import { ChevronDownIcon } from "@chakra-ui/icons"; | |
-import { Button, Menu, MenuButton, MenuItem, MenuList } from "@chakra-ui/react"; | |
+import { | |
+ Button, | |
+ Menu, | |
+ MenuButton, | |
+ MenuItem, | |
+ MenuList, | |
+ Portal, | |
+} from "@chakra-ui/react"; | |
import { useAuthorization } from "../../context/AuthorizationContext/AuthorizationContext"; | |
import { useEntitySelected } from "../../context/EntityProvider"; | |
import { useRegisterProvider } from "../../context/RegisterProvider"; | |
@@ -18,27 +25,35 @@ export const StoreInfoWithSelect: React.FC<StoreInfoWithSelectProps> = ({}) => { | |
} | |
return ( | |
- <Menu isLazy> | |
- <MenuButton as={Button} variant="outline" rightIcon={<ChevronDownIcon />}> | |
+ <Menu> | |
+ <MenuButton | |
+ data-testid="store-info-select" | |
+ as={Button} | |
+ variant="outline" | |
+ rightIcon={<ChevronDownIcon />} | |
+ onFocus={(e) => e.currentTarget.blur()} | |
+ > | |
<StoreInfo /> | |
</MenuButton> | |
- <MenuList> | |
- {authorizedUser?.user?.entities?.map((entity: UserEntityAuth) => ( | |
- <MenuItem | |
- key={entity.entity.id} | |
- onClick={() => { | |
- setSelectedEntity(entity.entity); | |
- setRegister({ | |
- registerNumber: entity.entity.registers[0].registerId, | |
- isRegisterOpen: true, | |
- openDateTime: new Date().toISOString(), | |
- }); | |
- }} | |
- > | |
- {entity.entity.name} | |
- </MenuItem> | |
- ))} | |
- </MenuList> | |
+ <Portal> | |
+ <MenuList> | |
+ {authorizedUser?.user?.entities?.map((entity: UserEntityAuth) => ( | |
+ <MenuItem | |
+ key={entity.entity.id} | |
+ onClick={() => { | |
+ setSelectedEntity(entity.entity); | |
+ setRegister({ | |
+ registerNumber: entity.entity.registers[0].registerId, | |
+ isRegisterOpen: true, | |
+ openDateTime: new Date().toISOString(), | |
+ }); | |
+ }} | |
+ > | |
+ {entity.entity.name} | |
+ </MenuItem> | |
+ ))} | |
+ </MenuList> | |
+ </Portal> | |
</Menu> | |
); | |
}; | |
diff --git a/src/components/promotions/PromotionMatcher/PromotionMatcherForm.tsx b/src/components/promotions/PromotionMatcher/PromotionMatcherForm.tsx | |
index 70afdcb..d169c26 100644 | |
--- a/src/components/promotions/PromotionMatcher/PromotionMatcherForm.tsx | |
+++ b/src/components/promotions/PromotionMatcher/PromotionMatcherForm.tsx | |
@@ -75,7 +75,7 @@ export const PromotionMatcherForm: React.FC<PromotionMatcherFormProps> = ({ | |
}, [defaultMatchers, matchers, uncontrolledMatchers.length]); | |
return ( | |
- <VStack alignItems={"flex-start"} w="100%"> | |
+ <VStack alignItems={"flex-start"} w="100%" h="100%"> | |
<CardList | |
emptyText="No matchers" | |
items={displayedMatchers} | |
diff --git a/src/components/promotions/PromotionsForm.tsx b/src/components/promotions/PromotionsForm.tsx | |
index 48453a0..75e2766 100644 | |
--- a/src/components/promotions/PromotionsForm.tsx | |
+++ b/src/components/promotions/PromotionsForm.tsx | |
@@ -139,11 +139,7 @@ export const PromotionsForm: React.FC<PromotionsFormProps> = ({ | |
}} | |
/> | |
</HStack> | |
- <HStack | |
- w="100%" | |
- justifyContent="space-between" | |
- alignItems="flex-start" | |
- > | |
+ <HStack w="100%" justifyContent="space-between" alignItems="stretch"> | |
<VStack alignItems="flex-start" w="50%"> | |
<FormLabel>Include Items</FormLabel> | |
<PromotionMatcherForm | |
diff --git a/src/components/promotions/PromotionsList.tsx b/src/components/promotions/PromotionsList.tsx | |
index f74af5d..d1788a6 100644 | |
--- a/src/components/promotions/PromotionsList.tsx | |
+++ b/src/components/promotions/PromotionsList.tsx | |
@@ -65,6 +65,10 @@ export const PromotionsList: React.FC<PromotionsListProps> = ({ | |
return ( | |
<VStack w="100%" h="100%" justifyContent="space-between" flexGrow={2}> | |
<CardList | |
+ containerProps={{ | |
+ // @ts-ignore | |
+ "data-testid": "promotions-list", | |
+ }} | |
isLoading={isLoading} | |
items={promotions} | |
estimatedItemHeight={100} | |
diff --git a/src/config/ItemsApi.ts b/src/config/ItemsApi.ts | |
index ce0b1f4..e06baf9 100644 | |
--- a/src/config/ItemsApi.ts | |
+++ b/src/config/ItemsApi.ts | |
@@ -1,7 +1,7 @@ | |
-import * as Sentry from "@sentry/react"; | |
import { useEffect, useState } from "react"; | |
import { useUserAuth } from "../context/AuthenticationContext/AuthenticationContext"; | |
import { Canny } from "../model/Canny"; | |
+import { Cart } from "../model/Cart"; | |
import { Department } from "../model/Department"; | |
import { Entity } from "../model/Entity"; | |
import { GiftCardApi } from "../model/GiftCardApi"; | |
@@ -56,10 +56,6 @@ export function useBackendAxios() { | |
} | |
return config; | |
}); | |
- i.instance.interceptors.response.use(undefined, (error) => { | |
- Sentry.captureException(error); | |
- return Promise.reject(error); | |
- }); | |
setAxiosInstance(i); | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
}, [firebaseAuth.currentUser]); | |
@@ -199,9 +195,21 @@ export function useInvoiceApi() { | |
return api; | |
} | |
+export function useCartApi() { | |
+ const [api, setApi] = useState<Cart>(); | |
+ const axiosInstance = axios; | |
+ | |
+ useEffect(() => { | |
+ if (!axiosInstance) return; | |
+ setApi(new Cart(axiosInstance)); | |
+ }, [axiosInstance]); | |
+ | |
+ return api; | |
+} | |
+ | |
export function usePromotionApi() { | |
const [api, setApi] = useState<PromotionApi>(); | |
- const axiosInstance = useBackendAxios(); | |
+ const axiosInstance = axios; | |
useEffect(() => { | |
if (!axiosInstance) return; | |
diff --git a/src/context/AuthorizationContext/AuthorizationContext.tsx b/src/context/AuthorizationContext/AuthorizationContext.tsx | |
index 6f46796..9dc3f72 100644 | |
--- a/src/context/AuthorizationContext/AuthorizationContext.tsx | |
+++ b/src/context/AuthorizationContext/AuthorizationContext.tsx | |
@@ -1,3 +1,4 @@ | |
+import { Button } from "@chakra-ui/react"; | |
import { jwtDecode } from "jwt-decode"; | |
import { | |
createContext, | |
@@ -13,6 +14,7 @@ import { | |
firebaseAuthStore, | |
} from "../../config/Firebase/firebase"; | |
import { AuthorizedUserDTO, EntityDTO } from "../../model/data-contracts"; | |
+import { UnauthorizedPage } from "../../pages/Auth/UnauthorizedPage"; | |
import { ErrorPage } from "../../pages/ErrorPage"; | |
import { Permission } from "../../utils/Permission"; | |
import { useUserAuth } from "../AuthenticationContext/AuthenticationContext"; | |
@@ -200,7 +202,21 @@ export const AuthorizationContextProvider = ({ | |
}, [isAuthenticationLoading, refreshPermissions]); | |
if (error) { | |
- return <ErrorPage error={error} />; | |
+ if (error.errorCode === 401) { | |
+ return ( | |
+ <UnauthorizedPage showNavbar={false} showBackButton={false}> | |
+ <Button | |
+ onClick={async () => { | |
+ await handleLogout(); | |
+ return window.location.reload(); | |
+ }} | |
+ > | |
+ Logout | |
+ </Button> | |
+ </UnauthorizedPage> | |
+ ); | |
+ } | |
+ return <ErrorPage error={error.message} />; | |
} | |
return ( | |
diff --git a/src/context/AuthorizationContext/useAuthorizeEmployee.tsx b/src/context/AuthorizationContext/useAuthorizeEmployee.tsx | |
index 789c88d..64a3308 100644 | |
--- a/src/context/AuthorizationContext/useAuthorizeEmployee.tsx | |
+++ b/src/context/AuthorizationContext/useAuthorizeEmployee.tsx | |
@@ -1,3 +1,4 @@ | |
+import * as Sentry from "@sentry/react"; | |
import { AxiosError } from "axios"; | |
import { useCallback, useState } from "react"; | |
import { useUserApi } from "../../config/ItemsApi"; | |
@@ -35,8 +36,10 @@ export const useAuthorizeEmployee = () => { | |
const error = e as AxiosError; | |
if (error.response?.status === 401) { | |
setError("Invalid PIN"); | |
+ return; | |
} | |
} | |
+ Sentry.captureException(e); | |
} | |
}; | |
return [authorizeEmployee, { isLoading, error, clear }] as const; | |
diff --git a/src/context/AuthorizationContext/useAuthorizeUser.tsx b/src/context/AuthorizationContext/useAuthorizeUser.tsx | |