Bläddra i källkod

Merge branch 'staging'

Owen Diffey 3 år sedan
förälder
incheckning
7e1f722201

+ 7 - 0
CHANGELOG.md

@@ -1,5 +1,12 @@
 # Changelog
 
+## [v3.6.0] - 2022-06-12
+
+This release includes all changes from v3.6.0-rc1, in addition to the following. Upgrade instructions can be found at [.wiki/Upgrading](.wiki/Upgrading.md).
+
+### Fixed
+- fix: Removed tag="transition-group" from draggable components
+
 ## [v3.6.0-rc1] - 2022-06-05
 
 Upgrade instructions can be found at [.wiki/Upgrading](.wiki/Upgrading.md).

+ 0 - 1
backend/index.js

@@ -91,7 +91,6 @@ class JobManager {
 	 * @param {object} job - the job object
 	 */
 	removeJob(job) {
-		return;
 		if (!this.jobs[job.module.name]) this.jobs[job.module.name] = {};
 		delete this.jobs[job.module.name][job.toString()];
 	}

+ 54 - 4
backend/logic/actions/playlists.js

@@ -2545,6 +2545,23 @@ export default {
 	 * @param {Function} cb - gets called with the result
 	 */
 	clearAndRefillAllArtistPlaylists: isAdminRequired(async function index(session, cb) {
+		this.keepLongJob();
+		this.publishProgress({
+			status: "started",
+			title: "Clear and refill all artist playlists",
+			message: "Clearing and refilling all artist playlists.",
+			id: this.toString()
+		});
+		await CacheModule.runJob("RPUSH", { key: `longJobs.${session.userId}`, value: this.toString() }, this);
+		await CacheModule.runJob(
+			"PUB",
+			{
+				channel: "longJob.added",
+				value: { jobId: this.toString(), userId: session.userId }
+			},
+			this
+		);
+
 		async.waterfall(
 			[
 				next => {
@@ -2562,6 +2579,10 @@ export default {
 						playlists,
 						1,
 						(playlist, next) => {
+							this.publishProgress({
+								status: "update",
+								message: `Clearing and refilling "${playlist._id}"`
+							});
 							PlaylistsModule.runJob(
 								"CLEAR_AND_REFILL_ARTIST_PLAYLIST",
 								{ playlistId: playlist._id },
@@ -2605,7 +2626,10 @@ export default {
 						"PLAYLIST_CLEAR_AND_REFILL_ALL_ARTIST_PLAYLISTS",
 						`Clearing and refilling all artist playlists failed for user "${session.userId}". "${err}"`
 					);
-
+					this.publishProgress({
+						status: "error",
+						message: err
+					});
 					return cb({ status: "error", message: err });
 				}
 
@@ -2614,7 +2638,10 @@ export default {
 					"PLAYLIST_CLEAR_AND_REFILL_ALL_ARTIST_PLAYLISTS",
 					`Successfully cleared and refilled all artist playlists for user "${session.userId}".`
 				);
-
+				this.publishProgress({
+					status: "success",
+					message: "Playlists have been successfully cleared and refilled."
+				});
 				return cb({
 					status: "success",
 					message: "Playlists have been successfully cleared and refilled"
@@ -2699,6 +2726,23 @@ export default {
 	 * @param {Function} cb - gets called with the result
 	 */
 	createMissingArtistPlaylists: isAdminRequired(async function index(session, cb) {
+		this.keepLongJob();
+		this.publishProgress({
+			status: "started",
+			title: "Create missing artist playlists",
+			message: "Creating missing artist playlists.",
+			id: this.toString()
+		});
+		await CacheModule.runJob("RPUSH", { key: `longJobs.${session.userId}`, value: this.toString() }, this);
+		await CacheModule.runJob(
+			"PUB",
+			{
+				channel: "longJob.added",
+				value: { jobId: this.toString(), userId: session.userId }
+			},
+			this
+		);
+
 		async.waterfall(
 			[
 				next => {
@@ -2720,7 +2764,10 @@ export default {
 						"PLAYLIST_CREATE_MISSING_ARTIST_PLAYLISTS",
 						`Creating missing artist playlists failed for user "${session.userId}". "${err}"`
 					);
-
+					this.publishProgress({
+						status: "error",
+						message: err
+					});
 					return cb({ status: "error", message: err });
 				}
 
@@ -2729,7 +2776,10 @@ export default {
 					"PLAYLIST_CREATE_MISSING_ARTIST_PLAYLISTS",
 					`Successfully created missing artist playlists for user "${session.userId}".`
 				);
-
+				this.publishProgress({
+					status: "success",
+					message: "Missing artist playlists have been successfully created."
+				});
 				return cb({
 					status: "success",
 					message: "Missing artist playlists have been successfully created"

+ 1 - 1
backend/logic/stations.js

@@ -562,7 +562,7 @@ class _StationsModule extends CoreClass {
 								if (err) next(err);
 								else {
 									const newSongsToAdd = songsToAdd.map(song =>
-										songs.find(newSong => newSong._id.toString() === song._id.toString())
+										songs.find(newSong => newSong.youtubeId === song.youtubeId)
 									);
 									next(null, currentSongs, newSongsToAdd, currentSongIndex);
 								}

+ 92 - 25
backend/package-lock.json

@@ -1,16 +1,16 @@
 {
   "name": "musare-backend",
-  "version": "3.6.0-rc1",
+  "version": "3.6.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "musare-backend",
-      "version": "3.6.0-rc1",
+      "version": "3.6.0",
       "license": "GPL-3.0",
       "dependencies": {
         "async": "^3.2.3",
-        "axios": "^0.26.1",
+        "axios": "^0.27.2",
         "bcrypt": "^5.0.1",
         "bluebird": "^3.7.2",
         "body-parser": "^1.20.0",
@@ -23,7 +23,7 @@
         "nodemailer": "^6.7.5",
         "oauth": "^0.9.15",
         "redis": "^3.1.2",
-        "retry-axios": "^2.6.0",
+        "retry-axios": "^3.0.0",
         "sha256": "^0.2.0",
         "socks": "^2.6.2",
         "underscore": "^1.13.4",
@@ -166,9 +166,9 @@
       "dev": true
     },
     "node_modules/@types/node": {
-      "version": "17.0.39",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.39.tgz",
-      "integrity": "sha512-JDU3YLlnPK3WDao6/DlXLOgSNpG13ct+CwIO17V8q0/9fWJyeMJJ/VyZ1lv8kDprihvZMydzVwf0tQOqGiY2Nw=="
+      "version": "17.0.40",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.40.tgz",
+      "integrity": "sha512-UXdBxNGqTMtm7hCwh9HtncFVLrXoqA3oJW30j6XWp5BH/wu3mVeaxo7cq5benFdBw34HB3XDT2TRPI7rXZ+mDg=="
     },
     "node_modules/@types/webidl-conversions": {
       "version": "6.1.1",
@@ -363,12 +363,18 @@
       "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz",
       "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="
     },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
     "node_modules/axios": {
-      "version": "0.26.1",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
-      "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
       "dependencies": {
-        "follow-redirects": "^1.14.8"
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
       }
     },
     "node_modules/balanced-match": {
@@ -558,6 +564,17 @@
         "color-support": "bin.js"
       }
     },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/comment-parser": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz",
@@ -704,6 +721,14 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/delegates": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -1402,6 +1427,19 @@
         "node": ">=8.0.0"
       }
     },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/forwarded": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -2845,11 +2883,11 @@
       }
     },
     "node_modules/retry-axios": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-2.6.0.tgz",
-      "integrity": "sha512-pOLi+Gdll3JekwuFjXO3fTq+L9lzMQGcSq7M5gIjExcl3Gu1hd4XXuf5o3+LuSBsaULQH7DiNbsqPd1chVpQGQ==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-3.0.0.tgz",
+      "integrity": "sha512-/jRv4nCDytusFZIY+zD6aKP29+wpgnbjfap5B76rqvgROWfONG5Bf7vCXx7grhxJwYGLlumraKZQUMzNgfXNCg==",
       "engines": {
-        "node": ">=10.7.0"
+        "node": ">=14"
       },
       "peerDependencies": {
         "axios": "*"
@@ -3589,9 +3627,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "17.0.39",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.39.tgz",
-      "integrity": "sha512-JDU3YLlnPK3WDao6/DlXLOgSNpG13ct+CwIO17V8q0/9fWJyeMJJ/VyZ1lv8kDprihvZMydzVwf0tQOqGiY2Nw=="
+      "version": "17.0.40",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.40.tgz",
+      "integrity": "sha512-UXdBxNGqTMtm7hCwh9HtncFVLrXoqA3oJW30j6XWp5BH/wu3mVeaxo7cq5benFdBw34HB3XDT2TRPI7rXZ+mDg=="
     },
     "@types/webidl-conversions": {
       "version": "6.1.1",
@@ -3738,12 +3776,18 @@
       "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz",
       "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="
     },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
     "axios": {
-      "version": "0.26.1",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
-      "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
       "requires": {
-        "follow-redirects": "^1.14.8"
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
       }
     },
     "balanced-match": {
@@ -3870,6 +3914,14 @@
       "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
       "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
     },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
     "comment-parser": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz",
@@ -3986,6 +4038,11 @@
         "object-keys": "^1.1.1"
       }
     },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+    },
     "delegates": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -4522,6 +4579,16 @@
         "signal-exit": "^3.0.2"
       }
     },
+    "form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      }
+    },
     "forwarded": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -5542,9 +5609,9 @@
       "dev": true
     },
     "retry-axios": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-2.6.0.tgz",
-      "integrity": "sha512-pOLi+Gdll3JekwuFjXO3fTq+L9lzMQGcSq7M5gIjExcl3Gu1hd4XXuf5o3+LuSBsaULQH7DiNbsqPd1chVpQGQ==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-3.0.0.tgz",
+      "integrity": "sha512-/jRv4nCDytusFZIY+zD6aKP29+wpgnbjfap5B76rqvgROWfONG5Bf7vCXx7grhxJwYGLlumraKZQUMzNgfXNCg==",
       "requires": {}
     },
     "rimraf": {

+ 3 - 3
backend/package.json

@@ -1,7 +1,7 @@
 {
   "name": "musare-backend",
   "private": true,
-  "version": "3.6.0-rc1",
+  "version": "3.6.0",
   "type": "module",
   "description": "An open-source collaborative music listening and catalogue curation application. Currently supporting YouTube based content.",
   "main": "index.js",
@@ -16,7 +16,7 @@
   },
   "dependencies": {
     "async": "^3.2.3",
-    "axios": "^0.26.1",
+    "axios": "^0.27.2",
     "bcrypt": "^5.0.1",
     "bluebird": "^3.7.2",
     "body-parser": "^1.20.0",
@@ -29,7 +29,7 @@
     "nodemailer": "^6.7.5",
     "oauth": "^0.9.15",
     "redis": "^3.1.2",
-    "retry-axios": "^2.6.0",
+    "retry-axios": "^3.0.0",
     "sha256": "^0.2.0",
     "socks": "^2.6.2",
     "underscore": "^1.13.4",

+ 2 - 1
frontend/Dockerfile

@@ -2,11 +2,12 @@ FROM node:16.15 AS musare_frontend
 
 ARG FRONTEND_MODE=prod
 ENV FRONTEND_MODE=${FRONTEND_MODE}
+ENV SUPPRESS_NO_CONFIG_WARNING=1
 
 RUN apt-get update
 RUN apt-get install nginx -y
 
-RUN npm install -g webpack@5.72.0 webpack-cli@4.9.2
+RUN npm install -g webpack@5.73.0 webpack-cli@4.9.2
 
 RUN mkdir -p /opt/app
 WORKDIR /opt/app

+ 137 - 366
frontend/package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "musare-frontend",
-  "version": "3.6.0-rc1",
+  "version": "3.6.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "musare-frontend",
-      "version": "3.6.0-rc1",
+      "version": "3.6.0",
       "license": "GPL-3.0",
       "dependencies": {
         "@babel/runtime": "^7.18.3",
@@ -21,7 +21,7 @@
         "marked": "^4.0.16",
         "normalize.css": "^8.0.1",
         "toasters": "^2.3.1",
-        "vue": "3.2.31",
+        "vue": "^3.2.36",
         "vue-chartjs": "^4.1.1",
         "vue-content-loader": "^2.0.1",
         "vue-json-pretty": "^2.1.0",
@@ -30,7 +30,7 @@
         "vue-tippy": "^6.0.0-alpha.57",
         "vuedraggable": "^4.1.0",
         "vuex": "^4.0.2",
-        "webpack": "5.72.0",
+        "webpack": "^5.73.0",
         "webpack-bundle-analyzer": "^4.5.0",
         "webpack-merge": "^5.8.0"
       },
@@ -48,11 +48,11 @@
         "eslint-config-prettier": "^8.5.0",
         "eslint-plugin-import": "^2.26.0",
         "eslint-plugin-prettier": "^4.0.0",
-        "eslint-plugin-vue": "^8.7.1",
+        "eslint-plugin-vue": "^9.1.0",
         "eslint-webpack-plugin": "^3.1.1",
         "fetch": "^1.1.0",
         "less": "^4.1.2",
-        "less-loader": "^10.2.0",
+        "less-loader": "^11.0.0",
         "prettier": "^2.6.2",
         "style-loader": "^3.3.1",
         "style-resources-loader": "^1.5.0",
@@ -1939,9 +1939,9 @@
       "dev": true
     },
     "node_modules/@types/node": {
-      "version": "17.0.39",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.39.tgz",
-      "integrity": "sha512-JDU3YLlnPK3WDao6/DlXLOgSNpG13ct+CwIO17V8q0/9fWJyeMJJ/VyZ1lv8kDprihvZMydzVwf0tQOqGiY2Nw=="
+      "version": "17.0.40",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.40.tgz",
+      "integrity": "sha512-UXdBxNGqTMtm7hCwh9HtncFVLrXoqA3oJW30j6XWp5BH/wu3mVeaxo7cq5benFdBw34HB3XDT2TRPI7rXZ+mDg=="
     },
     "node_modules/@types/qs": {
       "version": "6.9.7",
@@ -2002,7 +2002,6 @@
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.36.tgz",
       "integrity": "sha512-bbyZM5hvBicv0PW3KUfVi+x3ylHnfKG7DOn5wM+f2OztTzTjLEyBb/5yrarIYpmnGitVGbjZqDbODyW4iK8hqw==",
-      "dev": true,
       "dependencies": {
         "@babel/parser": "^7.16.4",
         "@vue/shared": "3.2.36",
@@ -2014,7 +2013,6 @@
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.36.tgz",
       "integrity": "sha512-tcOTAOiW4s24QLnq+ON6J+GRONXJ+A/mqKCORi0LSlIh8XQlNnlm24y8xIL8la+ZDgkdbjarQ9ZqYSvEja6gVA==",
-      "dev": true,
       "dependencies": {
         "@vue/compiler-core": "3.2.36",
         "@vue/shared": "3.2.36"
@@ -2024,7 +2022,6 @@
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.36.tgz",
       "integrity": "sha512-AvGb4bTj4W8uQ4BqaSxo7UwTEqX5utdRSMyHy58OragWlt8nEACQ9mIeQh3K4di4/SX+41+pJrLIY01lHAOFOA==",
-      "dev": true,
       "dependencies": {
         "@babel/parser": "^7.16.4",
         "@vue/compiler-core": "3.2.36",
@@ -2042,7 +2039,6 @@
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.36.tgz",
       "integrity": "sha512-+KugInUFRvOxEdLkZwE+W43BqHyhBh0jpYXhmqw1xGq2dmE6J9eZ8UUSOKNhdHtQ/iNLWWeK/wPZkVLUf3YGaw==",
-      "dev": true,
       "dependencies": {
         "@vue/compiler-dom": "3.2.36",
         "@vue/shared": "3.2.36"
@@ -2054,18 +2050,17 @@
       "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ=="
     },
     "node_modules/@vue/reactivity": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.31.tgz",
-      "integrity": "sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==",
+      "version": "3.2.36",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.36.tgz",
+      "integrity": "sha512-c2qvopo0crh9A4GXi2/2kfGYMxsJW4tVILrqRPydVGZHhq0fnzy6qmclWOhBFckEhmyxmpHpdJtIRYGeKcuhnA==",
       "dependencies": {
-        "@vue/shared": "3.2.31"
+        "@vue/shared": "3.2.36"
       }
     },
     "node_modules/@vue/reactivity-transform": {
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.36.tgz",
       "integrity": "sha512-Jk5o2BhpODC9XTA7o4EL8hSJ4JyrFWErLtClG3NH8wDS7ri9jBDWxI7/549T7JY9uilKsaNM+4pJASLj5dtRwA==",
-      "dev": true,
       "dependencies": {
         "@babel/parser": "^7.16.4",
         "@vue/compiler-core": "3.2.36",
@@ -2074,91 +2069,41 @@
         "magic-string": "^0.25.7"
       }
     },
-    "node_modules/@vue/reactivity/node_modules/@vue/shared": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-      "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
-    },
     "node_modules/@vue/runtime-core": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.31.tgz",
-      "integrity": "sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==",
+      "version": "3.2.36",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.36.tgz",
+      "integrity": "sha512-PTWBD+Lub+1U3/KhbCExrfxyS14hstLX+cBboxVHaz+kXoiDLNDEYAovPtxeTutbqtClIXtft+wcGdC+FUQ9qQ==",
       "dependencies": {
-        "@vue/reactivity": "3.2.31",
-        "@vue/shared": "3.2.31"
+        "@vue/reactivity": "3.2.36",
+        "@vue/shared": "3.2.36"
       }
     },
-    "node_modules/@vue/runtime-core/node_modules/@vue/shared": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-      "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
-    },
     "node_modules/@vue/runtime-dom": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz",
-      "integrity": "sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==",
+      "version": "3.2.36",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.36.tgz",
+      "integrity": "sha512-gYPYblm7QXHVuBohqNRRT7Wez0f2Mx2D40rb4fleehrJU9CnkjG0phhcGEZFfGwCmHZRqBCRgbFWE98bPULqkg==",
       "dependencies": {
-        "@vue/runtime-core": "3.2.31",
-        "@vue/shared": "3.2.31",
+        "@vue/runtime-core": "3.2.36",
+        "@vue/shared": "3.2.36",
         "csstype": "^2.6.8"
       }
     },
-    "node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-      "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
-    },
     "node_modules/@vue/server-renderer": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.31.tgz",
-      "integrity": "sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==",
+      "version": "3.2.36",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.36.tgz",
+      "integrity": "sha512-uZE0+jfye6yYXWvAQYeHZv+f50sRryvy16uiqzk3jn8hEY8zTjI+rzlmZSGoE915k+W/Ol9XSw6vxOUD8dGkUg==",
       "dependencies": {
-        "@vue/compiler-ssr": "3.2.31",
-        "@vue/shared": "3.2.31"
+        "@vue/compiler-ssr": "3.2.36",
+        "@vue/shared": "3.2.36"
       },
       "peerDependencies": {
-        "vue": "3.2.31"
+        "vue": "3.2.36"
       }
     },
-    "node_modules/@vue/server-renderer/node_modules/@vue/compiler-core": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
-      "integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
-      "dependencies": {
-        "@babel/parser": "^7.16.4",
-        "@vue/shared": "3.2.31",
-        "estree-walker": "^2.0.2",
-        "source-map": "^0.6.1"
-      }
-    },
-    "node_modules/@vue/server-renderer/node_modules/@vue/compiler-dom": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz",
-      "integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==",
-      "dependencies": {
-        "@vue/compiler-core": "3.2.31",
-        "@vue/shared": "3.2.31"
-      }
-    },
-    "node_modules/@vue/server-renderer/node_modules/@vue/compiler-ssr": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz",
-      "integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==",
-      "dependencies": {
-        "@vue/compiler-dom": "3.2.31",
-        "@vue/shared": "3.2.31"
-      }
-    },
-    "node_modules/@vue/server-renderer/node_modules/@vue/shared": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-      "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
-    },
     "node_modules/@vue/shared": {
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.36.tgz",
-      "integrity": "sha512-JtB41wXl7Au3+Nl3gD16Cfpj7k/6aCroZ6BbOiCMFCMvrOpkg/qQUXTso2XowaNqBbnkuGHurLAqkLBxNGc1hQ==",
-      "dev": true
+      "integrity": "sha512-JtB41wXl7Au3+Nl3gD16Cfpj7k/6aCroZ6BbOiCMFCMvrOpkg/qQUXTso2XowaNqBbnkuGHurLAqkLBxNGc1hQ=="
     },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.11.1",
@@ -3882,9 +3827,9 @@
       }
     },
     "node_modules/eslint-plugin-vue": {
-      "version": "8.7.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.7.1.tgz",
-      "integrity": "sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==",
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.1.0.tgz",
+      "integrity": "sha512-EPCeInPicQ/YyfOWJDr1yfEeSNoFCMzUus107lZyYi37xejdOolNzS5MXGXp8+9bkoKZMdv/1AcZzQebME6r+g==",
       "dev": true,
       "dependencies": {
         "eslint-utils": "^3.0.0",
@@ -3892,10 +3837,11 @@
         "nth-check": "^2.0.1",
         "postcss-selector-parser": "^6.0.9",
         "semver": "^7.3.5",
-        "vue-eslint-parser": "^8.0.1"
+        "vue-eslint-parser": "^9.0.1",
+        "xml-name-validator": "^4.0.0"
       },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^14.17.0 || >=16.0.0"
       },
       "peerDependencies": {
         "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
@@ -5418,10 +5364,10 @@
         "node": ">=4"
       }
     },
-    "node_modules/json-parse-better-errors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
     },
     "node_modules/json-schema-traverse": {
       "version": "0.4.1",
@@ -5488,15 +5434,15 @@
       }
     },
     "node_modules/less-loader": {
-      "version": "10.2.0",
-      "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.2.0.tgz",
-      "integrity": "sha512-AV5KHWvCezW27GT90WATaDnfXBv99llDbtaj4bshq6DvAihMdNjaPDcUMa6EXKLRF+P2opFenJp89BXg91XLYg==",
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz",
+      "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==",
       "dev": true,
       "dependencies": {
         "klona": "^2.0.4"
       },
       "engines": {
-        "node": ">= 12.13.0"
+        "node": ">= 14.15.0"
       },
       "funding": {
         "type": "opencollective",
@@ -7763,15 +7709,15 @@
       }
     },
     "node_modules/vue": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.31.tgz",
-      "integrity": "sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==",
+      "version": "3.2.36",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.36.tgz",
+      "integrity": "sha512-5yTXmrE6gW8IQgttzHW5bfBiFA6mx35ZXHjGLDmKYzW6MMmYvCwuKybANRepwkMYeXw2v1buGg3/lPICY5YlZw==",
       "dependencies": {
-        "@vue/compiler-dom": "3.2.31",
-        "@vue/compiler-sfc": "3.2.31",
-        "@vue/runtime-dom": "3.2.31",
-        "@vue/server-renderer": "3.2.31",
-        "@vue/shared": "3.2.31"
+        "@vue/compiler-dom": "3.2.36",
+        "@vue/compiler-sfc": "3.2.36",
+        "@vue/runtime-dom": "3.2.36",
+        "@vue/server-renderer": "3.2.36",
+        "@vue/shared": "3.2.36"
       }
     },
     "node_modules/vue-chartjs": {
@@ -7792,21 +7738,21 @@
       }
     },
     "node_modules/vue-eslint-parser": {
-      "version": "8.3.0",
-      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz",
-      "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==",
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.0.2.tgz",
+      "integrity": "sha512-uCPQwTGjOtAYrwnU+76pYxalhjsh7iFBsHwBqDHiOPTxtICDaraO4Szw54WFTNZTAEsgHHzqFOu1mmnBOBRzDA==",
       "dev": true,
       "dependencies": {
-        "debug": "^4.3.2",
-        "eslint-scope": "^7.0.0",
-        "eslint-visitor-keys": "^3.1.0",
-        "espree": "^9.0.0",
+        "debug": "^4.3.4",
+        "eslint-scope": "^7.1.1",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.3.1",
         "esquery": "^1.4.0",
         "lodash": "^4.17.21",
-        "semver": "^7.3.5"
+        "semver": "^7.3.6"
       },
       "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+        "node": "^14.17.0 || >=16.0.0"
       },
       "funding": {
         "url": "https://github.com/sponsors/mysticatea"
@@ -8017,69 +7963,6 @@
         "vue": "^3.0.0"
       }
     },
-    "node_modules/vue/node_modules/@vue/compiler-core": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
-      "integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
-      "dependencies": {
-        "@babel/parser": "^7.16.4",
-        "@vue/shared": "3.2.31",
-        "estree-walker": "^2.0.2",
-        "source-map": "^0.6.1"
-      }
-    },
-    "node_modules/vue/node_modules/@vue/compiler-dom": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz",
-      "integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==",
-      "dependencies": {
-        "@vue/compiler-core": "3.2.31",
-        "@vue/shared": "3.2.31"
-      }
-    },
-    "node_modules/vue/node_modules/@vue/compiler-sfc": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz",
-      "integrity": "sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==",
-      "dependencies": {
-        "@babel/parser": "^7.16.4",
-        "@vue/compiler-core": "3.2.31",
-        "@vue/compiler-dom": "3.2.31",
-        "@vue/compiler-ssr": "3.2.31",
-        "@vue/reactivity-transform": "3.2.31",
-        "@vue/shared": "3.2.31",
-        "estree-walker": "^2.0.2",
-        "magic-string": "^0.25.7",
-        "postcss": "^8.1.10",
-        "source-map": "^0.6.1"
-      }
-    },
-    "node_modules/vue/node_modules/@vue/compiler-ssr": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz",
-      "integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==",
-      "dependencies": {
-        "@vue/compiler-dom": "3.2.31",
-        "@vue/shared": "3.2.31"
-      }
-    },
-    "node_modules/vue/node_modules/@vue/reactivity-transform": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz",
-      "integrity": "sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==",
-      "dependencies": {
-        "@babel/parser": "^7.16.4",
-        "@vue/compiler-core": "3.2.31",
-        "@vue/shared": "3.2.31",
-        "estree-walker": "^2.0.2",
-        "magic-string": "^0.25.7"
-      }
-    },
-    "node_modules/vue/node_modules/@vue/shared": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-      "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
-    },
     "node_modules/vuedraggable": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
@@ -8124,9 +8007,9 @@
       }
     },
     "node_modules/webpack": {
-      "version": "5.72.0",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.72.0.tgz",
-      "integrity": "sha512-qmSmbspI0Qo5ld49htys8GY9XhS9CGqFoHTsOVAnjBdg0Zn79y135R+k4IR4rKK6+eKaabMhJwiVB7xw0SJu5w==",
+      "version": "5.73.0",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
+      "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
       "dependencies": {
         "@types/eslint-scope": "^3.7.3",
         "@types/estree": "^0.0.51",
@@ -8137,13 +8020,13 @@
         "acorn-import-assertions": "^1.7.6",
         "browserslist": "^4.14.5",
         "chrome-trace-event": "^1.0.2",
-        "enhanced-resolve": "^5.9.2",
+        "enhanced-resolve": "^5.9.3",
         "es-module-lexer": "^0.9.0",
         "eslint-scope": "5.1.1",
         "events": "^3.2.0",
         "glob-to-regexp": "^0.4.1",
         "graceful-fs": "^4.2.9",
-        "json-parse-better-errors": "^1.0.2",
+        "json-parse-even-better-errors": "^2.3.1",
         "loader-runner": "^4.2.0",
         "mime-types": "^2.1.27",
         "neo-async": "^2.6.2",
@@ -8647,6 +8530,15 @@
         }
       }
     },
+    "node_modules/xml-name-validator": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+      "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/yallist": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
@@ -9999,9 +9891,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "17.0.39",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.39.tgz",
-      "integrity": "sha512-JDU3YLlnPK3WDao6/DlXLOgSNpG13ct+CwIO17V8q0/9fWJyeMJJ/VyZ1lv8kDprihvZMydzVwf0tQOqGiY2Nw=="
+      "version": "17.0.40",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.40.tgz",
+      "integrity": "sha512-UXdBxNGqTMtm7hCwh9HtncFVLrXoqA3oJW30j6XWp5BH/wu3mVeaxo7cq5benFdBw34HB3XDT2TRPI7rXZ+mDg=="
     },
     "@types/qs": {
       "version": "6.9.7",
@@ -10062,7 +9954,6 @@
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.36.tgz",
       "integrity": "sha512-bbyZM5hvBicv0PW3KUfVi+x3ylHnfKG7DOn5wM+f2OztTzTjLEyBb/5yrarIYpmnGitVGbjZqDbODyW4iK8hqw==",
-      "dev": true,
       "requires": {
         "@babel/parser": "^7.16.4",
         "@vue/shared": "3.2.36",
@@ -10074,7 +9965,6 @@
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.36.tgz",
       "integrity": "sha512-tcOTAOiW4s24QLnq+ON6J+GRONXJ+A/mqKCORi0LSlIh8XQlNnlm24y8xIL8la+ZDgkdbjarQ9ZqYSvEja6gVA==",
-      "dev": true,
       "requires": {
         "@vue/compiler-core": "3.2.36",
         "@vue/shared": "3.2.36"
@@ -10084,7 +9974,6 @@
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.36.tgz",
       "integrity": "sha512-AvGb4bTj4W8uQ4BqaSxo7UwTEqX5utdRSMyHy58OragWlt8nEACQ9mIeQh3K4di4/SX+41+pJrLIY01lHAOFOA==",
-      "dev": true,
       "requires": {
         "@babel/parser": "^7.16.4",
         "@vue/compiler-core": "3.2.36",
@@ -10102,7 +9991,6 @@
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.36.tgz",
       "integrity": "sha512-+KugInUFRvOxEdLkZwE+W43BqHyhBh0jpYXhmqw1xGq2dmE6J9eZ8UUSOKNhdHtQ/iNLWWeK/wPZkVLUf3YGaw==",
-      "dev": true,
       "requires": {
         "@vue/compiler-dom": "3.2.36",
         "@vue/shared": "3.2.36"
@@ -10114,25 +10002,17 @@
       "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ=="
     },
     "@vue/reactivity": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.31.tgz",
-      "integrity": "sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==",
+      "version": "3.2.36",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.36.tgz",
+      "integrity": "sha512-c2qvopo0crh9A4GXi2/2kfGYMxsJW4tVILrqRPydVGZHhq0fnzy6qmclWOhBFckEhmyxmpHpdJtIRYGeKcuhnA==",
       "requires": {
-        "@vue/shared": "3.2.31"
-      },
-      "dependencies": {
-        "@vue/shared": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-          "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
-        }
+        "@vue/shared": "3.2.36"
       }
     },
     "@vue/reactivity-transform": {
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.36.tgz",
       "integrity": "sha512-Jk5o2BhpODC9XTA7o4EL8hSJ4JyrFWErLtClG3NH8wDS7ri9jBDWxI7/549T7JY9uilKsaNM+4pJASLj5dtRwA==",
-      "dev": true,
       "requires": {
         "@babel/parser": "^7.16.4",
         "@vue/compiler-core": "3.2.36",
@@ -10142,88 +10022,37 @@
       }
     },
     "@vue/runtime-core": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.31.tgz",
-      "integrity": "sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==",
+      "version": "3.2.36",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.36.tgz",
+      "integrity": "sha512-PTWBD+Lub+1U3/KhbCExrfxyS14hstLX+cBboxVHaz+kXoiDLNDEYAovPtxeTutbqtClIXtft+wcGdC+FUQ9qQ==",
       "requires": {
-        "@vue/reactivity": "3.2.31",
-        "@vue/shared": "3.2.31"
-      },
-      "dependencies": {
-        "@vue/shared": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-          "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
-        }
+        "@vue/reactivity": "3.2.36",
+        "@vue/shared": "3.2.36"
       }
     },
     "@vue/runtime-dom": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz",
-      "integrity": "sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==",
+      "version": "3.2.36",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.36.tgz",
+      "integrity": "sha512-gYPYblm7QXHVuBohqNRRT7Wez0f2Mx2D40rb4fleehrJU9CnkjG0phhcGEZFfGwCmHZRqBCRgbFWE98bPULqkg==",
       "requires": {
-        "@vue/runtime-core": "3.2.31",
-        "@vue/shared": "3.2.31",
+        "@vue/runtime-core": "3.2.36",
+        "@vue/shared": "3.2.36",
         "csstype": "^2.6.8"
-      },
-      "dependencies": {
-        "@vue/shared": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-          "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
-        }
       }
     },
     "@vue/server-renderer": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.31.tgz",
-      "integrity": "sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==",
+      "version": "3.2.36",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.36.tgz",
+      "integrity": "sha512-uZE0+jfye6yYXWvAQYeHZv+f50sRryvy16uiqzk3jn8hEY8zTjI+rzlmZSGoE915k+W/Ol9XSw6vxOUD8dGkUg==",
       "requires": {
-        "@vue/compiler-ssr": "3.2.31",
-        "@vue/shared": "3.2.31"
-      },
-      "dependencies": {
-        "@vue/compiler-core": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
-          "integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
-          "requires": {
-            "@babel/parser": "^7.16.4",
-            "@vue/shared": "3.2.31",
-            "estree-walker": "^2.0.2",
-            "source-map": "^0.6.1"
-          }
-        },
-        "@vue/compiler-dom": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz",
-          "integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==",
-          "requires": {
-            "@vue/compiler-core": "3.2.31",
-            "@vue/shared": "3.2.31"
-          }
-        },
-        "@vue/compiler-ssr": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz",
-          "integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==",
-          "requires": {
-            "@vue/compiler-dom": "3.2.31",
-            "@vue/shared": "3.2.31"
-          }
-        },
-        "@vue/shared": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-          "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
-        }
+        "@vue/compiler-ssr": "3.2.36",
+        "@vue/shared": "3.2.36"
       }
     },
     "@vue/shared": {
       "version": "3.2.36",
       "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.36.tgz",
-      "integrity": "sha512-JtB41wXl7Au3+Nl3gD16Cfpj7k/6aCroZ6BbOiCMFCMvrOpkg/qQUXTso2XowaNqBbnkuGHurLAqkLBxNGc1hQ==",
-      "dev": true
+      "integrity": "sha512-JtB41wXl7Au3+Nl3gD16Cfpj7k/6aCroZ6BbOiCMFCMvrOpkg/qQUXTso2XowaNqBbnkuGHurLAqkLBxNGc1hQ=="
     },
     "@webassemblyjs/ast": {
       "version": "1.11.1",
@@ -11623,9 +11452,9 @@
       }
     },
     "eslint-plugin-vue": {
-      "version": "8.7.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.7.1.tgz",
-      "integrity": "sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==",
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.1.0.tgz",
+      "integrity": "sha512-EPCeInPicQ/YyfOWJDr1yfEeSNoFCMzUus107lZyYi37xejdOolNzS5MXGXp8+9bkoKZMdv/1AcZzQebME6r+g==",
       "dev": true,
       "requires": {
         "eslint-utils": "^3.0.0",
@@ -11633,7 +11462,8 @@
         "nth-check": "^2.0.1",
         "postcss-selector-parser": "^6.0.9",
         "semver": "^7.3.5",
-        "vue-eslint-parser": "^8.0.1"
+        "vue-eslint-parser": "^9.0.1",
+        "xml-name-validator": "^4.0.0"
       },
       "dependencies": {
         "semver": {
@@ -12625,10 +12455,10 @@
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
       "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
     },
-    "json-parse-better-errors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
+    "json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
     },
     "json-schema-traverse": {
       "version": "0.4.1",
@@ -12695,9 +12525,9 @@
       }
     },
     "less-loader": {
-      "version": "10.2.0",
-      "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.2.0.tgz",
-      "integrity": "sha512-AV5KHWvCezW27GT90WATaDnfXBv99llDbtaj4bshq6DvAihMdNjaPDcUMa6EXKLRF+P2opFenJp89BXg91XLYg==",
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz",
+      "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==",
       "dev": true,
       "requires": {
         "klona": "^2.0.4"
@@ -14391,80 +14221,15 @@
       "dev": true
     },
     "vue": {
-      "version": "3.2.31",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.31.tgz",
-      "integrity": "sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==",
+      "version": "3.2.36",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.36.tgz",
+      "integrity": "sha512-5yTXmrE6gW8IQgttzHW5bfBiFA6mx35ZXHjGLDmKYzW6MMmYvCwuKybANRepwkMYeXw2v1buGg3/lPICY5YlZw==",
       "requires": {
-        "@vue/compiler-dom": "3.2.31",
-        "@vue/compiler-sfc": "3.2.31",
-        "@vue/runtime-dom": "3.2.31",
-        "@vue/server-renderer": "3.2.31",
-        "@vue/shared": "3.2.31"
-      },
-      "dependencies": {
-        "@vue/compiler-core": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.31.tgz",
-          "integrity": "sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==",
-          "requires": {
-            "@babel/parser": "^7.16.4",
-            "@vue/shared": "3.2.31",
-            "estree-walker": "^2.0.2",
-            "source-map": "^0.6.1"
-          }
-        },
-        "@vue/compiler-dom": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz",
-          "integrity": "sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==",
-          "requires": {
-            "@vue/compiler-core": "3.2.31",
-            "@vue/shared": "3.2.31"
-          }
-        },
-        "@vue/compiler-sfc": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz",
-          "integrity": "sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==",
-          "requires": {
-            "@babel/parser": "^7.16.4",
-            "@vue/compiler-core": "3.2.31",
-            "@vue/compiler-dom": "3.2.31",
-            "@vue/compiler-ssr": "3.2.31",
-            "@vue/reactivity-transform": "3.2.31",
-            "@vue/shared": "3.2.31",
-            "estree-walker": "^2.0.2",
-            "magic-string": "^0.25.7",
-            "postcss": "^8.1.10",
-            "source-map": "^0.6.1"
-          }
-        },
-        "@vue/compiler-ssr": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz",
-          "integrity": "sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==",
-          "requires": {
-            "@vue/compiler-dom": "3.2.31",
-            "@vue/shared": "3.2.31"
-          }
-        },
-        "@vue/reactivity-transform": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz",
-          "integrity": "sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==",
-          "requires": {
-            "@babel/parser": "^7.16.4",
-            "@vue/compiler-core": "3.2.31",
-            "@vue/shared": "3.2.31",
-            "estree-walker": "^2.0.2",
-            "magic-string": "^0.25.7"
-          }
-        },
-        "@vue/shared": {
-          "version": "3.2.31",
-          "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.31.tgz",
-          "integrity": "sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ=="
-        }
+        "@vue/compiler-dom": "3.2.36",
+        "@vue/compiler-sfc": "3.2.36",
+        "@vue/runtime-dom": "3.2.36",
+        "@vue/server-renderer": "3.2.36",
+        "@vue/shared": "3.2.36"
       }
     },
     "vue-chartjs": {
@@ -14480,18 +14245,18 @@
       "requires": {}
     },
     "vue-eslint-parser": {
-      "version": "8.3.0",
-      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz",
-      "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==",
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.0.2.tgz",
+      "integrity": "sha512-uCPQwTGjOtAYrwnU+76pYxalhjsh7iFBsHwBqDHiOPTxtICDaraO4Szw54WFTNZTAEsgHHzqFOu1mmnBOBRzDA==",
       "dev": true,
       "requires": {
-        "debug": "^4.3.2",
-        "eslint-scope": "^7.0.0",
-        "eslint-visitor-keys": "^3.1.0",
-        "espree": "^9.0.0",
+        "debug": "^4.3.4",
+        "eslint-scope": "^7.1.1",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.3.1",
         "esquery": "^1.4.0",
         "lodash": "^4.17.21",
-        "semver": "^7.3.5"
+        "semver": "^7.3.6"
       },
       "dependencies": {
         "eslint-scope": {
@@ -14677,9 +14442,9 @@
       }
     },
     "webpack": {
-      "version": "5.72.0",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.72.0.tgz",
-      "integrity": "sha512-qmSmbspI0Qo5ld49htys8GY9XhS9CGqFoHTsOVAnjBdg0Zn79y135R+k4IR4rKK6+eKaabMhJwiVB7xw0SJu5w==",
+      "version": "5.73.0",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
+      "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
       "requires": {
         "@types/eslint-scope": "^3.7.3",
         "@types/estree": "^0.0.51",
@@ -14690,13 +14455,13 @@
         "acorn-import-assertions": "^1.7.6",
         "browserslist": "^4.14.5",
         "chrome-trace-event": "^1.0.2",
-        "enhanced-resolve": "^5.9.2",
+        "enhanced-resolve": "^5.9.3",
         "es-module-lexer": "^0.9.0",
         "eslint-scope": "5.1.1",
         "events": "^3.2.0",
         "glob-to-regexp": "^0.4.1",
         "graceful-fs": "^4.2.9",
-        "json-parse-better-errors": "^1.0.2",
+        "json-parse-even-better-errors": "^2.3.1",
         "loader-runner": "^4.2.0",
         "mime-types": "^2.1.27",
         "neo-async": "^2.6.2",
@@ -15028,6 +14793,12 @@
       "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==",
       "requires": {}
     },
+    "xml-name-validator": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
+      "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
+      "dev": true
+    },
     "yallist": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",

+ 5 - 5
frontend/package.json

@@ -5,7 +5,7 @@
     "*.vue"
   ],
   "private": true,
-  "version": "3.6.0-rc1",
+  "version": "3.6.0",
   "description": "An open-source collaborative music listening and catalogue curation application. Currently supporting YouTube based content.",
   "main": "main.js",
   "author": "Musare Team",
@@ -30,11 +30,11 @@
     "eslint-config-prettier": "^8.5.0",
     "eslint-plugin-import": "^2.26.0",
     "eslint-plugin-prettier": "^4.0.0",
-    "eslint-plugin-vue": "^8.7.1",
+    "eslint-plugin-vue": "^9.1.0",
     "eslint-webpack-plugin": "^3.1.1",
     "fetch": "^1.1.0",
     "less": "^4.1.2",
-    "less-loader": "^10.2.0",
+    "less-loader": "^11.0.0",
     "prettier": "^2.6.2",
     "style-loader": "^3.3.1",
     "style-resources-loader": "^1.5.0",
@@ -55,7 +55,7 @@
     "marked": "^4.0.16",
     "normalize.css": "^8.0.1",
     "toasters": "^2.3.1",
-    "vue": "3.2.31",
+    "vue": "^3.2.36",
     "vue-chartjs": "^4.1.1",
     "vue-content-loader": "^2.0.1",
     "vue-json-pretty": "^2.1.0",
@@ -64,7 +64,7 @@
     "vue-tippy": "^6.0.0-alpha.57",
     "vuedraggable": "^4.1.0",
     "vuex": "^4.0.2",
-    "webpack": "5.72.0",
+    "webpack": "^5.73.0",
     "webpack-bundle-analyzer": "^4.5.0",
     "webpack-merge": "^5.8.0"
   }

+ 1 - 0
frontend/src/components/LineChart.vue

@@ -40,6 +40,7 @@ ChartJS.register(
 
 export default {
 	name: "LineChart",
+	// eslint-disable-next-line vue/no-reserved-component-names
 	components: { Line },
 	props: {
 		chartId: {

+ 0 - 1
frontend/src/components/PlaylistTabBase.vue

@@ -507,7 +507,6 @@
 					v-if="playlists.length > 0"
 				>
 					<draggable
-						tag="transition-group"
 						:component-data="{
 							name: !drag ? 'draggable-list-transition' : null
 						}"

+ 0 - 1
frontend/src/components/Queue.vue

@@ -8,7 +8,6 @@
 			}"
 		>
 			<draggable
-				tag="transition-group"
 				:component-data="{
 					name: !drag ? 'draggable-list-transition' : null
 				}"

+ 0 - 1
frontend/src/components/modals/EditPlaylist/index.vue

@@ -97,7 +97,6 @@
 
 					<aside class="menu">
 						<draggable
-							tag="transition-group"
 							:component-data="{
 								name: !drag ? 'draggable-list-transition' : null
 							}"

+ 31 - 11
frontend/src/components/modals/EditSong/index.vue

@@ -231,7 +231,11 @@
 							@loadError="onThumbnailLoadError"
 						/>
 						<img
-							v-if="!isYoutubeThumbnail && !songDeleted"
+							v-if="
+								!isYoutubeThumbnail &&
+								songDataLoaded &&
+								!songDeleted
+							"
 							class="thumbnail-dummy"
 							:src="song.thumbnail"
 							ref="thumbnailElement"
@@ -866,8 +870,11 @@ export default {
 			this.loadSong(youtubeId);
 		}
 	},
+	beforeMount() {
+		console.log("EDITSONG BEFOREMOUNT");
+	},
 	async mounted() {
-		console.log("MOUNTED");
+		console.log("EDITSONG MOUNTED");
 		this.activityWatchVideoDataInterval = setInterval(() => {
 			this.sendActivityWatchVideoData();
 		}, 1000);
@@ -1061,7 +1068,7 @@ export default {
 		*/
 	},
 	beforeUnmount() {
-		console.log("UNMOUNT");
+		console.log("EDITSONG BEFOREUNMOUNT");
 		this.unloadSong(this.youtubeId, this.song._id);
 
 		this.playerReady = false;
@@ -1096,8 +1103,19 @@ export default {
 				"editSong",
 				this.modalUuid
 			]);
+		} else {
+			console.log("UNREGISTER EDITSONG");
+			this.$store.unregisterModule([
+				"modals",
+				"editSongs",
+				this.modalUuid,
+				"editSong"
+			]);
 		}
 	},
+	unmounted() {
+		console.log("EDITSONG UNMOUNTED");
+	},
 	methods: {
 		onThumbnailLoad() {
 			if (this.$refs.thumbnailElement) {
@@ -1118,7 +1136,7 @@ export default {
 			this.thumbnailLoadError = error !== 0;
 		},
 		init() {
-			if (this.newSong && !this.youtubeId) {
+			if (this.newSong && !this.youtubeId && !this.bulk) {
 				this.setSong({
 					youtubeId: "",
 					title: "",
@@ -1368,7 +1386,8 @@ export default {
 			this.youtubeVideoCurrentTime = "0.000";
 			this.youtubeVideoDuration = "0.000";
 			this.youtubeVideoNote = "";
-			this.socket.dispatch("apis.leaveRoom", `edit-song.${songId}`);
+			if (songId)
+				this.socket.dispatch("apis.leaveRoom", `edit-song.${songId}`);
 			if (this.$refs.saveButton) this.$refs.saveButton.status = "default";
 		},
 		loadSong(youtubeId) {
@@ -1387,17 +1406,18 @@ export default {
 
 						this.songDataLoaded = true;
 
-						this.socket.dispatch(
-							"apis.joinRoom",
-							`edit-song.${this.song._id}`
-						);
+						if (song._id)
+							this.socket.dispatch(
+								"apis.joinRoom",
+								`edit-song.${song._id}`
+							);
 
 						if (
 							this.video.player &&
 							this.video.player.cueVideoById
 						) {
 							this.video.player.cueVideoById(
-								this.youtubeId,
+								youtubeId,
 								song.skipDuration
 							);
 						}
@@ -1857,9 +1877,9 @@ export default {
 				this.song.tags.splice(this.song.tags.indexOf(value), 1);
 		},
 		drawCanvas() {
-			if (!this.songDataLoaded) return;
 			const canvasElement =
 				this.$refs[`durationCanvas-${this.modalUuid}`];
+			if (!this.songDataLoaded || !canvasElement) return;
 			const ctx = canvasElement.getContext("2d");
 
 			const videoDuration = Number(this.youtubeVideoDuration);

+ 174 - 153
frontend/src/components/modals/EditSongs.vue

@@ -40,106 +40,114 @@
 					</header>
 					<section class="sidebar-body">
 						<div
-							class="item"
-							v-for="(
-								{ status, flagged, song }, index
-							) in filteredItems"
-							:key="song.youtubeId"
-							:ref="`edit-songs-item-${song.youtubeId}`"
+							v-show="filteredItems.length > 0"
+							class="edit-songs-items"
 						>
-							<song-item
-								:song="song"
-								:thumbnail="false"
-								:duration="false"
-								:disabled-actions="
-									song.removed ? ['all'] : ['report', 'edit']
-								"
-								:class="{
-									updated: song.updated,
-									removed: song.removed
-								}"
+							<div
+								class="item"
+								v-for="(
+									{ status, flagged, song }, index
+								) in filteredItems"
+								:key="`edit-songs-item-${index}`"
+								:ref="`edit-songs-item-${song.youtubeId}`"
 							>
-								<template #leftIcon>
-									<i
-										v-if="
-											currentSong.youtubeId ===
-												song.youtubeId && !song.removed
-										"
-										class="material-icons item-icon editing-icon"
-										content="Currently editing song"
-										v-tippy="{ theme: 'info' }"
-										@click="toggleDone(index)"
-										>edit</i
-									>
-									<i
-										v-else-if="song.removed"
-										class="material-icons item-icon removed-icon"
-										content="Song removed"
-										v-tippy="{ theme: 'info' }"
-										>delete_forever</i
-									>
-									<i
-										v-else-if="status === 'error'"
-										class="material-icons item-icon error-icon"
-										content="Error saving song"
-										v-tippy="{ theme: 'info' }"
-										@click="toggleDone(index)"
-										>error</i
-									>
-									<i
-										v-else-if="status === 'saving'"
-										class="material-icons item-icon saving-icon"
-										content="Currently saving song"
-										v-tippy="{ theme: 'info' }"
-										>pending</i
-									>
-									<i
-										v-else-if="flagged"
-										class="material-icons item-icon flag-icon"
-										content="Song flagged"
-										v-tippy="{ theme: 'info' }"
-										@click="toggleDone(index)"
-										>flag_circle</i
-									>
-									<i
-										v-else-if="status === 'done'"
-										class="material-icons item-icon done-icon"
-										content="Song marked complete"
-										v-tippy="{ theme: 'info' }"
-										@click="toggleDone(index)"
-										>check_circle</i
-									>
-									<i
-										v-else-if="status === 'todo'"
-										class="material-icons item-icon todo-icon"
-										content="Song marked todo"
-										v-tippy="{ theme: 'info' }"
-										@click="toggleDone(index)"
-										>cancel</i
-									>
-								</template>
-								<template v-if="!song.removed" #actions>
-									<i
-										class="material-icons edit-icon"
-										content="Edit Song"
-										v-tippy
-										@click="pickSong(song)"
-									>
-										edit
-									</i>
-								</template>
-								<template #tippyActions>
-									<i
-										class="material-icons flag-icon"
-										:class="{ flagged }"
-										content="Toggle Flag"
-										v-tippy
-										@click="toggleFlag(index)"
-									>
-										flag_circle
-									</i>
-								</template>
-							</song-item>
+								<song-item
+									:song="song"
+									:thumbnail="false"
+									:duration="false"
+									:disabled-actions="
+										song.removed
+											? ['all']
+											: ['report', 'edit']
+									"
+									:class="{
+										updated: song.updated,
+										removed: song.removed
+									}"
+								>
+									<template #leftIcon>
+										<i
+											v-if="
+												currentSong.youtubeId ===
+													song.youtubeId &&
+												!song.removed
+											"
+											class="material-icons item-icon editing-icon"
+											content="Currently editing song"
+											v-tippy="{ theme: 'info' }"
+											@click="toggleDone(index)"
+											>edit</i
+										>
+										<i
+											v-else-if="song.removed"
+											class="material-icons item-icon removed-icon"
+											content="Song removed"
+											v-tippy="{ theme: 'info' }"
+											>delete_forever</i
+										>
+										<i
+											v-else-if="status === 'error'"
+											class="material-icons item-icon error-icon"
+											content="Error saving song"
+											v-tippy="{ theme: 'info' }"
+											@click="toggleDone(index)"
+											>error</i
+										>
+										<i
+											v-else-if="status === 'saving'"
+											class="material-icons item-icon saving-icon"
+											content="Currently saving song"
+											v-tippy="{ theme: 'info' }"
+											>pending</i
+										>
+										<i
+											v-else-if="flagged"
+											class="material-icons item-icon flag-icon"
+											content="Song flagged"
+											v-tippy="{ theme: 'info' }"
+											@click="toggleDone(index)"
+											>flag_circle</i
+										>
+										<i
+											v-else-if="status === 'done'"
+											class="material-icons item-icon done-icon"
+											content="Song marked complete"
+											v-tippy="{ theme: 'info' }"
+											@click="toggleDone(index)"
+											>check_circle</i
+										>
+										<i
+											v-else-if="status === 'todo'"
+											class="material-icons item-icon todo-icon"
+											content="Song marked todo"
+											v-tippy="{ theme: 'info' }"
+											@click="toggleDone(index)"
+											>cancel</i
+										>
+									</template>
+									<template v-if="!song.removed" #actions>
+										<i
+											class="material-icons edit-icon"
+											content="Edit Song"
+											v-tippy
+											@click="pickSong(song)"
+										>
+											edit
+										</i>
+									</template>
+									<template #tippyActions>
+										<i
+											class="material-icons flag-icon"
+											:class="{ flagged }"
+											content="Toggle Flag"
+											v-tippy
+											@click="toggleFlag(index)"
+										>
+											flag_circle
+										</i>
+									</template>
+								</song-item>
+							</div>
 						</div>
 						<p v-if="filteredItems.length === 0" class="no-items">
 							{{
@@ -238,30 +246,33 @@ export default {
 			socket: "websockets/getSocket"
 		})
 	},
-	async mounted() {
-		this.socket.dispatch("apis.joinRoom", "edit-songs");
-
+	beforeMount() {
+		console.log("EDITSONGS BEFOREMOUNT");
 		this.$store.registerModule(
 			["modals", "editSongs", this.modalUuid, "editSong"],
 			editSong
 		);
+	},
+	async mounted() {
+		console.log("EDITSONGS MOUNTED");
+
+		this.socket.dispatch("apis.joinRoom", "edit-songs");
 
 		this.socket.dispatch(
 			"songs.getSongsFromYoutubeIds",
 			this.youtubeIds,
 			res => {
-				res.data.songs.forEach(song => {
-					this.items.push({
+				if (res.data.songs.length === 0) {
+					this.closeThisModal();
+					new Toast("You can't edit 0 songs.");
+				} else {
+					this.items = res.data.songs.map(song => ({
 						status: "todo",
 						flagged: false,
 						song
-					});
-				});
-
-				if (this.items.length === 0) {
-					this.closeThisModal();
-					new Toast("You can't edit 0 songs.");
-				} else this.editNextSong();
+					}));
+					this.editNextSong();
+				}
 			}
 		);
 
@@ -271,11 +282,12 @@ export default {
 				const index = this.items
 					.map(item => item.song.youtubeId)
 					.indexOf(res.data.song.youtubeId);
-				this.items[index].song = {
-					...this.items[index].song,
-					...res.data.song,
-					created: true
-				};
+				if (index >= 0)
+					this.items[index].song = {
+						...this.items[index].song,
+						...res.data.song,
+						created: true
+					};
 			},
 			{ modalUuid: this.modalUuid }
 		);
@@ -286,11 +298,12 @@ export default {
 				const index = this.items
 					.map(item => item.song.youtubeId)
 					.indexOf(res.data.song.youtubeId);
-				this.items[index].song = {
-					...this.items[index].song,
-					...res.data.song,
-					updated: true
-				};
+				if (index >= 0)
+					this.items[index].song = {
+						...this.items[index].song,
+						...res.data.song,
+						updated: true
+					};
 			},
 			{ modalUuid: this.modalUuid }
 		);
@@ -301,7 +314,7 @@ export default {
 				const index = this.items
 					.map(item => item.song._id)
 					.indexOf(res.data.songId);
-				this.items[index].song.removed = true;
+				if (index >= 0) this.items[index].song.removed = true;
 			},
 			{ modalUuid: this.modalUuid }
 		);
@@ -312,15 +325,17 @@ export default {
 				const index = this.items
 					.map(item => item.song.youtubeVideoId)
 					.indexOf(res.videoId);
-				if (index !== -1) this.items[index].song.removed = true;
+				if (index >= 0) this.items[index].song.removed = true;
 			},
 			{ modalUuid: this.modalUuid }
 		);
 	},
 	beforeUnmount() {
+		console.log("EDITSONGS BEFORE UNMOUNT");
 		this.socket.dispatch("apis.leaveRoom", "edit-songs");
 	},
 	unmounted() {
+		console.log("EDITSONGS UNMOUNTED");
 		// Delete the VueX module that was created for this modal, after all other cleanup tasks are performed
 		this.$store.unregisterModule(["modals", "editSongs", this.modalUuid]);
 	},
@@ -555,45 +570,51 @@ export default {
 		overflow: auto;
 		padding: 10px;
 
-		.item {
+		.edit-songs-items {
 			display: flex;
-			flex-direction: row;
-			align-items: center;
-			column-gap: 8px;
-
-			:deep(.song-item) {
-				.item-icon {
-					margin-right: 10px;
-					cursor: pointer;
-				}
+			flex-direction: column;
+			row-gap: 8px;
 
-				.removed-icon,
-				.error-icon {
-					color: var(--red);
-				}
+			.item {
+				display: flex;
+				flex-direction: row;
+				align-items: center;
+				column-gap: 8px;
+
+				:deep(.song-item) {
+					.item-icon {
+						margin-right: 10px;
+						cursor: pointer;
+					}
 
-				.saving-icon,
-				.todo-icon,
-				.editing-icon {
-					color: var(--primary-color);
-				}
+					.removed-icon,
+					.error-icon {
+						color: var(--red);
+					}
 
-				.done-icon {
-					color: var(--green);
-				}
+					.saving-icon,
+					.todo-icon,
+					.editing-icon {
+						color: var(--primary-color);
+					}
+
+					.done-icon {
+						color: var(--green);
+					}
 
-				.flag-icon {
-					color: var(--orange);
+					.flag-icon {
+						color: var(--orange);
 
-					&.flagged {
-						color: var(--grey);
+						&.flagged {
+							color: var(--grey);
+						}
 					}
-				}
 
-				&.removed {
-					filter: grayscale(100%);
-					cursor: not-allowed;
-					user-select: none;
+					&.removed {
+						filter: grayscale(100%);
+						cursor: not-allowed;
+						user-select: none;
+					}
 				}
 			}
 		}

+ 2 - 4
frontend/src/pages/Admin/Songs/Import.vue

@@ -265,8 +265,7 @@ export default {
 			createImport: {
 				stage: 2,
 				importMethod: "youtube",
-				youtubeUrl:
-					"https://www.youtube.com/playlist?list=PL3-sRm8xAzY9gpXTMGVHJWy_FMD67NBed",
+				youtubeUrl: "",
 				isImportingOnlyMusic: false
 			},
 			columnDefault: {
@@ -539,8 +538,7 @@ export default {
 			this.createImport = {
 				stage: 2,
 				importMethod: "youtube",
-				youtubeUrl:
-					"https://www.youtube.com/channel/UCio_FVgKVgqcHrRiXDpnqbw",
+				youtubeUrl: "",
 				isImportingOnlyMusic: false
 			};
 		},

+ 1 - 1
frontend/src/pages/Admin/YouTube/Videos.vue

@@ -18,7 +18,7 @@
 			data-action="youtube.getVideos"
 			name="admin-youtube-videos"
 			:max-width="1140"
-			:bulk-actions="{ width: 150 }"
+			:bulk-actions="{ width: 200 }"
 		>
 			<template #column-options="slotProps">
 				<div class="row-options">

+ 0 - 1
frontend/src/pages/Profile/Tabs/Playlists.vue

@@ -19,7 +19,6 @@
 			<hr class="section-horizontal-rule" />
 
 			<draggable
-				tag="transition-group"
 				:component-data="{
 					name: !drag ? 'draggable-list-transition' : null
 				}"

+ 0 - 1
frontend/src/pages/Station/Sidebar/Playlists.vue

@@ -2,7 +2,6 @@
 	<div id="my-playlists">
 		<div class="menu-list scrollable-list" v-if="playlists.length > 0">
 			<draggable
-				tag="transition-group"
 				:component-data="{
 					name: !drag ? 'draggable-list-transition' : null
 				}"

+ 2 - 2
frontend/src/store/modules/modals/editSong.js

@@ -59,13 +59,13 @@ export default {
 		},
 		editSong(state, song) {
 			state.newSong = !!song.newSong || !song._id;
-			state.youtubeId = song.newSong ? null : song.youtubeId;
+			state.youtubeId = song.youtubeId || null;
 			state.prefillData = song.prefill ? song.prefill : {};
 		},
 		setSong(state, song) {
 			if (song.discogs === undefined) song.discogs = null;
 			state.originalSong = JSON.parse(JSON.stringify(song));
-			state.song = { ...song };
+			state.song = JSON.parse(JSON.stringify(song));
 			state.newSong = !song._id;
 			state.youtubeId = song.youtubeId;
 		},

+ 33 - 12
frontend/src/vuex_helpers.js

@@ -2,20 +2,41 @@ import { defineAsyncComponent } from "vue";
 
 const mapModalState = (namespace, map) => {
 	const modalState = {};
+	// console.log("MAP MODAL STATE", namespace);
+
 	Object.entries(map).forEach(([mapKey, mapValue]) => {
 		modalState[mapKey] = function func() {
-			return mapValue(
-				namespace
-					.replace(
-						"MODAL_MODULE_PATH",
-						namespace.indexOf("MODAL_MODULE_PATH") !== -1
-							? this.modalModulePath
-							: null
-					)
-					.replace("MODAL_UUID", this.modalUuid)
-					.split("/")
-					.reduce((a, b) => a[b], this.$store.state)
-			);
+			// console.log(
+			// 	321,
+			// 	namespace
+			// 		.replace(
+			// 			"MODAL_MODULE_PATH",
+			// 			namespace.indexOf("MODAL_MODULE_PATH") !== -1
+			// 				? this.modalModulePath
+			// 				: null
+			// 		)
+			// 		.replace("MODAL_UUID", this.modalUuid)
+			// 		.split("/")
+			// );
+			// console.log(3211, mapKey);
+
+			const state = namespace
+				.replace(
+					"MODAL_MODULE_PATH",
+					namespace.indexOf("MODAL_MODULE_PATH") !== -1
+						? this.modalModulePath
+						: null
+				)
+				.replace("MODAL_UUID", this.modalUuid)
+				.split("/")
+				.reduce((a, b) => a[b], this.$store.state);
+
+			// console.log(32111, state);
+			// if (state) console.log(321111, mapValue(state));
+			// else console.log(321111, "NADA");
+
+			if (state) return mapValue(state);
+			return mapValue({});
 		};
 	});
 	return modalState;