diff options
Diffstat (limited to 'src/pages/ManageMaintenance.vue')
-rw-r--r-- | src/pages/ManageMaintenance.vue | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/src/pages/ManageMaintenance.vue b/src/pages/ManageMaintenance.vue new file mode 100644 index 0000000..8378736 --- /dev/null +++ b/src/pages/ManageMaintenance.vue @@ -0,0 +1,317 @@ +<template> + <transition name="slide-fade" appear> + <div> + <h1 class="mb-3"> + {{ $t("Maintenance") }} + </h1> + + <div> + <router-link to="/add-maintenance" class="btn btn-primary mb-3"> + <font-awesome-icon icon="plus" /> {{ $t("Schedule Maintenance") }} + </router-link> + </div> + + <div class="shadow-box"> + <span v-if="Object.keys(sortedMaintenanceList).length === 0" class="d-flex align-items-center justify-content-center my-3"> + {{ $t("No Maintenance") }} + </span> + + <div + v-for="(item, index) in sortedMaintenanceList" + :key="index" + class="item" + :class="item.status" + > + <div class="left-part"> + <div + class="circle" + ></div> + <div class="info"> + <div class="title">{{ item.title }}</div> + <div v-if="false">{{ item.description }}</div> + <div class="status"> + {{ $t("maintenanceStatus-" + item.status) }} + </div> + + <MaintenanceTime :maintenance="item" /> + </div> + </div> + + <div class="buttons"> + <router-link v-if="false" :to="maintenanceURL(item.id)" class="btn btn-light">{{ $t("Details") }}</router-link> + + <div class="btn-group" role="group"> + <button v-if="item.active" class="btn btn-normal" @click="pauseDialog(item.id)"> + <font-awesome-icon icon="pause" /> {{ $t("Pause") }} + </button> + + <button v-if="!item.active" class="btn btn-primary" @click="resumeMaintenance(item.id)"> + <font-awesome-icon icon="play" /> {{ $t("Resume") }} + </button> + + <router-link :to="'/maintenance/edit/' + item.id" class="btn btn-normal"> + <font-awesome-icon icon="edit" /> {{ $t("Edit") }} + </router-link> + + <button class="btn btn-normal text-danger" @click="deleteDialog(item.id)"> + <font-awesome-icon icon="trash" /> {{ $t("Delete") }} + </button> + </div> + </div> + </div> + </div> + + <div class="text-center mt-3" style="font-size: 13px;"> + <a href="https://github.com/louislam/uptime-kuma/wiki/Maintenance" target="_blank">{{ $t("Learn More") }}</a> + </div> + + <Confirm ref="confirmPause" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="pauseMaintenance"> + {{ $t("pauseMaintenanceMsg") }} + </Confirm> + + <Confirm ref="confirmDelete" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="deleteMaintenance"> + {{ $t("deleteMaintenanceMsg") }} + </Confirm> + </div> + </transition> +</template> + +<script> +import { getResBaseURL } from "../util-frontend"; +import { getMaintenanceRelativeURL } from "../util.ts"; +import Confirm from "../components/Confirm.vue"; +import MaintenanceTime from "../components/MaintenanceTime.vue"; + +export default { + components: { + MaintenanceTime, + Confirm, + }, + data() { + return { + selectedMaintenanceID: undefined, + statusOrderList: { + "under-maintenance": 1000, + "scheduled": 900, + "inactive": 800, + "ended": 700, + "unknown": 0, + } + }; + }, + computed: { + sortedMaintenanceList() { + let result = Object.values(this.$root.maintenanceList); + + result.sort((m1, m2) => { + if (this.statusOrderList[m1.status] === this.statusOrderList[m2.status]) { + return m1.title.localeCompare(m2.title); + } else { + return this.statusOrderList[m1.status] < this.statusOrderList[m2.status]; + } + }); + + return result; + }, + }, + mounted() { + + }, + methods: { + /** + * Get the correct URL for the icon + * @param {string} icon Path for icon + * @returns {string} Correctly formatted path including port numbers + */ + icon(icon) { + if (icon === "/icon.svg") { + return icon; + } else { + return getResBaseURL() + icon; + } + }, + + /** + * Get maintenance URL + * @param {number} id ID of maintenance to read + * @returns {string} Relative URL + */ + maintenanceURL(id) { + return getMaintenanceRelativeURL(id); + }, + + /** + * Show delete confirmation + * @param {number} maintenanceID ID of maintenance to show delete + * confirmation for. + * @returns {void} + */ + deleteDialog(maintenanceID) { + this.selectedMaintenanceID = maintenanceID; + this.$refs.confirmDelete.show(); + }, + + /** + * Delete maintenance after showing confirmation dialog + * @returns {void} + */ + deleteMaintenance() { + this.$root.deleteMaintenance(this.selectedMaintenanceID, (res) => { + this.$root.toastRes(res); + if (res.ok) { + this.$router.push("/maintenance"); + } + }); + }, + + /** + * Show dialog to confirm pause + * @param {number} maintenanceID ID of maintenance to confirm + * pause. + * @returns {void} + */ + pauseDialog(maintenanceID) { + this.selectedMaintenanceID = maintenanceID; + this.$refs.confirmPause.show(); + }, + + /** + * Pause maintenance + * @returns {void} + */ + pauseMaintenance() { + this.$root.getSocket().emit("pauseMaintenance", this.selectedMaintenanceID, (res) => { + this.$root.toastRes(res); + }); + }, + + /** + * Resume maintenance + * @param {number} id ID of maintenance to resume + * @returns {void} + */ + resumeMaintenance(id) { + this.$root.getSocket().emit("resumeMaintenance", id, (res) => { + this.$root.toastRes(res); + }); + }, + }, +}; +</script> + +<style lang="scss" scoped> + @import "../assets/vars.scss"; + + .mobile { + .item { + flex-direction: column; + align-items: flex-start; + margin-bottom: 20px; + } + } + + .item { + display: flex; + align-items: center; + gap: 10px; + text-decoration: none; + border-radius: 10px; + transition: all ease-in-out 0.15s; + justify-content: space-between; + padding: 10px; + min-height: 90px; + margin-bottom: 5px; + + &:hover { + background-color: $highlight-white; + } + + &.under-maintenance { + background-color: rgba(23, 71, 245, 0.16); + + &:hover { + background-color: rgba(23, 71, 245, 0.3) !important; + } + + .circle { + background-color: $maintenance; + } + } + + &.scheduled { + .circle { + background-color: $primary; + } + } + + &.inactive { + .circle { + background-color: $danger; + } + } + + &.ended { + .left-part { + opacity: 0.3; + } + + .circle { + background-color: $dark-font-color; + } + } + + &.unknown { + .circle { + background-color: $dark-font-color; + } + } + + .left-part { + display: flex; + gap: 12px; + align-items: center; + + .circle { + width: 25px; + height: 25px; + border-radius: 50rem; + } + + .info { + .title { + font-weight: bold; + font-size: 20px; + } + + .status { + font-size: 14px; + } + } + } + + .buttons { + display: flex; + gap: 8px; + flex-direction: row-reverse; + + @media (max-width: 550px) { + & { + width: 100%; + } + + .btn-group { + margin: 1em 1em 0 1em; + width: 100%; + } + } + } + } + + .dark { + .item { + &:hover { + background-color: $dark-bg2; + } + } + } +</style> |