diff --git a/frontend/README.md b/frontend/README.md index b97c7e21e..f56e29699 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,6 +1,10 @@ # GreedyBear - frontend -Built with [gb-ui](https://github.com/GreedyBear-Project/gb-ui). +## Shared UI components + +Shared UI components in `src/components/common/gb-ui/` were originally derived +from [certego-ui](https://github.com/certego/certego-ui) and are now maintained +locally in GreedyBear. ## Design thesis @@ -59,4 +63,3 @@ npm start - JS app main configs are available in `package.json` and `enviroments.js`. - diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c0721b2df..a6bd35c7a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,13 +8,16 @@ "name": "frontend", "version": "0.1.0", "dependencies": { - "@greedybear/gb-ui": "github:GreedyBear-Project/gb-ui#1.0.1", + "@tanstack/react-table": "^8.21.3", "@vnedyalk0v/react19-simple-maps": "^2.0.10", "axios": "^1.20.0", "axios-hooks": "^5.1.1", "bootstrap": ">=5.3.8", + "classnames": "^2.5.1", + "date-fns": "^2.30.0", "formik": "^2.4.9", "i18n-iso-countries": "^7.14.0", + "nanoid": "^3.3.11", "prop-types": "^15.8.1", "react": "^19.2.8", "react-dom": "^19.2.8", @@ -28,6 +31,7 @@ "zustand": "^5.0.15" }, "devDependencies": { + "@playwright/test": "^1.62.1", "@testing-library/jest-dom": "^7.0.1", "@testing-library/react": "^16.3.3", "@testing-library/user-event": "^14.6.6", @@ -85,6 +89,7 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", @@ -95,48 +100,11 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -146,6 +114,7 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -155,6 +124,7 @@ "version": "7.29.3", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" @@ -175,42 +145,11 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/types": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -514,126 +453,6 @@ "postcss-selector-parser": "^7.1.1" } }, - "node_modules/@emotion/babel-plugin": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", - "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/serialize": "^1.3.3", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "license": "MIT" - }, - "node_modules/@emotion/cache": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", - "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.9.0", - "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", - "license": "MIT" - }, - "node_modules/@emotion/memoize": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", - "license": "MIT" - }, - "node_modules/@emotion/react": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", - "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.13.5", - "@emotion/cache": "^11.14.0", - "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", - "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", - "license": "MIT", - "dependencies": { - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.2", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", - "license": "MIT" - }, - "node_modules/@emotion/unitless": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", - "license": "MIT" - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", - "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", - "license": "MIT" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", - "license": "MIT" - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -796,85 +615,6 @@ } } }, - "node_modules/@floating-ui/core": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", - "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.11" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", - "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.5", - "@floating-ui/utils": "^0.2.11" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", - "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", - "license": "MIT" - }, - "node_modules/@greedybear/gb-ui": { - "version": "1.0.1", - "resolved": "git+ssh://git@github.com/GreedyBear-Project/gb-ui.git#72f95eee3dc48e4353933ce747d2e6488bcc363c", - "license": "MIT", - "dependencies": { - "@microlink/react-json-view": "^1.31.22", - "@tanstack/react-table": "^8.21.3", - "classnames": "^2.5.1", - "date-fns": "^2.30.0", - "match-sorter": "^6.4.0", - "nanoid": "^6.0.0", - "react-compound-slider": "^3.4.0", - "react-icons": "^5.7.0", - "react-infinite-scroll-component": "^7.2.1", - "react-json-editor-ajrm": "^2.5.14", - "react-select": "^5.10.2", - "react-share": "^5.3.0", - "react-top-loading-bar": "^3.0.2", - "react-use": "^17.6.1", - "recharts": "^3.9.2", - "zustand": "^5.0.14" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "axios": "^1.6.0", - "axios-hooks": "^5.1.1", - "bootstrap": "^5.2.1", - "formik": "^2.2.9", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-router-dom": "^6.3.0 || ^7.0.0", - "reactstrap": "^9.0.3" - } - }, - "node_modules/@greedybear/gb-ui/node_modules/nanoid": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-6.0.0.tgz", - "integrity": "sha512-mkUH+rPkwU2qPadJ0oJZOjeZ5Mxn8Q1UhevwkTRWNuUZzyia3h4rhzK39hxaHTk0o2OxB8W2SQ6A8k23ZDi1pQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.js" - }, - "engines": { - "node": "^22 || ^24 || >=26" - } - }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -941,20 +681,11 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -970,6 +701,7 @@ "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -983,24 +715,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@microlink/react-json-view": { - "version": "1.31.22", - "resolved": "https://registry.npmjs.org/@microlink/react-json-view/-/react-json-view-1.31.22.tgz", - "integrity": "sha512-cfszidqClRqjnF5YQ5PSGeORo9MnMZvDv+csgq92G1LRZduWEBjeHXKLnulvhE7bX9+ORvjRCWcMQfwRlhPxmA==", - "license": "MIT", - "dependencies": { - "react-base16-styling": "~0.10.0", - "react-lifecycles-compat": "~3.0.4", - "react-textarea-autosize": "~8.5.9" - }, - "engines": { - "node": ">=17" - }, - "peerDependencies": { - "react": ">= 15", - "react-dom": ">= 15" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1044,7 +758,6 @@ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.147.0.tgz", "integrity": "sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" } @@ -1172,6 +885,9 @@ "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1192,6 +908,9 @@ "cpu": [ "arm" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1252,6 +971,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1272,6 +994,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1345,6 +1070,21 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@playwright/test": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz", + "integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==", + "dev": true, + "dependencies": { + "playwright": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -1406,7 +1146,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -1423,7 +1162,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -1440,7 +1178,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -1457,7 +1194,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1474,7 +1210,6 @@ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -1491,10 +1226,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1511,10 +1242,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1531,10 +1258,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1551,10 +1274,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1571,10 +1290,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1591,10 +1306,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1611,7 +1322,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openharmony" @@ -1628,7 +1338,6 @@ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -1645,7 +1354,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -1809,7 +1517,6 @@ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.6.tgz", "integrity": "sha512-Jbs9FpkkIDw8FgSc6kOVsOv8JuuqGAL7J4X1oot77JxAoDlkNn2GRkd0aYRVuQ+pVQAiHWVkE4rX/dkF5fBiCw==", "dev": true, - "license": "MIT", "engines": { "node": ">=12", "npm": ">=6" @@ -1939,18 +1646,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/lodash": { - "version": "4.17.24", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", - "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", - "license": "MIT" - }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "license": "MIT" - }, "node_modules/@types/react": { "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", @@ -1961,15 +1656,6 @@ "csstype": "^3.2.2" } }, - "node_modules/@types/react-transition-group": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", - "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*" - } - }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", @@ -1981,7 +1667,6 @@ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.1.tgz", "integrity": "sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==", "dev": true, - "license": "MIT", "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, @@ -2154,7 +1839,6 @@ "version": "2.0.10", "resolved": "https://registry.npmjs.org/@vnedyalk0v/react19-simple-maps/-/react19-simple-maps-2.0.10.tgz", "integrity": "sha512-tw8PYah/5c1UiwqhtB0k40p+jlYRW9QjdZZQ37CKONXErIZFHl0Sn8Ho03HNP/mXQs0YrogrBIeolln5mfbIsA==", - "license": "MIT", "dependencies": { "d3-color": "^3.1.0", "d3-geo": "^3.1.0", @@ -2357,42 +2041,6 @@ "node": ">=6.9.0" } }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/babel-plugin-macros/node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2430,9 +2078,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -2494,6 +2142,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2556,23 +2205,11 @@ "node": ">=6" } }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2585,18 +2222,9 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -2640,7 +2268,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -2658,31 +2285,6 @@ "toggle-selection": "^1.0.6" } }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cosmiconfig/node_modules/yaml": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", - "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3125,6 +2727,7 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -3205,6 +2808,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -3455,9 +3059,9 @@ "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==" }, "node_modules/fast-uri": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", - "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "dev": true, "funding": [ { @@ -3541,12 +3145,6 @@ "node": ">=8" } }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "license": "MIT" - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3652,7 +3250,6 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -4015,6 +3612,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -4087,23 +3685,9 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, "license": "MIT" }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4304,18 +3888,6 @@ "node": "^22.14.0 || >=24.0.0" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -4327,6 +3899,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { @@ -4343,29 +3916,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsonp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", - "integrity": "sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==", - "dependencies": { - "debug": "^2.1.3" - } - }, - "node_modules/jsonp/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/jsonp/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4405,7 +3955,6 @@ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, - "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" }, @@ -4438,7 +3987,6 @@ "arm64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "android" @@ -4459,7 +4007,6 @@ "arm64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "darwin" @@ -4480,7 +4027,6 @@ "x64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "darwin" @@ -4501,7 +4047,6 @@ "x64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "freebsd" @@ -4522,7 +4067,6 @@ "arm" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -4543,10 +4087,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -4567,10 +4107,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -4591,10 +4127,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -4615,10 +4147,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -4639,7 +4167,6 @@ "arm64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "win32" @@ -4660,7 +4187,6 @@ "x64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "win32" @@ -4677,6 +4203,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, "license": "MIT" }, "node_modules/locate-path": { @@ -4804,17 +4331,6 @@ "node": ">=10" } }, - "node_modules/match-sorter": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.4.0.tgz", - "integrity": "sha512-d4664ahzdL1QTTvmK1iI0JsrxWeJ6gn33qkYtnPg3mcn+naBLtXSgSPOe+X2vUgtgGwaAk3eiaj7gwKjjMAq+Q==", - "deprecated": "This was arguably a breaking change. Not in API, but more results can be returned. Upgrade to the next major when you are ready for that", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.8", - "remove-accents": "0.5.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4842,12 +4358,6 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", - "license": "MIT" - }, "node_modules/meow": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/meow/-/meow-14.1.0.tgz", @@ -5006,14 +4516,12 @@ "version": "3.3.18", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -5119,6 +4627,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -5131,6 +4640,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", @@ -5178,21 +4688,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -5204,6 +4699,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -5219,6 +4715,50 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", + "dev": true, + "dependencies": { + "playwright-core": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { "version": "8.5.26", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", @@ -5238,7 +4778,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", @@ -5441,57 +4980,14 @@ "version": "19.2.8", "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", - "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/react-base16-styling": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.10.0.tgz", - "integrity": "sha512-H1k2eFB6M45OaiRru3PBXkuCcn2qNmx+gzLb4a9IPMR7tMH8oBRXU5jGbPDYG1Hz+82d88ED0vjR8BmqU3pQdg==", - "license": "MIT", - "dependencies": { - "@types/lodash": "^4.17.0", - "color": "^4.2.3", - "csstype": "^3.1.3", - "lodash-es": "^4.17.21" - } - }, - "node_modules/react-compound-slider": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/react-compound-slider/-/react-compound-slider-3.4.0.tgz", - "integrity": "sha512-KSje/rB0xSvvcb7YV0+82hkiXTV5ljSS7axKrNiXLf9AEO+rrr1Xq4MJWA+6v030YNNo/RoSoEB6D6fnoy+8ng==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5", - "d3-array": "^2.8.0", - "warning": "^4.0.3" - }, - "peerDependencies": { - "react": ">=16.9" - } - }, - "node_modules/react-compound-slider/node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "license": "BSD-3-Clause", - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/react-compound-slider/node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", - "license": "ISC" - }, "node_modules/react-dom": { "version": "19.2.8", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", - "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, @@ -5523,7 +5019,6 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-2.2.4.tgz", "integrity": "sha512-Eb57FsgOMYOfsUGrMI1ku/FFR+dPNPrE8qo+3hwZubpqVSy4GO9v52DeX50Tl3JDYAlCypP4rmw7Vrqk/zOIvA==", - "license": "MIT", "dependencies": { "clsx": "^2.1.1", "fast-equals": "^4.0.3", @@ -5546,19 +5041,6 @@ "react": "*" } }, - "node_modules/react-infinite-scroll-component": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-7.2.1.tgz", - "integrity": "sha512-yPuEf6VHBJXFt+YXDxjlnAuhorUwiA2cKURx/P6a7WW3k+TvwY/mbx7KP1rQ1hxGzDcwreczRyLIv6eSd1sFIw==", - "license": "MIT", - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=17", - "react-dom": ">=17" - } - }, "node_modules/react-is": { "version": "19.2.6", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.6.tgz", @@ -5566,25 +5048,6 @@ "license": "MIT", "peer": true }, - "node_modules/react-json-editor-ajrm": { - "version": "2.5.14", - "resolved": "https://registry.npmjs.org/react-json-editor-ajrm/-/react-json-editor-ajrm-2.5.14.tgz", - "integrity": "sha512-z2035l5lnU0Wo73AUonj68d1Eh9vjVcLXEa8YogDEeFPcogzYkfT6R38fvYlLKDhP0oI+LyOO/IBWsFkXs4M0Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.0.0-rc.0" - }, - "peerDependencies": { - "react": ">=16.2.0", - "react-dom": ">=16.2.0" - } - }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", - "license": "MIT" - }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", @@ -5660,69 +5123,6 @@ "react-dom": ">=18" } }, - "node_modules/react-select": { - "version": "5.10.2", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.2.tgz", - "integrity": "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.0", - "@emotion/cache": "^11.4.0", - "@emotion/react": "^11.8.1", - "@floating-ui/dom": "^1.0.1", - "@types/react-transition-group": "^4.4.0", - "memoize-one": "^6.0.0", - "prop-types": "^15.6.0", - "react-transition-group": "^4.3.0", - "use-isomorphic-layout-effect": "^1.2.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-share": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/react-share/-/react-share-5.3.0.tgz", - "integrity": "sha512-NU4TqizVi2TuyLpN93/ng2ux+uIGHK5b2dT15cHvZDwfi5b5NuzGych4htCGObN8lubygeamhRxUot4lZ0Pv6Q==", - "license": "MIT", - "dependencies": { - "classnames": "^2.3.2", - "jsonp": "^0.2.1" - }, - "peerDependencies": { - "react": "^17 || ^18 || ^19" - } - }, - "node_modules/react-textarea-autosize": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", - "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.13", - "use-composed-ref": "^1.3.0", - "use-latest": "^1.2.1" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-top-loading-bar": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/react-top-loading-bar/-/react-top-loading-bar-3.0.2.tgz", - "integrity": "sha512-hW0CHrHqKdBOBsVhms73ka0rgb9/aoiRfqo7jiS1vwIYDK7VkyMj52ypo5ewhshTQGHKE6mRvR99GZBZ+FuM/Q==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16 || ^17 || ^18 || ^19" - } - }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -5839,10 +5239,6 @@ "version": "3.10.1", "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", "integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==", - "license": "MIT", - "workspaces": [ - "www" - ], "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", @@ -5900,12 +5296,6 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "license": "MIT" }, - "node_modules/remove-accents": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", - "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==", - "license": "MIT" - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -5932,6 +5322,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -5953,7 +5344,6 @@ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.6.tgz", "integrity": "sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==", "dev": true, - "license": "MIT", "dependencies": { "@oxc-project/types": "=0.147.0", "@rolldown/pluginutils": "^1.0.0" @@ -6069,8 +5459,7 @@ "node_modules/set-cookie-parser": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", - "license": "MIT" + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==" }, "node_modules/set-harmonic-interval": { "version": "1.0.1", @@ -6124,21 +5513,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/simple-swizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", - "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "license": "MIT" - }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -6170,15 +5544,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6438,12 +5803,6 @@ "node": ">= 4" } }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", - "license": "MIT" - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6500,18 +5859,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", @@ -6794,51 +6141,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-composed-ref": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", - "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-isomorphic-layout-effect": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", - "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-latest": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", - "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", - "license": "MIT", - "dependencies": { - "use-isomorphic-layout-effect": "^1.1.1" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", @@ -6882,7 +6184,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", "dev": true, - "license": "MIT", "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", diff --git a/frontend/package.json b/frontend/package.json index 7d21cf856..b5a71f08b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,13 +9,16 @@ "prettier": "../.github/configurations/node_linters/prettier/.prettierrc.js" }, "dependencies": { - "@greedybear/gb-ui": "github:GreedyBear-Project/gb-ui#1.0.1", + "@tanstack/react-table": "^8.21.3", "@vnedyalk0v/react19-simple-maps": "^2.0.10", "axios": "^1.20.0", "axios-hooks": "^5.1.1", "bootstrap": ">=5.3.8", + "classnames": "^2.5.1", + "date-fns": "^2.30.0", "formik": "^2.4.9", "i18n-iso-countries": "^7.14.0", + "nanoid": "^3.3.11", "prop-types": "^15.8.1", "react": "^19.2.8", "react-dom": "^19.2.8", @@ -34,6 +37,9 @@ "preview": "vite preview", "test": "vitest", "test-coverage": "vitest run --coverage", + "start:visual": "vite --host 127.0.0.1 --port 3002", + "test:visual": "playwright test", + "test:visual:update": "playwright test --update-snapshots", "lint-config-install": "cd ../.github/configurations/node_linters/eslint && npm i", "lint": "eslint 'src/**/*.{js,jsx}' 'tests/**/*.{js,jsx}'", "lint-fix": "npm run lint -- --fix", @@ -47,6 +53,7 @@ "d3-color": "^3.1.0" }, "devDependencies": { + "@playwright/test": "^1.62.1", "@testing-library/jest-dom": "^7.0.1", "@testing-library/react": "^16.3.3", "@testing-library/user-event": "^14.6.6", diff --git a/frontend/playwright.config.js b/frontend/playwright.config.js new file mode 100644 index 000000000..a440a4e28 --- /dev/null +++ b/frontend/playwright.config.js @@ -0,0 +1,33 @@ +import { defineConfig, devices } from "@playwright/test"; + +export default defineConfig({ + testDir: "./tests/visual", + fullyParallel: true, + reporter: "html", + snapshotPathTemplate: "{testDir}/{testFilePath}-snapshots/{arg}{ext}", + use: { + baseURL: "http://127.0.0.1:3002", + trace: "on-first-retry", + }, + projects: [ + { + name: "desktop-chromium", + use: { + ...devices["Desktop Chrome"], + viewport: { width: 1440, height: 1200 }, + }, + }, + { + name: "mobile-chromium", + use: { + ...devices["Pixel 7"], + }, + }, + ], + webServer: { + command: "npm run start:visual", + url: "http://127.0.0.1:3002/tests/visual.html", + reuseExistingServer: !process.env.CI, + timeout: 120000, + }, +}); diff --git a/frontend/src/components/Routes.jsx b/frontend/src/components/Routes.jsx index 95fa3786e..820d1c58b 100644 --- a/frontend/src/components/Routes.jsx +++ b/frontend/src/components/Routes.jsx @@ -1,5 +1,5 @@ import React, { Suspense } from "react"; -import { FallBackLoading } from "@greedybear/gb-ui"; +import { FallBackLoading } from "./common/gb-ui/index"; import IfAuthRedirectGuard from "../wrappers/ifAuthRedirectGuard"; import AuthGuard from "../wrappers/AuthGuard"; diff --git a/frontend/src/components/auth/EmailVerification.jsx b/frontend/src/components/auth/EmailVerification.jsx index 371c659b4..b339ed203 100644 --- a/frontend/src/components/auth/EmailVerification.jsx +++ b/frontend/src/components/auth/EmailVerification.jsx @@ -1,7 +1,7 @@ import React from "react"; import { Navigate, useSearchParams } from "react-router-dom"; import { Spinner } from "reactstrap"; -import { ContentSection } from "@greedybear/gb-ui"; +import { ContentSection } from "../common/gb-ui/index"; import { verifyEmail } from "./api"; diff --git a/frontend/src/components/auth/Login.jsx b/frontend/src/components/auth/Login.jsx index ba488f4ca..bf4253a2b 100644 --- a/frontend/src/components/auth/Login.jsx +++ b/frontend/src/components/auth/Login.jsx @@ -12,7 +12,7 @@ import { import { MdInfoOutline } from "react-icons/md"; import { Form, Formik } from "formik"; -import { ContentSection } from "@greedybear/gb-ui"; +import { ContentSection } from "../common/gb-ui/index"; import { useAuthStore } from "../../stores"; import { diff --git a/frontend/src/components/auth/Logout.jsx b/frontend/src/components/auth/Logout.jsx index a3fd1d2f2..ab3d57a6e 100644 --- a/frontend/src/components/auth/Logout.jsx +++ b/frontend/src/components/auth/Logout.jsx @@ -1,7 +1,7 @@ import React from "react"; import { useShallow } from "zustand/shallow"; -import { FallBackLoading } from "@greedybear/gb-ui"; +import { FallBackLoading } from "../common/gb-ui/index"; import { useAuthStore } from "../../stores"; import { AUTHENTICATION_STATUSES } from "../../constants"; diff --git a/frontend/src/components/auth/Register.jsx b/frontend/src/components/auth/Register.jsx index 62265ae32..fc37a60cf 100644 --- a/frontend/src/components/auth/Register.jsx +++ b/frontend/src/components/auth/Register.jsx @@ -14,7 +14,7 @@ import { import { Form, Formik } from "formik"; import { useTitle } from "react-use"; -import { ContentSection, Select } from "@greedybear/gb-ui"; +import { ContentSection, Select } from "../common/gb-ui/index"; import { AfterRegistrationModalAlert, diff --git a/frontend/src/components/auth/ResetPassword.jsx b/frontend/src/components/auth/ResetPassword.jsx index 7f319bcd2..cdce34664 100644 --- a/frontend/src/components/auth/ResetPassword.jsx +++ b/frontend/src/components/auth/ResetPassword.jsx @@ -4,7 +4,7 @@ import { FormGroup, Label, Col, Input, Button, Spinner } from "reactstrap"; import { Form, Formik } from "formik"; import { useTitle } from "react-use"; -import { ContentSection } from "@greedybear/gb-ui"; +import { ContentSection } from "../common/gb-ui/index"; import { UUID_REGEX } from "../../constants/index"; import { resetPassword } from "./api"; diff --git a/frontend/src/components/auth/api.js b/frontend/src/components/auth/api.js index 9f732943d..8fe4f72a0 100644 --- a/frontend/src/components/auth/api.js +++ b/frontend/src/components/auth/api.js @@ -1,6 +1,6 @@ import axios from "axios"; -import { addToast } from "@greedybear/gb-ui"; +import { addToast } from "../common/gb-ui/index"; import { AUTH_BASE_URI } from "../../constants/api"; diff --git a/frontend/src/components/auth/utils/registration-buttons.jsx b/frontend/src/components/auth/utils/registration-buttons.jsx index fb67d4084..bc52c6a58 100644 --- a/frontend/src/components/auth/utils/registration-buttons.jsx +++ b/frontend/src/components/auth/utils/registration-buttons.jsx @@ -2,7 +2,7 @@ import React from "react"; // import PropTypes from "prop-types"; import { Alert, PopoverBody } from "reactstrap"; import { IoMail } from "react-icons/io5"; -import { PopupFormButton } from "@greedybear/gb-ui"; +import { PopupFormButton } from "../../common/gb-ui/index"; import EmailForm from "./EmailForm"; import { resendVerificationMail, requestPasswordReset } from "../api"; diff --git a/frontend/src/components/common/gb-ui/components/alerts/ErrorAlert.jsx b/frontend/src/components/common/gb-ui/components/alerts/ErrorAlert.jsx new file mode 100644 index 000000000..aa1d0e643 --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/alerts/ErrorAlert.jsx @@ -0,0 +1,39 @@ +import React from "react"; +import classnames from "classnames"; +import { Alert, Col } from "reactstrap"; +import { MdError } from "react-icons/md"; + +const sizeToWidth = { + sm: 4, + md: 8, + lg: 10, + xl: 12, +}; + +function ErrorAlert({ error, size = "md", className = undefined, ...rest }) { + return ( + +

+ +   Error! +

+
+
+ {[error?.response?.status, error?.response?.statusText] + .filter(Boolean) + .join(" | ")} +
+
+ {error?.parsedMsg?.toString()} +
+
+ ); +} + +export default ErrorAlert; diff --git a/frontend/src/components/common/gb-ui/components/buttons/CopyToClipboardButton.jsx b/frontend/src/components/common/gb-ui/components/buttons/CopyToClipboardButton.jsx new file mode 100644 index 000000000..07a867956 --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/buttons/CopyToClipboardButton.jsx @@ -0,0 +1,73 @@ +import React from "react"; +import { Tooltip, UncontrolledTooltip } from "reactstrap"; +import { HiClipboardCopy } from "react-icons/hi"; + +import useCopyToClipboard from "react-use/lib/useCopyToClipboard"; + +function CopyToClipboardButton({ + id, + text, + children = null, + tooltip = null, + showOnHover = false, + ...rest +}) { + // props + + // local state + const [copied, setCopied] = React.useState(false); + const timeoutRef = React.useRef(null); + + const [, copyToClipboard] = useCopyToClipboard(); + + React.useEffect( + () => () => { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + }, + [], + ); + + // callbacks + const onCopy = () => { + copyToClipboard(text); + setCopied(true); + if (timeoutRef.current) clearTimeout(timeoutRef.current); + timeoutRef.current = setTimeout( + () => setCopied(false), + tooltip?.timeout || 1000, + ); + }; + + return ( + <> + + {children || } + + + {tooltip?.text || "Copied!"} + + {showOnHover && ( + + Click to copy + + )} + + ); +} + +export default CopyToClipboardButton; diff --git a/frontend/src/components/common/gb-ui/components/buttons/IconButton.jsx b/frontend/src/components/common/gb-ui/components/buttons/IconButton.jsx new file mode 100644 index 000000000..2ed4b827e --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/buttons/IconButton.jsx @@ -0,0 +1,33 @@ +import React from "react"; +import { Button, UncontrolledTooltip } from "reactstrap"; +import { nanoid } from "nanoid"; + +function IconButton({ + id = undefined, + title = undefined, + titlePlacement = "right-start", + Icon, + ...rest +}) { + // props + + // vars + const btnId = React.useMemo(() => id || `iconbtn-${nanoid(4)}`, [id]); + + return ( + + ); +} + +export default IconButton; diff --git a/frontend/src/components/common/gb-ui/components/buttons/PopupFormButton.jsx b/frontend/src/components/common/gb-ui/components/buttons/PopupFormButton.jsx new file mode 100644 index 000000000..4c69694b0 --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/buttons/PopupFormButton.jsx @@ -0,0 +1,71 @@ +import React from "react"; +import { Button, Popover, UncontrolledTooltip, PopoverBody } from "reactstrap"; +import { nanoid } from "nanoid"; +import { IoMdClose } from "react-icons/io"; + +export default function PopupFormButton({ + id = undefined, + title = null, + titlePlacement = "right-start", + popOverPlacement = "right-start", + Icon, + Form, + onFormSuccess = () => null, + ...rest +}) { + // props + + // state + const [popoverOpen, setPopoverOpen] = React.useState(false); + const closeTimeoutRef = React.useRef(null); + + React.useEffect( + () => () => { + if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current); + }, + [], + ); + + // callbacks + const onFormSubmit = React.useCallback(() => { + if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current); + closeTimeoutRef.current = setTimeout(() => setPopoverOpen(false), 400); + onFormSuccess(); + }, [onFormSuccess]); + + // vars + const btnId = React.useMemo(() => id || `popover-btn-${nanoid(4)}`, [id]); + + return ( + <> + + {title && ( + + {title} + + )} + setPopoverOpen(!popoverOpen)} + > + +
+ + + + ); +} diff --git a/frontend/src/components/common/gb-ui/components/buttons/ScrollToTopButton.jsx b/frontend/src/components/common/gb-ui/components/buttons/ScrollToTopButton.jsx new file mode 100644 index 000000000..1c7810cbd --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/buttons/ScrollToTopButton.jsx @@ -0,0 +1,54 @@ +import React from "react"; +import { RiArrowUpSLine } from "react-icons/ri"; +import { Button } from "reactstrap"; + +export default function ScrollToTopButton({ + defaultVisible = false, + scrollYOffset = 300, + ...rest +}) { + // props + + // local state + const [isVisible, setIsVisible] = React.useState(defaultVisible); + + // Show button when page is scrolled upto given distance + const toggleVisibility = React.useCallback( + () => + window.pageYOffset > scrollYOffset + ? setIsVisible(true) + : setIsVisible(false), + [scrollYOffset, setIsVisible], + ); + + // Set the top cordinate to 0 + // make scrolling smooth + const scrollToTop = () => + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + + React.useEffect(() => { + window.addEventListener("scroll", toggleVisibility); + toggleVisibility(); + + return () => { + window.removeEventListener("scroll", toggleVisibility); + }; + }, [toggleVisibility]); + + return ( +
+ {isVisible && ( + + )} +
+ ); +} diff --git a/frontend/src/components/common/gb-ui/components/containers/ContentSection.jsx b/frontend/src/components/common/gb-ui/components/containers/ContentSection.jsx new file mode 100644 index 000000000..0f883dd7f --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/containers/ContentSection.jsx @@ -0,0 +1,12 @@ +import React from "react"; +import classnames from "classnames"; + +function ContentSection({ className = null, children, ...rest }) { + return ( +
+ {children} +
+ ); +} + +export default ContentSection; diff --git a/frontend/src/components/common/gb-ui/components/containers/Loader.jsx b/frontend/src/components/common/gb-ui/components/containers/Loader.jsx new file mode 100644 index 000000000..85af15350 --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/containers/Loader.jsx @@ -0,0 +1,27 @@ +import React from "react"; +import { Spinner } from "reactstrap"; + +import ErrorAlert from "../alerts/ErrorAlert"; + +function Loader({ + loading, + error = null, + render, + renderError = ErrorAlert, + size = "md", +}) { + return ( + <> + {loading && ( + + )} + {error && renderError({ error, size })} + {!(loading || error) && render()} + + ); +} + +export default Loader; diff --git a/frontend/src/components/common/gb-ui/components/containers/LoadingBoundary.jsx b/frontend/src/components/common/gb-ui/components/containers/LoadingBoundary.jsx new file mode 100644 index 000000000..6f187ddac --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/containers/LoadingBoundary.jsx @@ -0,0 +1,30 @@ +import React from "react"; +import { Spinner } from "reactstrap"; + +import ErrorAlert from "../alerts/ErrorAlert"; + +function LoadingBoundary({ + loading = false, + error = null, + size = "md", + render, + renderError = ErrorAlert, +}) { + const hasError = Boolean(error?.response || error?.message); + const RenderError = renderError; + + return ( + <> + {loading && ( + + )} + {hasError && } + {!loading && !hasError && render()} + + ); +} + +export default LoadingBoundary; diff --git a/frontend/src/components/common/gb-ui/components/containers/SmallInfoCard.jsx b/frontend/src/components/common/gb-ui/components/containers/SmallInfoCard.jsx new file mode 100644 index 000000000..85fd5d8d1 --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/containers/SmallInfoCard.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import classNames from "classnames"; +import { Card, CardBody, CardHeader } from "reactstrap"; + +function SmallInfoCard({ header, body, className = undefined, ...rest }) { + return ( + + {header} + {body} + + ); +} + +export default SmallInfoCard; diff --git a/frontend/src/components/common/gb-ui/components/form/Select.jsx b/frontend/src/components/common/gb-ui/components/form/Select.jsx new file mode 100644 index 000000000..a4d710b18 --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/form/Select.jsx @@ -0,0 +1,20 @@ +import React from "react"; +import { Input } from "reactstrap"; + +export default function Select({ + choices, + allowEmpty = false, + emptyLabel = "", + ...rest +}) { + return ( + + {allowEmpty && } + {choices.map(({ label: text, value }) => ( + + ))} + + ); +} diff --git a/frontend/src/components/common/gb-ui/components/icons/ArrowToggleIcon.jsx b/frontend/src/components/common/gb-ui/components/icons/ArrowToggleIcon.jsx new file mode 100644 index 000000000..d59ef5e42 --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/icons/ArrowToggleIcon.jsx @@ -0,0 +1,16 @@ +import React from "react"; + +import { MdExpandLess, MdExpandMore } from "react-icons/md"; +import classNames from "classnames"; + +function ArrowToggleIcon({ isExpanded, className = null, ...rest }) { + const cls = classNames("pointer bg-tertiary rounded-pill", className); + + return isExpanded ? ( + + ) : ( + + ); +} + +export default ArrowToggleIcon; diff --git a/frontend/src/components/common/gb-ui/components/icons/BooleanIcon.jsx b/frontend/src/components/common/gb-ui/components/icons/BooleanIcon.jsx new file mode 100644 index 000000000..39e44e25f --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/icons/BooleanIcon.jsx @@ -0,0 +1,30 @@ +import React from "react"; +import classnames from "classnames"; +import { MdCheck, MdCheckCircleOutline } from "react-icons/md"; +import { IoMdCloseCircleOutline, IoMdClose } from "react-icons/io"; + +export default function BooleanIcon({ + truthy, + withColors = false, + outline = true, + ...rest +}) { + const Check = outline ? MdCheckCircleOutline : MdCheck; + const Close = outline ? IoMdCloseCircleOutline : IoMdClose; + + return truthy ? ( + + ) : ( + + ); +} diff --git a/frontend/src/components/common/gb-ui/components/misc/FallbackLoading.jsx b/frontend/src/components/common/gb-ui/components/misc/FallbackLoading.jsx new file mode 100644 index 000000000..a082b694a --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/misc/FallbackLoading.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import { Spinner } from "reactstrap"; + +export default function FallBackLoading({ text = "Loading...", ...rest }) { + return ( +
+
+ +

{text}

+
+
+ ); +} diff --git a/frontend/src/components/common/gb-ui/components/misc/Toaster.jsx b/frontend/src/components/common/gb-ui/components/misc/Toaster.jsx new file mode 100644 index 000000000..86fd4072c --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/misc/Toaster.jsx @@ -0,0 +1,49 @@ +import React, { useState, useCallback } from "react"; +import { Toast, ToastBody, ToastHeader, Spinner } from "reactstrap"; +import { MdError, MdWarning, MdInfo, MdCheckCircle } from "react-icons/md"; + +function getIcon(color) { + const iconChoices = { + danger: MdError, + warning: MdWarning, + info: MdInfo, + success: MdCheckCircle, + spinner: Spinner, + }; + return iconChoices[color] ? iconChoices[color] : iconChoices.info; +} + +export default function Toaster({ + header, + body = null, + color = "info", + timeout = 4000, + showToggle = false, + ...props +}) { + // state + const [show, setShow] = useState(true); + const toggle = useCallback(() => setShow((v) => !v), [setShow]); + + React.useEffect(() => { + const timer = setTimeout(() => setShow(false), timeout); + return () => clearTimeout(timer); + }, [timeout]); + + const Icon = getIcon(color); + + const toastColor = color === "spinner" ? "info" : color; + + return ( + + + + {header} + + {body && {body}} + + ); +} diff --git a/frontend/src/components/common/gb-ui/components/misc/UserBubble.jsx b/frontend/src/components/common/gb-ui/components/misc/UserBubble.jsx new file mode 100644 index 000000000..14907659f --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/misc/UserBubble.jsx @@ -0,0 +1,42 @@ +import React from "react"; +import classnames from "classnames"; + +/** + * @type {component} + * @param props + */ +export default function UserBubble({ + userInfo, + size = "sm", + className = undefined, + ...elProps +}) { + const userInitials = + userInfo?.first_name?.charAt(0).concat(userInfo?.last_name?.charAt(0)) || + userInfo?.username?.slice(0, 2); + + const bubbleClassName = React.useMemo( + () => + classnames( + "chat-bubble", + { + "chat-bubble-sm": size === "sm", + "chat-bubble-xs": size === "xs", + highlight: true, + }, + className, + ), + [className, size], + ); + + return ( +
+ {userInitials} +
+ ); +} diff --git a/frontend/src/components/common/gb-ui/components/modals/ConfirmModal.jsx b/frontend/src/components/common/gb-ui/components/modals/ConfirmModal.jsx new file mode 100644 index 000000000..29ad0b88c --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/modals/ConfirmModal.jsx @@ -0,0 +1,69 @@ +import React, { Fragment } from "react"; +import { createRoot } from "react-dom/client"; +import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from "reactstrap"; + +function ConfirmModal({ + onClose, + message = "Are you sure?", + title = "Warning!", + confirmText = "Ok", + cancelText = "Cancel", + confirmColor = "primary", + cancelColor = "", + className = "", + buttonsComponent = null, + size = null, + bodyComponent = null, + modalProps = {}, +}) { + let buttonsContent = ( + + {cancelText && ( + + )}{" "} + + + ); + + if (buttonsComponent) { + const CustomComponent = buttonsComponent; + buttonsContent = ; + } + + const BodyComponent = bodyComponent; + + return ( + onClose(false)} + className={`reactstrap-confirm ${className}`} + {...modalProps} + > + {title && ( + onClose(false)}>{title || null} + )} + {bodyComponent ? : message} + {buttonsContent} + + ); +} + +export const confirm = (props) => + new Promise((resolve) => { + const el = document.createElement("div"); + document.body.appendChild(el); + const root = createRoot(el); + const handleResolve = (result) => { + root.unmount(); + el.remove(); + resolve(result); + }; + root.render(); + }); + +export default ConfirmModal; diff --git a/frontend/src/components/common/gb-ui/components/nav/DropdownNavLink.jsx b/frontend/src/components/common/gb-ui/components/nav/DropdownNavLink.jsx new file mode 100644 index 000000000..4a5cf9ff9 --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/nav/DropdownNavLink.jsx @@ -0,0 +1,19 @@ +import React from "react"; +import { NavLink as RRNavLink } from "react-router-dom"; + +/** + * @type {component} + * @param props + */ +export default function DropdownNavLink(props) { + const { children, ...toPassProps } = props; + + return ( + `dropdown-item${isActive ? " active" : ""}`} + {...toPassProps} + > + {children} + + ); +} diff --git a/frontend/src/components/common/gb-ui/components/nav/NavLink.jsx b/frontend/src/components/common/gb-ui/components/nav/NavLink.jsx new file mode 100644 index 000000000..b7f1784de --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/nav/NavLink.jsx @@ -0,0 +1,37 @@ +import React from "react"; +import classnames from "classnames"; +import { NavLink as RRNavLink } from "react-router-dom"; + +// constants +const type2ClassnameMap = { + default: "", + primary: "link-primary", + primaryUl: "link-ul-primary", + muted: "link-muted", + mutedUl: "link-ul-muted", +}; + +// component +export default function NavLink({ + type = "default", + children, + className = null, + ...props +}) { + return ( + + classnames( + "nav-link", + "text-lg-center", + type2ClassnameMap[type], + className, + { active: isActive }, + ) + } + {...props} + > + {children} + + ); +} diff --git a/frontend/src/components/common/gb-ui/components/table/DataTable.jsx b/frontend/src/components/common/gb-ui/components/table/DataTable.jsx new file mode 100644 index 000000000..341899044 --- /dev/null +++ b/frontend/src/components/common/gb-ui/components/table/DataTable.jsx @@ -0,0 +1,570 @@ +/* eslint-disable no-nested-ternary */ +import React from "react"; +import classnames from "classnames"; +import { Table } from "reactstrap"; +import { + useReactTable, + getCoreRowModel, + getFilteredRowModel, + getSortedRowModel, + getPaginationRowModel, +} from "@tanstack/react-table"; +import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa"; + +import { makeTableArgs, defaultConfig, defaultInitialState } from "./utils"; +import Paginator from "./Paginator"; + +function getColumnId(column) { + return column.id || column.accessor; +} + +function getAccessorFn(column) { + if (typeof column.accessor === "function") return column.accessor; + if (typeof column.accessor === "string") + return (row) => row?.[column.accessor]; + return () => undefined; +} + +function normalizeColumn(column, index) { + const id = getColumnId(column) || `column_${index}`; + const accessorFn = getAccessorFn(column); + + return { + ...column, + id, + accessorFn, + }; +} + +function getLegacyRowValue(row, column) { + if (!column?.accessorFn) return undefined; + return column.accessorFn(row); +} + +function legacyDefaultFilterFn(row, columnId, filterValue) { + if (filterValue === undefined || filterValue === null || filterValue === "") + return true; + + const normalizedFilter = `${filterValue}`.toLowerCase(); + const rowValue = row.getValue(columnId); + + if (Array.isArray(rowValue)) { + return rowValue.some((value) => + `${value ?? ""}`.toLowerCase().includes(normalizedFilter), + ); + } + + return `${rowValue ?? ""}`.toLowerCase().includes(normalizedFilter); +} + +/** + * Suitable when data is already available client side. Thus, pagination/filtering/sorting can be performed client side too. + */ +function DataTable({ + config: userConfig = defaultConfig, + onSelectedRowChange = undefined, + SubComponent = undefined, + tableProps = undefined, + tableEmptyNode = "No Data", + TableBodyComponent = undefined, + columns = [], + data = [], + initialState = defaultInitialState, + stateReducer = undefined, + isRowSelectable = () => true, + customProps = undefined, + manualPagination = false, + manualFilters = false, + manualSortBy = false, + pageCount = undefined, +}) { + const config = React.useMemo( + () => ({ ...defaultConfig, ...userConfig }), + [userConfig], + ); + + const tableArgs = React.useMemo(() => makeTableArgs(config), [config]); + + const hookRegistry = React.useMemo(() => { + const registry = { visibleColumns: [], getRowProps: [] }; + tableArgs.forEach((hook) => { + if (typeof hook === "function") hook(registry); + }); + return registry; + }, [tableArgs]); + + const baseColumns = React.useMemo( + () => columns.map((column, index) => normalizeColumn(column, index)), + [columns], + ); + + const baseInstance = React.useMemo( + () => ({ customProps, isRowSelectable }), + [customProps, isRowSelectable], + ); + + const legacyColumns = React.useMemo(() => { + let transformedColumns = baseColumns; + hookRegistry.visibleColumns.forEach((transformer) => { + transformedColumns = transformer(transformedColumns, { + instance: baseInstance, + }); + }); + return transformedColumns; + }, [baseColumns, hookRegistry, baseInstance]); + + const tanstackColumns = React.useMemo( + () => + legacyColumns.map((column) => ({ + id: column.id, + accessorFn: column.accessorFn, + enableSorting: config.enableSortBy && !column.disableSortBy, + enableColumnFilter: config.enableFilters && !!column.Filter, + filterFn: column.filterFn || legacyDefaultFilterFn, + meta: { legacyColumn: column }, + })), + [legacyColumns, config.enableSortBy, config.enableFilters], + ); + + const legacyInitialState = React.useMemo( + () => ({ ...defaultInitialState, ...initialState }), + [initialState], + ); + + const [tableState, setTableState] = React.useState(() => ({ + pagination: { + pageIndex: legacyInitialState.pageIndex || 0, + pageSize: legacyInitialState.pageSize || 10, + }, + sorting: legacyInitialState.sortBy || [], + columnFilters: legacyInitialState.filters || [], + rowSelection: legacyInitialState.selectedRowIds || {}, + expanded: {}, + })); + + const reduceState = React.useCallback( + (nextState, action, prevState) => { + if (!stateReducer) return nextState; + return stateReducer(nextState, action, prevState) || nextState; + }, + [stateReducer], + ); + + const updateState = React.useCallback( + (producer, action) => { + setTableState((previousState) => { + const nextState = producer(previousState); + return reduceState(nextState, action, previousState); + }); + }, + [reduceState], + ); + + const toggleRowSelected = React.useCallback( + (rowId, selected) => { + updateState( + (previousState) => ({ + ...previousState, + rowSelection: { + ...previousState.rowSelection, + [rowId]: selected, + }, + }), + { type: "toggleRowSelected", id: rowId, value: selected }, + ); + }, + [updateState], + ); + + const table = useReactTable({ + data, + columns: tanstackColumns, + state: tableState, + pageCount, + manualPagination, + manualFiltering: manualFilters, + manualSorting: manualSortBy, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getSortedRowModel: getSortedRowModel(), + getPaginationRowModel: getPaginationRowModel(), + enableRowSelection: (row) => + isRowSelectable({ + id: row.id, + original: row.original, + }), + onPaginationChange: (updater) => { + updateState( + (previousState) => { + const nextPagination = + typeof updater === "function" + ? updater(previousState.pagination) + : updater; + + return { + ...previousState, + pagination: nextPagination, + }; + }, + { + type: + typeof updater === "function" + ? "gotoPage" + : updater.pageIndex !== tableState.pagination.pageIndex + ? "gotoPage" + : "setPageSize", + pageIndex: + typeof updater === "function" + ? updater(tableState.pagination).pageIndex + : updater.pageIndex, + }, + ); + }, + onSortingChange: (updater) => { + updateState( + (previousState) => ({ + ...previousState, + sorting: + typeof updater === "function" + ? updater(previousState.sorting) + : updater, + }), + { type: "toggleSortBy" }, + ); + }, + onColumnFiltersChange: (updater) => { + updateState( + (previousState) => ({ + ...previousState, + columnFilters: + typeof updater === "function" + ? updater(previousState.columnFilters) + : updater, + }), + { type: "setFilter" }, + ); + }, + onRowSelectionChange: (updater) => { + updateState( + (previousState) => ({ + ...previousState, + rowSelection: + typeof updater === "function" + ? updater(previousState.rowSelection) + : updater, + }), + { type: "toggleRowSelected" }, + ); + }, + onExpandedChange: (updater) => { + updateState( + (previousState) => ({ + ...previousState, + expanded: + typeof updater === "function" + ? updater(previousState.expanded) + : updater, + }), + { type: "toggleRowExpanded" }, + ); + }, + }); + + const toLegacyRow = React.useCallback( + (row) => ({ + id: row.id, + original: row.original, + values: Object.fromEntries( + legacyColumns.map((column) => [ + column.id, + getLegacyRowValue(row.original, column), + ]), + ), + isSelected: row.getIsSelected(), + isExpanded: row.getIsExpanded(), + getToggleRowSelectedProps: () => ({ + checked: row.getIsSelected(), + onChange: (event) => row.toggleSelected(event.target.checked), + }), + getToggleRowExpandedProps: () => ({ + role: "button", + tabIndex: 0, + onClick: () => row.toggleExpanded(), + onKeyDown: (event) => { + if (event.key === "Enter" || event.key === " ") row.toggleExpanded(); + }, + }), + }), + [legacyColumns], + ); + + const pageRows = table.getRowModel().rows; + const legacyPageRows = React.useMemo( + () => pageRows.map((row) => toLegacyRow(row)), + [pageRows, toLegacyRow], + ); + + const selectedFlatRows = React.useMemo( + () => table.getSelectedRowModel().flatRows.map((row) => toLegacyRow(row)), + [table, toLegacyRow], + ); + + React.useEffect(() => { + if (!onSelectedRowChange) return; + onSelectedRowChange(selectedFlatRows.map((row) => row.original)); + }, [onSelectedRowChange, selectedFlatRows]); + + const tableInstanceContext = React.useMemo( + () => ({ + ...baseInstance, + page: legacyPageRows, + selectedFlatRows, + toggleRowSelected, + }), + [baseInstance, legacyPageRows, selectedFlatRows, toggleRowSelected], + ); + + const renderHeaderContent = React.useCallback( + (column) => { + const Header = column?.Header; + if (typeof Header === "function") { + return ( +
+ ); + } + return Header; + }, + [selectedFlatRows, toggleRowSelected, legacyPageRows], + ); + + const renderFooterContent = React.useCallback((column) => { + const Footer = column?.Footer; + if (typeof Footer === "function") return