diff --git a/package-lock.json b/package-lock.json index 84a3c85..4bfaf39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "farmcontrol-ui", "version": "0.1.0", "dependencies": { + "@ant-design/charts": "^2.3.0", + "@ant-design/pro-components": "^2.8.7", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@simplewebauthn/browser": "^13.1.0", "@tsparticles/react": "^3.0.0", @@ -32,9 +34,12 @@ "react": "^18.3.1", "react-country-flag": "^3.1.0", "react-dom": "^18.3.1", + "react-markdown": "^10.1.0", + "react-responsive": "^10.0.1", "react-router-dom": "*", "react-scripts": "*", "react-stl-viewer": "^2.5.0", + "remark-gfm": "^4.0.1", "socket.io-client": "*", "standard": "^17.1.2", "styled-components": "*", @@ -89,6 +94,34 @@ "node": ">=6.0.0" } }, + "node_modules/@ant-design/charts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ant-design/charts/-/charts-2.3.0.tgz", + "integrity": "sha512-uX/Re6C7t+JleAYKyWu2jv08gAxq2QcZVeGjfgNCV3VC/ODBmqRvu8H0m3Lrla2aHKjCyksirTZI7y2BOKfA1w==", + "license": "MIT", + "dependencies": { + "@ant-design/graphs": "^2.1.0", + "@ant-design/plots": "^2.4.0", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": ">=16.8.4", + "react-dom": ">=16.8.4" + } + }, + "node_modules/@ant-design/charts-util": { + "version": "0.0.1-alpha.7", + "resolved": "https://registry.npmjs.org/@ant-design/charts-util/-/charts-util-0.0.1-alpha.7.tgz", + "integrity": "sha512-Yh0o6EdO6SvdSnStFZMbnUzjyymkVzV+TQ9ymVW9hlVgO/fUkUII3JYSdV+UVcFnYwUF0YiDKuSTLCZNAzg2bQ==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": ">=16.8.4", + "react-dom": ">=16.8.4" + } + }, "node_modules/@ant-design/colors": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.1.tgz", @@ -144,6 +177,24 @@ "node": ">=8.x" } }, + "node_modules/@ant-design/graphs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@ant-design/graphs/-/graphs-2.1.0.tgz", + "integrity": "sha512-JavZyJVDRyO5wjReqz3CRYhml5MMpOe+fT4ucebdkfOfWYTlOG+W9vxtNSITJmCGHUVphQkQo9r1CPkZysDT0g==", + "license": "MIT", + "dependencies": { + "@ant-design/charts-util": "0.0.1-alpha.7", + "@antv/g6": "^5.0.44", + "@antv/g6-extension-react": "^0.2.0", + "@antv/graphin": "^3.0.4", + "lodash": "^4.17.21", + "styled-components": "^6.1.15" + }, + "peerDependencies": { + "react": ">=16.8.4", + "react-dom": ">=16.8.4" + } + }, "node_modules/@ant-design/icons": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz", @@ -170,6 +221,329 @@ "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", "license": "MIT" }, + "node_modules/@ant-design/plots": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@ant-design/plots/-/plots-2.4.0.tgz", + "integrity": "sha512-5JxX6gDp9VyQizkQsCBKjGqlHpgKhfV6XTRNqKnrJMYet0FBNO0srDsa/rmQoZZLxMRvE8eZhCXnM7DhRUWUdA==", + "license": "MIT", + "dependencies": { + "@ant-design/charts-util": "0.0.1", + "@antv/event-emitter": "^0.1.3", + "@antv/g": "^6.1.7", + "@antv/g2": "^5.2.7", + "@antv/g2-extension-plot": "^0.2.1", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": ">=16.8.4", + "react-dom": ">=16.8.4" + } + }, + "node_modules/@ant-design/plots/node_modules/@ant-design/charts-util": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/charts-util/-/charts-util-0.0.1.tgz", + "integrity": "sha512-zz9aCD8z90gzLm3XK17jyFdVtmpLrFApvexzIl5n9+TrxvIgrmOIqemlvx6QvzkmmXcOA6VIEJGzqQBSBAq55A==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": ">=16.8.4", + "react-dom": ">=16.8.4" + } + }, + "node_modules/@ant-design/pro-card": { + "version": "2.9.7", + "resolved": "https://registry.npmjs.org/@ant-design/pro-card/-/pro-card-2.9.7.tgz", + "integrity": "sha512-uDDYowmYH1ldRfG8Mb4QOwcEEz6ptRBQDLO1tkVADCRkdOMwz82xlZneR4uVuFyKcuNmgHzarYNncozBKhFuaA==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-utils": "2.17.0", + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.4.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-components": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/@ant-design/pro-components/-/pro-components-2.8.7.tgz", + "integrity": "sha512-QhibkPsUJryEjI1QmwUn+XCngGHidu0ekvricL6TIEvPgP+AUAca29XutN5+Mmn8Xfja1ca9HFTHTgFoV74Z7Q==", + "license": "MIT", + "dependencies": { + "@ant-design/pro-card": "2.9.7", + "@ant-design/pro-descriptions": "2.6.7", + "@ant-design/pro-field": "3.0.4", + "@ant-design/pro-form": "2.31.7", + "@ant-design/pro-layout": "7.22.4", + "@ant-design/pro-list": "2.6.7", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-skeleton": "2.2.1", + "@ant-design/pro-table": "3.19.0", + "@ant-design/pro-utils": "2.17.0", + "@babel/runtime": "^7.16.3" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-descriptions": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@ant-design/pro-descriptions/-/pro-descriptions-2.6.7.tgz", + "integrity": "sha512-fgn2d0kDWUODGDWKpgziZuuqPlmIoKxQFJY9Yg4nbaRp8GDDKZeSSqgvW+OxjpYM8dxq31fiz1dZlZnOPoYKpg==", + "license": "MIT", + "dependencies": { + "@ant-design/pro-field": "3.0.4", + "@ant-design/pro-form": "2.31.7", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-skeleton": "2.2.1", + "@ant-design/pro-utils": "2.17.0", + "@babel/runtime": "^7.18.0", + "rc-resize-observer": "^0.2.3", + "rc-util": "^5.0.6" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-descriptions/node_modules/rc-resize-observer": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-0.2.6.tgz", + "integrity": "sha512-YX6nYnd6fk7zbuvT6oSDMKiZjyngjHoy+fz+vL3Tez38d/G5iGdaDJa2yE7345G6sc4Mm1IGRUIwclvltddhmA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-util": "^5.0.0", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/pro-field": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@ant-design/pro-field/-/pro-field-3.0.4.tgz", + "integrity": "sha512-nJSng/6/pPZFdiFeTtZcBQLNrHg9tIeiKFR1+zzbnQbI3qBOFP9aBZS/+LwkQZcI2G71vrRgz2x5OhHb7AX0wQ==", + "license": "MIT", + "dependencies": { + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-utils": "2.17.0", + "@babel/runtime": "^7.18.0", + "@chenshuai2144/sketch-color": "^1.0.8", + "classnames": "^2.3.2", + "dayjs": "^1.11.10", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "rc-util": "^5.4.0", + "swr": "^2.0.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-form": { + "version": "2.31.7", + "resolved": "https://registry.npmjs.org/@ant-design/pro-form/-/pro-form-2.31.7.tgz", + "integrity": "sha512-0TCtIC/ynbLPoes8sLBFwFbi0tkeNmSU6the2EcyKIKDLfWHDbfkLM1OSFrzv3QD+H8OgFWMkTSOjhMOKSsOBg==", + "license": "MIT", + "dependencies": { + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-field": "3.0.4", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-utils": "2.17.0", + "@babel/runtime": "^7.18.0", + "@chenshuai2144/sketch-color": "^1.0.7", + "@umijs/use-params": "^1.0.9", + "classnames": "^2.3.2", + "dayjs": "^1.11.10", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.0.6" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "rc-field-form": ">=1.22.0", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-layout": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@ant-design/pro-layout/-/pro-layout-7.22.4.tgz", + "integrity": "sha512-X2WO4L2itXemX4zhS+0NG+8kXQD5SX9sG+zjx/15BmIO3FvsUGqOHgoCg0vhd424EiyPj7WtdMZJ39G1xdgDwA==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-utils": "2.17.0", + "@babel/runtime": "^7.18.0", + "@umijs/route-utils": "^4.0.0", + "@umijs/use-params": "^1.0.9", + "classnames": "^2.3.2", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "path-to-regexp": "8.2.0", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.0.6", + "swr": "^2.0.0", + "warning": "^4.0.3" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-layout/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/@ant-design/pro-list": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@ant-design/pro-list/-/pro-list-2.6.7.tgz", + "integrity": "sha512-6k/En7pioMgepho/1HMf2DAnkSTZiat1lDg2ggCok2lhSgqXzir7x22ewJQRgPvEiVb6/qqaFQNd7a8dnrFj1w==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-card": "2.9.7", + "@ant-design/pro-field": "3.0.4", + "@ant-design/pro-table": "3.19.0", + "@ant-design/pro-utils": "2.17.0", + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "dayjs": "^1.11.10", + "rc-resize-observer": "^1.0.0", + "rc-util": "^4.19.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-list/node_modules/rc-util": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.21.1.tgz", + "integrity": "sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==", + "license": "MIT", + "dependencies": { + "add-dom-event-listener": "^1.1.0", + "prop-types": "^15.5.10", + "react-is": "^16.12.0", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.1.0" + } + }, + "node_modules/@ant-design/pro-provider": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/@ant-design/pro-provider/-/pro-provider-2.15.4.tgz", + "integrity": "sha512-DBX0JNUNOYXAucVqd/zTdqtXckCDqr2Lo85KIku2YzWdhptDPDZRTNqL04JShjGejDl8fzwQ8yREHgVUfzn6Gg==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@babel/runtime": "^7.18.0", + "@ctrl/tinycolor": "^3.4.0", + "dayjs": "^1.11.10", + "rc-util": "^5.0.1", + "swr": "^2.0.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-skeleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ant-design/pro-skeleton/-/pro-skeleton-2.2.1.tgz", + "integrity": "sha512-3M2jNOZQZWEDR8pheY00OkHREfb0rquvFZLCa6DypGmiksiuuYuR9Y4iA82ZF+mva2FmpHekdwbje/GpbxqBeg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-table": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@ant-design/pro-table/-/pro-table-3.19.0.tgz", + "integrity": "sha512-nL25734d5q5oqtmG7Apn2TNJUnJE8m9dkopXMQdoNZnv8qeRQLBH+i5cZT1yh7FIO8z6QLXleg+KnR/cI7VRRw==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.21.1", + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-card": "2.9.7", + "@ant-design/pro-field": "3.0.4", + "@ant-design/pro-form": "2.31.7", + "@ant-design/pro-provider": "2.15.4", + "@ant-design/pro-utils": "2.17.0", + "@babel/runtime": "^7.18.0", + "@dnd-kit/core": "^6.0.8", + "@dnd-kit/modifiers": "^6.0.1", + "@dnd-kit/sortable": "^7.0.2", + "@dnd-kit/utilities": "^3.2.1", + "classnames": "^2.3.2", + "dayjs": "^1.11.10", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.0.1" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "rc-field-form": ">=1.22.0", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@ant-design/pro-utils": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@ant-design/pro-utils/-/pro-utils-2.17.0.tgz", + "integrity": "sha512-hHKUISjMEoS+E5ltJWyvNTrlEA3IimZNxtDrEhorRIbgVYAlmEN5Mj/ESSofzDM3+UlxiI5+A/Y6IHkByTfDEA==", + "license": "MIT", + "dependencies": { + "@ant-design/icons": "^5.0.0", + "@ant-design/pro-provider": "2.15.4", + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "dayjs": "^1.11.10", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "rc-util": "^5.0.6", + "safe-stable-stringify": "^2.4.3", + "swr": "^2.0.0" + }, + "peerDependencies": { + "antd": "^4.24.15 || ^5.11.2", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, "node_modules/@ant-design/react-slick": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz", @@ -186,6 +560,488 @@ "react": ">=16.9.0" } }, + "node_modules/@antv/algorithm": { + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/@antv/algorithm/-/algorithm-0.1.26.tgz", + "integrity": "sha512-DVhcFSQ8YQnMNW34Mk8BSsfc61iC1sAnmcfYoXTAshYHuU50p/6b7x3QYaGctDNKWGvi1ub7mPcSY0bK+aN0qg==", + "license": "MIT", + "dependencies": { + "@antv/util": "^2.0.13", + "tslib": "^2.0.0" + } + }, + "node_modules/@antv/algorithm/node_modules/@antv/util": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.17.tgz", + "integrity": "sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q==", + "license": "ISC", + "dependencies": { + "csstype": "^3.0.8", + "tslib": "^2.0.3" + } + }, + "node_modules/@antv/component": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@antv/component/-/component-2.1.3.tgz", + "integrity": "sha512-TDePMyrx6rvOeUizcKwrNfSb4vL/hcIsIY01dTD1nXKpR3eDf/q558wN4zGH2NmgX/4TOes7UgSH4iCrpB0eMg==", + "license": "MIT", + "dependencies": { + "@antv/g": "^6.1.11", + "@antv/scale": "^0.4.16", + "@antv/util": "^3.3.10", + "svg-path-parser": "^1.1.0" + } + }, + "node_modules/@antv/coord": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@antv/coord/-/coord-0.4.7.tgz", + "integrity": "sha512-UTbrMLhwJUkKzqJx5KFnSRpU3BqrdLORJbwUbHK2zHSCT3q3bjcFA//ZYLVfIlwqFDXp/hzfMyRtp0c77A9ZVA==", + "license": "MIT", + "dependencies": { + "@antv/scale": "^0.4.12", + "@antv/util": "^2.0.13", + "gl-matrix": "^3.4.3" + } + }, + "node_modules/@antv/coord/node_modules/@antv/util": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.17.tgz", + "integrity": "sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q==", + "license": "ISC", + "dependencies": { + "csstype": "^3.0.8", + "tslib": "^2.0.3" + } + }, + "node_modules/@antv/event-emitter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@antv/event-emitter/-/event-emitter-0.1.3.tgz", + "integrity": "sha512-4ddpsiHN9Pd4UIlWuKVK1C4IiZIdbwQvy9i7DUSI3xNJ89FPUFt8lxDYj8GzzfdllV0NkJTRxnG+FvLk0llidg==", + "license": "MIT" + }, + "node_modules/@antv/expr": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@antv/expr/-/expr-1.0.2.tgz", + "integrity": "sha512-vrfdmPHkTuiS5voVutKl2l06w1ihBh9A8SFdQPEE+2KMVpkymzGOF1eWpfkbGZ7tiFE15GodVdhhHomD/hdIwg==", + "license": "MIT" + }, + "node_modules/@antv/g": { + "version": "6.1.25", + "resolved": "https://registry.npmjs.org/@antv/g/-/g-6.1.25.tgz", + "integrity": "sha512-qkXztWRVYQDl/x3tlA9Oww5DwaBCDDYXq6Wai9jfO8TZeIV3T8Dbw5eG/M115doyHX2vIVRkrE6+xiFe5weIHQ==", + "license": "MIT", + "dependencies": { + "@antv/g-camera-api": "2.0.38", + "@antv/g-dom-mutation-observer-api": "2.0.35", + "@antv/g-lite": "2.2.19", + "@antv/g-web-animations-api": "2.1.25", + "@babel/runtime": "^7.25.6" + } + }, + "node_modules/@antv/g-camera-api": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/@antv/g-camera-api/-/g-camera-api-2.0.38.tgz", + "integrity": "sha512-BgFkUMcTO06Oz37Z+hVqxATwdWFE5DfBgMKlFaMwKKF/8n+7eNhlif1KBfcf2rEfGijS0FD0ZGKCr9uJ06+GIg==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-canvas": { + "version": "2.0.44", + "resolved": "https://registry.npmjs.org/@antv/g-canvas/-/g-canvas-2.0.44.tgz", + "integrity": "sha512-nsV+CErhptyAKQg+5g8RlW6N2oGTn53uUaNu/q6F41gyZm7oL1nHwxI12mbBCGMUlI0JVHsmEIOw5tJ3frkUFg==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/g-plugin-canvas-path-generator": "2.1.19", + "@antv/g-plugin-canvas-picker": "2.1.23", + "@antv/g-plugin-canvas-renderer": "2.2.23", + "@antv/g-plugin-dom-interaction": "2.1.24", + "@antv/g-plugin-html-renderer": "2.1.24", + "@antv/g-plugin-image-loader": "2.1.23", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-dom-mutation-observer-api": { + "version": "2.0.35", + "resolved": "https://registry.npmjs.org/@antv/g-dom-mutation-observer-api/-/g-dom-mutation-observer-api-2.0.35.tgz", + "integrity": "sha512-bAl3ViXDHvLEbGvGZwZBg4gpoNjUTwVQ3XTmRAkymkFGkUy+KV0ZwFdqEegP25TQGPl85er/hB6MCu6Yt58AJA==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@babel/runtime": "^7.25.6" + } + }, + "node_modules/@antv/g-lite": { + "version": "2.2.19", + "resolved": "https://registry.npmjs.org/@antv/g-lite/-/g-lite-2.2.19.tgz", + "integrity": "sha512-QfxZsbLGTSGL18NgSOAVQURXC3xMXbmmS125EF7/vCzW2Lw2nF5I8k0KW4N09ty+/FtVpSESJX652g2phIvd5g==", + "license": "MIT", + "dependencies": { + "@antv/g-math": "3.0.1", + "@antv/util": "^3.3.5", + "@antv/vendor": "^1.0.3", + "@babel/runtime": "^7.25.6", + "eventemitter3": "^5.0.1", + "gl-matrix": "^3.4.3", + "rbush": "^3.0.1", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-lite/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/@antv/g-math": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@antv/g-math/-/g-math-3.0.1.tgz", + "integrity": "sha512-FvkDBNRpj+HsLINunrL2PW0OlG368MlpHuihbxleuajGim5kra8tgISwCLmAf8Yz2b1CgZ9PvpohqiLzHS7HLg==", + "license": "MIT", + "dependencies": { + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-plugin-canvas-path-generator": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/@antv/g-plugin-canvas-path-generator/-/g-plugin-canvas-path-generator-2.1.19.tgz", + "integrity": "sha512-+tc97NLvVYEFQnrLffmyxPpVXwUuTPbXBGy3aUTBYKd3YXhFBIKJYpQR39jsX2skgUvLh/67ZtA9QeUt6U41oQ==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/g-math": "3.0.1", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-plugin-canvas-picker": { + "version": "2.1.23", + "resolved": "https://registry.npmjs.org/@antv/g-plugin-canvas-picker/-/g-plugin-canvas-picker-2.1.23.tgz", + "integrity": "sha512-ADA8Newb+w3wCVWLGWP9EqOb2HjAEOj992L2ywC6Wz3uPNp72dLK2YtKfqm6dApEh8htQ9u0QrnS1tGA3kgrcA==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/g-math": "3.0.1", + "@antv/g-plugin-canvas-path-generator": "2.1.19", + "@antv/g-plugin-canvas-renderer": "2.2.23", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-plugin-canvas-renderer": { + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/@antv/g-plugin-canvas-renderer/-/g-plugin-canvas-renderer-2.2.23.tgz", + "integrity": "sha512-v/XDy0vSy4RvMUdI6fwB2UpdmbnJIf7ixBe9dFJMfH4Ue3I6EDRBRgFRGFIwcTo4EhTlUG1woX1mo4Nwc91Adw==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/g-math": "3.0.1", + "@antv/g-plugin-canvas-path-generator": "2.1.19", + "@antv/g-plugin-image-loader": "2.1.23", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-plugin-dom-interaction": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@antv/g-plugin-dom-interaction/-/g-plugin-dom-interaction-2.1.24.tgz", + "integrity": "sha512-1IrsUp2k+4oi2brVNstgxoisdwcdwqSNdEYJBDtVP1Bv5KZabKSs9lxlkxVR0DTb8BJtWBi80gmKQFIJ8znofQ==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@babel/runtime": "^7.25.6", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-plugin-dragndrop": { + "version": "2.0.35", + "resolved": "https://registry.npmjs.org/@antv/g-plugin-dragndrop/-/g-plugin-dragndrop-2.0.35.tgz", + "integrity": "sha512-1ZG+j91uEQAiFN0UqRkYCx3G8WWlKYoCXgTTx6m4YFJESJiab5M1C4OAi7zXclt1maOR154x3L/j3sRmBHFA+A==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-plugin-html-renderer": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@antv/g-plugin-html-renderer/-/g-plugin-html-renderer-2.1.24.tgz", + "integrity": "sha512-UPEitSu5F42kRgqy8Cr34aC6O4+0cCnC+avv0ZMXUFOf7AMhMnjQLlHHo+GDfM/0r6m//0ZCsqHpv8vB0A+sUA==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-plugin-image-loader": { + "version": "2.1.23", + "resolved": "https://registry.npmjs.org/@antv/g-plugin-image-loader/-/g-plugin-image-loader-2.1.23.tgz", + "integrity": "sha512-LHTESl8BE6GO2EdaTehrCj2V82y4lQ13lFOvImQOI1JzZ/9PJ/vrStMzN1bg/CCqmJn07eVHlqxW9QJAQOOCzA==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-plugin-svg-picker": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/@antv/g-plugin-svg-picker/-/g-plugin-svg-picker-2.0.38.tgz", + "integrity": "sha512-9XuT3VRFtUrdhMYmib7uB/sjXG9orQ7yGzIwYp+mCI734mnmJApOrB+J3UcSt3s+1PAIcABQkHT1MRxFII2w7w==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/g-plugin-svg-renderer": "2.2.20", + "@babel/runtime": "^7.25.6", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-plugin-svg-renderer": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/@antv/g-plugin-svg-renderer/-/g-plugin-svg-renderer-2.2.20.tgz", + "integrity": "sha512-HjLyQMcMm/kRVhwkmdEkWGGZAHUhIuyztOzO0dzWucfGqXsusNZvKHpiWUMl3DBm6ID6qziYCRw5IIpqlsh3Jw==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-svg": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/@antv/g-svg/-/g-svg-2.0.38.tgz", + "integrity": "sha512-S11RB+4Yh3nel+wHChcbB4TlaJFyKl4gP9sUUgUzwgWiAFNxiHU4fM3+sb3f4AQyToIZZd1sqH0TscQ3psX5Yg==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/g-plugin-dom-interaction": "2.1.24", + "@antv/g-plugin-svg-picker": "2.0.38", + "@antv/g-plugin-svg-renderer": "2.2.20", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-web-animations-api": { + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/@antv/g-web-animations-api/-/g-web-animations-api-2.1.25.tgz", + "integrity": "sha512-xljNU+mDsdaDr+DwP77te2ZkNLcLiwuwppwXuRRpv/wVxUue726c/QbfYj/wMwJoBcOEtl/5hjAks/+gdvr3ag==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.2.19", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g2": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@antv/g2/-/g2-5.3.3.tgz", + "integrity": "sha512-K+Pf1ZRslGn2IHQzA+2NrukeaNqrpOZB76zytkmt5bhGOhZgSWSfc9ubxi0OAlrBY+Yc6DfYcLiHziuASYoG5w==", + "license": "MIT", + "dependencies": { + "@antv/component": "^2.1.2", + "@antv/coord": "^0.4.7", + "@antv/event-emitter": "^0.1.3", + "@antv/expr": "^1.0.2", + "@antv/g": "^6.1.23", + "@antv/g-canvas": "^2.0.42", + "@antv/g-plugin-dragndrop": "^2.0.34", + "@antv/scale": "^0.4.16", + "@antv/util": "^3.3.10", + "@antv/vendor": "^1.0.8", + "flru": "^1.0.2", + "pdfast": "^0.2.0" + } + }, + "node_modules/@antv/g2-extension-plot": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@antv/g2-extension-plot/-/g2-extension-plot-0.2.2.tgz", + "integrity": "sha512-KJXCXO7as+h0hDqirGXf1omrNuYzQmY3VmBmp7lIvkepbQ7sz3pPwy895r1FWETGF3vTk5UeFcAF5yzzBHWgbw==", + "dependencies": { + "@antv/g2": "^5.1.8", + "@antv/util": "^3.3.5", + "@antv/vendor": "^1.0.10" + } + }, + "node_modules/@antv/g6": { + "version": "5.0.48", + "resolved": "https://registry.npmjs.org/@antv/g6/-/g6-5.0.48.tgz", + "integrity": "sha512-ngACp0NTJE1Dg03myB6Tqj0iVALiwEl83sul+xFqwASxjmw9dv3UeGa6tFGDp+4QvzXSh5GM8SlbTMTlfgEMnQ==", + "license": "MIT", + "dependencies": { + "@antv/algorithm": "^0.1.26", + "@antv/component": "^2.1.3", + "@antv/event-emitter": "^0.1.3", + "@antv/g": "^6.1.24", + "@antv/g-canvas": "^2.0.43", + "@antv/g-plugin-dragndrop": "^2.0.35", + "@antv/graphlib": "^2.0.4", + "@antv/hierarchy": "^0.6.14", + "@antv/layout": "1.2.14-beta.9", + "@antv/util": "^3.3.10", + "bubblesets-js": "^2.3.4" + } + }, + "node_modules/@antv/g6-extension-react": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@antv/g6-extension-react/-/g6-extension-react-0.2.3.tgz", + "integrity": "sha512-CUEeMSqC6B1oZN3Sfq8hN3rWRCt4JhEF32Sqa/RXv7sRnJzLU9o98XqN2DjFgsQ8CpKN/MkRiR73/9zkJBGOMg==", + "license": "MIT", + "dependencies": { + "@antv/g": "^6.1.24", + "@antv/g-svg": "^2.0.38" + }, + "peerDependencies": { + "@antv/g6": "^5.0.47", + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@antv/graphin": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@antv/graphin/-/graphin-3.0.5.tgz", + "integrity": "sha512-V/j8R8Ty44wUqxVIYLdpPuIO8WWCTIVq1eBJg5YRunL5t5o5qAFpC/qkQxslbBMWyKdIH0oWBnvHA74riGi7cw==", + "license": "MIT", + "dependencies": { + "@antv/g6": "^5.0.28" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.1.0", + "react-dom": "^18.0.0 || ^19.1.0" + } + }, + "node_modules/@antv/graphlib": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@antv/graphlib/-/graphlib-2.0.4.tgz", + "integrity": "sha512-zc/5oQlsdk42Z0ib1mGklwzhJ5vczLFiPa1v7DgJkTbgJ2YxRh9xdarf86zI49sKVJmgbweRpJs7Nu5bIiwv4w==", + "license": "MIT", + "dependencies": { + "@antv/event-emitter": "^0.1.3" + } + }, + "node_modules/@antv/hierarchy": { + "version": "0.6.14", + "resolved": "https://registry.npmjs.org/@antv/hierarchy/-/hierarchy-0.6.14.tgz", + "integrity": "sha512-V3uknf7bhynOqQDw2sg+9r9DwZ9pc6k/EcqyTFdfXB1+ydr7urisP0MipIuimucvQKN+Qkd+d6w601r1UIroqQ==", + "license": "MIT" + }, + "node_modules/@antv/layout": { + "version": "1.2.14-beta.9", + "resolved": "https://registry.npmjs.org/@antv/layout/-/layout-1.2.14-beta.9.tgz", + "integrity": "sha512-wPlwBFMtq2lWZFc89/7Lzb8fjHnyKVZZ9zBb2h+zZIP0YWmVmHRE8+dqCiPKOyOGUXEdDtn813f1g107dCHZlg==", + "license": "MIT", + "dependencies": { + "@antv/event-emitter": "^0.1.3", + "@antv/graphlib": "^2.0.0", + "@antv/util": "^3.3.2", + "@naoak/workerize-transferable": "^0.1.0", + "comlink": "^4.4.1", + "d3-force": "^3.0.0", + "d3-force-3d": "^3.0.5", + "d3-octree": "^1.0.2", + "d3-quadtree": "^3.0.1", + "dagre": "^0.8.5", + "ml-matrix": "^6.10.4", + "tslib": "^2.5.0" + } + }, + "node_modules/@antv/scale": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@antv/scale/-/scale-0.4.16.tgz", + "integrity": "sha512-5wg/zB5kXHxpTV5OYwJD3ja6R8yTiqIOkjOhmpEJiowkzRlbEC/BOyMvNUq5fqFIHnMCE9woO7+c3zxEQCKPjw==", + "license": "MIT", + "dependencies": { + "@antv/util": "^3.3.7", + "color-string": "^1.5.5", + "fecha": "^4.2.1" + } + }, + "node_modules/@antv/util": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@antv/util/-/util-3.3.10.tgz", + "integrity": "sha512-basGML3DFA3O87INnzvDStjzS+n0JLEhRnRsDzP9keiXz8gT1z/fTdmJAZFOzMMWxy+HKbi7NbSt0+8vz/OsBQ==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "gl-matrix": "^3.3.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@antv/vendor": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@antv/vendor/-/vendor-1.0.11.tgz", + "integrity": "sha512-LmhPEQ+aapk3barntaiIxJ5VHno/Tyab2JnfdcPzp5xONh/8VSfed4bo/9xKo5HcUAEydko38vYLfj6lJliLiw==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.2.1", + "@types/d3-color": "^3.1.3", + "@types/d3-dispatch": "^3.0.6", + "@types/d3-dsv": "^3.0.7", + "@types/d3-ease": "^3.0.2", + "@types/d3-fetch": "^3.0.7", + "@types/d3-force": "^3.0.10", + "@types/d3-format": "^3.0.4", + "@types/d3-geo": "^3.1.0", + "@types/d3-hierarchy": "^3.1.7", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-path": "^3.1.0", + "@types/d3-quadtree": "^3.0.6", + "@types/d3-random": "^3.0.3", + "@types/d3-scale": "^4.0.9", + "@types/d3-scale-chromatic": "^3.1.0", + "@types/d3-shape": "^3.1.7", + "@types/d3-time": "^3.0.4", + "@types/d3-timer": "^3.0.2", + "d3-array": "^3.2.4", + "d3-color": "^3.1.0", + "d3-dispatch": "^3.0.1", + "d3-dsv": "^3.0.1", + "d3-ease": "^3.0.1", + "d3-fetch": "^3.0.1", + "d3-force": "^3.0.0", + "d3-force-3d": "^3.0.5", + "d3-format": "^3.1.0", + "d3-geo": "^3.1.1", + "d3-geo-projection": "^4.0.0", + "d3-hierarchy": "^3.1.2", + "d3-interpolate": "^3.0.1", + "d3-path": "^3.1.0", + "d3-quadtree": "^3.0.1", + "d3-random": "^3.0.1", + "d3-regression": "^1.3.10", + "d3-scale": "^4.0.2", + "d3-scale-chromatic": "^3.1.0", + "d3-shape": "^3.2.0", + "d3-time": "^3.1.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -2158,6 +3014,19 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "license": "MIT" }, + "node_modules/@chenshuai2144/sketch-color": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@chenshuai2144/sketch-color/-/sketch-color-1.0.9.tgz", + "integrity": "sha512-obzSy26cb7Pm7OprWyVpgMpIlrZpZ0B7vbrU0RMbvRg0YAI890S5Xy02Aj1Nhl4+KTbi1lVYHt6HQP8Hm9s+1w==", + "license": "MIT", + "dependencies": { + "reactcss": "^1.2.3", + "tinycolor2": "^1.4.2" + }, + "peerDependencies": { + "react": ">=16.12.0" + } + }, "node_modules/@chevrotain/cst-dts-gen": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", @@ -2477,6 +3346,15 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", @@ -2487,6 +3365,73 @@ "node": ">=14.17.0" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/modifiers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-6.0.1.tgz", + "integrity": "sha512-rbxcsg3HhzlcMHVHWDuh9LCjpOVAgqbV78wLGI8tziXY3+qcMQ61qVXIvNKQFuhj75dSfD+o+PYZQ/NUk2A23A==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.1", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.0.6", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-7.0.2.tgz", + "integrity": "sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.0", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.0.7", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", @@ -3343,6 +4288,15 @@ "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "license": "MIT" }, + "node_modules/@naoak/workerize-transferable": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@naoak/workerize-transferable/-/workerize-transferable-0.1.0.tgz", + "integrity": "sha512-fDLfuP71IPNP5+zSfxFb52OHgtjZvauRJWbVnpzQ7G7BjcbLjTny0OW1d3ZO806XKpLWNKmeeW3MhE0sy8iwYQ==", + "license": "MIT", + "peerDependencies": { + "workerize-loader": "*" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -4928,6 +5882,144 @@ "@types/node": "*" } }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", + "license": "MIT" + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/eslint": { "version": "8.56.12", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", @@ -4954,6 +6046,15 @@ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/express": { "version": "4.17.22", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.22.tgz", @@ -4990,6 +6091,12 @@ "@types/send": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -4999,6 +6106,15 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -5056,12 +6172,27 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "license": "MIT" }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "license": "MIT" }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.15.29", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", @@ -5212,6 +6343,12 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT" }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/webxr": { "version": "0.5.22", "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.22.tgz", @@ -5638,6 +6775,21 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@umijs/route-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@umijs/route-utils/-/route-utils-4.0.1.tgz", + "integrity": "sha512-+1ixf1BTOLuH+ORb4x8vYMPeIt38n9q0fJDwhv9nSxrV46mxbLF0nmELIo9CKQB2gHfuC4+hww6xejJ6VYnBHQ==", + "license": "MIT" + }, + "node_modules/@umijs/use-params": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@umijs/use-params/-/use-params-1.0.9.tgz", + "integrity": "sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", @@ -5943,6 +7095,15 @@ "node": ">=0.4.0" } }, + "node_modules/add-dom-event-listener": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz", + "integrity": "sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==", + "license": "MIT", + "dependencies": { + "object-assign": "4.x" + } + }, "node_modules/address": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", @@ -6796,6 +7957,16 @@ "babel-plugin-transform-react-remove-prop-types": "^0.4.24" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -7147,6 +8318,12 @@ "node-int64": "^0.4.0" } }, + "node_modules/bubblesets-js": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/bubblesets-js/-/bubblesets-js-2.3.4.tgz", + "integrity": "sha512-DyMjHmpkS2+xcFNtyN00apJYL3ESdp9fTrkDr5+9Qg/GPqFmcWgGsK1akZnttE1XFxJ/VMy4DNNGMGYtmFp1Sg==", + "license": "MIT" + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -7358,6 +8535,16 @@ "node": ">=4" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7399,6 +8586,46 @@ "node": ">=10" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/check-types": { "version": "11.2.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", @@ -7688,6 +8915,16 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "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", @@ -7712,6 +8949,22 @@ "node": ">= 0.8" } }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==", + "license": "Apache-2.0" + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", @@ -8049,6 +9302,12 @@ "node": ">=10" } }, + "node_modules/css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==", + "license": "BSD" + }, "node_modules/css-minimizer-webpack-plugin": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", @@ -8340,6 +9599,322 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-binarytree": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d3-binarytree/-/d3-binarytree-1.0.2.tgz", + "integrity": "sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==", + "license": "MIT" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force-3d": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/d3-force-3d/-/d3-force-3d-3.0.6.tgz", + "integrity": "sha512-4tsKHUPLOVkyfEffZo1v6sFHvGFwAIIjt/W8IThbp08DYAsXZck+2pSHEG5W1+gQgEvFLdZkYvmJAbRM2EzMnA==", + "license": "MIT", + "dependencies": { + "d3-binarytree": "1", + "d3-dispatch": "1 - 3", + "d3-octree": "1", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo-projection": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz", + "integrity": "sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==", + "license": "ISC", + "dependencies": { + "commander": "7", + "d3-array": "1 - 3", + "d3-geo": "1.12.0 - 3" + }, + "bin": { + "geo2svg": "bin/geo2svg.js", + "geograticule": "bin/geograticule.js", + "geoproject": "bin/geoproject.js", + "geoquantize": "bin/geoquantize.js", + "geostitch": "bin/geostitch.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo-projection/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-octree": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-1.1.0.tgz", + "integrity": "sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A==", + "license": "MIT" + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-regression": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/d3-regression/-/d3-regression-1.3.10.tgz", + "integrity": "sha512-PF8GWEL70cHHWpx2jUQXc68r1pyPHIA+St16muk/XRokETzlegj5LriNKg7o4LR0TySug4nHYPJNNRz/W+/Niw==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz", + "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", + "license": "MIT", + "dependencies": { + "graphlib": "^2.1.8", + "lodash": "^4.17.15" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -8449,6 +10024,19 @@ "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", "license": "MIT" }, + "node_modules/decode-named-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz", + "integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -8553,6 +10141,15 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -8610,6 +10207,19 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -10135,6 +11745,16 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", @@ -10290,6 +11910,12 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10399,6 +12025,12 @@ "bser": "2.1.1" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, "node_modules/fflate": { "version": "0.6.10", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", @@ -10608,6 +12240,15 @@ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "license": "ISC" }, + "node_modules/flru": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flru/-/flru-1.0.2.tgz", + "integrity": "sha512-kWyh8ADvHBFz6ua5xYOPnUroZTT/bwWfrCeL0Wj1dzG4/YOmOcfJ99W8dOVyyynJN35rZ9aCOtHChqQovV7yog==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", @@ -11032,6 +12673,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==", + "license": "MIT" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -11178,6 +12825,15 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "license": "MIT" }, + "node_modules/graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + } + }, "node_modules/gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -11315,6 +12971,46 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -11445,6 +13141,16 @@ "node": ">=12" } }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html-webpack-plugin": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", @@ -11598,6 +13304,12 @@ "node": ">=10.17.0" } }, + "node_modules/hyphenate-style-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", + "license": "BSD-3-Clause" + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -11756,6 +13468,12 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -11770,6 +13488,15 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", @@ -11789,6 +13516,36 @@ "node": ">= 10" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-any-array": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-any-array/-/is-any-array-2.0.1.tgz", + "integrity": "sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ==", + "license": "MIT" + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -11934,6 +13691,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -12021,6 +13788,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -14195,6 +15972,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -14336,6 +16119,16 @@ "node": ">=0.8.0" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -14399,6 +16192,25 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/matchmediaquery": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.4.2.tgz", + "integrity": "sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==", + "license": "MIT", + "dependencies": { + "css-mediaquery": "^0.1.2" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -14408,6 +16220,288 @@ "node": ">= 0.4" } }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -14469,6 +16563,569 @@ "node": ">= 0.6" } }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -14592,6 +17249,45 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/ml-array-max": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/ml-array-max/-/ml-array-max-1.2.4.tgz", + "integrity": "sha512-BlEeg80jI0tW6WaPyGxf5Sa4sqvcyY6lbSn5Vcv44lp1I2GR6AWojfUvLnGTNsIXrZ8uqWmo8VcG1WpkI2ONMQ==", + "license": "MIT", + "dependencies": { + "is-any-array": "^2.0.0" + } + }, + "node_modules/ml-array-min": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/ml-array-min/-/ml-array-min-1.2.3.tgz", + "integrity": "sha512-VcZ5f3VZ1iihtrGvgfh/q0XlMobG6GQ8FsNyQXD3T+IlstDv85g8kfV0xUG1QPRO/t21aukaJowDzMTc7j5V6Q==", + "license": "MIT", + "dependencies": { + "is-any-array": "^2.0.0" + } + }, + "node_modules/ml-array-rescale": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ml-array-rescale/-/ml-array-rescale-1.3.7.tgz", + "integrity": "sha512-48NGChTouvEo9KBctDfHC3udWnQKNKEWN0ziELvY3KG25GR5cA8K8wNVzracsqSW1QEkAXjTNx+ycgAv06/1mQ==", + "license": "MIT", + "dependencies": { + "is-any-array": "^2.0.0", + "ml-array-max": "^1.2.4", + "ml-array-min": "^1.2.3" + } + }, + "node_modules/ml-matrix": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/ml-matrix/-/ml-matrix-6.12.1.tgz", + "integrity": "sha512-TJ+8eOFdp+INvzR4zAuwBQJznDUfktMtOB6g/hUcGh3rcyjxbz4Te57Pgri8Q9bhSQ7Zys4IYOGhFdnlgeB6Lw==", + "license": "MIT", + "dependencies": { + "is-any-array": "^2.0.1", + "ml-array-rescale": "^1.3.7" + } + }, "node_modules/mmd-parser": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.4.tgz", @@ -15116,6 +17812,31 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -15236,6 +17957,12 @@ "node": ">=8" } }, + "node_modules/pdfast": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/pdfast/-/pdfast-0.2.0.tgz", + "integrity": "sha512-cq6TTu6qKSFUHwEahi68k/kqN2mfepjkGrG9Un70cgdRRKLKY6Rf8P8uvP2NvZktaQZNF3YE7agEkLj0vGK9bA==", + "license": "MIT" + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -17017,6 +19744,16 @@ "react-is": "^16.13.1" } }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -17118,6 +19855,12 @@ ], "license": "MIT" }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "license": "ISC" + }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -17172,6 +19915,15 @@ "node": ">=0.10.0" } }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "license": "MIT", + "dependencies": { + "quickselect": "^2.0.0" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -17920,6 +20672,39 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "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-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-reconciler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz", @@ -17954,6 +20739,24 @@ "node": ">=0.10.0" } }, + "node_modules/react-responsive": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-10.0.1.tgz", + "integrity": "sha512-OM5/cRvbtUWEX8le8RCT8scA8y2OPtb0Q/IViEyCEM5FBN8lRrkUOZnu87I88A6njxDldvxG+rLBxWiA7/UM9g==", + "license": "MIT", + "dependencies": { + "hyphenate-style-name": "^1.0.0", + "matchmediaquery": "^0.4.2", + "prop-types": "^15.6.1", + "shallow-equal": "^3.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-router": { "version": "7.6.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.1.tgz", @@ -18174,6 +20977,15 @@ } } }, + "node_modules/reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "license": "MIT", + "dependencies": { + "lodash": "^4.0.1" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -18414,6 +21226,72 @@ "node": ">= 0.10" } }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -18707,6 +21585,12 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -18779,6 +21663,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -19359,6 +22252,12 @@ "node": ">=8" } }, + "node_modules/shallow-equal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz", + "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==", + "license": "MIT" + }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", @@ -19476,6 +22375,21 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -19635,6 +22549,16 @@ "deprecated": "Please use @jridgewell/sourcemap-codec instead", "license": "MIT" }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -20129,6 +23053,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", @@ -20223,6 +23161,24 @@ "webpack": "^5.0.0" } }, + "node_modules/style-to-js": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz", + "integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.8" + } + }, + "node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, "node_modules/styled-components": { "version": "6.1.18", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.18.tgz", @@ -20446,6 +23402,12 @@ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", "license": "MIT" }, + "node_modules/svg-path-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/svg-path-parser/-/svg-path-parser-1.1.0.tgz", + "integrity": "sha512-jGCUqcQyXpfe38R7RFfhrMyfXcBmpMNJI/B+4CE9/Unkh98UporAc461GTthv+TVDuZXsBx7/WiwJb1Oh4tt4A==", + "license": "MIT" + }, "node_modules/svgo": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", @@ -20568,6 +23530,19 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/swr": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", + "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -20857,6 +23832,12 @@ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "license": "MIT" }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -20926,6 +23907,26 @@ "node": ">=8" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -21259,6 +24260,37 @@ "node": ">=4" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -21271,6 +24303,74 @@ "node": ">=8" } }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -21374,6 +24474,15 @@ "react": ">= 16.x" } }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -21467,6 +24576,34 @@ "node": ">=0.10.48" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/virtualizedtableforantd4": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/virtualizedtableforantd4/-/virtualizedtableforantd4-1.3.1.tgz", @@ -21547,6 +24684,15 @@ "makeerror": "1.0.12" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/watchpack": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", @@ -22443,6 +25589,19 @@ "workbox-core": "6.6.0" } }, + "node_modules/workerize-loader": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/workerize-loader/-/workerize-loader-2.0.2.tgz", + "integrity": "sha512-HoZ6XY4sHWxA2w0WpzgBwUiR3dv1oo7bS+oCwIpb6n54MclQ/7KXdXsVIChTCygyuHtVuGBO1+i3HzTt699UJQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loader-utils": "^2.0.0" + }, + "peerDependencies": { + "webpack": "*" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -22632,6 +25791,16 @@ "optional": true } } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index 50d5efe..e1b964e 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,8 @@ "version": "0.1.0", "private": true, "dependencies": { + "@ant-design/charts": "^2.3.0", + "@ant-design/pro-components": "^2.8.7", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@simplewebauthn/browser": "^13.1.0", "@tsparticles/react": "^3.0.0", @@ -27,9 +29,12 @@ "react": "^18.3.1", "react-country-flag": "^3.1.0", "react-dom": "^18.3.1", + "react-markdown": "^10.1.0", + "react-responsive": "^10.0.1", "react-router-dom": "*", "react-scripts": "*", "react-stl-viewer": "^2.5.0", + "remark-gfm": "^4.0.1", "socket.io-client": "*", "standard": "^17.1.2", "styled-components": "*", diff --git a/public/index.html b/public/index.html index aa069f2..362c43f 100644 --- a/public/index.html +++ b/public/index.html @@ -1,4 +1,4 @@ - + @@ -24,7 +24,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - React App + Farm Control diff --git a/src/App.css b/src/App.css index ca5c511..af348a5 100644 --- a/src/App.css +++ b/src/App.css @@ -1,6 +1,18 @@ -body, -.ant-typography { - font-family: 'SF Pro'; +:root { + --unit-100vh: 100vh; +} +@supports (height: 100dvh) { + :root { + --unit-100vh: 100dvh; + } +} + +.ant-menu-overflow-item-rest::after { + border-bottom: none !important; +} + +.ant-menu-overflow-item > div:hover { + background-color: transparent !important; } .App { diff --git a/src/App.jsx b/src/App.jsx index 46e1028..63f8563 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -13,8 +13,8 @@ import Printers from './components/Dashboard/Production/Printers' import ControlPrinter from './components/Dashboard/Production/Printers/ControlPrinter.jsx' import PrinterInfo from './components/Dashboard/Production/Printers/PrinterInfo.jsx' -import PrintJobs from './components/Dashboard/Production/PrintJobs.jsx' -import PrintJobInfo from './components/Dashboard/Production/PrintJobs/PrintJobInfo.jsx' +import Jobs from './components/Dashboard/Production/Jobs.jsx' +import JobInfo from './components/Dashboard/Production/Jobs/JobInfo.jsx' import Filaments from './components/Dashboard/Management/Filaments' import FilamentInfo from './components/Dashboard/Management/Filaments/FilamentInfo.jsx' @@ -48,12 +48,17 @@ import { SocketProvider } from './components/Dashboard/context/SocketContext.js' import { AuthProvider } from './components/Dashboard/context/AuthContext.js' import { SpotlightProvider } from './components/Dashboard/context/SpotlightContext.js' import StockEvents from './components/Dashboard/Inventory/StockEvents.jsx' + import Settings from './components/Dashboard/Management/Settings' +import AuditLogs from './components/Dashboard/Management/AuditLogs.jsx' + import { ThemeProvider, useThemeContext } from './components/Dashboard/context/ThemeContext' import AppError from './components/App/AppError' +import NoteTypes from './components/Dashboard/Management/NoteTypes.jsx' +import NoteTypeInfo from './components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx' const AppContent = () => { const { themeConfig } = useThemeContext() @@ -97,14 +102,8 @@ const AppContent = () => { path='production/printers/info' element={} /> - } - /> - } - /> + } /> + } /> } @@ -168,7 +167,19 @@ const AppContent = () => { path='management/materials' element={} /> + } + /> + } + /> } /> + } + /> \ No newline at end of file diff --git a/src/assets/icons/auditlogicon.svg b/src/assets/icons/auditlogicon.svg new file mode 100644 index 0000000..b2ef6c7 --- /dev/null +++ b/src/assets/icons/auditlogicon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icons/inventoryicon.afdesign b/src/assets/icons/inventoryicon.afdesign index eaed37e..d34bf8b 100644 Binary files a/src/assets/icons/inventoryicon.afdesign and b/src/assets/icons/inventoryicon.afdesign differ diff --git a/src/assets/icons/materialicon.afdesign b/src/assets/icons/materialicon.afdesign index 533c24f..f196712 100644 Binary files a/src/assets/icons/materialicon.afdesign and b/src/assets/icons/materialicon.afdesign differ diff --git a/src/assets/icons/materialicon.min.svg b/src/assets/icons/materialicon.min.svg index 95ea81f..b6922f4 100644 --- a/src/assets/icons/materialicon.min.svg +++ b/src/assets/icons/materialicon.min.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/icons/materialicon.svg b/src/assets/icons/materialicon.svg index b875109..6816f53 100644 --- a/src/assets/icons/materialicon.svg +++ b/src/assets/icons/materialicon.svg @@ -1,12 +1,9 @@ - - - - - - - - - - + + + + + + + diff --git a/src/assets/icons/notetypeicon.afdesign b/src/assets/icons/notetypeicon.afdesign new file mode 100644 index 0000000..ce3c6f6 Binary files /dev/null and b/src/assets/icons/notetypeicon.afdesign differ diff --git a/src/assets/icons/notetypeicon.min.svg b/src/assets/icons/notetypeicon.min.svg new file mode 100644 index 0000000..20acc9a --- /dev/null +++ b/src/assets/icons/notetypeicon.min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/notetypeicon.svg b/src/assets/icons/notetypeicon.svg new file mode 100644 index 0000000..cac3e53 --- /dev/null +++ b/src/assets/icons/notetypeicon.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/icons/settingsicon.afdesign b/src/assets/icons/settingsicon.afdesign new file mode 100644 index 0000000..30c9625 Binary files /dev/null and b/src/assets/icons/settingsicon.afdesign differ diff --git a/src/assets/icons/settingsicon.min.svg b/src/assets/icons/settingsicon.min.svg new file mode 100644 index 0000000..d4ba9f0 --- /dev/null +++ b/src/assets/icons/settingsicon.min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/settingsicon.svg b/src/assets/icons/settingsicon.svg new file mode 100644 index 0000000..219aba6 --- /dev/null +++ b/src/assets/icons/settingsicon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/icons/stockeventicon.afdesign b/src/assets/icons/stockeventicon.afdesign index 76fb424..ecb86a9 100644 Binary files a/src/assets/icons/stockeventicon.afdesign and b/src/assets/icons/stockeventicon.afdesign differ diff --git a/src/assets/logos/farmcontrollogosmall.afdesign b/src/assets/logos/farmcontrollogosmall.afdesign new file mode 100644 index 0000000..c71ef80 Binary files /dev/null and b/src/assets/logos/farmcontrollogosmall.afdesign differ diff --git a/src/assets/logos/farmcontrollogosmall.svg b/src/assets/logos/farmcontrollogosmall.svg new file mode 100644 index 0000000..1dc52ec --- /dev/null +++ b/src/assets/logos/farmcontrollogosmall.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/components/App/AppError.jsx b/src/components/App/AppError.jsx index 0fbf09c..39d5c17 100644 --- a/src/components/App/AppError.jsx +++ b/src/components/App/AppError.jsx @@ -32,7 +32,7 @@ const AppError = ({ > - + { + const [isVisible, setIsVisible] = useState(false) + + useEffect(() => { + const timer = setTimeout(() => { + setIsVisible(true) + }, 1000) + + return () => clearTimeout(timer) + }, []) + return ( - <> - - +
- - - - - - } - showIcon - /> - - + + + + + + + + } + showIcon + /> + +
+ ) } diff --git a/src/components/Dashboard/Inventory/FilamentStocks.jsx b/src/components/Dashboard/Inventory/FilamentStocks.jsx index 68baf6e..276bd16 100644 --- a/src/components/Dashboard/Inventory/FilamentStocks.jsx +++ b/src/components/Dashboard/Inventory/FilamentStocks.jsx @@ -1,24 +1,19 @@ // src/filamentStocks.js -import React, { useEffect, useState, useContext, useCallback } from 'react' +import React, { useState, useContext, useRef } from 'react' import { useNavigate } from 'react-router-dom' -import axios from 'axios' import { - Table, Button, Flex, Space, Modal, - message, Dropdown, Typography, - Popover, + message, Checkbox, - Input, - Spin + Popover, + Input } from 'antd' -import { createStyles } from 'antd-style' -import { LoadingOutlined } from '@ant-design/icons' import { AuthContext } from '../context/AuthContext' import { SocketContext } from '../context/SocketContext' @@ -34,49 +29,20 @@ import TimeDisplay from '../common/TimeDisplay' import XMarkIcon from '../../Icons/XMarkIcon' import CheckIcon from '../../Icons/CheckIcon' import useColumnVisibility from '../hooks/useColumnVisibility' +import DashboardTable from '../common/DashboardTable' import config from '../../../config' const { Text } = Typography -const useStyle = createStyles(({ css, token }) => { - const { antCls } = token - return { - customTable: css` - ${antCls}-table { - ${antCls}-table-container { - ${antCls}-table-body, - ${antCls}-table-content { - scrollbar-width: thin; - scrollbar-color: #eaeaea transparent; - scrollbar-gutter: stable; - } - } - } - ` - } -}) - const FilamentStocks = () => { const [messageApi, contextHolder] = message.useMessage() const navigate = useNavigate() - const { styles } = useStyle() const { socket } = useContext(SocketContext) - - const [filamentStocksData, setFilamentStocksData] = useState([]) - const [page, setPage] = useState(1) - const [hasMore, setHasMore] = useState(true) - const [loading, setLoading] = useState(true) - const [lazyLoading, setLazyLoading] = useState(false) - - const [filters, setFilters] = useState({}) - const [sorter, setSorter] = useState({ - field: 'createdAt', - order: 'descend' - }) + const [initialized, setInitialized] = useState(false) + const tableRef = useRef() const [newFilamentStockOpen, setNewFilamentStockOpen] = useState(false) - const [initialized, setInitialized] = useState(false) const { authenticated } = useContext(AuthContext) @@ -116,135 +82,6 @@ const FilamentStocks = () => { ) } - const fetchFilamentStocksData = useCallback( - async (pageNum = 1, append = false) => { - try { - const response = await axios.get( - `${config.backendUrl}/filamentstocks`, - { - params: { - page: pageNum, - limit: 25, - ...filters, - sort: sorter.field, - order: sorter.order - }, - headers: { - Accept: 'application/json' - }, - withCredentials: true - } - ) - - const newData = response.data - setHasMore(newData.length === 25) - - if (append) { - setFilamentStocksData((prev) => [...prev, ...newData]) - } else { - setFilamentStocksData(newData) - } - - setLoading(false) - setLazyLoading(false) - } catch (err) { - messageApi.error('Error fetching filament stocks:', err) - setLoading(false) - setLazyLoading(false) - } - }, - [messageApi, filters, sorter] - ) - - useEffect(() => { - if (authenticated) { - fetchFilamentStocksData() - } - }, [authenticated, fetchFilamentStocksData]) - - useEffect(() => { - if (socket && !initialized) { - setInitialized(true) - socket.on('notify_filamentstock_update', (updateData) => { - console.log('Received filament stock update:', updateData) - setFilamentStocksData((prevData) => { - return prevData.map((stock) => { - if (stock._id === updateData._id) { - return { - ...stock, - ...updateData - } - } - return stock - }) - }) - }) - } - - return () => { - if (socket && initialized) { - console.log('Deregistering filament stock update listener') - socket.off('notify_filamentstock_update') - } - } - }, [socket, initialized]) - - const getFilamentStockActionItems = (id) => { - return { - items: [ - { - label: 'Info', - key: 'info', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'info') { - navigate( - `/dashboard/inventory/filamentstocks/info?filamentStockId=${id}` - ) - } - } - } - } - - const handleScroll = useCallback( - (e) => { - const { target } = e - const scrollHeight = target.scrollHeight - const scrollTop = target.scrollTop - const clientHeight = target.clientHeight - - if ( - scrollHeight - scrollTop - clientHeight < 100 && - !lazyLoading && - hasMore - ) { - setLazyLoading(true) - const nextPage = page + 1 - setPage(nextPage) - fetchFilamentStocksData(nextPage, true) - } - }, - [page, lazyLoading, hasMore, fetchFilamentStocksData] - ) - - const handleTableChange = (pagination, filters, sorter) => { - const newFilters = {} - Object.entries(filters).forEach(([key, value]) => { - if (value && value.length > 0) { - newFilters[key] = value[0] - } - }) - setPage(1) - setFilters(newFilters) - setSorter({ - field: sorter.field, - order: sorter.order - }) - fetchFilamentStocksData(1) - } - // Column definitions const columns = [ { @@ -281,7 +118,7 @@ const FilamentStocks = () => { title: 'ID', dataIndex: '_id', key: 'id', - width: 165, + width: 180, render: (text) => ( ) @@ -366,6 +203,72 @@ const FilamentStocks = () => { } ] + const [columnVisibility, updateColumnVisibility] = useColumnVisibility( + 'FilamentStocks', + columns + ) + + React.useEffect(() => { + if (socket && !initialized) { + setInitialized(true) + socket.on('notify_filamentstock_update', (updateData) => { + console.log('Received filament stock update:', updateData) + if (tableRef.current) { + tableRef.current.updateData(updateData._id, updateData) + } + }) + } + + return () => { + if (socket && initialized) { + console.log('Deregistering filament stock update listener') + socket.off('notify_filamentstock_update') + } + } + }, [socket, initialized]) + + const getFilamentStockActionItems = (id) => { + return { + items: [ + { + label: 'Info', + key: 'info', + icon: + } + ], + onClick: ({ key }) => { + if (key === 'info') { + navigate( + `/dashboard/inventory/filamentstocks/info?filamentStockId=${id}` + ) + } + } + } + } + + const actionItems = { + items: [ + { + label: 'New Filament Stock', + key: 'newFilamentStock', + icon: + }, + { type: 'divider' }, + { + label: 'Reload List', + key: 'reloadList', + icon: + } + ], + onClick: ({ key }) => { + if (key === 'reloadList') { + tableRef.current?.reload() + } else if (key === 'newFilamentStock') { + setNewFilamentStockOpen(true) + } + } + } + const getViewDropdownItems = () => { const columnItems = columns .filter((col) => col.key && col.title !== '') @@ -390,39 +293,10 @@ const FilamentStocks = () => { ) } - const [columnVisibility, updateColumnVisibility] = useColumnVisibility( - 'FilamentStocks', - columns - ) - const visibleColumns = columns.filter( (col) => !col.key || columnVisibility[col.key] ) - const actionItems = { - items: [ - { - label: 'New Filament Stock', - key: 'newFilamentStock', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - setPage(1) - fetchFilamentStocksData(1) - } else if (key === 'newFilamentStock') { - setNewFilamentStockOpen(true) - } - } - } - return ( <> @@ -440,19 +314,13 @@ const FilamentStocks = () => { - {lazyLoading && } />} - }} - scroll={{ y: 'calc(100vh - 270px)' }} - onScroll={handleScroll} - onChange={handleTableChange} - showSorterTooltip={false} + url={`${config.backendUrl}/filamentstocks`} + authenticated={authenticated} /> { onCancel={() => { setNewFilamentStockOpen(false) }} - destroyOnClose + destroyOnHidden={true} > { setNewFilamentStockOpen(false) messageApi.success('New filament stock created successfully.') - fetchFilamentStocksData() + tableRef.current?.reload() }} reset={newFilamentStockOpen} /> diff --git a/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx b/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx index 9fd4349..9971d81 100644 --- a/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx +++ b/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx @@ -10,7 +10,8 @@ import { Typography, Form, Badge, - Collapse + Collapse, + Flex } from 'antd' import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons' import IdText from '../../common/IdText' @@ -132,155 +133,158 @@ const FilamentStockInfo = () => { return (
{contextHolder} - updateCollapseState('info', keys.length > 0)} - expandIcon={({ isActive }) => ( - - )} - className='no-h-padding-collapse no-t-padding-collapse' - > - - Filament Stock Information - - } - key='1' + + updateCollapseState('info', keys.length > 0)} + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse no-t-padding-collapse' > -
+ Filament Stock Information + + } + key='1' > - - {/* Read-only fields */} - - {filamentStockData.id ? ( - - ) : ( - 'n/a' - )} - - - - - - - - - - - - - - - {filamentStockData.filament ? ( - - - - - ) : ( - 'n/a' - )} - - - - {filamentStockData.filament ? ( - + {/* Read-only fields */} + + {filamentStockData.id ? ( + + ) : ( + 'n/a' + )} + + + - ) : ( - 'n/a' - )} - - - {filamentStockData.currentGrossWeight ? ( - - - {filamentStockData.currentNetWeight.toFixed(2) + 'g'} - - - {filamentStockData.currentGrossWeight.toFixed(2) + 'g'} - - - ) : ( - 'n/a' - )} - - - {filamentStockData.startingGrossWeight ? ( - + + + + + + + + + + + + {filamentStockData.filament ? ( + + + + + ) : ( + 'n/a' + )} + + + + {filamentStockData.filament ? ( + + ) : ( + 'n/a' + )} + + + {filamentStockData.currentGrossWeight ? ( - {filamentStockData.startingNetWeight.toFixed(2) + 'g'} + {filamentStockData.currentNetWeight.toFixed(2) + 'g'} - {filamentStockData.startingGrossWeight.toFixed(2) + 'g'} + {filamentStockData.currentGrossWeight.toFixed(2) + 'g'} - - ) : ( - 'n/a' - )} - - - -
-
+ ) : ( + 'n/a' + )} + + + {filamentStockData.startingGrossWeight ? ( + + + + {filamentStockData.startingNetWeight.toFixed(2) + 'g'} + + + {filamentStockData.startingGrossWeight.toFixed(2) + + 'g'} + + + + ) : ( + 'n/a' + )} + + + + + - updateCollapseState('events', keys.length > 0)} - expandIcon={({ isActive }) => ( - - )} - className='no-h-padding-collapse' - > - - Filament Stock Events - - } - key='2' + updateCollapseState('events', keys.length > 0)} + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse' > - - - + + Filament Stock Events + + } + key='2' + > + + + +
) } diff --git a/src/components/Dashboard/Inventory/FilamentStocks/LoadFilamentStock.jsx b/src/components/Dashboard/Inventory/FilamentStocks/LoadFilamentStock.jsx index 6c878a7..cb3598d 100644 --- a/src/components/Dashboard/Inventory/FilamentStocks/LoadFilamentStock.jsx +++ b/src/components/Dashboard/Inventory/FilamentStocks/LoadFilamentStock.jsx @@ -9,6 +9,7 @@ import { Descriptions, Alert } from 'antd' +import { useMediaQuery } from 'react-responsive' import PropTypes from 'prop-types' import { SocketContext } from '../../context/SocketContext' @@ -28,6 +29,8 @@ const LoadFilamentStock = ({ printer = null, filamentStockLoaded = false }) => { + const isMobile = useMediaQuery({ maxWidth: 768 }) + LoadFilamentStock.propTypes = { onOk: PropTypes.func.isRequired, reset: PropTypes.bool.isRequired, @@ -266,16 +269,18 @@ const LoadFilamentStock = ({ return ( -
- -
+ {!isMobile && ( +
+ +
+ )} - + {!isMobile && } diff --git a/src/components/Dashboard/Inventory/FilamentStocks/UnloadFilamentStock.jsx b/src/components/Dashboard/Inventory/FilamentStocks/UnloadFilamentStock.jsx index db06633..215880f 100644 --- a/src/components/Dashboard/Inventory/FilamentStocks/UnloadFilamentStock.jsx +++ b/src/components/Dashboard/Inventory/FilamentStocks/UnloadFilamentStock.jsx @@ -1,5 +1,6 @@ import React, { useState, useContext, useEffect } from 'react' import { Form, Button, Typography, Flex, Steps, Divider, Alert } from 'antd' +import { useMediaQuery } from 'react-responsive' import PropTypes from 'prop-types' import { SocketContext } from '../../context/SocketContext' @@ -18,6 +19,7 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => { } const { socket } = useContext(SocketContext) + const isMobile = useMediaQuery({ maxWidth: 768 }) const initialUnloadFilamentStockForm = { printer: printer @@ -194,16 +196,18 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => { return ( <Flex gap={'middle'}> - <div style={{ minWidth: '160px' }}> - <Steps - current={currentStep} - items={steps} - direction='vertical' - style={{ width: 'fit-content' }} - /> - </div> + {!isMobile && ( + <div style={{ minWidth: '160px' }}> + <Steps + current={currentStep} + items={steps} + direction='vertical' + style={{ width: 'fit-content' }} + /> + </div> + )} - <Divider type={'vertical'} style={{ height: 'unset' }} /> + {!isMobile && <Divider type={'vertical'} style={{ height: 'unset' }} />} <Flex vertical={'true'} style={{ flexGrow: 1 }} gap='middle'> <Title level={2} style={{ marginTop: 0, marginBottom: 4 }}> diff --git a/src/components/Dashboard/Inventory/PartStocks.jsx b/src/components/Dashboard/Inventory/PartStocks.jsx index 49086e9..ec7f42c 100644 --- a/src/components/Dashboard/Inventory/PartStocks.jsx +++ b/src/components/Dashboard/Inventory/PartStocks.jsx @@ -1,20 +1,8 @@ // src/partStocks.js -import React, { useEffect, useState, useContext, useCallback } from 'react' +import React, { useState, useContext, useRef } from 'react' import { useNavigate } from 'react-router-dom' -import axios from 'axios' -import { - Table, - Button, - Flex, - Space, - Modal, - message, - Dropdown, - Typography -} from 'antd' -import { createStyles } from 'antd-style' -import { LoadingOutlined } from '@ant-design/icons' +import { Button, Flex, Space, Modal, message, Dropdown, Typography } from 'antd' import { AuthContext } from '../context/AuthContext' @@ -26,64 +14,21 @@ import PlusIcon from '../../Icons/PlusIcon' import ReloadIcon from '../../Icons/ReloadIcon' import PartStockState from '../common/PartStockState' import TimeDisplay from '../common/TimeDisplay' +import DashboardTable from '../common/DashboardTable' import config from '../../../config' const { Text } = Typography -const useStyle = createStyles(({ css, token }) => { - const { antCls } = token - return { - customTable: css` - ${antCls}-table { - ${antCls}-table-container { - ${antCls}-table-body, - ${antCls}-table-content { - scrollbar-width: thin; - scrollbar-color: #eaeaea transparent; - scrollbar-gutter: stable; - } - } - } - ` - } -}) - const PartStocks = () => { const [messageApi, contextHolder] = message.useMessage() const navigate = useNavigate() - const { styles } = useStyle() - - const [partStocksData, setPartStocksData] = useState([]) + const tableRef = useRef() const [newPartStockOpen, setNewPartStockOpen] = useState(false) - const [loading, setLoading] = useState(true) - const { authenticated } = useContext(AuthContext) - const fetchPartStocksData = useCallback(async () => { - try { - const response = await axios.get(`${config.backendUrl}/partstocks`, { - headers: { - Accept: 'application/json' - }, - withCredentials: true // Important for including cookies - }) - setPartStocksData(response.data) - setLoading(false) - } catch (err) { - messageApi.info(err) - } - }, [messageApi]) - - useEffect(() => { - // Fetch initial data - if (authenticated) { - fetchPartStocksData() - } - }, [authenticated, fetchPartStocksData]) - const getPartStockActionItems = (id) => { return { items: [ @@ -123,7 +68,7 @@ const PartStocks = () => { title: 'ID', dataIndex: '_id', key: 'id', - width: 165, + width: 180, render: (text) => <IdText id={text} type={'partstock'} longId={false} /> }, { @@ -213,7 +158,7 @@ const PartStocks = () => { ], onClick: ({ key }) => { if (key === 'reloadList') { - fetchPartStocksData() + tableRef.current?.reload() } else if (key === 'newPartStock') { setNewPartStockOpen(true) } @@ -229,14 +174,11 @@ const PartStocks = () => { <Button>Actions</Button> </Dropdown> </Space> - <Table - dataSource={partStocksData} - className={styles.customTable} + <DashboardTable + ref={tableRef} columns={columns} - pagination={false} - rowKey='_id' - loading={{ spinning: loading, indicator: <LoadingOutlined spin /> }} - scroll={{ y: 'calc(100vh - 270px)' }} + url={`${config.backendUrl}/partstocks`} + authenticated={authenticated} /> </Flex> <Modal @@ -247,13 +189,13 @@ const PartStocks = () => { onCancel={() => { setNewPartStockOpen(false) }} - destroyOnClose + destroyOnHidden={true} > <NewPartStock onOk={() => { setNewPartStockOpen(false) messageApi.success('New part stock created successfully.') - fetchPartStocksData() + tableRef.current?.reload() }} reset={newPartStockOpen} /> diff --git a/src/components/Dashboard/Inventory/StockAudits.jsx b/src/components/Dashboard/Inventory/StockAudits.jsx index 7ccc85c..2febd96 100644 --- a/src/components/Dashboard/Inventory/StockAudits.jsx +++ b/src/components/Dashboard/Inventory/StockAudits.jsx @@ -1,10 +1,6 @@ -import React, { useEffect, useState, useContext, useCallback } from 'react' +import React, { useState, useContext, useRef } from 'react' import { useNavigate } from 'react-router-dom' -import axios from 'axios' - -import { Table, Button, Flex, Space, message, Dropdown, Typography } from 'antd' -import { createStyles } from 'antd-style' -import { LoadingOutlined } from '@ant-design/icons' +import { Button, Flex, Space, message, Dropdown, Typography } from 'antd' import { AuthContext } from '../context/AuthContext' import { SocketContext } from '../context/SocketContext' @@ -15,78 +11,29 @@ import InfoCircleIcon from '../../Icons/InfoCircleIcon' import PlusIcon from '../../Icons/PlusIcon' import ReloadIcon from '../../Icons/ReloadIcon' import TimeDisplay from '../common/TimeDisplay' +import DashboardTable from '../common/DashboardTable' import config from '../../../config' const { Text } = Typography -const useStyle = createStyles(({ css, token }) => { - const { antCls } = token - return { - customTable: css` - ${antCls}-table { - ${antCls}-table-container { - ${antCls}-table-body, - ${antCls}-table-content { - scrollbar-width: thin; - scrollbar-color: #eaeaea transparent; - scrollbar-gutter: stable; - } - } - } - ` - } -}) - const StockAudits = () => { const [messageApi, contextHolder] = message.useMessage() const navigate = useNavigate() - const { styles } = useStyle() const { socket } = useContext(SocketContext) - - const [stockAuditsData, setStockAuditsData] = useState([]) - const [loading, setLoading] = useState(true) const [initialized, setInitialized] = useState(false) + const tableRef = useRef() const { authenticated } = useContext(AuthContext) - const fetchStockAuditsData = useCallback(async () => { - try { - const response = await axios.get(`${config.backendUrl}/stockaudits`, { - headers: { - Accept: 'application/json' - }, - withCredentials: true - }) - setStockAuditsData(response.data) - setLoading(false) - } catch (err) { - messageApi.info(err) - } - }, [messageApi]) - - useEffect(() => { - if (authenticated) { - fetchStockAuditsData() - } - }, [authenticated, fetchStockAuditsData]) - - useEffect(() => { + React.useEffect(() => { if (socket && !initialized) { setInitialized(true) socket.on('notify_stockaudit_update', (updateData) => { console.log('Received stock audit update:', updateData) - setStockAuditsData((prevData) => { - return prevData.map((audit) => { - if (audit._id === updateData._id) { - return { - ...audit, - ...updateData - } - } - return audit - }) - }) + if (tableRef.current) { + tableRef.current.updateData(updateData._id, updateData) + } }) } @@ -128,7 +75,7 @@ const StockAudits = () => { title: 'ID', dataIndex: '_id', key: 'id', - width: 165, + width: 180, render: (text) => <IdText id={text} type={'stockaudit'} longId={false} /> }, { @@ -203,7 +150,7 @@ const StockAudits = () => { ], onClick: ({ key }) => { if (key === 'reloadList') { - fetchStockAuditsData() + tableRef.current?.reload() } else if (key === 'newStockAudit') { // TODO: Implement new stock audit creation messageApi.info('New stock audit creation not implemented yet') @@ -220,14 +167,11 @@ const StockAudits = () => { <Button>Actions</Button> </Dropdown> </Space> - <Table - dataSource={stockAuditsData} - className={styles.customTable} + <DashboardTable + ref={tableRef} columns={columns} - pagination={false} - rowKey='_id' - loading={{ spinning: loading, indicator: <LoadingOutlined spin /> }} - scroll={{ y: 'calc(100vh - 270px)' }} + url={`${config.backendUrl}/stockaudits`} + authenticated={authenticated} /> </Flex> </> diff --git a/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx b/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx index 12abd6d..eada020 100644 --- a/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx +++ b/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx @@ -103,7 +103,7 @@ const StockAuditInfo = () => { title: 'Item ID', dataIndex: '_id', key: 'id', - width: 165, + width: 180, render: (text) => ( <IdText id={text} type={'stockaudititem'} longId={false} /> ) diff --git a/src/components/Dashboard/Inventory/StockEvents.jsx b/src/components/Dashboard/Inventory/StockEvents.jsx index 8b762a7..36605d7 100644 --- a/src/components/Dashboard/Inventory/StockEvents.jsx +++ b/src/components/Dashboard/Inventory/StockEvents.jsx @@ -1,21 +1,15 @@ -import React, { useEffect, useState, useContext, useCallback } from 'react' -import axios from 'axios' - +import React, { useState, useContext, useRef } from 'react' import { Button, Flex, Space, - message, - Spin, Popover, Checkbox, Dropdown, - Table, Typography, Input } from 'antd' -import { createStyles } from 'antd-style' -import { LoadingOutlined, AuditOutlined } from '@ant-design/icons' +import { AuditOutlined } from '@ant-design/icons' import { AuthContext } from '../context/AuthContext' import { SocketContext } from '../context/SocketContext' @@ -28,63 +22,24 @@ import PlayCircleIcon from '../../Icons/PlayCircleIcon' import XMarkIcon from '../../Icons/XMarkIcon' import CheckIcon from '../../Icons/CheckIcon' import useColumnVisibility from '../hooks/useColumnVisibility' +import DashboardTable from '../common/DashboardTable' import config from '../../../config' const { Text } = Typography -const useStyle = createStyles(({ css, token }) => { - const { antCls } = token - return { - customTable: css` - ${antCls}-table { - ${antCls}-table-container { - ${antCls}-table-body, - ${antCls}-table-content { - scrollbar-width: thin; - scrollbar-color: #eaeaea transparent; - scrollbar-gutter: stable; - } - } - } - ` - } -}) - const StockEvents = () => { - const [messageApi, contextHolder] = message.useMessage() - const { styles } = useStyle() const { socket } = useContext(SocketContext) const [initialized, setInitialized] = useState(false) - - // Helper function to convert text to camelCase - const toCamelCase = (text) => { - return text - .toLowerCase() - .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => { - return index === 0 ? word.toLowerCase() : word.toUpperCase() - }) - .replace(/\s+/g, '') - } - - const [stockEventsData, setStockEventsData] = useState([]) - const [page, setPage] = useState(1) - const [hasMore, setHasMore] = useState(true) - const [loading, setLoading] = useState(true) - const [lazyLoading, setLazyLoading] = useState(false) - - const [filters, setFilters] = useState({}) - const [sorter, setSorter] = useState({ - field: 'createdAt', - order: 'descend' - }) + const tableRef = useRef() // Column definitions for visibility const columns = [ { title: '', key: 'icon', - width: 50, + width: 40, + fixed: 'left', render: (record) => { switch (record.type.toLowerCase()) { case 'subjob': @@ -103,6 +58,7 @@ const StockEvents = () => { dataIndex: 'type', key: 'type', width: 200, + fixed: 'left', sorter: true, filterDropdown: ({ setSelectedKeys, @@ -118,6 +74,15 @@ const StockEvents = () => { propertyName: 'type' }) }, + { + title: 'ID', + key: 'id', + dataIndex: '_id', + width: 170, + render: (id) => { + return <IdText id={id} longId={false} type={'stockevent'} /> + } + }, { title: <PlusMinusIcon />, dataIndex: 'value', @@ -134,26 +99,17 @@ const StockEvents = () => { } }, { - title: 'Linked ID', - key: 'linkedId', - width: 100, + title: 'Stock ID', + key: 'stockId', + width: 170, render: (record) => { - if (record.subJob?.number) { + if (record.filamentStock?._id) { return ( <IdText - id={record.subJob.number.toString().padStart(6, '0')} + id={record.filamentStock._id} longId={false} - type={'subjob'} - /> - ) - } - if (record.stockAudit) { - return ( - <IdText - id={record.stockAudit._id} - longId={false} - type={'stockaudit'} showHyperlink={true} + type={'filamentstock'} /> ) } @@ -161,21 +117,41 @@ const StockEvents = () => { } }, { - title: 'Job ID', - key: 'jobId', - width: 100, + title: 'Linked IDs', + key: 'linkedIds', + width: 170 * 2, render: (record) => { - if (record.subJob) { - return ( - <IdText - id={record.job} - longId={false} - type={'job'} - showHyperlink={true} - /> - ) + const ids = ( + <Space size={'middle'}> + {record.job ? ( + <IdText + id={record.job} + longId={false} + showHyperlink={true} + type={'job'} + /> + ) : null} + {record.subJob?.number ? ( + <IdText + id={record.subJob.number.toString().padStart(6, '0')} + longId={false} + type={'subjob'} + /> + ) : null} + {record.stockAudit ? ( + <IdText + id={record.stockAudit._id} + longId={false} + type={'stockaudit'} + showHyperlink={true} + /> + ) : null} + </Space> + ) + if (!record.stockAudit && !record.job && !record.subJob) { + return 'n/a' } - return 'n/a' + return ids } }, { @@ -252,79 +228,15 @@ const StockEvents = () => { const { authenticated } = useContext(AuthContext) - const fetchStockEventsData = useCallback( - async (pageNum = 1, append = false) => { - try { - const response = await axios.get(`${config.backendUrl}/stockevents`, { - params: { - page: pageNum, - limit: 25, - type: filters.type, - ...filters, - sort: sorter.field, - order: sorter.order - }, - headers: { - Accept: 'application/json' - }, - withCredentials: true - }) - - const newData = response.data - setHasMore(newData.length === 25) - - if (append) { - setStockEventsData((prev) => [...prev, ...newData]) - } else { - setStockEventsData(newData) - } - - setLoading(false) - setLazyLoading(false) - } catch (error) { - if (error.response) { - messageApi.error( - 'Error fetching stock events:', - error.response.status - ) - } else { - messageApi.error( - 'An unexpected error occurred. Please try again later.' - ) - } - setLoading(false) - setLazyLoading(false) - } - }, - [messageApi, filters, sorter] - ) - - useEffect(() => { - if (authenticated) { - fetchStockEventsData() - } - }, [authenticated, fetchStockEventsData]) - - useEffect(() => { + React.useEffect(() => { // Add WebSocket event listener for real-time updates if (socket && !initialized) { setInitialized(true) socket.on('notify_stockevent_update', (updateData) => { console.log('Received stock event update:', updateData) - setStockEventsData((prevData) => { - return prevData.map((stockEvent) => { - if (stockEvent?._id) { - if (stockEvent._id === updateData._id) { - return { - ...stockEvent, - ...updateData - } - } else { - return stockEvent - } - } - }) - }) + if (tableRef.current) { + tableRef.current.updateData(updateData._id, updateData) + } }) } @@ -336,27 +248,6 @@ const StockEvents = () => { } }, [socket, initialized]) - const handleScroll = useCallback( - (e) => { - const { target } = e - const scrollHeight = target.scrollHeight - const scrollTop = target.scrollTop - const clientHeight = target.clientHeight - - if ( - scrollHeight - scrollTop - clientHeight < 100 && - !lazyLoading && - hasMore - ) { - setLazyLoading(true) - const nextPage = page + 1 - setPage(nextPage) - fetchStockEventsData(nextPage, true) - } - }, - [page, lazyLoading, hasMore, fetchStockEventsData] - ) - const actionItems = { items: [ { @@ -367,34 +258,11 @@ const StockEvents = () => { ], onClick: ({ key }) => { if (key === 'reloadList') { - setPage(1) - fetchStockEventsData(1) + tableRef.current?.reload() } } } - const handleTableChange = (pagination, filters, sorter) => { - const newFilters = {} - Object.entries(filters).forEach(([key, value]) => { - if (value && value.length > 0) { - // Convert type filter to camelCase - if (key === 'type') { - newFilters[key] = toCamelCase(value[0]) - } else { - newFilters[key] = value[0] - } - } - }) - setPage(1) - setFilters(newFilters) - setSorter({ - field: sorter.field, - order: sorter.order - }) - // Trigger a new fetch with the updated filters - fetchStockEventsData(1) - } - const getViewDropdownItems = () => { const columnItems = columns .filter((col) => col.key && col.title !== '') @@ -426,7 +294,6 @@ const StockEvents = () => { return ( <> <Flex vertical={'true'} gap='large'> - {contextHolder} <Flex justify={'space-between'}> <Space size='small'> <Dropdown menu={actionItems}> @@ -440,19 +307,13 @@ const StockEvents = () => { <Button>View</Button> </Popover> </Space> - {lazyLoading && <Spin indicator={<LoadingOutlined />} />} </Flex> - <Table - dataSource={stockEventsData} + + <DashboardTable + ref={tableRef} columns={visibleColumns} - className={styles.customTable} - pagination={false} - scroll={{ y: 'calc(100vh - 270px)' }} - rowKey='_id' - loading={{ spinning: loading, indicator: <LoadingOutlined spin /> }} - onScroll={handleScroll} - onChange={handleTableChange} - showSorterTooltip={false} + url={`${config.backendUrl}/stockevents`} + authenticated={authenticated} /> </Flex> </> diff --git a/src/components/Dashboard/Management/AuditLogs.jsx b/src/components/Dashboard/Management/AuditLogs.jsx new file mode 100644 index 0000000..b3a0bd3 --- /dev/null +++ b/src/components/Dashboard/Management/AuditLogs.jsx @@ -0,0 +1,333 @@ +import React, { useContext, useRef } from 'react' +import { + Button, + Flex, + Space, + Typography, + Popover, + Checkbox, + Dropdown, + Tag, + Descriptions, + Input, + Badge +} from 'antd' + +import { AuthContext } from '../context/AuthContext' +import IdText from '../common/IdText' +import ReloadIcon from '../../Icons/ReloadIcon' +import useColumnVisibility from '../hooks/useColumnVisibility' +import TimeDisplay from '../common/TimeDisplay' +import DashboardTable from '../common/DashboardTable' + +import config from '../../../config' +import AuditLogIcon from '../../Icons/AuditLogIcon' +import XMarkIcon from '../../Icons/XMarkIcon' +import CheckIcon from '../../Icons/CheckIcon' + +const { Text } = Typography + +const formatPropertyName = (name) => { + return name + .replace(/([A-Z])/g, ' $1') + .replace(/^./, (str) => str.toUpperCase()) +} + +const isObjectId = (value) => { + return typeof value === 'string' && /^[0-9a-fA-F]{24}$/.test(value) +} + +const formatValue = (value, propertyName) => { + if (value === null || value === undefined || value === '') { + return <Text type='secondary'>n/a</Text> + } + + // Handle colors specifically + if (propertyName === 'color' && value) { + return <Badge color={value} text={value} /> + } + + if (propertyName === 'state' && typeof value === 'object' && value.type) { + return ( + <Text>{value.type.charAt(0).toUpperCase() + value.type.slice(1)}</Text> + ) + } + + // Check if the value is a timestamp (ISO date string) + if ( + typeof value === 'string' && + /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value) + ) { + return <TimeDisplay dateTime={value} /> + } + + if (typeof value === 'boolean') { + return ( + <Tag color={value ? 'success' : 'error'} style={{ margin: 0 }}> + {value ? 'Yes' : 'No'} + </Tag> + ) + } + + if (isObjectId(value)) { + return ( + <IdText + id={value} + type={propertyName.toLowerCase().replaceAll('current', '')} + longId={false} + showHyperlink={true} + /> + ) + } + + if (typeof value === 'object') { + return <Text>{JSON.stringify(value)}</Text> + } + + return <Text>{value}</Text> +} + +const AuditLogs = () => { + const tableRef = useRef() + + // Column definitions + const columns = [ + { + title: '', + dataIndex: '', + key: '', + width: 40, + fixed: 'left', + render: () => <AuditLogIcon /> + }, + { + title: 'ID', + dataIndex: '_id', + key: 'id', + fixed: 'left', + width: 180, + render: (text) => <IdText id={text} type={'auditlog'} longId={false} />, + filterDropdown: ({ + setSelectedKeys, + selectedKeys, + confirm, + clearFilters + }) => + getFilterDropdown({ + setSelectedKeys, + selectedKeys, + confirm, + clearFilters, + propertyName: 'ID' + }), + onFilter: (value, record) => + record._id.toLowerCase().includes(value.toLowerCase()), + sorter: true + }, + { + title: 'Owner Name', + dataIndex: ['owner', 'name'], + key: 'name', + width: 200, + fixed: 'left', + filterDropdown: ({ + setSelectedKeys, + selectedKeys, + confirm, + clearFilters + }) => + getFilterDropdown({ + setSelectedKeys, + selectedKeys, + confirm, + clearFilters, + propertyName: 'name' + }), + onFilter: (value, record) => + record.owner?.name?.toLowerCase().includes(value.toLowerCase()), + sorter: true + }, + { + title: 'Owner', + key: 'owner', + width: 180, + render: (record) => ( + <IdText + id={record.owner._id} + type={record.ownerModel.toLowerCase()} + longId={false} + showHyperlink={true} + /> + ) + }, + { + title: 'Target', + key: 'target', + width: 180, + render: (record) => ( + <IdText + id={record.target} + type={record.targetModel.toLowerCase()} + longId={false} + showHyperlink={true} + /> + ) + }, + { + title: 'Properties', + dataIndex: 'type', + key: 'type', + width: 550, + render: (_, record) => { + const oldValue = record.oldValue || {} + const newValue = record.newValue || {} + + return ( + <Descriptions size='small' column={1}> + {Object.keys(newValue).map((key) => ( + <Descriptions.Item key={key} label={formatPropertyName(key)}> + <Space> + {formatValue(oldValue[key], key)} + <Text type='secondary'>→</Text> + {formatValue(newValue[key], key)} + </Space> + </Descriptions.Item> + ))} + </Descriptions> + ) + } + }, + { + title: 'Timestamp', + dataIndex: 'createdAt', + key: 'createdAt', + width: 180, + fixed: 'right', + render: (createdAt) => { + if (createdAt) { + return <TimeDisplay dateTime={createdAt} /> + } else { + return 'n/a' + } + }, + sorter: true, + defaultSortOrder: 'descend' + } + ] + + const getFilterDropdown = ({ + setSelectedKeys, + selectedKeys, + confirm, + clearFilters, + propertyName + }) => { + return ( + <div style={{ padding: 8 }}> + <Space.Compact> + <Input + placeholder={'Search ' + propertyName} + value={selectedKeys[0]} + onChange={(e) => + setSelectedKeys(e.target.value ? [e.target.value] : []) + } + onPressEnter={() => confirm()} + style={{ width: 200, display: 'block' }} + /> + <Button + onClick={() => { + clearFilters() + confirm() + }} + icon={<XMarkIcon />} + /> + <Button + type='primary' + onClick={() => confirm()} + icon={<CheckIcon />} + /> + </Space.Compact> + </div> + ) + } + + const [columnVisibility, updateColumnVisibility] = useColumnVisibility( + 'AuditLogs', + columns + ) + + const { authenticated } = useContext(AuthContext) + + const actionItems = { + items: [ + { + label: 'Reload List', + key: 'reloadList', + icon: <ReloadIcon /> + } + ], + onClick: ({ key }) => { + if (key === 'reloadList') { + tableRef.current?.reload() + } + } + } + + const getViewDropdownItems = () => { + const columnItems = columns + .filter((col) => col.key && col.title !== '') + .map((col) => ( + <Checkbox + checked={columnVisibility[col.key]} + key={col.key} + onChange={(e) => { + updateColumnVisibility(col.key, e.target.checked) + }} + > + {col.title} + </Checkbox> + )) + + return ( + <Flex vertical> + <Flex vertical gap='middle' style={{ margin: '4px 8px' }}> + {columnItems} + </Flex> + </Flex> + ) + } + + const visibleColumns = columns.filter( + (col) => !col.key || columnVisibility[col.key] + ) + + return ( + <> + <Flex vertical={'true'} gap='large'> + <Flex justify={'space-between'}> + <Space size='small'> + <Dropdown menu={actionItems}> + <Button>Actions</Button> + </Dropdown> + <Popover + content={getViewDropdownItems()} + placement='bottomLeft' + arrow={false} + > + <Button>View</Button> + </Popover> + </Space> + </Flex> + + <DashboardTable + ref={tableRef} + columns={visibleColumns} + url={`${config.backendUrl}/auditlogs`} + authenticated={authenticated} + /> + </Flex> + </> + ) +} + +export default AuditLogs diff --git a/src/components/Dashboard/Management/Filaments.jsx b/src/components/Dashboard/Management/Filaments.jsx index 03dec51..05f7a97 100644 --- a/src/components/Dashboard/Management/Filaments.jsx +++ b/src/components/Dashboard/Management/Filaments.jsx @@ -282,7 +282,7 @@ const Filaments = () => { title: 'ID', dataIndex: '_id', key: 'id', - width: 165, + width: 180, render: (text) => <IdText id={text} type={'filament'} longId={false} />, filterDropdown: ({ setSelectedKeys, @@ -493,7 +493,7 @@ const Filaments = () => { onCancel={() => { setNewFilamentOpen(false) }} - destroyOnClose + destroyOnHidden={true} > <NewFilament onOk={() => { diff --git a/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx b/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx index ee3c092..9980bf5 100644 --- a/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx +++ b/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx @@ -34,7 +34,7 @@ import useCollapseState from '../../hooks/useCollapseState' import config from '../../../../config.js' -const { Title, Link } = Typography +const { Title, Link, Text } = Typography const FilamentInfo = () => { const [filamentData, setFilamentData] = useState(null) @@ -211,296 +211,333 @@ const FilamentInfo = () => { return ( <div style={{ height: '100%', minHeight: 0, overflowY: 'auto' }}> {contextHolder} - <Collapse - ghost - collapsible='icon' - activeKey={collapseState.info ? ['1'] : []} - onChange={(keys) => updateCollapseState('info', keys.length > 0)} - expandIcon={({ isActive }) => ( - <CaretRightOutlined - rotate={isActive ? 90 : 0} - style={{ paddingTop: '9px' }} - /> - )} - className='no-h-padding-collapse no-t-padding-collapse' - > - <Collapse.Panel - header={ - <Flex - align='center' - justify='space-between' - style={{ width: '100%' }} - > - <Title level={5} style={{ margin: 0 }}> - Filament Information - - - + + {currentStep < steps.length - 1 ? ( + + ) : ( + + )} + +
+ + ) +} + +NewNoteType.propTypes = { + onOk: PropTypes.func.isRequired, + reset: PropTypes.bool +} + +export default NewNoteType diff --git a/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx b/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx new file mode 100644 index 0000000..1b7b8e0 --- /dev/null +++ b/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx @@ -0,0 +1,402 @@ +import React, { useState, useEffect } from 'react' +import { useLocation } from 'react-router-dom' +import axios from 'axios' +import { + Descriptions, + Spin, + Space, + Button, + message, + Typography, + Flex, + Form, + Input, + Collapse, + Switch, + ColorPicker, + Checkbox, + Tag, + Dropdown, + Popover, + Badge +} from 'antd' +import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons' +import IdText from '../../common/IdText' +import TimeDisplay from '../../common/TimeDisplay' +import ReloadIcon from '../../../Icons/ReloadIcon' +import EditIcon from '../../../Icons/EditIcon.jsx' +import XMarkIcon from '../../../Icons/XMarkIcon.jsx' +import CheckIcon from '../../../Icons/CheckIcon.jsx' +import useCollapseState from '../../hooks/useCollapseState' +import AuditLogTable from '../../common/AuditLogTable' + +import config from '../../../../config.js' + +const { Title } = Typography + +const NoteTypeInfo = () => { + const [noteTypeData, setNoteTypeData] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const location = useLocation() + const [messageApi, contextHolder] = message.useMessage() + const noteTypeId = new URLSearchParams(location.search).get('noteTypeId') + const [isEditing, setIsEditing] = useState(false) + const [form] = Form.useForm() + const [colorEnabled, setColorEnabled] = useState(false) + const [collapseState, updateCollapseState] = useCollapseState( + 'NoteTypeInfo', + { + info: true, + auditLogs: true + } + ) + + useEffect(() => { + if (noteTypeId) { + fetchNoteTypeDetails() + } + }, [noteTypeId]) + + useEffect(() => { + if (noteTypeData) { + form.setFieldsValue({ + name: noteTypeData.name || '', + color: noteTypeData.color || '#000000', + active: noteTypeData.active ?? true + }) + setColorEnabled(!!noteTypeData.color) + } + }, [noteTypeData, form]) + + const fetchNoteTypeDetails = async () => { + try { + setLoading(true) + const response = await axios.get( + `${config.backendUrl}/noteTypes/${noteTypeId}`, + { + headers: { + Accept: 'application/json' + }, + withCredentials: true + } + ) + setNoteTypeData(response.data) + setError(null) + } catch (err) { + setError('Failed to fetch note type details') + messageApi.error('Failed to fetch note type details') + } finally { + setLoading(false) + } + } + + const startEditing = () => { + updateCollapseState('info', true) + setIsEditing(true) + } + + const cancelEditing = () => { + form.setFieldsValue({ + name: noteTypeData?.name || '', + color: noteTypeData?.color || '#000000', + active: noteTypeData?.active ?? true + }) + setIsEditing(false) + } + + const updateInfo = async () => { + try { + const values = await form.validateFields() + setLoading(true) + + await axios.put(`${config.backendUrl}/noteTypes/${noteTypeId}`, values, { + headers: { + 'Content-Type': 'application/json' + }, + withCredentials: true + }) + + setNoteTypeData({ ...noteTypeData, ...values }) + setIsEditing(false) + messageApi.success('Note type information updated successfully') + } catch (err) { + if (err.errorFields) { + return + } + console.error('Failed to update note type information:', err) + messageApi.error('Failed to update note type information') + } finally { + fetchNoteTypeDetails() + setLoading(false) + } + } + + const getViewDropdownItems = () => { + const sections = [ + { key: 'info', label: 'Note Type Information' }, + { key: 'auditLogs', label: 'Audit Logs' } + ] + + return ( + + + {sections.map((section) => ( + { + updateCollapseState(section.key, e.target.checked) + }} + > + {section.label} + + ))} + + + ) + } + + const actionItems = { + items: [ + { + label: 'Reload Note Type', + key: 'reload', + icon: + } + ], + onClick: ({ key }) => { + if (key === 'reload') { + fetchNoteTypeDetails() + } + } + } + + return ( + <> + + + + + + + + + + + {isEditing ? ( + <> + + + ) : ( +
+ {contextHolder} + + updateCollapseState('info', keys.length > 0)} + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse no-t-padding-collapse' + > + + + Note Type Information + + + } + key='1' + > + }> +
+ + + + + + + + + + {isEditing ? ( + + + + ) : ( + noteTypeData?.name + )} + + + + + + + {isEditing ? ( + + { + if (color != null) { + return '#' + color.toHex() + } + return null + }} + > + + + { + setColorEnabled(e.target.checked) + if (!e.target.checked) { + form.setFieldValue('color', null) + } else if (e.target.checked) { + form.setFieldValue('color', '#000000') + } + }} + /> + + ) : noteTypeData?.color ? ( + + ) : ( + 'No color set' + )} + + + + {isEditing ? ( + + + + ) : noteTypeData?.active ? ( + Yes + ) : ( + No + )} + + + +
+ + + + + updateCollapseState('auditLogs', keys.length > 0) + } + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse' + > + + Audit Logs + + } + key='2' + > + + + + +
+ )} + + ) +} + +export default NoteTypeInfo diff --git a/src/components/Dashboard/Management/Parts.jsx b/src/components/Dashboard/Management/Parts.jsx index bbeb03a..d923595 100644 --- a/src/components/Dashboard/Management/Parts.jsx +++ b/src/components/Dashboard/Management/Parts.jsx @@ -1,28 +1,24 @@ // src/gcodefiles.js -import React, { useEffect, useState, useContext, useCallback } from 'react' +import React, { useState, useContext, useRef } from 'react' import { useNavigate } from 'react-router-dom' -import axios from 'axios' - import { - Table, Button, Flex, Space, Modal, Dropdown, - message, Typography, - Spin, Checkbox, Popover, - Input + Input, + message } from 'antd' -import { createStyles } from 'antd-style' -import { LoadingOutlined, DownloadOutlined } from '@ant-design/icons' +import { DownloadOutlined } from '@ant-design/icons' import { AuthContext } from '../context/AuthContext' import IdText from '../common/IdText' +import DashboardTable from '../common/DashboardTable' import NewProduct from './Products/NewProduct' import PartIcon from '../../Icons/PartIcon' import InfoCircleIcon from '../../Icons/InfoCircleIcon' @@ -37,35 +33,12 @@ import config from '../../../config' const { Text } = Typography -const useStyle = createStyles(({ css, token }) => { - const { antCls } = token - return { - customTable: css` - ${antCls}-table { - ${antCls}-table-container { - ${antCls}-table-body, - ${antCls}-table-content { - scrollbar-width: thin; - scrollbar-color: #eaeaea transparent; - scrollbar-gutter: stable; - } - } - } - ` - } -}) - const Parts = () => { const [messageApi, contextHolder] = message.useMessage() const navigate = useNavigate() - const { styles } = useStyle() - - const [partsData, setPartsData] = useState([]) - const [page, setPage] = useState(1) - const [hasMore, setHasMore] = useState(true) - const [loading, setLoading] = useState(true) - const [lazyLoading, setLazyLoading] = useState(false) const [newProductOpen, setNewProductOpen] = useState(false) + const tableRef = useRef() + const { authenticated } = useContext(AuthContext) // Column definitions const columns = [ @@ -104,7 +77,7 @@ const Parts = () => { title: 'ID', dataIndex: '_id', key: 'id', - width: 165, + width: 180, render: (text) => }, { @@ -131,7 +104,7 @@ const Parts = () => { { title: 'Product ID', key: 'productId', - width: 165, + width: 180, render: (record) => ( { return 'n/a' } }, - sorter: true, - defaultSortOrder: 'descend' + sorter: true }, { title: 'Updated At', @@ -168,8 +140,7 @@ const Parts = () => { return 'n/a' } }, - sorter: true, - defaultSortOrder: 'descend' + sorter: true }, { title: 'Actions', @@ -196,89 +167,11 @@ const Parts = () => { } ] - const [filters, setFilters] = useState({}) - const [sorter, setSorter] = useState({}) const [columnVisibility, updateColumnVisibility] = useColumnVisibility( 'Parts', columns ) - const { authenticated } = useContext(AuthContext) - - const fetchPartsData = useCallback( - async (pageNum = 1, append = false) => { - try { - const response = await axios.get(`${config.backendUrl}/parts`, { - params: { - page: pageNum, - limit: 25, - ...filters, - sort: sorter.field, - order: sorter.order - }, - headers: { - Accept: 'application/json' - }, - withCredentials: true - }) - - const newData = response.data - setHasMore(newData.length === 25) - - if (append) { - setPartsData((prev) => [...prev, ...newData]) - } else { - setPartsData(newData) - } - - setLoading(false) - setLazyLoading(false) - } catch (error) { - if (error.response) { - messageApi.error( - 'Error updating printer details:', - error.response.status - ) - } else { - messageApi.error( - 'An unexpected error occurred. Please try again later.' - ) - } - setLoading(false) - setLazyLoading(false) - } - }, - [messageApi, filters, sorter] - ) - - useEffect(() => { - if (authenticated) { - fetchPartsData() - } - }, [authenticated, fetchPartsData]) - - const handleScroll = useCallback( - (e) => { - const { target } = e - const scrollHeight = target.scrollHeight - const scrollTop = target.scrollTop - const clientHeight = target.clientHeight - - // If we're near the bottom (within 100px) and not currently loading - if ( - scrollHeight - scrollTop - clientHeight < 100 && - !lazyLoading && - hasMore - ) { - setLazyLoading(true) - const nextPage = page + 1 - setPage(nextPage) - fetchPartsData(nextPage, true) - } - }, - [page, lazyLoading, hasMore, fetchPartsData] - ) - const getPartActionItems = (id) => { return { items: [ @@ -353,29 +246,13 @@ const Parts = () => { ], onClick: ({ key }) => { if (key === 'reloadList') { - setPage(1) - fetchPartsData(1) + tableRef.current?.reload() } else if (key === 'newProduct') { setNewProductOpen(true) } } } - const handleTableChange = (pagination, filters, sorter) => { - const newFilters = {} - Object.entries(filters).forEach(([key, value]) => { - if (value && value.length > 0) { - newFilters[key] = value[0] - } - }) - setPage(1) - setFilters(newFilters) - setSorter({ - field: sorter.field, - order: sorter.order - }) - } - const getViewDropdownItems = () => { const columnItems = columns .filter((col) => col.key && col.title !== '') @@ -421,19 +298,12 @@ const Parts = () => { - {lazyLoading && } />}
-
}} - onScroll={handleScroll} - onChange={handleTableChange} - showSorterTooltip={false} + url={`${config.backendUrl}/parts`} + authenticated={authenticated} /> { onCancel={() => { setNewProductOpen(false) }} - destroyOnClose + destroyOnHidden={true} > { setNewProductOpen(false) - setPage(1) - fetchPartsData(1) + messageApi.success('Product created successfully!') + tableRef.current?.reload() }} reset={newProductOpen} /> diff --git a/src/components/Dashboard/Management/Parts/PartInfo.jsx b/src/components/Dashboard/Management/Parts/PartInfo.jsx index 400fb84..c4aff16 100644 --- a/src/components/Dashboard/Management/Parts/PartInfo.jsx +++ b/src/components/Dashboard/Management/Parts/PartInfo.jsx @@ -208,7 +208,7 @@ const PartInfo = () => { console.error('Failed to update part information:', err) messageApi.error('Failed to update part information') } finally { - fetchPartDetails() + await fetchPartDetails() setLoading(false) } } @@ -238,282 +238,287 @@ const PartInfo = () => { return (
{contextHolder} - updateCollapseState('info', keys.length > 0)} - expandIcon={({ isActive }) => ( - - )} - className='no-h-padding-collapse no-t-padding-collapse' - > - - - Part Information - - - {isEditing ? ( - <> -
) } diff --git a/src/components/Dashboard/Management/Products.jsx b/src/components/Dashboard/Management/Products.jsx index 750a453..170f79e 100644 --- a/src/components/Dashboard/Management/Products.jsx +++ b/src/components/Dashboard/Management/Products.jsx @@ -1,29 +1,25 @@ // src/gcodefiles.js -import React, { useEffect, useState, useContext, useCallback } from 'react' +import React, { useState, useContext, useRef } from 'react' import { useNavigate } from 'react-router-dom' -import axios from 'axios' import { - Table, Button, Flex, Space, Modal, Dropdown, message, - Spin, Tag, Checkbox, Popover, Input } from 'antd' -import { createStyles } from 'antd-style' -import { LoadingOutlined, DownloadOutlined } from '@ant-design/icons' +import { DownloadOutlined } from '@ant-design/icons' import { AuthContext } from '../context/AuthContext' - import IdText from '../common/IdText' import TimeDisplay from '../common/TimeDisplay' +import DashboardTable from '../common/DashboardTable' import NewProduct from './Products/NewProduct' import ProductIcon from '../../Icons/ProductIcon' import InfoCircleIcon from '../../Icons/InfoCircleIcon' @@ -31,120 +27,17 @@ import PlusIcon from '../../Icons/PlusIcon' import ReloadIcon from '../../Icons/ReloadIcon' import XMarkIcon from '../../Icons/XMarkIcon' import CheckIcon from '../../Icons/CheckIcon' - import useColumnVisibility from '../hooks/useColumnVisibility' import config from '../../../config' -const useStyle = createStyles(({ css, token }) => { - const { antCls } = token - return { - customTable: css` - ${antCls}-table { - ${antCls}-table-container { - ${antCls}-table-body, - ${antCls}-table-content { - scrollbar-width: thin; - scrollbar-color: #eaeaea transparent; - scrollbar-gutter: stable; - } - } - } - ` - } -}) - const Products = () => { const [messageApi, contextHolder] = message.useMessage() const navigate = useNavigate() - const { styles } = useStyle() - - const [productsData, setProductsData] = useState([]) - const [page, setPage] = useState(1) - const [hasMore, setHasMore] = useState(true) - const [lazyLoading, setLazyLoading] = useState(false) - const [filters, setFilters] = useState({}) - const [sorter, setSorter] = useState({}) - const [newProductOpen, setNewProductOpen] = useState(false) - - const [loading, setLoading] = useState(true) - + const tableRef = useRef() const { authenticated } = useContext(AuthContext) - const fetchProductsData = useCallback( - async (pageNum = 1, append = false) => { - try { - const response = await axios.get(`${config.backendUrl}/products`, { - params: { - page: pageNum, - limit: 25, - ...filters, - sort: sorter.field, - order: sorter.order - }, - headers: { - Accept: 'application/json' - }, - withCredentials: true - }) - - const newData = response.data - setHasMore(newData.length === 25) - - if (append) { - setProductsData((prev) => [...prev, ...newData]) - } else { - setProductsData(newData) - } - - setLoading(false) - setLazyLoading(false) - } catch (error) { - if (error.response) { - messageApi.error( - 'Error updating printer details:', - error.response.status - ) - } else { - messageApi.error( - 'An unexpected error occurred. Please try again later.' - ) - } - setLoading(false) - setLazyLoading(false) - } - }, - [messageApi, filters, sorter] - ) - - useEffect(() => { - if (authenticated) { - fetchProductsData() - } - }, [authenticated, fetchProductsData]) - - const handleScroll = useCallback( - (e) => { - const { target } = e - const scrollHeight = target.scrollHeight - const scrollTop = target.scrollTop - const clientHeight = target.clientHeight - - if ( - scrollHeight - scrollTop - clientHeight < 100 && - !lazyLoading && - hasMore - ) { - setLazyLoading(true) - const nextPage = page + 1 - setPage(nextPage) - fetchProductsData(nextPage, true) - } - }, - [page, lazyLoading, hasMore, fetchProductsData] - ) - const getProductActionItems = (id) => { return { items: [ @@ -205,7 +98,7 @@ const Products = () => { dataIndex: '_id', key: 'id', fixed: 'left', - width: 165, + width: 180, render: (text) => , filterDropdown: ({ setSelectedKeys, @@ -360,7 +253,7 @@ const Products = () => { ], onClick: ({ key }) => { if (key === 'reloadList') { - fetchProductsData() + tableRef.current?.reload() } else if (key === 'newProduct') { setNewProductOpen(true) } @@ -427,21 +320,6 @@ const Products = () => { ) } - const handleTableChange = (pagination, filters, sorter) => { - const newFilters = {} - Object.entries(filters).forEach(([key, value]) => { - if (value && value.length > 0) { - newFilters[key] = value[0] - } - }) - setPage(1) - setFilters(newFilters) - setSorter({ - field: sorter.field, - order: sorter.order - }) - } - const visibleColumns = columns.filter( (col) => !col.key || columnVisibility[col.key] ) @@ -463,19 +341,12 @@ const Products = () => { - {lazyLoading && } />} -
}} - onScroll={handleScroll} - onChange={handleTableChange} - showSorterTooltip={false} + url={`${config.backendUrl}/products`} + authenticated={authenticated} /> { onCancel={() => { setNewProductOpen(false) }} - destroyOnClose + destroyOnHidden={true} > { setNewProductOpen(false) messageApi.success('Product created successfully!') - fetchProductsData() + tableRef.current?.reload() }} reset={newProductOpen} /> diff --git a/src/components/Dashboard/Management/Products/NewProduct.jsx b/src/components/Dashboard/Management/Products/NewProduct.jsx index e289bc4..fd92cb2 100644 --- a/src/components/Dashboard/Management/Products/NewProduct.jsx +++ b/src/components/Dashboard/Management/Products/NewProduct.jsx @@ -1,6 +1,7 @@ import PropTypes from 'prop-types' import React, { useState, useContext, useEffect, useRef } from 'react' import axios from 'axios' +import { useMediaQuery } from 'react-responsive' import { Input, Button, @@ -65,6 +66,8 @@ const NewProduct = ({ onOk, reset }) => { const { token, authenticated } = useContext(AuthContext) + const isMobile = useMediaQuery({ maxWidth: 768 }) + useEffect(() => { newProductForm .validateFields({ @@ -393,16 +396,18 @@ const NewProduct = ({ onOk, reset }) => { {contextHolder} -
- -
+ {!isMobile && ( +
+ +
+ )} - + {!isMobile && } diff --git a/src/components/Dashboard/Management/Products/ProductInfo.jsx b/src/components/Dashboard/Management/Products/ProductInfo.jsx index af6ebca..955ec6e 100644 --- a/src/components/Dashboard/Management/Products/ProductInfo.jsx +++ b/src/components/Dashboard/Management/Products/ProductInfo.jsx @@ -72,6 +72,7 @@ const ProductInfo = () => { useEffect(() => { async function fetchData() { + console.log('hello') await fetchProductDetails() } if (productId) { @@ -114,7 +115,6 @@ const ProductInfo = () => { console.log(err) messageApi.error('Failed to fetch product details') } finally { - fetchProductDetails() setFetchLoading(false) } } @@ -164,6 +164,7 @@ const ProductInfo = () => { console.error('Failed to update product information:', err) messageApi.error('Failed to update product information') } finally { + await fetchProductDetails() setLoading(false) } } @@ -193,285 +194,290 @@ const ProductInfo = () => { return ( <div style={{ height: '100%', minHeight: 0, overflowY: 'auto' }}> {contextHolder} - <Collapse - ghost - collapsible='icon' - activeKey={collapseState.info ? ['1'] : []} - onChange={(keys) => updateCollapseState('info', keys.length > 0)} - expandIcon={({ isActive }) => ( - <CaretRightOutlined - rotate={isActive ? 90 : 0} - style={{ paddingTop: '9px' }} - /> - )} - className='no-h-padding-collapse no-t-padding-collapse' - > - <Collapse.Panel - header={ - <Flex - align='center' - justify='space-between' - style={{ width: '100%' }} - > - <Title level={5} style={{ margin: 0 }}> - Product Information - - - {isEditing ? ( - <> - - {lazyLoading && } />} -
}} - onScroll={handleScroll} - onChange={handleTableChange} - showSorterTooltip={false} + url={`${config.backendUrl}/vendors`} + authenticated={authenticated} /> setNewVendorOpen(false)} footer={null} - destroyOnClose + destroyOnHidden={true} width={700} > { setNewVendorOpen(false) messageApi.success('New vendor created successfully.') - fetchVendorsData() + tableRef.current?.reload() }} reset={!newVendorOpen} /> diff --git a/src/components/Dashboard/Management/Vendors/NewVendor.jsx b/src/components/Dashboard/Management/Vendors/NewVendor.jsx index 9cecadc..8d7d5cb 100644 --- a/src/components/Dashboard/Management/Vendors/NewVendor.jsx +++ b/src/components/Dashboard/Management/Vendors/NewVendor.jsx @@ -1,6 +1,7 @@ import PropTypes from 'prop-types' import React, { useState } from 'react' import axios from 'axios' +import { useMediaQuery } from 'react-responsive' import { Form, Input, @@ -37,6 +38,7 @@ const NewVendor = ({ onOk, reset }) => { const [newVendorForm] = Form.useForm() const [newVendorFormValues, setNewVendorFormValues] = useState(initialNewVendorForm) + const isMobile = useMediaQuery({ maxWidth: 768 }) const newVendorFormUpdateValues = Form.useWatch([], newVendorForm) @@ -181,16 +183,18 @@ const NewVendor = ({ onOk, reset }) => { {contextHolder} -
- -
+ {!isMobile && ( +
+ +
+ )} - + {!isMobile && } diff --git a/src/components/Dashboard/Management/Vendors/VendorInfo.jsx b/src/components/Dashboard/Management/Vendors/VendorInfo.jsx index c91dced..83a9cd4 100644 --- a/src/components/Dashboard/Management/Vendors/VendorInfo.jsx +++ b/src/components/Dashboard/Management/Vendors/VendorInfo.jsx @@ -156,202 +156,216 @@ const VendorInfo = () => { return ( <div style={{ height: '100%', minHeight: 0, overflowY: 'auto' }}> {contextHolder} - <Collapse - ghost - collapsible='icon' - activeKey={collapseState.info ? ['1'] : []} - onChange={(keys) => updateCollapseState('info', keys.length > 0)} - expandIcon={({ isActive }) => ( - <CaretRightOutlined - rotate={isActive ? 90 : 0} - style={{ paddingTop: '9px' }} - /> - )} - className='no-h-padding-collapse no-t-padding-collapse' - > - <Collapse.Panel - header={ - <Flex - align='center' - justify='space-between' - style={{ width: '100%' }} - > - <Title level={5} style={{ margin: 0 }}> - Vendor Information - - - {isEditing ? ( - <> - - {lazyLoading && } />} -
}} - onChange={handleTableChange} - onScroll={handleScroll} - showSorterTooltip={false} + url={`${config.backendUrl}/gcodefiles`} + authenticated={authenticated} /> { onCancel={() => { setNewGCodeFileOpen(false) }} - destroyOnClose + destroyOnHidden={true} > { setNewGCodeFileOpen(false) messageApi.success('Finished uploading GCode file!') - fetchGCodeFilesData() + tableRef.current?.reload() }} reset={newGCodeFileOpen} /> diff --git a/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx b/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx index 0e7dc80..342cd87 100644 --- a/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx +++ b/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx @@ -155,255 +155,263 @@ const GCodeFileInfo = () => { return (
{contextHolder} - updateCollapseState('info', keys.length > 0)} - expandIcon={({ isActive }) => ( - - )} - className='no-h-padding-collapse no-t-padding-collapse' - > - - - GCode File Information - - - {isEditing ? ( - <> -
) } diff --git a/src/components/Dashboard/Production/GCodeFiles/NewGCodeFile.jsx b/src/components/Dashboard/Production/GCodeFiles/NewGCodeFile.jsx index 5936a08..3ef9fc3 100644 --- a/src/components/Dashboard/Production/GCodeFiles/NewGCodeFile.jsx +++ b/src/components/Dashboard/Production/GCodeFiles/NewGCodeFile.jsx @@ -1,6 +1,7 @@ import PropTypes from 'prop-types' import React, { useState, useContext, useEffect } from 'react' import axios from 'axios' +import { useMediaQuery } from 'react-responsive' import { capitalizeFirstLetter, timeStringToMinutes @@ -48,6 +49,7 @@ const initialNewGCodeFileForm = { const NewGCodeFile = ({ onOk, reset }) => { const [messageApi] = message.useMessage() + const isMobile = useMediaQuery({ maxWidth: 768 }) const [newGCodeFileLoading, setNewGCodeFileLoading] = useState(false) const [gcodeParsing, setGcodeParsing] = useState(false) @@ -470,16 +472,18 @@ const NewGCodeFile = ({ onOk, reset }) => { return ( -
- -
+ {!isMobile && ( +
+ +
+ )} - + {!isMobile && } diff --git a/src/components/Dashboard/Production/PrintJobs.jsx b/src/components/Dashboard/Production/Jobs.jsx similarity index 60% rename from src/components/Dashboard/Production/PrintJobs.jsx rename to src/components/Dashboard/Production/Jobs.jsx index dc3eb59..4de127f 100644 --- a/src/components/Dashboard/Production/PrintJobs.jsx +++ b/src/components/Dashboard/Production/Jobs.jsx @@ -1,10 +1,8 @@ -// src/PrintJobs.js +// src/Jobs.js -import React, { useEffect, useState, useCallback, useContext } from 'react' +import React, { useState, useContext, useRef } from 'react' import { useNavigate } from 'react-router-dom' -import axios from 'axios' import { - Table, Button, Flex, Space, @@ -15,71 +13,42 @@ import { Input, Typography, Checkbox, - Popover, - Spin + Popover } from 'antd' -import { createStyles } from 'antd-style' -import { LoadingOutlined } from '@ant-design/icons' import { AuthContext } from '../context/AuthContext.js' -import { SocketContext } from '../context/SocketContext' -import NewPrintJob from './PrintJobs/NewPrintJob' -import JobState from '../common/JobState' -import SubJobCounter from '../common/SubJobCounter' -import TimeDisplay from '../common/TimeDisplay' -import IdText from '../common/IdText' -import useColumnVisibility from '../hooks/useColumnVisibility' -import JobIcon from '../../Icons/JobIcon' -import InfoCircleIcon from '../../Icons/InfoCircleIcon' -import PlusIcon from '../../Icons/PlusIcon' -import ReloadIcon from '../../Icons/ReloadIcon' +import { SocketContext } from '../context/SocketContext.js' +import NewJob from './Jobs/NewJob.jsx' +import JobState from '../common/JobState.jsx' +import SubJobCounter from '../common/SubJobCounter.jsx' +import TimeDisplay from '../common/TimeDisplay.jsx' +import IdText from '../common/IdText.jsx' +import useColumnVisibility from '../hooks/useColumnVisibility.js' +import JobIcon from '../../Icons/JobIcon.jsx' +import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx' +import PlusIcon from '../../Icons/PlusIcon.jsx' +import ReloadIcon from '../../Icons/ReloadIcon.jsx' import EditIcon from '../../Icons/EditIcon.jsx' import XMarkIcon from '../../Icons/XMarkIcon.jsx' import CheckIcon from '../../Icons/CheckIcon.jsx' import PlayCircleIcon from '../../Icons/PlayCircleIcon.jsx' - -import config from '../../../config.js' import CheckCircleIcon from '../../Icons/CheckCircleIcon.jsx' import PauseCircleIcon from '../../Icons/PauseCircleIcon.jsx' import XMarkCircleIcon from '../../Icons/XMarkCircleIcon.jsx' import QuestionCircleIcon from '../../Icons/QuestionCircleIcon.jsx' +import DashboardTable from '../common/DashboardTable' + +import config from '../../../config.js' const { Text } = Typography -const useStyle = createStyles(({ css, token }) => { - const { antCls } = token - return { - customTable: css` - ${antCls}-table { - ${antCls}-table-container { - ${antCls}-table-body, - ${antCls}-table-content { - scrollbar-width: thin; - scrollbar-color: #eaeaea transparent; - scrollbar-gutter: stable; - } - } - } - ` - } -}) - -const PrintJobs = () => { - const { styles } = useStyle() +const Jobs = () => { const [messageApi, contextHolder] = message.useMessage() const [notificationApi, notificationContextHolder] = notification.useNotification() const navigate = useNavigate() - const [printJobsData, setPrintJobsData] = useState([]) - const [page, setPage] = useState(1) - const [hasMore, setHasMore] = useState(true) - const [lazyLoading, setLazyLoading] = useState(false) - - const [filters, setFilters] = useState({}) - const [sorter, setSorter] = useState({}) - - const [newPrintJobOpen, setNewPrintJobOpen] = useState(false) - const [loading, setLoading] = useState(true) + const [newJobOpen, setNewJobOpen] = useState(false) + const tableRef = useRef() const getFilterDropdown = ({ setSelectedKeys, @@ -154,7 +123,7 @@ const PrintJobs = () => { title: 'ID', dataIndex: 'id', key: 'id', - width: 165, + width: 180, render: (text) => <IdText id={text} type={'job'} longId={false} />, filterDropdown: ({ setSelectedKeys, @@ -266,19 +235,17 @@ const PrintJobs = () => { {record.state.type === 'draft' ? ( <Button icon={<PlayCircleIcon />} - onClick={() => handleDeployPrintJob(record.id)} + onClick={() => handleDeployJob(record.id)} /> ) : ( <Button icon={<InfoCircleIcon />} onClick={() => - navigate( - `/dashboard/production/printjobs/info?printJobId=${record.id}` - ) + navigate(`/dashboard/production/jobs/info?jobId=${record.id}`) } /> )} - <Dropdown menu={getPrintJobActionItems(record.id)}> + <Dropdown menu={getJobActionItems(record.id)}> <Button>Actions</Button> </Dropdown> </Space> @@ -291,14 +258,14 @@ const PrintJobs = () => { const { socket } = useContext(SocketContext) const [columnVisibility, updateColumnVisibility] = useColumnVisibility( - 'PrintJobs', + 'Jobs', columns ) - const handleDeployPrintJob = (printJobId) => { + const handleDeployJob = (jobId) => { if (socket) { - messageApi.info(`Print job ${printJobId} deployment initiated`) - socket.emit('server.job_queue.deploy', { printJobId }, (response) => { + messageApi.info(`Print job ${jobId} deployment initiated`) + socket.emit('server.job_queue.deploy', { jobId }, (response) => { if (response == false) { notificationApi.error({ message: 'Print job deployment failed', @@ -311,110 +278,13 @@ const PrintJobs = () => { }) } }) - navigate(`/dashboard/production/printjobs/info?printJobId=${printJobId}`) + navigate(`/dashboard/production/jobs/info?jobId=${jobId}`) } else { messageApi.error('Socket connection not available') } } - const fetchPrintJobsData = useCallback( - async (pageNum = 1, append = false) => { - if (!authenticated) { - return - } - try { - const params = { - page: pageNum, - limit: 25, - ...filters, - sort: sorter.field, - order: sorter.order - } - - const response = await axios.get(`${config.backendUrl}/printjobs`, { - params, - headers: { - Accept: 'application/json' - }, - withCredentials: true - }) - - const newData = response.data - setHasMore(newData.length === 25) - - if (append) { - setPrintJobsData((prev) => [...prev, ...newData]) - } else { - setPrintJobsData(newData) - } - - setLoading(false) - setLazyLoading(false) - } catch (error) { - setLoading(false) - setLazyLoading(false) - if (error.response) { - messageApi.error( - 'Error fetching print jobs data:', - error.response.status - ) - } else { - messageApi.error( - 'An unexpected error occurred. Please try again later.' - ) - } - } - }, - [authenticated, messageApi, filters, sorter] - ) - - const handleScroll = useCallback( - (e) => { - const { target } = e - const scrollHeight = target.scrollHeight - const scrollTop = target.scrollTop - const clientHeight = target.clientHeight - - // If we're near the bottom (within 100px) and not currently loading - if ( - scrollHeight - scrollTop - clientHeight < 100 && - !lazyLoading && - hasMore - ) { - setLazyLoading(true) - const nextPage = page + 1 - setPage(nextPage) - fetchPrintJobsData(nextPage, true) - } - }, - [page, lazyLoading, hasMore, fetchPrintJobsData] - ) - - useEffect(() => { - // Fetch initial data - if (authenticated) { - fetchPrintJobsData() - } - }, [authenticated, fetchPrintJobsData]) - - const handleTableChange = (pagination, filters, sorter) => { - const newFilters = {} - Object.entries(filters).forEach(([key, value]) => { - if (value && value.length > 0) { - newFilters[key] = value[0] - } - }) - - setFilters(newFilters) - setSorter({ - field: sorter.field, - order: sorter.order - }) - setPage(1) - fetchPrintJobsData(1) - } - - const getPrintJobActionItems = (printJobId) => { + const getJobActionItems = (jobId) => { return { items: [ { @@ -430,11 +300,9 @@ const PrintJobs = () => { ], onClick: ({ key }) => { if (key === 'edit') { - showNewPrintJobModal(printJobId) + showNewJobModal(jobId) } else if (key === 'info') { - navigate( - `/dashboard/production/printjobs/info?printJobId=${printJobId}` - ) + navigate(`/dashboard/production/jobs/info?jobId=${jobId}`) } } } @@ -444,7 +312,7 @@ const PrintJobs = () => { items: [ { label: 'New Print Job', - key: 'newPrintJob', + key: 'newJob', icon: <PlusIcon /> }, { type: 'divider' }, @@ -455,16 +323,16 @@ const PrintJobs = () => { } ], onClick: ({ key }) => { - if (key === 'newPrintJob') { - showNewPrintJobModal() + if (key === 'newJob') { + showNewJobModal() } else if (key === 'reloadList') { - fetchPrintJobsData() + tableRef.current?.reload() } } } - const showNewPrintJobModal = () => { - setNewPrintJobOpen(true) + const showNewJobModal = () => { + setNewJobOpen(true) } const getViewDropdownItems = () => { @@ -511,41 +379,34 @@ const PrintJobs = () => { > <Button>View</Button> </Popover> - {lazyLoading && <Spin indicator={<LoadingOutlined />} />} </Space> - <Table - className={styles.customTable} - dataSource={printJobsData} + <DashboardTable + ref={tableRef} columns={visibleColumns} - rowKey='id' - pagination={false} - loading={{ spinning: loading, indicator: <LoadingOutlined spin /> }} - scroll={{ y: 'calc(100vh - 270px)' }} - onChange={handleTableChange} - onScroll={handleScroll} - showSorterTooltip={false} + url={`${config.backendUrl}/jobs`} + authenticated={authenticated} /> </Flex> <Modal - open={newPrintJobOpen} + open={newJobOpen} footer={null} width={700} onCancel={() => { - setNewPrintJobOpen(false) + setNewJobOpen(false) }} > - <NewPrintJob + <NewJob onOk={() => { - setNewPrintJobOpen(false) + setNewJobOpen(false) messageApi.success('New print job created successfully.') - fetchPrintJobsData() + tableRef.current?.reload() }} - reset={newPrintJobOpen} + reset={newJobOpen} /> </Modal> </> ) } -export default PrintJobs +export default Jobs diff --git a/src/components/Dashboard/Production/Jobs/JobInfo.jsx b/src/components/Dashboard/Production/Jobs/JobInfo.jsx new file mode 100644 index 0000000..4f1170a --- /dev/null +++ b/src/components/Dashboard/Production/Jobs/JobInfo.jsx @@ -0,0 +1,398 @@ +import React, { useState, useEffect, useContext } from 'react' +import { useLocation } from 'react-router-dom' +import axios from 'axios' +import { + Descriptions, + Spin, + Space, + Button, + message, + Progress, + Typography, + Collapse, + Flex, + Dropdown, + Popover, + Checkbox, + Card +} from 'antd' +import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons' +import TimeDisplay from '../../common/TimeDisplay' +import JobState from '../../common/JobState' +import IdText from '../../common/IdText' +import SubJobsTree from '../../common/SubJobsTree' +import { SocketContext } from '../../context/SocketContext' +import GCodeFileIcon from '../../../Icons/GCodeFileIcon' +import ReloadIcon from '../../../Icons/ReloadIcon' +import useCollapseState from '../../hooks/useCollapseState' +import config from '../../../../config' +import AuditLogTable from '../../common/AuditLogTable' +import DashboardNotes from '../../common/DashboardNotes' + +const { Title, Text } = Typography + +const JobInfo = () => { + const [jobData, setJobData] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const location = useLocation() + const [messageApi] = message.useMessage() + const jobId = new URLSearchParams(location.search).get('jobId') + const { socket } = useContext(SocketContext) + const [collapseState, updateCollapseState] = useCollapseState('JobInfo', { + info: true, + subJobs: true, + notes: true, + auditLogs: true + }) + + useEffect(() => { + if (jobId) { + fetchJobDetails() + } + }, [jobId]) + + useEffect(() => { + if (socket && jobId) { + socket.on('notify_job_update', (updateData) => { + if (updateData._id === jobId) { + setJobData((prevData) => { + if (!prevData) return prevData + return { + ...prevData, + state: updateData.state, + ...updateData + } + }) + } + }) + } + + return () => { + if (socket) { + socket.off('notify_job_update') + } + } + }, [socket, jobId]) + + const fetchJobDetails = async () => { + try { + setLoading(true) + const response = await axios.get(`${config.backendUrl}/jobs/${jobId}`, { + headers: { + Accept: 'application/json' + }, + withCredentials: true // Important for including cookies + }) + setJobData(response.data) + setError(null) + } catch (err) { + setError('Failed to fetch print job details') + messageApi.error('Failed to fetch print job details') + } finally { + setLoading(false) + } + } + + const getViewDropdownItems = () => { + const sections = [ + { key: 'info', label: 'Job Information' }, + { key: 'subJobs', label: 'Sub Jobs' }, + { key: 'notes', label: 'Notes' }, + { key: 'auditLogs', label: 'Audit Logs' } + ] + + return ( + <Flex vertical> + <Flex vertical gap='middle' style={{ margin: '4px 8px' }}> + {sections.map((section) => ( + <Checkbox + checked={collapseState[section.key]} + key={section.key} + onChange={(e) => { + updateCollapseState(section.key, e.target.checked) + }} + > + {section.label} + </Checkbox> + ))} + </Flex> + </Flex> + ) + } + + const actionItems = { + items: [ + { + label: 'Reload Job', + key: 'reload', + icon: <ReloadIcon /> + } + ], + onClick: ({ key }) => { + if (key === 'edit') { + // TODO: Implement edit functionality + messageApi.info('Edit functionality coming soon') + } else if (key === 'reload') { + fetchJobDetails() + } + } + } + + return ( + <> + <Flex + gap='large' + vertical='true' + style={{ height: '100%', minHeight: 0 }} + > + <Flex justify={'space-between'}> + <Space size='small'> + <Dropdown menu={actionItems}> + <Button>Actions</Button> + </Dropdown> + <Popover + content={getViewDropdownItems()} + placement='bottomLeft' + arrow={false} + > + <Button>View</Button> + </Popover> + </Space> + </Flex> + + {error ? ( + <Space + direction='vertical' + style={{ width: '100%', textAlign: 'center' }} + > + <p>{error || 'Print job not found'}</p> + <Button icon={<ReloadIcon />} onClick={fetchJobDetails}> + Retry + </Button> + </Space> + ) : ( + <div style={{ height: '100%', overflow: 'auto' }}> + <Flex vertical style={{ flexGrow: 1 }} gap={'large'}> + <Collapse + ghost + collapsible='icon' + activeKey={collapseState.info ? ['info'] : []} + onChange={(keys) => + updateCollapseState('info', keys.length > 0) + } + expandIcon={({ isActive }) => ( + <CaretRightOutlined + rotate={isActive ? 90 : 0} + style={{ paddingTop: '2px' }} + /> + )} + className='no-h-padding-collapse no-t-padding-collapse' + > + <Collapse.Panel + header={ + <Title level={5} style={{ margin: 0 }}> + Job Information + + } + key='info' + > + }> + + + {jobData?._id ? ( + + ) : ( + n/a + )} + + + {jobData?.state ? ( + + ) : ( + n/a + )} + + + {jobData?.gcodeFile ? ( + + + + {jobData.gcodeFile.name || 'Not specified'} + + + ) : ( + n/a + )} + + + {jobData?.gcodeFile?._id ? ( + + ) : ( + n/a + )} + + + {jobData?.quantity ? ( + {jobData.quantity} + ) : ( + n/a + )} + + + {jobData?.createdAt ? ( + + ) : ( + n/a + )} + + + {jobData?.startedAt ? ( + + ) : ( + n/a + )} + + {jobData?.state?.type === 'printing' && ( + + + + )} + + {jobData?.printers ? ( + + {jobData.printers.length} printers assigned + + ) : ( + n/a + )} + + + + + + + + updateCollapseState('subJobs', keys.length > 0) + } + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse' + > + + Sub Job Information + + } + key='2' + > + + + + + + updateCollapseState('notes', keys.length > 0) + } + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse' + > + + Notes + + } + key='notes' + > + + + + + + + + updateCollapseState('auditLogs', keys.length > 0) + } + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse' + > + + Audit Logs + + } + key='auditLogs' + > + + + + + + )} +
+ + ) +} + +export default JobInfo diff --git a/src/components/Dashboard/Production/PrintJobs/NewPrintJob.jsx b/src/components/Dashboard/Production/Jobs/NewJob.jsx similarity index 68% rename from src/components/Dashboard/Production/PrintJobs/NewPrintJob.jsx rename to src/components/Dashboard/Production/Jobs/NewJob.jsx index 406a213..bcd13f6 100644 --- a/src/components/Dashboard/Production/PrintJobs/NewPrintJob.jsx +++ b/src/components/Dashboard/Production/Jobs/NewJob.jsx @@ -1,5 +1,6 @@ import React, { useState } from 'react' import axios from 'axios' +import { useMediaQuery } from 'react-responsive' import { Form, Button, @@ -20,70 +21,66 @@ import config from '../../../../config' const { Title } = Typography -const initialNewPrintJobForm = { +const initialNewJobForm = { gcodeFile: null, quantity: 1 } -const NewPrintJob = ({ onOk, reset }) => { - NewPrintJob.propTypes = { +const NewJob = ({ onOk, reset }) => { + NewJob.propTypes = { onOk: PropTypes.func.isRequired, reset: PropTypes.bool.isRequired } const [messageApi, contextHolder] = message.useMessage() - const [newPrintJobLoading, setNewPrintJobLoading] = useState(false) + const [newJobLoading, setNewJobLoading] = useState(false) const [currentStep, setCurrentStep] = useState(0) const [nextEnabled, setNextEnabled] = useState(false) - const [newPrintJobForm] = Form.useForm() - const [newPrintJobFormValues, setNewPrintJobFormValues] = useState( - initialNewPrintJobForm - ) + const [newJobForm] = Form.useForm() + const [newJobFormValues, setNewJobFormValues] = useState(initialNewJobForm) - const newPrintJobFormUpdateValues = Form.useWatch([], newPrintJobForm) + const newJobFormUpdateValues = Form.useWatch([], newJobForm) + + const isMobile = useMediaQuery({ maxWidth: 768 }) React.useEffect(() => { - newPrintJobForm + newJobForm .validateFields({ validateOnly: true }) .then(() => setNextEnabled(true)) .catch(() => setNextEnabled(false)) - }, [newPrintJobForm, newPrintJobFormUpdateValues]) + }, [newJobForm, newJobFormUpdateValues]) const summaryItems = [ { key: 'quantity', label: 'Quantity', - children: newPrintJobFormValues.quantity + children: newJobFormValues.quantity } ] React.useEffect(() => { if (reset) { - newPrintJobForm.resetFields() + newJobForm.resetFields() } - }, [reset, newPrintJobForm]) + }, [reset, newJobForm]) - const handleNewPrintJob = async () => { - setNewPrintJobLoading(true) + const handleNewJob = async () => { + setNewJobLoading(true) try { - await axios.post( - `${config.backendUrl}/printjobs`, - newPrintJobFormValues, - { - headers: { - Accept: 'application/json' - }, - withCredentials: true // Important for including cookies - } - ) + await axios.post(`${config.backendUrl}/jobs`, newJobFormValues, { + headers: { + Accept: 'application/json' + }, + withCredentials: true // Important for including cookies + }) onOk() } catch (error) { messageApi.error('Error creating new print job: ' + error.message) } finally { - setNewPrintJobLoading(false) + setNewJobLoading(false) } } @@ -108,7 +105,6 @@ const NewPrintJob = ({ onOk, reset }) => { { } ]} > - + { return ( {contextHolder} -
- -
+ {!isMobile && ( +
+ +
+ )} - + {!isMobile && } - New PrintJob + New Job
- setNewPrintJobFormValues((prevValues) => ({ + setNewJobFormValues((prevValues) => ({ ...prevValues, ...changedValues })) } - initialValues={initialNewPrintJobForm} + initialValues={initialNewJobForm} >
{steps[currentStep].content}
@@ -204,11 +202,7 @@ const NewPrintJob = ({ onOk, reset }) => { )} {currentStep === steps.length - 1 && ( - )} @@ -219,4 +213,4 @@ const NewPrintJob = ({ onOk, reset }) => { ) } -export default NewPrintJob +export default NewJob diff --git a/src/components/Dashboard/Production/PrintJobs/PrintJobInfo.jsx b/src/components/Dashboard/Production/PrintJobs/PrintJobInfo.jsx deleted file mode 100644 index 81b09dc..0000000 --- a/src/components/Dashboard/Production/PrintJobs/PrintJobInfo.jsx +++ /dev/null @@ -1,236 +0,0 @@ -import React, { useState, useEffect, useContext } from 'react' -import { useLocation } from 'react-router-dom' -import axios from 'axios' -import { - Descriptions, - Spin, - Space, - Button, - message, - Progress, - Typography, - Collapse -} from 'antd' -import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons' -import TimeDisplay from '../../common/TimeDisplay' -import JobState from '../../common/JobState' -import IdText from '../../common/IdText' -import SubJobsTree from '../../common/SubJobsTree' -import { SocketContext } from '../../context/SocketContext' -import GCodeFileIcon from '../../../Icons/GCodeFileIcon' -import ReloadIcon from '../../../Icons/ReloadIcon' -import useCollapseState from '../../hooks/useCollapseState' -import config from '../../../../config' - -const { Title } = Typography - -const PrintJobInfo = () => { - const [printJobData, setPrintJobData] = useState(null) - const [loading, setLoading] = useState(true) - const [error, setError] = useState(null) - const location = useLocation() - const [messageApi] = message.useMessage() - const printJobId = new URLSearchParams(location.search).get('printJobId') - const { socket } = useContext(SocketContext) - const [collapseState, updateCollapseState] = useCollapseState( - 'PrintJobInfo', - { - info: true, - subJobs: true - } - ) - - useEffect(() => { - if (printJobId) { - fetchPrintJobDetails() - } - }, [printJobId]) - - useEffect(() => { - if (socket && printJobId) { - socket.on('notify_job_update', (updateData) => { - if (updateData._id === printJobId) { - setPrintJobData((prevData) => { - if (!prevData) return prevData - return { - ...prevData, - state: updateData.state, - ...updateData - } - }) - } - }) - } - - return () => { - if (socket) { - socket.off('notify_job_update') - } - } - }, [socket, printJobId]) - - const fetchPrintJobDetails = async () => { - try { - setLoading(true) - const response = await axios.get( - `${config.backendUrl}/printjobs/${printJobId}`, - { - headers: { - Accept: 'application/json' - }, - withCredentials: true // Important for including cookies - } - ) - setPrintJobData(response.data) - setError(null) - } catch (err) { - setError('Failed to fetch print job details') - messageApi.error('Failed to fetch print job details') - } finally { - setLoading(false) - } - } - - if (loading) { - return ( -
- } /> -
- ) - } - - if (error || !printJobData) { - return ( - -

{error || 'Print job not found'}

- -
- ) - } - - return ( -
- updateCollapseState('info', keys.length > 0)} - expandIcon={({ isActive }) => ( - - )} - className='no-h-padding-collapse no-t-padding-collapse' - > - - Print Job Information - - } - key='1' - > - - - - - - - - - - - {printJobData.gcodeFile?.name || 'Not specified'} - - - - - - - {printJobData.quantity || 1} - - - - - - {printJobData.startedAt ? ( - - ) : ( - 'n/a' - )} - - {printJobData.state.type === 'printing' && ( - - - - )} - - {printJobData.printers?.length > 0 ? ( - {printJobData.printers.length} printers assigned - ) : ( - 'Any available printer' - )} - - - - - - updateCollapseState('subJobs', keys.length > 0)} - expandIcon={({ isActive }) => ( - - )} - className='no-h-padding-collapse' - > - - Sub Job Information - - } - key='2' - > - - - -
- ) -} - -export default PrintJobInfo diff --git a/src/components/Dashboard/Production/Printers.jsx b/src/components/Dashboard/Production/Printers.jsx index 698bf8c..0e6285e 100644 --- a/src/components/Dashboard/Production/Printers.jsx +++ b/src/components/Dashboard/Production/Printers.jsx @@ -1,10 +1,8 @@ // src/Printers.js -import React, { useEffect, useState, useContext, useCallback } from 'react' +import React, { useState, useContext, useRef } from 'react' import { useNavigate } from 'react-router-dom' -import axios from 'axios' import { - Table, Button, message, Dropdown, @@ -14,11 +12,8 @@ import { Tag, Modal, Popover, - Checkbox, - Spin + Checkbox } from 'antd' -import { createStyles } from 'antd-style' -import { LoadingOutlined } from '@ant-design/icons' import { AuthContext } from '../context/AuthContext' import PrinterState from '../common/PrinterState' @@ -31,36 +26,16 @@ import PlusIcon from '../../Icons/PlusIcon' import ReloadIcon from '../../Icons/ReloadIcon' import XMarkIcon from '../../Icons/XMarkIcon' import CheckIcon from '../../Icons/CheckIcon' +import DashboardTable from '../common/DashboardTable' import config from '../../../config' -const useStyle = createStyles(({ css, token }) => { - const { antCls } = token - return { - customTable: css` - ${antCls}-table { - ${antCls}-table-container { - ${antCls}-table-body, - ${antCls}-table-content { - scrollbar-width: thin; - scrollbar-color: #eaeaea transparent; - scrollbar-gutter: stable; - } - } - } - ` - } -}) - const Printers = () => { - const { styles } = useStyle() - const [printerData, setPrinterData] = useState([]) - const [page, setPage] = useState(1) - const [hasMore, setHasMore] = useState(true) - const [loading, setLoading] = useState(true) - const [lazyLoading, setLazyLoading] = useState(false) - const [filters, setFilters] = useState({}) - const [sorter, setSorter] = useState({}) + const [messageApi, contextHolder] = message.useMessage() + const { authenticated } = useContext(AuthContext) + const [newPrinterOpen, setNewPrinterOpen] = useState(false) + const navigate = useNavigate() + const tableRef = useRef() // Column definitions const columns = [ @@ -98,7 +73,7 @@ const Printers = () => { title: 'ID', dataIndex: 'id', key: 'id', - width: 165, + width: 180, render: (text) => }, { @@ -186,11 +161,6 @@ const Printers = () => { }, {}) ) - const [messageApi] = message.useMessage() - const { authenticated } = useContext(AuthContext) - const [newPrinterOpen, setNewPrinterOpen] = useState(false) - const navigate = useNavigate() - const getFilterDropdown = ({ setSelectedKeys, selectedKeys, @@ -227,91 +197,6 @@ const Printers = () => { ) } - const fetchPrintersData = useCallback( - async (pageNum = 1, append = false) => { - try { - const params = { - page: pageNum, - limit: 25, - ...filters, - sort: sorter.field, - order: sorter.order - } - - const response = await axios.get(`${config.backendUrl}/printers`, { - params, - headers: { - Accept: 'application/json' - }, - withCredentials: true - }) - - const newData = response.data - setHasMore(newData.length === 25) // If we get less than 25 items, we've reached the end - - if (append) { - setPrinterData((prev) => [...prev, ...newData]) - } else { - setPrinterData(newData) - } - - setLoading(false) - setLazyLoading(false) - } catch (error) { - if (error.response) { - messageApi.error( - 'Error fetching printer data:', - error.response.status - ) - } else { - messageApi.error( - 'An unexpected error occurred. Please try again later.' - ) - } - setLoading(false) - setLazyLoading(false) - } - }, - [messageApi, filters, sorter] - ) - - const handleScroll = useCallback( - (e) => { - const { target } = e - const scrollHeight = target.scrollHeight - const scrollTop = target.scrollTop - const clientHeight = target.clientHeight - - // If we're near the bottom (within 100px) and not currently loading - if ( - scrollHeight - scrollTop - clientHeight < 100 && - !lazyLoading && - hasMore - ) { - setLazyLoading(true) - const nextPage = page + 1 - setPage(nextPage) - fetchPrintersData(nextPage, true) - } - }, - [page, lazyLoading, hasMore, fetchPrintersData] - ) - - const handleTableChange = (pagination, filters, sorter) => { - const newFilters = {} - Object.entries(filters).forEach(([key, value]) => { - if (value && value.length > 0) { - newFilters[key] = value[0] // Take the first filter value - } - }) - - setFilters(newFilters) - setSorter({ - field: sorter.field, - order: sorter.order - }) - } - const getPrinterActionItems = (printerId) => { return { items: [ @@ -343,7 +228,7 @@ const Printers = () => { const getViewDropdownItems = () => { const columnItems = columns - .filter((col) => col.key && col.title !== '') // Filter out empty title columns and ensure key exists + .filter((col) => col.key && col.title !== '') .map((col) => ( { ], onClick: ({ key }) => { if (key === 'reloadList') { - fetchPrintersData() + tableRef.current?.reload() } else if (key === 'newPrinter') { setNewPrinterOpen(true) } } } - useEffect(() => { - if (authenticated) { - fetchPrintersData() - } - }, [fetchPrintersData, authenticated]) - return ( <> + {contextHolder} @@ -415,21 +295,15 @@ const Printers = () => { > - {lazyLoading && } />} -
}} - scroll={{ y: 'calc(100vh - 270px)' }} - onChange={handleTableChange} - onScroll={handleScroll} - showSorterTooltip={false} + url={`${config.backendUrl}/printers`} + authenticated={authenticated} /> + { onOk={() => { setNewPrinterOpen(false) messageApi.success('New printer added successfully.') - fetchPrintersData() + tableRef.current?.reload() }} reset={newPrinterOpen} /> diff --git a/src/components/Dashboard/Production/Printers/ControlPrinter.jsx b/src/components/Dashboard/Production/Printers/ControlPrinter.jsx index a8bd5e4..e6cf365 100644 --- a/src/components/Dashboard/Production/Printers/ControlPrinter.jsx +++ b/src/components/Dashboard/Production/Printers/ControlPrinter.jsx @@ -1,6 +1,7 @@ import React, { useState, useContext, useCallback, useEffect } from 'react' import axios from 'axios' import { useLocation } from 'react-router-dom' +import { useMediaQuery } from 'react-responsive' import { Button, message, @@ -26,6 +27,7 @@ import { SocketContext } from '../../context/SocketContext' import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel' import PrinterPositionPanel from '../../common/PrinterPositionPanel' import PrinterMovementPanel from '../../common/PrinterMovementPanel' +import PrinterMiscPanel from '../../common/PrinterMiscPanel' import PrinterState from '../../common/PrinterState' import { AuthContext } from '../../context/AuthContext' import PrinterSubJobsTree from '../../common/PrinterJobsTree' @@ -47,6 +49,7 @@ import PlayCircleIcon from '../../../Icons/PlayCircleIcon' import XMarkCircleIcon from '../../../Icons/XMarkCircleIcon' import PauseCircleIcon from '../../../Icons/PauseCircleIcon' import ExclamationOctagonIcon from '../../../Icons/ExclamationOctagonIcon' +import TimeDisplay from '../../common/TimeDisplay' const { Text, Title } = Typography @@ -59,6 +62,7 @@ const ControlPrinter = () => { const [messageApi] = message.useMessage() const query = useQuery() const printerId = query.get('printerId') + const isMobile = useMediaQuery({ maxWidth: 768 }) const [printerData, setPrinterData] = useState(null) const [initialized, setInitialized] = useState(false) @@ -390,6 +394,17 @@ const ControlPrinter = () => { > Sub Jobs + { + setComponentVisibility((prev) => ({ + ...prev, + misc: e.target.checked + })) + }} + > + Misc Panel + ) @@ -471,8 +486,8 @@ const ControlPrinter = () => {
{printerData ? ( - - + + {printerData?.alerts?.some( (alert) => alert.type === 'klippyError' @@ -540,14 +555,16 @@ const ControlPrinter = () => { - { + {printerData.currentJob?.gcodeFile?.name ? ( - {printerData.currentJob?.gcodeFile?.name || 'n/a'} + {printerData.currentJob?.gcodeFile?.name} - } + ) : ( + 'n/a' + )} {printerData.currentJob?.gcodeFile ? ( @@ -601,7 +618,14 @@ const ControlPrinter = () => { /> - {printerData.name} + {printerData.currentSubJob?.startedAt ? ( + + ) : ( + 'n/a' + )} )} @@ -740,18 +764,24 @@ const ControlPrinter = () => { {printerData.currentFilamentStock?.currentNetWeight ? ( - - - {printerData.currentFilamentStock.currentNetWeight.toFixed( - 2 - ) + 'g'} - - - {printerData.currentFilamentStock.currentGrossWeight.toFixed( - 2 - ) + 'g'} - - +
+ + + {printerData.currentFilamentStock.currentNetWeight.toFixed( + 2 + ) + 'g'} + + + {printerData.currentFilamentStock.currentGrossWeight.toFixed( + 2 + ) + 'g'} + + +
) : ( 'n/a' )} @@ -793,7 +823,7 @@ const ControlPrinter = () => {
- + {componentVisibility.temperature && ( { > )} + + {componentVisibility.misc && ( + + + + )} ) : ( diff --git a/src/components/Dashboard/Production/Printers/PrinterInfo.jsx b/src/components/Dashboard/Production/Printers/PrinterInfo.jsx index 3e00038..1bf2296 100644 --- a/src/components/Dashboard/Production/Printers/PrinterInfo.jsx +++ b/src/components/Dashboard/Production/Printers/PrinterInfo.jsx @@ -31,6 +31,7 @@ import CheckIcon from '../../../Icons/CheckIcon.jsx' import useCollapseState from '../../hooks/useCollapseState' import config from '../../../../config.js' +import AuditLogTable from '../../common/AuditLogTable.jsx' const { Title } = Typography @@ -46,7 +47,8 @@ const PrinterInfo = () => { const [form] = Form.useForm() const [collapseState, updateCollapseState] = useCollapseState('PrinterInfo', { info: true, - jobs: true + jobs: true, + auditLogs: true }) useEffect(() => { @@ -199,311 +201,348 @@ const PrinterInfo = () => { return (
{contextHolder} - updateCollapseState('info', keys.length > 0)} - expandIcon={({ isActive }) => ( - - )} - className='no-h-padding-collapse no-t-padding-collapse' - > - - - Printer Information - - - {isEditing ? ( - <> -
) } diff --git a/src/components/Dashboard/Production/ProductionOverview.jsx b/src/components/Dashboard/Production/ProductionOverview.jsx index 0ff4870..8cd9f91 100644 --- a/src/components/Dashboard/Production/ProductionOverview.jsx +++ b/src/components/Dashboard/Production/ProductionOverview.jsx @@ -9,17 +9,16 @@ import { message, Button, Collapse, - Segmented + Segmented, + Card } from 'antd' import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons' +import { Line } from '@ant-design/charts' import axios from 'axios' import PrinterIcon from '../../Icons/PrinterIcon' import JobIcon from '../../Icons/JobIcon' import ReloadIcon from '../../Icons/ReloadIcon' -import PauseIcon from '../../Icons/PauseIcon' -import XMarkIcon from '../../Icons/XMarkIcon' import useCollapseState from '../hooks/useCollapseState' -import CheckIcon from '../../Icons/CheckIcon' import config from '../../../config' @@ -29,6 +28,7 @@ const ProductionOverview = () => { const [messageApi, contextHolder] = message.useMessage() const [error, setError] = useState(null) const [fetchPrinterStatsLoading, setFetchPrinterStatsLoading] = useState(true) + const [chartData, setChartData] = useState([]) const [collapseState, updateCollapseState] = useCollapseState( 'ProductionOverview', { @@ -46,9 +46,9 @@ const ProductionOverview = () => { const [stats, setStats] = useState({ totalPrinters: 0, activePrinters: 0, - totalPrintJobs: 0, - activePrintJobs: 0, - completedPrintJobs: 0, + totalJobs: 0, + activeJobs: 0, + completedJobs: 0, printerStatus: { idle: 0, printing: 0, @@ -59,7 +59,8 @@ const ProductionOverview = () => { const fetchAllStats = useCallback(async () => { await fetchPrinterStats() - await fetchPrintJobStats() + await fetchJobstats() + await fetchChartData() console.log(stats) }, []) @@ -84,17 +85,17 @@ const ProductionOverview = () => { } } - const fetchPrintJobStats = async () => { + const fetchJobstats = async () => { try { setFetchPrinterStatsLoading(true) - const response = await axios.get(`${config.backendUrl}/printjobs/stats`, { + const response = await axios.get(`${config.backendUrl}/jobs/stats`, { headers: { Accept: 'application/json' }, withCredentials: true }) - const printJobStats = response.data - setStats((prev) => ({ ...prev, printJobs: printJobStats })) + const jobstats = response.data + setStats((prev) => ({ ...prev, jobs: jobstats })) setError(null) } catch (err) { setError('Failed to fetch printer details') @@ -104,6 +105,20 @@ const ProductionOverview = () => { } } + const fetchChartData = async () => { + try { + const response = await axios.get(`${config.backendUrl}/stats/history`, { + headers: { + Accept: 'application/json' + }, + withCredentials: true + }) + setChartData(response.data) + } catch (err) { + console.error('Failed to fetch chart data:', err) + } + } + useEffect(() => { fetchAllStats() }, [fetchAllStats]) @@ -131,287 +146,298 @@ const ProductionOverview = () => { } return ( - +
{contextHolder} - updateCollapseState('overview', keys.length > 0)} - expandIcon={({ isActive }) => ( - - )} - className='no-h-padding-collapse no-t-padding-collapse' - > - - Status Overview - - } - key='1' + + updateCollapseState('overview', keys.length > 0)} + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse no-t-padding-collapse' > - + Status Overview + + } + key='1' > - - Ready - - - - {(stats.printers.standby || 0) + - (stats.printers.complete || 0)} - + + + Ready + + + + {(stats.printers.standby || 0) + + (stats.printers.complete || 0)} + + - - } - /> - - Printing - - - - {stats.printers.printing || 0} - + } + /> + + Printing + + + + {stats.printers.printing || 0} + + - - } - /> - - Queued - - - - {stats.printJobs.queued || 0} - + } + /> + + Queued + + + + {stats.jobs.queued || 0} + + - - } - /> - - Printing - - - - {stats.printJobs.printing || 0} - + } + /> + + Printing + + + + {stats.jobs.printing || 0} + + - - } - /> - - Failed - - - - {(stats.printJobs.failed || 0) + - (stats.printJobs.cancelled || 0)} - + } + /> + + Failed + + + + {(stats.jobs.failed || 0) + (stats.jobs.cancelled || 0)} + + - - } - /> - - Complete - - - - {stats.printJobs.complete || 0} - + } + /> + + Complete + + + + {stats.jobs.complete || 0} + + - - } - /> - - - + } + /> + + + - - - - updateCollapseState('printerStats', keys.length > 0) - } - expandIcon={({ isActive }) => ( - - )} - className='no-h-padding-collapse' - > - - - Printer Statistics - - - setTimeRanges((prev) => ({ - ...prev, - printerStats: value - })) - } - size='small' - /> - + + + + updateCollapseState('printerStats', keys.length > 0) } - key='2' + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse' > - - - - - {'Completed'} - - } + - {stats.totalPrinters} - - - - {'Error'} - - } - > - {stats.activePrinters} - - - - {'Paused'} - - } - > - {stats.activePrinters} - - - - - - - - - updateCollapseState('jobStats', keys.length > 0) - } - expandIcon={({ isActive }) => ( - - )} - className='no-h-padding-collapse' - > - - - Job Statistics - - - setTimeRanges((prev) => ({ ...prev, jobStats: value })) - } - size='small' - /> + + Production Statistics + + + setTimeRanges((prev) => ({ + ...prev, + printerStats: value + })) + } + size='small' + /> + + } + key='2' + > + + + + + + + {stats.totalPrinters} + + + {stats.activePrinters} + + + {stats.activePrinters} + + + + + + + + updateCollapseState('jobStats', keys.length > 0) } - key='3' + expandIcon={({ isActive }) => ( + + )} + className='no-h-padding-collapse' > - - - - - {'Completed'} - - } + - {stats.totalPrintJobs} - - - - {'Failed'} - - } + + Job Statistics + + + setTimeRanges((prev) => ({ ...prev, jobStats: value })) + } + size='small' + /> + + } + key='3' + > + + - {stats.activePrintJobs} - - - - {'Queued'} - - } - > - {stats.completedPrintJobs} - - - - - + + + + + {stats.totalJobs} + + + {stats.activeJobs} + + + {stats.completedJobs} + + + + + + - +
) } diff --git a/src/components/Dashboard/common/AuditLogTable.jsx b/src/components/Dashboard/common/AuditLogTable.jsx new file mode 100644 index 0000000..e77a32e --- /dev/null +++ b/src/components/Dashboard/common/AuditLogTable.jsx @@ -0,0 +1,214 @@ +import React, { forwardRef, useState } from 'react' +import { Typography, Space, Descriptions, Badge, Tag, Table } from 'antd' +import PropTypes from 'prop-types' +import IdText from './IdText' +import { AuditOutlined, LoadingOutlined } from '@ant-design/icons' +import TimeDisplay from '../common/TimeDisplay' + +const { Text } = Typography + +const formatPropertyName = (name) => { + return name + .replace(/([A-Z])/g, ' $1') + .replace(/^./, (str) => str.toUpperCase()) +} + +const isObjectId = (value) => { + return typeof value === 'string' && /^[0-9a-fA-F]{24}$/.test(value) +} + +const formatValue = (value, propertyName) => { + if (value === null || value === undefined || value === '') { + return n/a + } + + // Handle colors specifically + if (propertyName === 'color') { + return + } + + if (propertyName === 'state' && typeof value === 'object' && value.type) { + return ( + {value.type.charAt(0).toUpperCase() + value.type.slice(1)} + ) + } + + // Check if the value is a timestamp (ISO date string) + if ( + typeof value === 'string' && + /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value) + ) { + return + } + + if (typeof value === 'boolean' || value === true || value === false) { + return ( + + {value ? 'Yes' : 'No'} + + ) + } + + if (isObjectId(value)) { + return ( + + ) + } + + if (typeof value === 'object') { + return {JSON.stringify(value)} + } + + return {value} +} + +const AuditLogTable = forwardRef( + ({ items, loading, showTargetColumn, showOwnerColumn }, ref) => { + const [sortedInfo, setSortedInfo] = useState({ + columnKey: 'createdAt', + order: 'descend' + }) + + const handleChange = (pagination, filters, sorter) => { + setSortedInfo(sorter) + } + + const columns = [ + { + title: '', + key: 'icon', + width: 50, + render: () => + }, + { + title: 'ID', + dataIndex: '_id', + key: 'id', + width: 180, + render: (text) => , + sorter: (a, b) => a._id.localeCompare(b._id) + } + ] + + if (showOwnerColumn) { + columns.push( + { + title: 'Owner Name', + dataIndex: ['owner', 'name'], + key: 'name', + width: 200, + sorter: (a, b) => + (a.owner?.name || '').localeCompare(b.owner?.name || '') + }, + { + title: 'Owner', + key: 'owner', + width: 180, + render: (record) => ( + + ) + } + ) + } + + if (showTargetColumn) { + columns.push({ + title: 'Target', + key: 'target', + width: 180, + render: (record) => ( + + ) + }) + } + + columns.push({ + title: 'Properties', + dataIndex: 'type', + key: 'type', + width: 550, + render: (_, record) => { + const oldValue = record.oldValue || {} + const newValue = record.newValue || {} + + return ( + + {Object.keys(newValue).map((key) => ( + + + {formatValue(oldValue[key], key)} + → + {formatValue(newValue[key], key)} + + + ))} + + ) + } + }) + + columns.push({ + title: 'Timestamp', + dataIndex: 'createdAt', + key: 'createdAt', + width: 180, + defaultSortOrder: 'descend', + sorter: (a, b) => new Date(a.createdAt) - new Date(b.createdAt), + render: (createdAt) => { + if (createdAt) { + return + } else { + return 'n/a' + } + } + }) + + return ( +
}} + pagination={false} + scroll={{ x: 'max-content' }} + onChange={handleChange} + sortDirections={['ascend', 'descend']} + sortOrder={ + sortedInfo.columnKey === 'createdAt' ? sortedInfo.order : null + } + /> + ) + } +) + +AuditLogTable.displayName = 'AuditLogTable' + +AuditLogTable.propTypes = { + items: PropTypes.arrayOf(PropTypes.object).isRequired, + loading: PropTypes.bool, + showTargetColumn: PropTypes.bool, + showOwnerColumn: PropTypes.bool +} + +AuditLogTable.defaultProps = { + loading: false, + showTargetColumn: true, + showOwnerColumn: true +} + +export default AuditLogTable diff --git a/src/components/Dashboard/common/DashboardBreadcrumb.jsx b/src/components/Dashboard/common/DashboardBreadcrumb.jsx index 11c2a51..175e036 100644 --- a/src/components/Dashboard/common/DashboardBreadcrumb.jsx +++ b/src/components/Dashboard/common/DashboardBreadcrumb.jsx @@ -13,8 +13,8 @@ const breadcrumbNameMap = { '/dashboard/production/printers': 'Printers', '/dashboard/production/printers/control': 'Control', '/dashboard/production/printers/info': 'Info', - '/dashboard/production/printjobs': 'Print Jobs', - '/dashboard/production/printjobs/info': 'Info', + '/dashboard/production/jobs': 'Print Jobs', + '/dashboard/production/jobs/info': 'Info', '/dashboard/production/gcodefiles': 'G Code Files', '/dashboard/production/gcodefiles/info': 'Info', '/dashboard/management/filaments': 'Filaments', @@ -27,7 +27,10 @@ const breadcrumbNameMap = { '/dashboard/management/vendors/info': 'Info', '/dashboard/management/materials': 'Materials', '/dashboard/management/materials/info': 'Info', + '/dashboard/management/notetypes': 'Note Types', + '/dashboard/management/notetypes/info': 'Info', '/dashboard/management/settings': 'Settings', + '/dashboard/management/auditlogs': 'Audit Logs', '/dashboard/inventory/filamentstocks': 'Filament Stocks', '/dashboard/inventory/filamentstocks/info': 'Info', '/dashboard/inventory/partstocks': 'Part Stocks', @@ -70,7 +73,7 @@ const DashboardBreadcrumb = () => { }) return ( - + + + +