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}
}
+
+ ))
) : (
)}
diff --git a/frontend/src/pages/DegreeWizard/StartBrowsingStep/StartBrowsingStep.tsx b/frontend/src/pages/DegreeWizard/StartBrowsingStep/StartBrowsingStep.tsx
index 080de64a3..a41ab6283 100644
--- a/frontend/src/pages/DegreeWizard/StartBrowsingStep/StartBrowsingStep.tsx
+++ b/frontend/src/pages/DegreeWizard/StartBrowsingStep/StartBrowsingStep.tsx
@@ -34,7 +34,10 @@ const StartBrowsingStep = ({ degreeInfo }: Props) => {
navigate('/course-selector');
},
onError: (err) => {
- // TODO: Give the user a notification for stuff like this
+ openNotification({
+ type: 'error',
+ message: 'Error setting up degree, ensure your specialisations are valid.'
+ });
// eslint-disable-next-line no-console
console.error('Error at resetDegreeMutation: ', err);
}