Browse Source

Merge tag 'v3.1.0' into staging

Owen Diffey 3 years ago
parent
commit
bfe7d63918

+ 1 - 1
backend/package-lock.json

@@ -1,6 +1,6 @@
 {
   "name": "musare-backend",
-  "version": "3.0.0",
+  "version": "3.1.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {

+ 1 - 1
backend/package.json

@@ -1,7 +1,7 @@
 {
   "name": "musare-backend",
   "private": true,
-  "version": "3.0.0",
+  "version": "3.1.0",
   "type": "module",
   "description": "An open-source collaborative music listening and catalogue curation application. Currently supporting YouTube based content.",
   "main": "index.js",

+ 1 - 1
frontend/package-lock.json

@@ -1,6 +1,6 @@
 {
   "name": "musare-frontend",
-  "version": "3.0.0",
+  "version": "3.1.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {

+ 1 - 1
frontend/package.json

@@ -5,7 +5,7 @@
     "*.vue"
   ],
   "private": true,
-  "version": "3.0.0",
+  "version": "3.1.0",
   "description": "An open-source collaborative music listening and catalogue curation application. Currently supporting YouTube based content.",
   "main": "main.js",
   "author": "Musare Team",

+ 88 - 16
frontend/src/App.vue

@@ -343,6 +343,11 @@ export default {
 	code {
 		background-color: var(--dark-grey-2) !important;
 	}
+
+	.button.is-dark {
+		background-color: var(--light-grey) !important;
+		color: var(--dark-grey-2) !important;
+	}
 }
 
 /* nunito-200 - latin */
@@ -518,7 +523,7 @@ body {
 	background-color: var(--light-grey);
 	color: var(--dark-grey);
 	height: 100%;
-	line-height: 1.428;
+	line-height: 1.4285714;
 	font-size: 1rem;
 	font-family: Nunito, Arial, sans-serif;
 }
@@ -540,6 +545,7 @@ body {
 .column {
 	display: flex;
 	flex: 1 1 0;
+	flex-direction: column;
 	padding: 10px;
 }
 
@@ -549,11 +555,6 @@ ul {
 	display: block;
 }
 
-ol,
-ul {
-	margin-left: 2em;
-}
-
 h1,
 h2,
 h3,
@@ -606,6 +607,12 @@ h6 {
 	margin: 0.5rem 0 0.4rem 0;
 }
 
+.content {
+	h4 {
+		line-height: 1.125;
+	}
+}
+
 .thin {
 	font-weight: 200;
 }
@@ -723,6 +730,12 @@ textarea {
 a {
 	color: var(--primary-color);
 	text-decoration: none;
+	cursor: pointer;
+
+	&:hover,
+	&:focus {
+		filter: brightness(90%);
+	}
 }
 
 table {
@@ -1183,6 +1196,40 @@ button.delete:focus {
 	padding-right: 6px !important;
 }
 
+#tab-selection,
+.tab-selection {
+	overflow-x: auto;
+	.button {
+		white-space: nowrap;
+	}
+}
+
+.table {
+	background-color: var(--white);
+	color: var(--dark-grey);
+	width: 100%;
+	border-collapse: collapse;
+	border-spacing: 0;
+	border-radius: 5px;
+
+	thead th {
+		padding: 5px 10px;
+		text-align: left;
+		font-weight: 600;
+		color: var(--grey-3);
+	}
+
+	tr {
+		&:nth-child(even) {
+			background-color: #fafafa;
+		}
+		&:hover,
+		&:focus {
+			background-color: var(--light-grey);
+		}
+	}
+}
+
 .button {
 	border: 1px solid var(--light-grey-2);
 	background-color: var(--white);
@@ -1197,6 +1244,7 @@ button.delete:focus {
 	justify-content: center;
 	cursor: pointer;
 	user-select: none;
+	white-space: nowrap;
 
 	&:hover,
 	&:focus {
@@ -1232,6 +1280,12 @@ button.delete:focus {
 		border-width: 0;
 		color: rgba(0, 0, 0, 0.7);
 	}
+
+	&.is-dark {
+		background-color: var(--dark-grey-2);
+		border-width: 0;
+		color: var(--light-grey);
+	}
 }
 
 .input,
@@ -1304,6 +1358,28 @@ button.delete:focus {
 
 	&.has-addons {
 		display: flex;
+
+		.button {
+			border-radius: 0;
+			margin-right: -1px;
+
+			&:first-child {
+				border-radius: 3px 0 0 3px;
+			}
+
+			&:last-child {
+				border-radius: 0 3px 3px 0;
+				padding-left: 10px;
+			}
+		}
+
+		.input {
+			margin-right: -1px;
+
+			&:first-child {
+				border-radius: 3px 0 0 3px;
+			}
+		}
 	}
 }
 
@@ -1504,11 +1580,14 @@ h4.section-title {
 }
 
 #forgot-password {
-	display: flex;
 	justify-content: flex-start;
 	margin: 5px 0;
 }
 
+.steps-fade-leave-active {
+	display: none;
+}
+
 .steps-fade-enter-active,
 .steps-fade-leave-active {
 	transition: all 0.3s ease;
@@ -1565,25 +1644,19 @@ h4.section-title {
 
 /* This class is used for content-box in ResetPassword, but not in RemoveAccount. This is because ResetPassword uses transitions and RemoveAccount does not */
 .content-box-wrapper {
-	position: relative;
+	margin-top: 90px;
 	width: 100%;
 	display: flex;
-	flex-direction: column;
 	align-items: center;
-	min-height: 200px;
-
-	.content-box {
-		position: absolute;
-	}
 }
 
 .content-box {
-	margin-top: 90px;
 	border-radius: 3px;
 	background-color: var(--white);
 	border: 1px solid var(--dark-grey);
 	max-width: 580px;
 	padding: 40px;
+	flex: 1;
 
 	@media screen and (max-width: 300px) {
 		margin-top: 30px;
@@ -1594,7 +1667,6 @@ h4.section-title {
 .content-box-optional-helper {
 	margin-top: 15px;
 	color: var(--primary-color);
-	text-decoration: underline;
 	font-size: 16px;
 
 	a {

+ 3 - 1
frontend/src/components/Modal.vue

@@ -56,7 +56,7 @@ export default {
 	.modal-card-head,
 	.modal-card-foot {
 		background-color: var(--dark-grey-3);
-		border-color: var(--dark-grey-2);
+		border: none;
 	}
 
 	.modal-card-body {
@@ -165,6 +165,7 @@ export default {
 		}
 
 		.modal-card-head {
+			border-bottom: 1px solid var(--light-grey-2);
 			border-radius: 5px 5px 0 0;
 
 			.modal-card-title {
@@ -186,6 +187,7 @@ export default {
 		}
 
 		.modal-card-foot {
+			border-top: 1px solid var(--light-grey-2);
 			border-radius: 0 0 5px 5px;
 			overflow: initial;
 			column-gap: 16px;

+ 14 - 4
frontend/src/components/modals/EditNews.vue

@@ -46,9 +46,8 @@
 							:user-id="createdBy"
 							:alt="createdBy"
 							:link="true"
-						/>
-					</span>
-					<span :title="new Date(createdAt)">
+						/> </span
+					>&nbsp;<span :title="new Date(createdAt)">
 						{{
 							formatDistance(createdAt, new Date(), {
 								addSuffix: true
@@ -96,7 +95,7 @@ export default {
 		marked.use({
 			renderer: {
 				table(header, body) {
-					return `<table class="table is-striped">
+					return `<table class="table">
 					<thead>${header}</thead>
 					<tbody>${body}</tbody>
 					</table>`;
@@ -257,4 +256,15 @@ export default {
 		width: 100%;
 	}
 }
+
+.edit-news-modal .modal-card .modal-card-foot {
+	.control {
+		margin-bottom: 0 !important;
+	}
+
+	.right {
+		line-height: 36px;
+		column-gap: 0;
+	}
+}
 </style>

+ 23 - 0
frontend/src/components/modals/EditSong/index.vue

@@ -1605,10 +1605,31 @@ export default {
 		background: var(--dark-grey) !important;
 		color: var(--white) !important;
 	}
+
+	.left-section {
+		.edit-section {
+			.album-get-button,
+			.duration-fill-button,
+			.add-button {
+				&:focus,
+				&:hover {
+					border: none !important;
+				}
+			}
+		}
+	}
+}
+
+.modal-card-body {
+	display: flex;
 }
 
 .left-section {
 	flex-basis: unset !important;
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	margin-right: 16px;
 
 	.top-section {
 		display: flex;
@@ -1981,6 +2002,8 @@ export default {
 .right-section {
 	flex-basis: unset !important;
 	flex-grow: 0 !important;
+	display: flex;
+	height: 100%;
 
 	#tabs-container {
 		width: 376px;

+ 19 - 13
frontend/src/components/modals/Report.vue

@@ -1,6 +1,10 @@
 <template>
 	<div>
-		<modal class="report-modal" title="Report">
+		<modal
+			class="report-modal"
+			title="Report"
+			:wide="existingReports.length > 0"
+		>
 			<template #body>
 				<div class="report-modal-inner-container">
 					<div id="left-part">
@@ -442,18 +446,10 @@ export default {
 </script>
 
 <style lang="scss">
-.report-modal {
-	.modal-card {
-		width: 1050px;
-	}
-
-	.song-item {
-		.thumbnail {
-			min-width: 130px;
-			width: 130px;
-			height: 130px;
-		}
-	}
+.report-modal .song-item .thumbnail {
+	min-width: 130px;
+	width: 130px;
+	height: 130px;
 }
 </style>
 
@@ -488,6 +484,7 @@ export default {
 			margin-bottom: 20px;
 			padding: 20px;
 			background-color: var(--light-grey);
+			border-radius: 5px;
 		}
 	}
 
@@ -513,10 +510,19 @@ export default {
 }
 
 .columns {
+	display: flex;
+	flex-wrap: wrap;
 	margin-left: unset;
 	margin-right: unset;
 	margin-top: 20px;
 
+	.column {
+		flex-basis: 50%;
+		@media screen and (max-width: 900px) {
+			flex-basis: 100% !important;
+		}
+	}
+
 	.control {
 		display: flex;
 		flex-direction: column;

+ 1 - 1
frontend/src/components/modals/WhatIsNew.vue

@@ -54,7 +54,7 @@ export default {
 		marked.use({
 			renderer: {
 				table(header, body) {
-					return `<table class="table is-striped">
+					return `<table class="table">
 					<thead>${header}</thead>
 					<tbody>${body}</tbody>
 					</table>`;

+ 4 - 0
frontend/src/pages/About.vue

@@ -96,4 +96,8 @@ export default {
 		padding-bottom: 10px;
 	}
 }
+
+ol {
+	margin-left: 2em;
+}
 </style>

+ 1 - 0
frontend/src/pages/Admin/index.vue

@@ -304,6 +304,7 @@ export default {
 		flex-grow: 1;
 		flex-shrink: 0;
 		justify-content: center;
+		border-bottom: 1px solid var(--light-grey-2);
 	}
 
 	.unverifiedsongs {

+ 1 - 1
frontend/src/pages/Admin/tabs/HiddenSongs.vue

@@ -110,7 +110,7 @@
 				<span>Loaded songs: {{ songs.length }}</span>
 			</p>
 			<br />
-			<table class="table is-striped">
+			<table class="table">
 				<thead>
 					<tr>
 						<td>Thumbnail</td>

+ 1 - 1
frontend/src/pages/Admin/tabs/News.vue

@@ -7,7 +7,7 @@
 					Create News Item
 				</button>
 			</div>
-			<table class="table is-striped">
+			<table class="table">
 				<thead>
 					<tr>
 						<td>Status</td>

+ 1 - 1
frontend/src/pages/Admin/tabs/Playlists.vue

@@ -60,7 +60,7 @@
 					</button>
 				</confirm>
 			</div>
-			<table class="table is-striped">
+			<table class="table">
 				<thead>
 					<tr>
 						<td>Display name</td>

+ 1 - 1
frontend/src/pages/Admin/tabs/Punishments.vue

@@ -2,7 +2,7 @@
 	<div>
 		<page-metadata title="Admin | Punishments" />
 		<div class="container">
-			<table class="table is-striped">
+			<table class="table">
 				<thead>
 					<tr>
 						<td>Status</td>

+ 1 - 1
frontend/src/pages/Admin/tabs/Reports.vue

@@ -2,7 +2,7 @@
 	<div>
 		<page-metadata title="Admin | Reports" />
 		<div class="container">
-			<table class="table is-striped">
+			<table class="table">
 				<thead>
 					<tr>
 						<td>Summary</td>

+ 2 - 2
frontend/src/pages/Admin/tabs/Stations.vue

@@ -15,7 +15,7 @@
 					</button>
 				</confirm>
 			</div>
-			<table class="table is-striped">
+			<table class="table">
 				<thead>
 					<tr>
 						<td>ID</td>
@@ -79,13 +79,13 @@
 		</div>
 
 		<request-song v-if="modals.requestSong" />
-		<edit-playlist v-if="modals.editPlaylist" />
 		<create-playlist v-if="modals.createPlaylist" />
 		<manage-station
 			v-if="modals.manageStation"
 			:station-id="editingStationId"
 			sector="admin"
 		/>
+		<edit-playlist v-if="modals.editPlaylist" />
 		<edit-song v-if="modals.editSong" song-type="songs" sector="admin" />
 		<report v-if="modals.report" />
 		<create-station v-if="modals.createStation" :official="true" />

+ 1 - 1
frontend/src/pages/Admin/tabs/UnverifiedSongs.vue

@@ -110,7 +110,7 @@
 				<span>Loaded songs: {{ songs.length }}</span>
 			</p>
 			<br />
-			<table class="table is-striped">
+			<table class="table">
 				<thead>
 					<tr>
 						<td>Thumbnail</td>

+ 6 - 2
frontend/src/pages/Admin/tabs/Users.vue

@@ -4,7 +4,7 @@
 		<div class="container">
 			<h2 v-if="dataRequests.length > 0">Data Requests</h2>
 
-			<table class="table is-striped" v-if="dataRequests.length > 0">
+			<table class="table" v-if="dataRequests.length > 0">
 				<thead>
 					<tr>
 						<td>User ID</td>
@@ -36,7 +36,7 @@
 
 			<h1 id="page-title">Users</h1>
 
-			<table class="table is-striped">
+			<table class="table">
 				<thead>
 					<tr>
 						<td class="ppRow">Profile Picture</td>
@@ -206,6 +206,10 @@ export default {
 	}
 }
 
+#page-title {
+	margin: 30px 0;
+}
+
 h2 {
 	font-size: 30px;
 	text-align: center;

+ 1 - 1
frontend/src/pages/Admin/tabs/VerifiedSongs.vue

@@ -120,7 +120,7 @@
 				<span>Loaded songs: {{ songs.length }}</span>
 			</p>
 			<br />
-			<table class="table is-striped">
+			<table class="table">
 				<thead>
 					<tr>
 						<td>Thumbnail</td>

+ 2 - 1
frontend/src/pages/Home.vue

@@ -783,7 +783,7 @@ export default {
 };
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
 * {
 	box-sizing: border-box;
 }
@@ -1044,6 +1044,7 @@ html {
 	overflow: hidden;
 	margin: 10px;
 	cursor: pointer;
+	filter: none;
 	height: 150px;
 	width: calc(100% - 30px);
 	max-width: 400px;

+ 1 - 1
frontend/src/pages/News.vue

@@ -62,7 +62,7 @@ export default {
 		marked.use({
 			renderer: {
 				table(header, body) {
-					return `<table class="table is-striped">
+					return `<table class="table">
 					<thead>${header}</thead>
 					<tbody>${body}</tbody>
 					</table>`;

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

@@ -78,7 +78,7 @@
 			</button>
 		</div>
 		<div v-else>
-			<h3>No playlists here.</h3>
+			<h5>No playlists here.</h5>
 		</div>
 	</div>
 </template>

+ 11 - 13
frontend/src/pages/Profile/Tabs/RecentActivity.vue

@@ -11,7 +11,7 @@
 
 			<hr class="section-horizontal-rule" />
 
-			<div id="activity-items" @scroll="handleScroll">
+			<div id="activity-items">
 				<activity-item
 					class="item activity-item universal-item"
 					v-for="activity in activities"
@@ -34,7 +34,7 @@
 			</div>
 		</div>
 		<div v-else>
-			<h3>No recent activity.</h3>
+			<h5>No recent activity.</h5>
 		</div>
 	</div>
 </template>
@@ -77,6 +77,8 @@ export default {
 		})
 	},
 	mounted() {
+		window.addEventListener("scroll", this.handleScroll);
+
 		ws.onConnect(this.init);
 
 		this.socket.on("event:activity.updated", res => {
@@ -105,6 +107,9 @@ export default {
 			this.offsettedFromNextSet = 0;
 		});
 	},
+	unmounted() {
+		window.removeEventListener("scroll", this.handleScroll);
+	},
 	methods: {
 		init() {
 			if (this.myUserId !== this.userId)
@@ -145,14 +150,13 @@ export default {
 				}
 			);
 		},
-		handleScroll(event) {
-			const scrollPosition =
-				event.target.clientHeight + event.target.scrollTop;
-			const bottomPosition = event.target.scrollHeight;
+		handleScroll() {
+			const scrollPosition = document.body.clientHeight + window.scrollY;
+			const bottomPosition = document.body.scrollHeight;
 
 			if (this.loadAllSongs) return false;
 
-			if (scrollPosition + 100 >= bottomPosition) this.getSet();
+			if (scrollPosition + 400 >= bottomPosition) this.getSet();
 
 			return this.maxPosition === this.position;
 		},
@@ -167,12 +171,6 @@ export default {
 	border: 0 !important;
 }
 
-#activity-items {
-	overflow: auto;
-	min-height: auto;
-	max-height: 570px;
-}
-
 .content a {
 	border-bottom: 0;
 }

+ 1 - 1
frontend/src/pages/Profile/index.vue

@@ -424,7 +424,7 @@ export default {
 		}
 
 		.item {
-			overflow: initial;
+			overflow: hidden;
 
 			&:not(:last-of-type) {
 				margin-bottom: 10px;

+ 242 - 222
frontend/src/pages/ResetPassword.vue

@@ -20,252 +20,272 @@
 
 				<div class="content-box-wrapper">
 					<transition-group name="steps-fade" mode="out-in">
-						<!-- Step 1 -- Enter email address -->
-						<div class="content-box" v-if="step === 1" key="1">
-							<h2 class="content-box-title">
-								Enter your email address
-							</h2>
-							<p class="content-box-description">
-								We will send a code to your email address to
-								verify your identity.
-							</p>
-
-							<p class="content-box-optional-helper">
-								<a @click="step = 2">Already have a code?</a>
-							</p>
-
-							<div class="content-box-inputs">
-								<div
-									class="control is-grouped input-with-button"
-								>
-									<p class="control is-expanded">
-										<input
-											class="input"
-											type="email"
-											placeholder="Enter email address here..."
-											autofocus
-											v-model="email.value"
-											@keyup.enter="submitEmail()"
-											@keypress="onInput('email')"
-											@paste="onInput('email')"
-										/>
-									</p>
-									<p class="control">
-										<button
-											class="button is-info"
-											@click="submitEmail()"
-										>
-											<i
-												class="
-													material-icons
-													icon-with-button
-												"
-												>mail</i
-											>Request
-										</button>
-									</p>
-								</div>
-								<transition name="fadein-helpbox">
-									<input-help-box
-										:entered="email.entered"
-										:valid="email.valid"
-										:message="email.message"
-									/>
-								</transition>
-							</div>
-						</div>
+						<div class="content-box">
+							<!-- Step 1 -- Enter email address -->
+							<div v-if="step === 1" key="1">
+								<h2 class="content-box-title">
+									Enter your email address
+								</h2>
+								<p class="content-box-description">
+									We will send a code to your email address to
+									verify your identity.
+								</p>
 
-						<!-- Step 2 -- Enter code -->
-						<div class="content-box" v-if="step === 2" key="2">
-							<h2 class="content-box-title">
-								Enter the code sent to your email
-							</h2>
-							<p
-								class="content-box-description"
-								v-if="!email.hasBeenSentAlready"
-							>
-								A code has been sent to
-								<strong>{{ email.value }}.</strong>
-							</p>
-
-							<p class="content-box-optional-helper">
-								<a
-									@click="
-										email.value ? submitEmail() : (step = 1)
-									"
-									>Request another code</a
-								>
-							</p>
+								<p class="content-box-optional-helper">
+									<a @click="step = 2"
+										>Already have a code?</a
+									>
+								</p>
 
-							<div class="content-box-inputs">
-								<div
-									class="control is-grouped input-with-button"
-								>
-									<p class="control is-expanded">
-										<input
-											class="input"
-											type="text"
-											placeholder="Enter code here..."
-											autofocus
-											v-model="code"
-											@keyup.enter="verifyCode()"
+								<div class="content-box-inputs">
+									<div
+										class="
+											control
+											is-grouped
+											input-with-button
+										"
+									>
+										<p class="control is-expanded">
+											<input
+												class="input"
+												type="email"
+												placeholder="Enter email address here..."
+												autofocus
+												v-model="email.value"
+												@keyup.enter="submitEmail()"
+												@keypress="onInput('email')"
+												@paste="onInput('email')"
+											/>
+										</p>
+										<p class="control">
+											<button
+												class="button is-info"
+												@click="submitEmail()"
+											>
+												<i
+													class="
+														material-icons
+														icon-with-button
+													"
+													>mail</i
+												>Request
+											</button>
+										</p>
+									</div>
+									<transition name="fadein-helpbox">
+										<input-help-box
+											:entered="email.entered"
+											:valid="email.valid"
+											:message="email.message"
 										/>
-									</p>
-									<p class="control">
-										<button
-											class="button is-info"
-											@click="verifyCode()"
-										>
-											<i
-												class="
-													material-icons
-													icon-with-button
-												"
-												>vpn_key</i
-											>Verify
-										</button>
-									</p>
+									</transition>
 								</div>
 							</div>
-						</div>
 
-						<!-- Step 3 -- Set new password -->
-						<div class="content-box" v-if="step === 3" key="3">
-							<h2 class="content-box-title">
-								Set a new password
-							</h2>
-							<p class="content-box-description">
-								Create a new password for your account.
-							</p>
-
-							<div class="content-box-inputs">
-								<p class="control is-expanded">
-									<label for="new-password"
-										>New password</label
-									>
+							<!-- Step 2 -- Enter code -->
+							<div v-if="step === 2" key="2">
+								<h2 class="content-box-title">
+									Enter the code sent to your email
+								</h2>
+								<p
+									class="content-box-description"
+									v-if="!email.hasBeenSentAlready"
+								>
+									A code has been sent to
+									<strong>{{ email.value }}.</strong>
 								</p>
 
-								<div id="password-visibility-container">
-									<input
-										class="input"
-										id="new-password"
-										type="password"
-										ref="password"
-										placeholder="Enter password here..."
-										v-model="password.value"
-										@keypress="onInput('password')"
-										@paste="onInput('password')"
-									/>
+								<p class="content-box-optional-helper">
 									<a
 										@click="
-											togglePasswordVisibility('password')
+											email.value
+												? submitEmail()
+												: (step = 1)
+										"
+										>Request another code</a
+									>
+								</p>
+
+								<div class="content-box-inputs">
+									<div
+										class="
+											control
+											is-grouped
+											input-with-button
 										"
 									>
-										<i class="material-icons">
-											{{
-												!password.visible
-													? "visibility"
-													: "visibility_off"
-											}}
-										</i>
-									</a>
+										<p class="control is-expanded">
+											<input
+												class="input"
+												type="text"
+												placeholder="Enter code here..."
+												autofocus
+												v-model="code"
+												@keyup.enter="verifyCode()"
+											/>
+										</p>
+										<p class="control">
+											<button
+												class="button is-info"
+												@click="verifyCode()"
+											>
+												<i
+													class="
+														material-icons
+														icon-with-button
+													"
+													>vpn_key</i
+												>Verify
+											</button>
+										</p>
+									</div>
 								</div>
+							</div>
 
-								<transition name="fadein-helpbox">
-									<input-help-box
-										:entered="password.entered"
-										:valid="password.valid"
-										:message="password.message"
-									/>
-								</transition>
+							<!-- Step 3 -- Set new password -->
+							<div v-if="step === 3" key="3">
+								<h2 class="content-box-title">
+									Set a new password
+								</h2>
+								<p class="content-box-description">
+									Create a new password for your account.
+								</p>
 
-								<p
-									id="new-password-again-input"
-									class="control is-expanded"
-								>
-									<label for="new-password-again"
-										>New password again</label
+								<div class="content-box-inputs">
+									<p class="control is-expanded">
+										<label for="new-password"
+											>New password</label
+										>
+									</p>
+
+									<div id="password-visibility-container">
+										<input
+											class="input"
+											id="new-password"
+											type="password"
+											ref="password"
+											placeholder="Enter password here..."
+											v-model="password.value"
+											@keypress="onInput('password')"
+											@paste="onInput('password')"
+										/>
+										<a
+											@click="
+												togglePasswordVisibility(
+													'password'
+												)
+											"
+										>
+											<i class="material-icons">
+												{{
+													!password.visible
+														? "visibility"
+														: "visibility_off"
+												}}
+											</i>
+										</a>
+									</div>
+
+									<transition name="fadein-helpbox">
+										<input-help-box
+											:entered="password.entered"
+											:valid="password.valid"
+											:message="password.message"
+										/>
+									</transition>
+
+									<p
+										id="new-password-again-input"
+										class="control is-expanded"
 									>
-								</p>
+										<label for="new-password-again"
+											>New password again</label
+										>
+									</p>
 
-								<div id="password-visibility-container">
-									<input
-										class="input"
-										id="new-password-again"
-										type="password"
-										ref="passwordAgain"
-										placeholder="Enter password here..."
-										v-model="passwordAgain.value"
-										@keyup.enter="changePassword()"
-										@keypress="onInput('passwordAgain')"
-										@paste="onInput('passwordAgain')"
-									/>
-									<a
-										@click="
-											togglePasswordVisibility(
-												'passwordAgain'
-											)
-										"
+									<div id="password-visibility-container">
+										<input
+											class="input"
+											id="new-password-again"
+											type="password"
+											ref="passwordAgain"
+											placeholder="Enter password here..."
+											v-model="passwordAgain.value"
+											@keyup.enter="changePassword()"
+											@keypress="onInput('passwordAgain')"
+											@paste="onInput('passwordAgain')"
+										/>
+										<a
+											@click="
+												togglePasswordVisibility(
+													'passwordAgain'
+												)
+											"
+										>
+											<i class="material-icons">
+												{{
+													!passwordAgain.visible
+														? "visibility"
+														: "visibility_off"
+												}}
+											</i>
+										</a>
+									</div>
+
+									<transition name="fadein-helpbox">
+										<input-help-box
+											:entered="passwordAgain.entered"
+											:valid="passwordAgain.valid"
+											:message="passwordAgain.message"
+										/>
+									</transition>
+
+									<button
+										id="change-password-button"
+										class="button is-success"
+										@click="changePassword()"
 									>
-										<i class="material-icons">
-											{{
-												!passwordAgain.visible
-													? "visibility"
-													: "visibility_off"
-											}}
-										</i>
-									</a>
+										Change password
+									</button>
 								</div>
-
-								<transition name="fadein-helpbox">
-									<input-help-box
-										:entered="passwordAgain.entered"
-										:valid="passwordAgain.valid"
-										:message="passwordAgain.message"
-									/>
-								</transition>
-
-								<button
-									id="change-password-button"
-									class="button is-success"
-									@click="changePassword()"
-								>
-									Change password
-								</button>
 							</div>
-						</div>
 
-						<div
-							class="content-box reset-status-box"
-							v-if="step === 4"
-							key="4"
-						>
-							<i class="material-icons success-icon"
-								>check_circle</i
-							>
-							<h2>Password successfully {{ mode }}</h2>
-							<router-link class="button is-dark" to="/settings"
-								><i class="material-icons icon-with-button"
-									>undo</i
-								>Return to Settings</router-link
+							<div
+								class="reset-status-box"
+								v-if="step === 4"
+								key="4"
 							>
-						</div>
+								<i class="material-icons success-icon"
+									>check_circle</i
+								>
+								<h2>Password successfully {{ mode }}</h2>
+								<router-link
+									class="button is-dark"
+									to="/settings"
+									><i class="material-icons icon-with-button"
+										>undo</i
+									>Return to Settings</router-link
+								>
+							</div>
 
-						<div
-							class="content-box reset-status-box"
-							v-if="step === 5"
-							key="5"
-						>
-							<i class="material-icons error-icon">error</i>
-							<h2>
-								Password {{ mode }} failed, please try again
-								later
-							</h2>
-							<router-link class="button is-dark" to="/settings"
-								><i class="material-icons icon-with-button"
-									>undo</i
-								>Return to Settings</router-link
+							<div
+								class="reset-status-box"
+								v-if="step === 5"
+								key="5"
 							>
+								<i class="material-icons error-icon">error</i>
+								<h2>
+									Password {{ mode }} failed, please try again
+									later
+								</h2>
+								<router-link
+									class="button is-dark"
+									to="/settings"
+									><i class="material-icons icon-with-button"
+										>undo</i
+									>Return to Settings</router-link
+								>
+							</div>
 						</div>
 					</transition-group>
 				</div>

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

@@ -141,6 +141,7 @@ export default {
 
 		.menu-list {
 			padding: 0 10px;
+			margin-left: 0;
 		}
 
 		li {

+ 1 - 1
frontend/src/pages/Station/index.vue

@@ -686,13 +686,13 @@
 				</div>
 
 				<request-song v-if="modals.requestSong" />
-				<edit-playlist v-if="modals.editPlaylist" />
 				<create-playlist v-if="modals.createPlaylist" />
 				<manage-station
 					v-if="modals.manageStation"
 					:station-id="station._id"
 					sector="station"
 				/>
+				<edit-playlist v-if="modals.editPlaylist" />
 				<edit-song
 					v-if="modals.editSong"
 					song-type="songs"