Ver Fonte

fix(Punishments): improved design of Punishments modal, admin tab

Signed-off-by: Jonathan Graham <theflametrooper@gmail.com>
Jonathan Graham há 3 anos atrás
pai
commit
e6d03fe7d0

+ 44 - 28
backend/logic/actions/punishments.js

@@ -60,40 +60,56 @@ export default {
 	}),
 
 	/**
-	 * Gets a punishment by id
+	 * Gets all punishments for a user
+	 *
+	 * @param {object} session - the session object automatically added by the websocket
+	 * @param {string} userId - the id of the user
+	 * @param {Function} cb - gets called with the result
+	 */
+	getPunishmentsForUser: isAdminRequired(async function getPunishmentsForUser(session, userId, cb) {
+		const punishmentModel = await DBModule.runJob("GET_MODEL", { modelName: "punishment" }, this);
+
+		punishmentModel.find({ type: "banUserId", value: userId }, async (err, punishments) => {
+			if (err) {
+				err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+
+				this.log(
+					"ERROR",
+					"GET_PUNISHMENTS_FOR_USER",
+					`Getting punishments for user ${userId} failed. "${err}"`
+				);
+
+				return cb({ status: "error", message: err });
+			}
+
+			this.log("SUCCESS", "GET_PUNISHMENTS_FOR_USER", `Got punishments for user ${userId} successful.`);
+			return cb({ status: "success", data: { punishments } });
+		});
+	}),
+
+	/**
+	 * Returns a punishment by id
 	 *
 	 * @param {object} session - the session object automatically added by the websocket
 	 * @param {string} punishmentId - the punishment id
 	 * @param {Function} cb - gets called with the result
 	 */
-	getPunishmentById: isAdminRequired(async function index(session, punishmentId, cb) {
-		const punishmentModel = await DBModule.runJob(
-			"GET_MODEL",
-			{
-				modelName: "punishment"
-			},
-			this
-		);
-		async.waterfall(
-			[
-				next => {
-					punishmentModel.findOne({ _id: punishmentId }, next);
-				}
-			],
-			async (err, punishment) => {
-				if (err) {
-					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
-					this.log(
-						"ERROR",
-						"GET_PUNISHMENT_BY_ID",
-						`Getting punishment with id ${punishmentId} failed. "${err}"`
-					);
-					return cb({ status: "error", message: err });
-				}
-				this.log("SUCCESS", "GET_PUNISHMENT_BY_ID", `Got punishment with id ${punishmentId} successful.`);
-				return cb({ status: "success", data: { punishment } });
+	findOne: isAdminRequired(async function findOne(session, punishmentId, cb) {
+		const punishmentModel = await DBModule.runJob("GET_MODEL", { modelName: "punishment" }, this);
+
+		async.waterfall([next => punishmentModel.findOne({ _id: punishmentId }, next)], async (err, punishment) => {
+			if (err) {
+				err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+				this.log(
+					"ERROR",
+					"GET_PUNISHMENT_BY_ID",
+					`Getting punishment with id ${punishmentId} failed. "${err}"`
+				);
+				return cb({ status: "error", message: err });
 			}
-		);
+			this.log("SUCCESS", "GET_PUNISHMENT_BY_ID", `Got punishment with id ${punishmentId} successful.`);
+			return cb({ status: "success", data: { punishment } });
+		});
 	}),
 
 	/**

+ 1 - 1
backend/logic/actions/users.js

@@ -2545,7 +2545,7 @@ export default {
 							value: userId,
 							reason,
 							expiresAt,
-							punishedBy: "" // needs changed
+							punishedBy: session.userId
 						},
 						this
 					)

+ 156 - 0
frontend/src/components/PunishmentItem.vue

@@ -0,0 +1,156 @@
+<template>
+	<div class="universal-item punishment-item">
+		<div class="item-icon">
+			<p class="is-expanded checkbox-control">
+				<label class="switch">
+					<input type="checkbox" v-model="active" disabled />
+					<span class="slider round"></span>
+				</label>
+			</p>
+			<p>
+				<strong>{{ active ? "Active" : "Inactive" }}</strong>
+			</p>
+		</div>
+
+		<div class="item-title-description">
+			<h2 v-if="punishment.type === 'banUserId'" class="item-title">
+				<strong>Punishment</strong> for user
+				<user-id-to-username
+					:user-id="punishment.value"
+					:alt="punishment.value"
+					:link="true"
+				/>
+			</h2>
+			<h2 class="item-title" v-else>
+				<strong>Punishment</strong> for IP
+				{{ punishment.value }}
+			</h2>
+			<h3 class="item-title-2">Reason: {{ punishment.reason }}</h3>
+			<ul>
+				<li class="item-description" :title="punishment.expiresAt">
+					Expires
+					{{
+						formatDistance(
+							parseISO(punishment.expiresAt),
+							new Date(),
+							{ addSuffix: true }
+						)
+					}}
+					({{
+						format(
+							parseISO(punishment.expiresAt),
+							"MMMM do yyyy, h:mm:ss a"
+						)
+					}})
+				</li>
+				<li class="item-description">
+					Punished by
+					<user-id-to-username
+						:user-id="punishment.punishedBy"
+						:alt="punishment.punishedBy"
+					/>
+
+					<span :title="punishment.punishedAt">
+						&nbsp;{{
+							formatDistance(
+								parseISO(punishment.punishedAt),
+								new Date(),
+								{ addSuffix: true }
+							)
+						}}
+						({{
+							format(
+								parseISO(punishment.punishedAt),
+								"MMMM do yyyy, h:mm:ss a"
+							)
+						}})
+					</span>
+				</li>
+			</ul>
+		</div>
+	</div>
+</template>
+
+<script>
+import { mapActions } from "vuex";
+import { format, formatDistance, parseISO } from "date-fns";
+
+import UserIdToUsername from "@/components/UserIdToUsername.vue";
+
+export default {
+	components: { UserIdToUsername },
+	props: {
+		punishment: { type: Object, default: () => {} }
+	},
+	data() {
+		return {
+			active: false
+		};
+	},
+	watch: {
+		punishment(punishment) {
+			this.active =
+				punishment.active &&
+				new Date(this.punishment.expiresAt).getTime() > Date.now();
+		}
+	},
+	methods: {
+		formatDistance,
+		format,
+		parseISO,
+		...mapActions("modalVisibility", ["closeModal"])
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.night-mode {
+	.punishment-item {
+		background-color: var(--dark-grey-2) !important;
+		border: 0 !important;
+	}
+}
+
+.punishment-item {
+	padding: 15px;
+	justify-content: flex-start;
+
+	.item-icon {
+		min-width: 85px;
+		max-width: 85px;
+		height: 85px;
+		margin-left: 20px;
+		margin-right: 35px;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: space-evenly;
+		border: 1px solid var(--light-grey-3);
+		border-radius: 5px;
+
+		.checkbox-control .slider {
+			cursor: default;
+		}
+	}
+
+	.item-title {
+		font-size: 19px;
+		margin: 0;
+	}
+
+	.item-title-2 {
+		font-size: 17px;
+		margin: 0;
+	}
+
+	ul {
+		list-style: inside;
+		margin-top: 10px;
+
+		.item-description {
+			font-size: 14px;
+			margin: 0;
+		}
+	}
+}
+</style>

+ 5 - 2
frontend/src/components/modals/EditNews.vue

@@ -45,8 +45,11 @@
 						<user-id-to-username
 							:user-id="createdBy"
 							:alt="createdBy"
-							:link="true" /></span
-					><span :title="new Date(createdAt)">
+							:link="true"
+						/>
+						&nbsp;
+					</span>
+					<span :title="new Date(createdAt)">
 						{{
 							formatDistance(createdAt, new Date(), {
 								addSuffix: true

+ 3 - 3
frontend/src/components/modals/EditUser.vue

@@ -69,9 +69,9 @@
 							placeholder="Ban reason"
 							autofocus
 						/>
-						<a class="button is-danger" @click="banUser()"
-							>Ban user</a
-						>
+						<a class="button is-danger" @click="banUser()">
+							Ban user
+						</a>
 					</p>
 				</div>
 			</template>

+ 5 - 56
frontend/src/components/modals/ViewPunishment.vue

@@ -2,58 +2,7 @@
 	<div>
 		<modal title="View Punishment">
 			<template #body v-if="punishment && punishment._id">
-				<article class="message">
-					<div class="message-body">
-						<strong>Type:</strong>
-						{{ punishment.type }}
-						<br />
-						<strong>Value:</strong>
-						{{ punishment.value }}
-						<br />
-						<strong>Reason:</strong>
-						{{ punishment.reason }}
-						<br />
-						<strong>Active:</strong>
-						{{ punishment.active }}
-						<br />
-						<strong>Expires at:</strong>
-						{{
-							format(
-								parseISO(punishment.expiresAt),
-								"MMMM do yyyy, h:mm:ss a"
-							)
-						}}
-						({{
-							formatDistance(
-								parseISO(punishment.expiresAt),
-								new Date(),
-								{ addSuffix: true }
-							)
-						}})
-						<br />
-						<strong>Punished at:</strong>
-						{{
-							format(
-								parseISO(punishment.punishedAt),
-								"MMMM do yyyy, h:mm:ss a"
-							)
-						}}
-						({{
-							formatDistance(
-								parseISO(punishment.punishedAt),
-								new Date(),
-								{ addSuffix: true }
-							)
-						}})
-						<br />
-						<strong>Punished by:</strong>
-						<user-id-to-username
-							:user-id="punishment.punishedBy"
-							:alt="punishment.punishedBy"
-						/>
-						<br />
-					</div>
-				</article>
+				<punishment-item :punishment="punishment" />
 			</template>
 		</modal>
 	</div>
@@ -61,15 +10,15 @@
 
 <script>
 import { mapState, mapGetters, mapActions } from "vuex";
-import { format, formatDistance, parseISO } from "date-fns"; // eslint-disable-line no-unused-vars
+import { format, formatDistance, parseISO } from "date-fns";
 import ws from "@/ws";
 
 import Toast from "toasters";
 import Modal from "../Modal.vue";
-import UserIdToUsername from "../UserIdToUsername.vue";
+import PunishmentItem from "../PunishmentItem.vue";
 
 export default {
-	components: { Modal, UserIdToUsername },
+	components: { Modal, PunishmentItem },
 	props: {
 		punishmentId: { type: String, default: "" },
 		sector: { type: String, default: "admin" }
@@ -93,7 +42,7 @@ export default {
 	methods: {
 		init() {
 			this.socket.dispatch(
-				`punishments.getPunishmentById`,
+				`punishments.findOne`,
 				this.punishmentId,
 				res => {
 					if (res.status === "success") {

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

@@ -26,7 +26,7 @@
 							/>
 						</td>
 						<td class="news-item-markdown">{{ news.markdown }}</td>
-						<td>
+						<td id="options-column">
 							<button
 								class="button is-primary"
 								@click="edit(news._id)"
@@ -205,4 +205,12 @@ td {
 	overflow: hidden;
 	max-width: 400px;
 }
+
+#options-column {
+	display: flex;
+
+	button {
+		margin-right: 5px;
+	}
+}
 </style>

+ 28 - 9
frontend/src/pages/Admin/tabs/Punishments.vue

@@ -5,10 +5,10 @@
 			<table class="table is-striped">
 				<thead>
 					<tr>
+						<td>Status</td>
 						<td>Type</td>
 						<td>Value</td>
 						<td>Reason</td>
-						<td>Status</td>
 						<td>Options</td>
 					</tr>
 				</thead>
@@ -17,10 +17,6 @@
 						v-for="punishment in sortedPunishments"
 						:key="punishment._id"
 					>
-						<td v-if="punishment.type === 'banUserId'">User ID</td>
-						<td v-else>IP Address</td>
-						<td>{{ punishment.value }}</td>
-						<td>{{ punishment.reason }}</td>
 						<td>
 							{{
 								punishment.active &&
@@ -30,13 +26,33 @@
 									: "Inactive"
 							}}
 						</td>
+						<td v-if="punishment.type === 'banUserId'">User ID</td>
+						<td v-else>IP Address</td>
+						<td v-if="punishment.type === 'banUserId'">
+							<user-id-to-username
+								:user-id="punishment.value"
+								:alt="punishment.value"
+								:link="true"
+							/>
+							({{ punishment.value }})
+						</td>
+						<td v-else>
+							{{ punishment.value }}
+						</td>
+						<td>{{ punishment.reason }}</td>
+
 						<td>
-							<button
+							<a
 								class="button is-primary"
 								@click="view(punishment)"
+								content="Expand"
+								v-tippy
 							>
-								View
-							</button>
+								<i class="material-icons icon-with-button">
+									open_in_full
+								</i>
+								Expand
+							</a>
 						</td>
 					</tr>
 				</tbody>
@@ -100,11 +116,14 @@ import { defineAsyncComponent } from "vue";
 
 import ws from "@/ws";
 
+import UserIdToUsername from "@/components/UserIdToUsername.vue";
+
 export default {
 	components: {
 		ViewPunishment: defineAsyncComponent(() =>
 			import("@/components/modals/ViewPunishment.vue")
-		)
+		),
+		UserIdToUsername
 	},
 	data() {
 		return {