diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ed11ea5eb..1a5af3aff 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,7 +18,7 @@ "@rive-app/react-canvas": "4.8.3", "@tanstack/react-query": "5.29.2", "@tippyjs/react": "4.2.6", - "antd": "5.18.1", + "antd": "5.20.2", "axios": "1.6.8", "dayjs": "1.11.11", "fast-fuzzy": "1.12.0", @@ -139,6 +139,33 @@ "react-dom": ">=16.0.0" } }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.0.3.tgz", + "integrity": "sha512-BrztZZKuoYcJK8uEH40ylBemf/Mu/QPiDos56g2bv6eUoniQkgQHOCOvA3+pncoFO1TaS8xcUCIqGzDA0I+ZVQ==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.21.0", + "@babel/runtime": "^7.23.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/fast-color": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz", + "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + } + }, "node_modules/@ant-design/icons": { "version": "5.3.7", "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.3.7.tgz", @@ -1312,9 +1339,10 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2220,6 +2248,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz", "integrity": "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.24.4" }, @@ -2228,12 +2257,13 @@ } }, "node_modules/@rc-component/color-picker": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-1.5.3.tgz", - "integrity": "sha512-+tGGH3nLmYXTalVe0L8hSZNs73VTP5ueSHwUlDC77KKRaN7G4DS4wcpG5DTDzdcV/Yas+rzA6UGgIyzd8fS4cw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz", + "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==", + "license": "MIT", "dependencies": { + "@ant-design/fast-color": "^2.0.6", "@babel/runtime": "^7.23.6", - "@ctrl/tinycolor": "^3.6.1", "classnames": "^2.2.6", "rc-util": "^5.38.1" }, @@ -2259,6 +2289,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz", "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.0" }, @@ -2300,6 +2331,24 @@ "react-dom": ">=16.9.0" } }, + "node_modules/@rc-component/qrcode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.0.0.tgz", + "integrity": "sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, "node_modules/@rc-component/tour": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.0.tgz", @@ -2320,9 +2369,10 @@ } }, "node_modules/@rc-component/trigger": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.0.tgz", - "integrity": "sha512-QarBCji02YE9aRFhZgRZmOpXBj0IZutRippsVBv85sxvG4FGk/vRxwAlkn3MS9zK5mwbETd86mAVg2tKqTkdJA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.1.tgz", + "integrity": "sha512-fuU11J8pOt6+U/tU6/CAv8wjCwGaNeRk9f5k8HQth7JBbJ6MMH62WhGycVW75VnXfBZgL/7kO+wbiO2Xc9U9sQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.2", "@rc-component/portal": "^1.1.0", @@ -3980,58 +4030,60 @@ } }, "node_modules/antd": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/antd/-/antd-5.18.1.tgz", - "integrity": "sha512-l762vsoIpA3xsVsbIExlce3hgFgiLdflGEgdo8NFnq17Qq3fHIMJklGzM8WNpdJJ4iUk4FzritmSSnHk0Y5kZA==", - "dependencies": { - "@ant-design/colors": "^7.0.2", - "@ant-design/cssinjs": "^1.19.1", - "@ant-design/icons": "^5.3.7", + "version": "5.20.2", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.20.2.tgz", + "integrity": "sha512-9d6Bs5ZKIV+JhB0eD7KxYnIfnhUh86kNtTGIuNiIxHFUhbuyT1DXN2SuMksDmtSfuRYZ82/C4hq+OJjWNNbmHg==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^7.1.0", + "@ant-design/cssinjs": "^1.21.0", + "@ant-design/cssinjs-utils": "^1.0.3", + "@ant-design/icons": "^5.4.0", "@ant-design/react-slick": "~1.1.2", - "@babel/runtime": "^7.24.5", + "@babel/runtime": "^7.24.8", "@ctrl/tinycolor": "^3.6.1", - "@rc-component/color-picker": "~1.5.3", + "@rc-component/color-picker": "~2.0.1", "@rc-component/mutate-observer": "^1.1.0", + "@rc-component/qrcode": "~1.0.0", "@rc-component/tour": "~1.15.0", - "@rc-component/trigger": "^2.2.0", + "@rc-component/trigger": "^2.2.1", "classnames": "^2.5.1", "copy-to-clipboard": "^3.3.3", - "dayjs": "^1.11.10", - "qrcode.react": "^3.1.0", - "rc-cascader": "~3.26.0", + "dayjs": "^1.11.11", + "rc-cascader": "~3.27.0", "rc-checkbox": "~3.3.0", "rc-collapse": "~3.7.3", "rc-dialog": "~9.5.2", "rc-drawer": "~7.2.0", "rc-dropdown": "~4.2.0", - "rc-field-form": "~2.2.1", + "rc-field-form": "~2.4.0", "rc-image": "~7.9.0", - "rc-input": "~1.5.1", - "rc-input-number": "~9.1.0", - "rc-mentions": "~2.14.0", - "rc-menu": "~9.14.0", - "rc-motion": "^2.9.1", + "rc-input": "~1.6.3", + "rc-input-number": "~9.2.0", + "rc-mentions": "~2.15.0", + "rc-menu": "~9.14.1", + "rc-motion": "^2.9.2", "rc-notification": "~5.6.0", - "rc-pagination": "~4.0.4", - "rc-picker": "~4.5.0", + "rc-pagination": "~4.2.0", + "rc-picker": "~4.6.13", "rc-progress": "~4.0.0", "rc-rate": "~2.13.0", "rc-resize-observer": "^1.4.0", "rc-segmented": "~2.3.0", - "rc-select": "~14.14.0", - "rc-slider": "~10.6.2", + "rc-select": "~14.15.1", + "rc-slider": "~11.1.5", "rc-steps": "~6.0.1", "rc-switch": "~4.1.0", "rc-table": "~7.45.7", - "rc-tabs": "~15.1.0", - "rc-textarea": "~1.7.0", + "rc-tabs": "~15.1.1", + "rc-textarea": "~1.8.1", "rc-tooltip": "~6.2.0", - "rc-tree": "~5.8.7", - "rc-tree-select": "~5.21.0", - "rc-upload": "~4.5.2", - "rc-util": "^5.41.0", + "rc-tree": "~5.8.8", + "rc-tree-select": "~5.22.1", + "rc-upload": "~4.7.0", + "rc-util": "^5.43.0", "scroll-into-view-if-needed": "^3.1.0", - "throttle-debounce": "^5.0.0" + "throttle-debounce": "^5.0.2" }, "funding": { "type": "opencollective", @@ -4042,17 +4094,47 @@ "react-dom": ">=16.9.0" } }, + "node_modules/antd/node_modules/@ant-design/colors": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.1.0.tgz", + "integrity": "sha512-MMoDGWn1y9LdQJQSHiCC20x3uZ3CwQnv9QMz6pCmJOrqdgM9YxsoVVY0wtrdXbmfSgnV0KNk6zi09NAhMR2jvg==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^3.6.1" + } + }, + "node_modules/antd/node_modules/@ant-design/icons": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.4.0.tgz", + "integrity": "sha512-QZbWC5xQYexCI5q4/fehSEkchJr5UGtvAJweT743qKUQQGs9IH2DehNLP49DJ3Ii9m9CijD2HN6fNy3WKhIFdA==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, "node_modules/antd/node_modules/rc-picker": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.5.0.tgz", - "integrity": "sha512-suqz9bzuhBQlf7u+bZd1bJLPzhXpk12w6AjQ9BTPTiFwexVZgUKViG1KNLyfFvW6tCUZZK0HmCCX7JAyM+JnCg==", + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.6.13.tgz", + "integrity": "sha512-yi4JWPGjm420Q8rHjZ6YNy2c5IfV+9EAzx2pewVRPOjJqfg7uifO/Z0uqxdl/h6AhBocuvRvtlyz6ehrAvTq7A==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.10.1", + "@babel/runtime": "^7.24.7", "@rc-component/trigger": "^2.0.0", "classnames": "^2.2.1", "rc-overflow": "^1.3.2", "rc-resize-observer": "^1.4.0", - "rc-util": "^5.38.1" + "rc-util": "^5.43.0" }, "engines": { "node": ">=8.x" @@ -4146,7 +4228,8 @@ "node_modules/array-tree-filter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz", - "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==" + "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==", + "license": "MIT" }, "node_modules/array-union": { "version": "2.1.0", @@ -9374,14 +9457,6 @@ "node": ">=6" } }, - "node_modules/qrcode.react": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-3.1.0.tgz", - "integrity": "sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -9436,14 +9511,15 @@ } }, "node_modules/rc-cascader": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.26.0.tgz", - "integrity": "sha512-L1dml383TPSJD1I11YwxuVbmqaJY64psZqFp1ETlgl3LEOwDu76Cyl11fw5dmjJhMlUWwM5dECQfqJgfebhUjg==", + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.27.0.tgz", + "integrity": "sha512-z5uq8VvQadFUBiuZJ7YF5UAUGNkZtdEtcEYiIA94N/Kc2MIKr6lEbN5HyVddvYSgwWlKqnL6pH5bFXFuIK3MNg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", "array-tree-filter": "^2.1.0", "classnames": "^2.3.1", - "rc-select": "~14.14.0", + "rc-select": "~14.15.0", "rc-tree": "~5.8.1", "rc-util": "^5.37.0" }, @@ -9529,9 +9605,10 @@ } }, "node_modules/rc-field-form": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.2.1.tgz", - "integrity": "sha512-uoNqDoR7A4tn4QTSqoWPAzrR7ZwOK5I+vuZ/qdcHtbKx+ZjEsTg7QXm2wk/jalDiSksAQmATxL0T5LJkRREdIA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.4.0.tgz", + "integrity": "sha512-XZ/lF9iqf9HXApIHQHqzJK5v2w4mkUMsVqAzOyWVzoiwwXEavY6Tpuw7HavgzIoD+huVff4JghSGcgEfX6eycg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.0", "@rc-component/async-validator": "^5.0.3", @@ -9563,9 +9640,10 @@ } }, "node_modules/rc-input": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.5.1.tgz", - "integrity": "sha512-+nOzQJDeIfIpNP/SgY45LXSKbuMlp4Yap2y8c+ZpU7XbLmNzUd6+d5/S75sA/52jsVE6S/AkhkkDEAOjIu7i6g==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.6.3.tgz", + "integrity": "sha512-wI4NzuqBS8vvKr8cljsvnTUqItMfG1QbJoxovCgL+DX4eVUcHIjVwharwevIxyy7H/jbLryh+K7ysnJr23aWIA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.11.1", "classnames": "^2.2.1", @@ -9577,14 +9655,15 @@ } }, "node_modules/rc-input-number": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.1.0.tgz", - "integrity": "sha512-NqJ6i25Xn/AgYfVxynlevIhX3FuKlMwIFpucGG1h98SlK32wQwDK0zhN9VY32McOmuaqzftduNYWWooWz8pXQA==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.2.0.tgz", + "integrity": "sha512-5XZFhBCV5f9UQ62AZ2hFbEY8iZT/dm23Q1kAg0H8EvOgD3UDbYYJAayoVIkM3lQaCqYAW5gV0yV3vjw1XtzWHg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/mini-decimal": "^1.0.1", "classnames": "^2.2.5", - "rc-input": "~1.5.0", + "rc-input": "~1.6.0", "rc-util": "^5.40.1" }, "peerDependencies": { @@ -9593,16 +9672,17 @@ } }, "node_modules/rc-mentions": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.14.0.tgz", - "integrity": "sha512-qKR59FMuF8PK4ZqsbWX3UuA5P1M/snzyqV6Yt3y1DCFbCEdqUGIBgQp6vEfLCO6Z0RoRFlzXtCeSlBTcDDpg1A==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.15.0.tgz", + "integrity": "sha512-f5v5i7VdqvBDXbphoqcQWmXDif2Msd2arritVoWybrVDuHE6nQ7XCYsybHbV//WylooK52BFDouFvyaRDtXZEw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.22.5", "@rc-component/trigger": "^2.0.0", "classnames": "^2.2.6", - "rc-input": "~1.5.0", + "rc-input": "~1.6.0", "rc-menu": "~9.14.0", - "rc-textarea": "~1.7.0", + "rc-textarea": "~1.8.0", "rc-util": "^5.34.1" }, "peerDependencies": { @@ -9675,9 +9755,10 @@ } }, "node_modules/rc-pagination": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-4.0.4.tgz", - "integrity": "sha512-GGrLT4NgG6wgJpT/hHIpL9nELv27A1XbSZzECIuQBQTVSf4xGKxWr6I/jhpRPauYEWEbWVw22ObG6tJQqwJqWQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-4.2.0.tgz", + "integrity": "sha512-V6qeANJsT6tmOcZ4XiUmj8JXjRLbkusuufpuoBw2GiAn94fIixYjFLmbruD1Sbhn8fPLDnWawPp4CN37zQorvw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "^2.3.2", @@ -9788,9 +9869,10 @@ } }, "node_modules/rc-select": { - "version": "14.14.0", - "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.14.0.tgz", - "integrity": "sha512-Uo2wulrjoPPRLCPd7zlK4ZFVJxlTN//yp1xWP/U+TUOQCyXrT+Duvq/Si5OzVcmQyWAUSbsplc2OwNNhvbOeKQ==", + "version": "14.15.1", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.15.1.tgz", + "integrity": "sha512-mGvuwW1RMm1NCSI8ZUoRoLRK51R2Nb+QJnmiAvbDRcjh2//ulCkxeV6ZRFTECPpE1t2DPfyqZMPw90SVJzQ7wQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/trigger": "^2.1.1", @@ -9809,9 +9891,10 @@ } }, "node_modules/rc-slider": { - "version": "10.6.2", - "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-10.6.2.tgz", - "integrity": "sha512-FjkoFjyvUQWcBo1F3RgSglky3ar0+qHLM41PlFVYB4Bj3RD8E/Mv7kqMouLFBU+3aFglMzzctAIWRwajEuueSw==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.5.tgz", + "integrity": "sha512-b77H5PbjMKsvkYXAYIkn50QuFX6ICQmCTibDinI9q+BHx65/TV4TeU25+oadhSRzykxs0/vBWeKBwRyySOeWlg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "^2.2.5", @@ -9898,13 +9981,14 @@ } }, "node_modules/rc-textarea": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.7.0.tgz", - "integrity": "sha512-UxizYJkWkmxP3zofXgc487QiGyDmhhheDLLjIWbFtDmiru1ls30KpO8odDaPyqNUIy9ugj5djxTEuezIn6t3Jg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.8.1.tgz", + "integrity": "sha512-bm36N2ZqwZAP60ZQg2OY9mPdqWC+m6UTjHc+CqEZOxb3Ia29BGHazY/s5bI8M4113CkqTzhtFUDNA078ZiOx3Q==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "^2.2.1", - "rc-input": "~1.5.0", + "rc-input": "~1.6.0", "rc-resize-observer": "^1.0.0", "rc-util": "^5.27.0" }, @@ -9931,6 +10015,7 @@ "version": "5.8.8", "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.8.8.tgz", "integrity": "sha512-S+mCMWo91m5AJqjz3PdzKilGgbFm7fFJRFiTDOcoRbD7UfMOPnerXwMworiga0O2XIo383UoWuEfeHs1WOltag==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "2.x", @@ -9947,13 +10032,14 @@ } }, "node_modules/rc-tree-select": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.21.0.tgz", - "integrity": "sha512-w+9qEu6zh0G3wt9N/hzWNSnqYH1i9mH1Nqxo0caxLRRFXF5yZWYmpCDoDTMdQM1Y4z3Q5yj08qyrPH/d4AtumA==", + "version": "5.22.1", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.22.1.tgz", + "integrity": "sha512-b8mAK52xEpRgS+b2PTapCt29GoIrO5cO8jB7AfHttFsIJfcnynY9FCtnYzURsKXJkGHbFY6UzSEB2I3TETtdWg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "2.x", - "rc-select": "~14.14.0", + "rc-select": "~14.15.0", "rc-tree": "~5.8.1", "rc-util": "^5.16.1" }, @@ -9963,9 +10049,10 @@ } }, "node_modules/rc-upload": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.5.2.tgz", - "integrity": "sha512-QO3ne77DwnAPKFn0bA5qJM81QBjQi0e0NHdkvpFyY73Bea2NfITiotqJqVjHgeYPOJu5lLVR32TNGP084aSoXA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.7.0.tgz", + "integrity": "sha512-eUwxYNHlsYe5vYhKFAUGrQG95JrnPzY+BmPi1Daq39fWNl/eOc7v4UODuWrVp2LFkQBuV3cMCG/I68iub6oBrg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", "classnames": "^2.2.5", diff --git a/frontend/package.json b/frontend/package.json index 9635575ca..8550653e6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,7 +14,7 @@ "@rive-app/react-canvas": "4.8.3", "@tanstack/react-query": "5.29.2", "@tippyjs/react": "4.2.6", - "antd": "5.18.1", + "antd": "5.20.2", "axios": "1.6.8", "dayjs": "1.11.11", "fast-fuzzy": "1.12.0", diff --git a/frontend/src/pages/DegreeWizard/DegreeStep/DegreeStep.test.tsx b/frontend/src/pages/DegreeWizard/DegreeStep/DegreeStep.test.tsx index dc66464ac..55c395620 100644 --- a/frontend/src/pages/DegreeWizard/DegreeStep/DegreeStep.test.tsx +++ b/frontend/src/pages/DegreeWizard/DegreeStep/DegreeStep.test.tsx @@ -19,13 +19,6 @@ axiosMock.onGet('/programs/getPrograms').reply(200, { const incrementStepMock = vi.fn(); const setDegreeInfoMock = vi.fn(); -const degreeInfo = { - programCode: '', - startYear: undefined, - endYear: undefined, - specs: [] -}; - describe('DegreeStep', () => { const useDispatchMock = vi.spyOn(hooks, 'useAppDispatch'); @@ -36,11 +29,7 @@ describe('DegreeStep', () => { it('should render', () => { renderWithProviders( - + ); expect(screen.getByText('What are you studying?')).toBeInTheDocument(); expect(screen.getByPlaceholderText('Search Degree')).toBeInTheDocument(); @@ -51,11 +40,7 @@ describe('DegreeStep', () => { useDispatchMock.mockReturnValue(dummyDispatch); renderWithProviders( - + ); expect(screen.getByPlaceholderText('Search Degree')).toBeInTheDocument(); userEvent.type(screen.getByPlaceholderText('Search Degree'), 'comp'); @@ -72,22 +57,14 @@ describe('DegreeStep', () => { it('should show no degree options on mount', async () => { renderWithProviders( - + ); expect(screen.queryByText('Computer Science')).not.toBeInTheDocument(); }); it('should search degree based on program code', async () => { renderWithProviders( - + ); userEvent.type(screen.getByPlaceholderText('Search Degree'), '3778'); expect(await screen.findByText('3778 Computer Science')).toBeInTheDocument(); @@ -95,11 +72,7 @@ describe('DegreeStep', () => { it('should search degree based on program name', async () => { renderWithProviders( - + ); userEvent.type(screen.getByPlaceholderText('Search Degree'), 'Computer Science'); expect(await screen.findByText('3778 Computer Science')).toBeInTheDocument(); @@ -107,11 +80,7 @@ describe('DegreeStep', () => { it('should search degree case insensitively', async () => { renderWithProviders( - + ); userEvent.type(screen.getByPlaceholderText('Search Degree'), 'computer science'); expect(await screen.findByText('3778 Computer Science')).toBeInTheDocument(); @@ -119,11 +88,7 @@ describe('DegreeStep', () => { it('should not show degree options if no match', async () => { renderWithProviders( - + ); userEvent.type(screen.getByPlaceholderText('Search Degree'), 'Economics'); expect(screen.queryByTestId('antd-degree-menu')).not.toBeInTheDocument(); diff --git a/frontend/src/pages/DegreeWizard/DegreeStep/DegreeStep.tsx b/frontend/src/pages/DegreeWizard/DegreeStep/DegreeStep.tsx index fffff8906..a0b283deb 100644 --- a/frontend/src/pages/DegreeWizard/DegreeStep/DegreeStep.tsx +++ b/frontend/src/pages/DegreeWizard/DegreeStep/DegreeStep.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { animated, useSpring } from '@react-spring/web'; import { useQuery } from '@tanstack/react-query'; -import { Input, Menu, Typography } from 'antd'; +import { Select, Typography } from 'antd'; import { fuzzy } from 'fast-fuzzy'; import { DegreeWizardPayload } from 'types/degreeWizard'; import { fetchAllDegrees } from 'utils/api/programsApi'; @@ -15,82 +15,84 @@ type SetState = React.Dispatch>; type Props = { incrementStep: (stepTo?: Steps) => void; - degreeInfo: DegreeWizardPayload; setDegreeInfo: SetState; }; -const DegreeStep = ({ incrementStep, degreeInfo, setDegreeInfo }: Props) => { - const [input, setInput] = useState(''); - const [options, setOptions] = useState([]); - +const DegreeStep = ({ incrementStep, setDegreeInfo }: Props) => { const allDegreesQuery = useQuery({ queryKey: ['programs'], queryFn: fetchAllDegrees, - select: (data) => data.programs + select: (data) => + Object.keys(data.programs).map((code) => ({ + label: `${code} ${data.programs[code]}`, + value: `${code} ${data.programs[code]}` + })) }); - const allDegrees = allDegreesQuery.data ?? {}; + const allDegrees = allDegreesQuery.data ?? []; - const onDegreeChange = async ({ key }: { key: string }) => { - setInput(key); + const onDegreeChange = async (key: string) => { setDegreeInfo((prev) => ({ ...prev, // key is of format `${programCode} - ${title}`; Need to extract code - programCode: key.slice(0, 4) + programCode: key.slice(0, 4), + specs: [] })); if (key) incrementStep(Steps.SPECS); }; + const props = useSpring(springProps); + + const [items, setItems] = useState<{ label: string; value: string }[]>([]); + const searchDegree = (newInput: string) => { - setInput(newInput); + // List all degrees if input is empty + if (newInput === '') { + setItems(allDegrees); + return; + } - const fuzzedDegrees = Object.keys(allDegrees) - .map((code) => `${code} ${allDegrees[code]}`) - .map((title) => { + // score is a number between 0 and 1 where 1 is a perfect match + const fuzzedDegrees = allDegrees + .map((item) => { return { - distance: fuzzy(newInput, title), - name: title + score: fuzzy(newInput, item.label), + ...item }; - }); + }) + .filter((pair) => pair.score > 0.5); - fuzzedDegrees.sort((a, b) => a.name.length - b.name.length); - fuzzedDegrees.sort((a, b) => b.distance - a.distance); + // Shorter name with greater or equal score means better match + fuzzedDegrees.sort((a, b) => { + if (a.score > b.score) return -1; + if (a.score < b.score) return 1; + if (a.label.length < b.label.length) return -1; + if (a.label.length > b.label.length) return 1; + return 0; + }); - setOptions(fuzzedDegrees.splice(0, 8).map((pair) => pair.name)); + setItems(fuzzedDegrees); }; - const props = useSpring(springProps); - - const items = options.map((degreeName) => ({ - label: degreeName, - key: degreeName - })); - return ( What are you studying? - searchDegree(e.target.value)} + style={{ width: '100%' }} + onSelect={onDegreeChange} + options={items} + filterOption={false} + onSearch={searchDegree} + // items should be initialised with all degrees + onClick={() => searchDegree('')} /> - {input && options && ( - - )} ); diff --git a/frontend/src/pages/DegreeWizard/DegreeWizard.tsx b/frontend/src/pages/DegreeWizard/DegreeWizard.tsx index 60d756156..543deb450 100644 --- a/frontend/src/pages/DegreeWizard/DegreeWizard.tsx +++ b/frontend/src/pages/DegreeWizard/DegreeWizard.tsx @@ -113,11 +113,7 @@ const DegreeWizard = () => { {currStep >= Steps.DEGREE && ( - + )} {specs.map( (stepName, index) => diff --git a/frontend/src/pages/DegreeWizard/SpecialisationStep/SpecialisationStep.tsx b/frontend/src/pages/DegreeWizard/SpecialisationStep/SpecialisationStep.tsx index d4dd1a0d3..d4d8117de 100644 --- a/frontend/src/pages/DegreeWizard/SpecialisationStep/SpecialisationStep.tsx +++ b/frontend/src/pages/DegreeWizard/SpecialisationStep/SpecialisationStep.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { animated, useSpring } from '@react-spring/web'; import { useQuery } from '@tanstack/react-query'; -import type { MenuProps } from 'antd'; -import { Button, Typography } from 'antd'; +import { Button, Select, Typography } from 'antd'; import { DegreeWizardPayload } from 'types/degreeWizard'; import { getSpecialisationsForProgram } from 'utils/api/specsApi'; import openNotification from 'utils/openNotification'; @@ -10,7 +9,6 @@ import Spinner from 'components/Spinner'; import springProps from '../common/spring'; import Steps from '../common/steps'; import CS from '../common/styles'; -import S from './styles'; const { Title } = Typography; @@ -55,29 +53,25 @@ const SpecialisationStep = ({ }); const options = specsQuery.data; - const menuItems: MenuProps['items'] = options - ? Object.keys(options).map((program, index) => ({ + type SelectGroup = { + label: string; + note: string | undefined; + children: { + label: string; + value: string; + }[]; + }; + + const selectGroups: SelectGroup[] | undefined = options + ? Object.keys(options).map((program) => ({ label: `${type.replace(/^\w/, (c) => c.toUpperCase())} for ${program}`, - key: index, - children: options[program].notes - ? [ - { - label: `Note: ${options[program].notes}`, - type: 'group', - children: Object.keys(options[program].specs) - .sort() - .map((spec) => ({ - label: `${spec} ${options[program].specs[spec]}`, - key: spec - })) - } - ] - : Object.keys(options[program].specs) - .sort() - .map((spec) => ({ - label: `${spec} ${options[program].specs[spec]}`, - key: spec - })) + note: options[program].notes, + children: Object.keys(options[program].specs) + .sort() + .map((spec) => ({ + label: `${spec} ${options[program].specs[spec]}`, + value: spec + })) })) : undefined; @@ -126,21 +120,27 @@ const SpecialisationStep = ({ )} - {menuItems ? ( - handleAddSpecialisation(e.key)} - onDeselect={(e) => handleRemoveSpecialisation(e.key)} - selectedKeys={degreeInfo.specs} - defaultOpenKeys={['0']} - mode="inline" - style={{ - gap: '10px', - display: 'flex', - flexDirection: 'column' - }} - items={menuItems} - /> + {selectGroups ? ( + selectGroups.map((group) => ( + + {group.label} + {group.note &&

{group.note}

} +