Browse Source

feat: added ability to deactivate a punishment

Kristian Vos 2 years ago
parent
commit
e566641e1a

+ 38 - 0
backend/logic/actions/punishments.js

@@ -347,5 +347,43 @@ export default {
 				});
 			}
 		);
+	}),
+
+	/**
+	 * Deactivates a punishment
+	 *
+	 * @param {object} session - the session object automatically added by the websocket
+	 * @param {string} punishmentId - the MongoDB id of the punishment
+	 * @param {Function} cb - gets called with the result
+	 */
+	deactivatePunishment: isAdminRequired(function deactivatePunishment(session, punishmentId, cb) {
+		async.waterfall(
+			[
+				next => {
+					PunishmentsModule.runJob("DEACTIVATE_PUNISHMENT", { punishmentId }, this)
+						.then(punishment => next(null, punishment))
+						.catch(next);
+				}
+			],
+			async (err, punishment) => {
+				if (err) {
+					err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
+					this.log(
+						"ERROR",
+						"DEACTIVATE_PUNISHMENT",
+						`Deactivating punishment ${punishmentId} failed. "${err}"`
+					);
+					return cb({ status: "error", message: err });
+				}
+				this.log("SUCCESS", "DEACTIVATE_PUNISHMENT", `Deactivated punishment ${punishmentId} successful.`);
+
+				WSModule.runJob("EMIT_TO_ROOM", {
+					room: `admin.punishments`,
+					args: ["event:admin.punishment.updated", { data: { punishment } }]
+				});
+
+				return cb({ status: "success" });
+			}
+		);
 	})
 };

+ 50 - 0
backend/logic/punishments.js

@@ -301,6 +301,56 @@ class _PunishmentsModule extends CoreClass {
 			);
 		});
 	}
+
+	/**
+	 * Deactivates a punishment
+	 *
+	 * @param {object} payload - object containing the payload
+	 * @param {string} payload.punishmentId - the MongoDB id of the punishment
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
+	DEACTIVATE_PUNISHMENT(payload) {
+		return new Promise((resolve, reject) => {
+			async.waterfall(
+				[
+					next => {
+						PunishmentsModule.punishmentModel.findOne({ _id: payload.punishmentId }, next);
+					},
+
+					(punishment, next) => {
+						if (!punishment) next("Punishment does not exist.");
+						else
+							PunishmentsModule.punishmentModel.updateOne(
+								{ _id: payload.punishmentId },
+								{ $set: { active: false } },
+								next
+							);
+					},
+
+					(res, next) => {
+						CacheModule.runJob(
+							"HDEL",
+							{
+								table: "punishments",
+								key: payload.punishmentId
+							},
+							this
+						)
+							.then(() => next())
+							.catch(next);
+					},
+
+					next => {
+						PunishmentsModule.punishmentModel.findOne({ _id: payload.punishmentId }, next);
+					}
+				],
+				(err, punishment) => {
+					if (err) return reject(new Error(err));
+					return resolve(punishment);
+				}
+			);
+		});
+	}
 }
 
 export default new _PunishmentsModule();

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

@@ -906,6 +906,7 @@ onMounted(async () => {
 
 	ws.onConnect(init);
 
+	// TODO, this doesn't address special properties
 	if (props.events && props.events.updated)
 		socket.on(`event:${props.events.updated.event}`, res => {
 			const index = rows.value

+ 15 - 10
frontend/src/components/PunishmentItem.vue

@@ -1,20 +1,17 @@
 <script setup lang="ts">
-import { ref, watch } from "vue";
+import { ref, computed } from "vue";
 import { format, formatDistance, parseISO } from "date-fns";
 
 const props = defineProps({
 	punishment: { type: Object, default: () => {} }
 });
 
-const active = ref(false);
+defineEmits(["deactivate"]);
 
-watch(
-	() => props.punishment,
-	punishment => {
-		active.value =
-			punishment.active &&
-			new Date(punishment.expiresAt).getTime() > Date.now();
-	}
+const active = computed(
+	() =>
+		props.punishment.active &&
+		new Date(props.punishment.expiresAt).getTime() > Date.now()
 );
 </script>
 
@@ -23,7 +20,15 @@ watch(
 		<div class="item-icon">
 			<p class="is-expanded checkbox-control">
 				<label class="switch">
-					<input type="checkbox" v-model="active" disabled />
+					<input
+						type="checkbox"
+						:checked="active"
+						@click="
+							active
+								? $emit('deactivate', $event)
+								: $event.preventDefault()
+						"
+					/>
 					<span class="slider round"></span>
 				</label>
 			</p>

+ 19 - 1
frontend/src/components/modals/ViewPunishment.vue

@@ -34,6 +34,21 @@ const init = () => {
 	});
 };
 
+const deactivatePunishment = event => {
+	event.preventDefault();
+	socket.dispatch(
+		"punishments.deactivatePunishment",
+		punishmentId.value,
+		res => {
+			if (res.status === "success") {
+				viewPunishmentStore.deactivatePunishment();
+			} else {
+				new Toast(res.message);
+			}
+		}
+	);
+};
+
 onMounted(() => {
 	ws.onConnect(init);
 });
@@ -48,7 +63,10 @@ onBeforeUnmount(() => {
 	<div>
 		<modal title="View Punishment">
 			<template #body v-if="punishment && punishment._id">
-				<punishment-item :punishment="punishment" />
+				<punishment-item
+					:punishment="punishment"
+					@deactivate="deactivatePunishment"
+				/>
 			</template>
 		</modal>
 	</div>

+ 32 - 1
frontend/src/pages/Admin/Users/Punishments.vue

@@ -26,7 +26,7 @@ const columns = ref([
 	{
 		name: "options",
 		displayName: "Options",
-		properties: ["_id"],
+		properties: ["_id", "status"],
 		sortable: false,
 		hidable: false,
 		resizable: false,
@@ -143,6 +143,14 @@ const filters = ref([
 		defaultFilterType: "datetimeBefore"
 	}
 ]);
+const events = ref({
+	adminRoom: "punishments",
+	updated: {
+		event: "admin.punishment.updated",
+		id: "punishment._id",
+		item: "punishment"
+	}
+});
 
 const { openModal } = useModalsStore();
 
@@ -167,6 +175,16 @@ const getDateFormatted = createdAt => {
 	const minute = `${date.getMinutes()}`.padStart(2, 0);
 	return `${year}-${month}-${day} ${hour}:${minute}`;
 };
+
+const deactivatePunishment = punishmentId => {
+	socket.dispatch("punishments.deactivatePunishment", punishmentId, res => {
+		if (res.status === "success") {
+			new Toast("Successfully deactivated punishment.");
+		} else {
+			new Toast(res.message);
+		}
+	});
+};
 </script>
 
 <template>
@@ -182,6 +200,7 @@ const getDateFormatted = createdAt => {
 			:column-default="columnDefault"
 			:columns="columns"
 			:filters="filters"
+			:events="events"
 			data-action="punishments.getData"
 			name="admin-punishments"
 			:max-width="1200"
@@ -202,6 +221,18 @@ const getDateFormatted = createdAt => {
 					>
 						open_in_full
 					</button>
+					<button
+						class="button is-primary icon-with-button material-icons"
+						@click="deactivatePunishment(slotProps.item._id)"
+						:disabled="
+							slotProps.item.removed ||
+							slotProps.item.status === 'Inactive'
+						"
+						content="Deactivate Punishment"
+						v-tippy
+					>
+						gavel
+					</button>
 				</div>
 			</template>
 			<template #column-status="slotProps">

+ 3 - 0
frontend/src/stores/viewPunishment.ts

@@ -14,6 +14,9 @@ export const useViewPunishmentStore = props => {
 			},
 			viewPunishment(punishment) {
 				this.punishment = punishment;
+			},
+			deactivatePunishment() {
+				this.punishment.active = false;
 			}
 		}
 	})();