diff options
Diffstat (limited to 'src/pages/DashboardHome.vue')
-rw-r--r-- | src/pages/DashboardHome.vue | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/src/pages/DashboardHome.vue b/src/pages/DashboardHome.vue new file mode 100644 index 0000000..a00dedb --- /dev/null +++ b/src/pages/DashboardHome.vue @@ -0,0 +1,248 @@ +<template> + <transition ref="tableContainer" name="slide-fade" appear> + <div v-if="$route.name === 'DashboardHome'"> + <h1 class="mb-3"> + {{ $t("Quick Stats") }} + </h1> + + <div class="shadow-box big-padding text-center mb-4"> + <div class="row"> + <div class="col"> + <h3>{{ $t("Up") }}</h3> + <span + class="num" + :class="$root.stats.up === 0 && 'text-secondary'" + > + {{ $root.stats.up }} + </span> + </div> + <div class="col"> + <h3>{{ $t("Down") }}</h3> + <span + class="num" + :class="$root.stats.down > 0 ? 'text-danger' : 'text-secondary'" + > + {{ $root.stats.down }} + </span> + </div> + <div class="col"> + <h3>{{ $t("Maintenance") }}</h3> + <span + class="num" + :class="$root.stats.maintenance > 0 ? 'text-maintenance' : 'text-secondary'" + > + {{ $root.stats.maintenance }} + </span> + </div> + <div class="col"> + <h3>{{ $t("Unknown") }}</h3> + <span class="num text-secondary">{{ $root.stats.unknown }}</span> + </div> + <div class="col"> + <h3>{{ $t("pauseDashboardHome") }}</h3> + <span class="num text-secondary">{{ $root.stats.pause }}</span> + </div> + </div> + </div> + + <div class="shadow-box table-shadow-box" style="overflow-x: hidden;"> + <table class="table table-borderless table-hover"> + <thead> + <tr> + <th>{{ $t("Name") }}</th> + <th>{{ $t("Status") }}</th> + <th>{{ $t("DateTime") }}</th> + <th>{{ $t("Message") }}</th> + </tr> + </thead> + <tbody> + <tr v-for="(beat, index) in displayedRecords" :key="index" :class="{ 'shadow-box': $root.windowWidth <= 550}"> + <td class="name-column"><router-link :to="`/dashboard/${beat.monitorID}`">{{ $root.monitorList[beat.monitorID]?.name }}</router-link></td> + <td><Status :status="beat.status" /></td> + <td :class="{ 'border-0':! beat.msg}"><Datetime :value="beat.time" /></td> + <td class="border-0">{{ beat.msg }}</td> + </tr> + + <tr v-if="importantHeartBeatListLength === 0"> + <td colspan="4"> + {{ $t("No important events") }} + </td> + </tr> + </tbody> + </table> + + <div class="d-flex justify-content-center kuma_pagination"> + <pagination + v-model="page" + :records="importantHeartBeatListLength" + :per-page="perPage" + :options="paginationConfig" + /> + </div> + </div> + </div> + </transition> + <router-view ref="child" /> +</template> + +<script> +import Status from "../components/Status.vue"; +import Datetime from "../components/Datetime.vue"; +import Pagination from "v-pagination-3"; + +export default { + components: { + Datetime, + Status, + Pagination, + }, + props: { + calculatedHeight: { + type: Number, + default: 0 + } + }, + data() { + return { + page: 1, + perPage: 25, + initialPerPage: 25, + paginationConfig: { + hideCount: true, + chunksNavigation: "scroll", + }, + importantHeartBeatListLength: 0, + displayedRecords: [], + }; + }, + watch: { + perPage() { + this.$nextTick(() => { + this.getImportantHeartbeatListPaged(); + }); + }, + + page() { + this.getImportantHeartbeatListPaged(); + }, + }, + + mounted() { + this.getImportantHeartbeatListLength(); + + this.$root.emitter.on("newImportantHeartbeat", this.onNewImportantHeartbeat); + + this.initialPerPage = this.perPage; + + window.addEventListener("resize", this.updatePerPage); + this.updatePerPage(); + }, + + beforeUnmount() { + this.$root.emitter.off("newImportantHeartbeat", this.onNewImportantHeartbeat); + + window.removeEventListener("resize", this.updatePerPage); + }, + + methods: { + /** + * Updates the displayed records when a new important heartbeat arrives. + * @param {object} heartbeat - The heartbeat object received. + * @returns {void} + */ + onNewImportantHeartbeat(heartbeat) { + if (this.page === 1) { + this.displayedRecords.unshift(heartbeat); + if (this.displayedRecords.length > this.perPage) { + this.displayedRecords.pop(); + } + this.importantHeartBeatListLength += 1; + } + }, + + /** + * Retrieves the length of the important heartbeat list for all monitors. + * @returns {void} + */ + getImportantHeartbeatListLength() { + this.$root.getSocket().emit("monitorImportantHeartbeatListCount", null, (res) => { + if (res.ok) { + this.importantHeartBeatListLength = res.count; + this.getImportantHeartbeatListPaged(); + } + }); + }, + + /** + * Retrieves the important heartbeat list for the current page. + * @returns {void} + */ + getImportantHeartbeatListPaged() { + const offset = (this.page - 1) * this.perPage; + this.$root.getSocket().emit("monitorImportantHeartbeatListPaged", null, offset, this.perPage, (res) => { + if (res.ok) { + this.displayedRecords = res.data; + } + }); + }, + + /** + * Updates the number of items shown per page based on the available height. + * @returns {void} + */ + updatePerPage() { + const tableContainer = this.$refs.tableContainer; + const tableContainerHeight = tableContainer.offsetHeight; + const availableHeight = window.innerHeight - tableContainerHeight; + const additionalPerPage = Math.floor(availableHeight / 58); + + if (additionalPerPage > 0) { + this.perPage = Math.max(this.initialPerPage, this.perPage + additionalPerPage); + } else { + this.perPage = this.initialPerPage; + } + + }, + }, +}; +</script> + +<style lang="scss" scoped> +@import "../assets/vars"; + +.num { + font-size: 30px; + color: $primary; + font-weight: bold; + display: block; +} + +.shadow-box { + padding: 20px; +} + +table { + font-size: 14px; + + tr { + transition: all ease-in-out 0.2ms; + } + + @media (max-width: 550px) { + table-layout: fixed; + overflow-wrap: break-word; + } +} + +@media screen and (max-width: 1280px) { + .name-column { + min-width: 150px; + } +} + +@media screen and (min-aspect-ratio: 4/3) { + .name-column { + min-width: 200px; + } +} +</style> |