swap in new DashboardHome.vue and add OffLineList.vue

This commit is contained in:
Evert-vh 2025-06-25 15:24:11 +02:00
parent 10fd6ede1e
commit dce8553296
3 changed files with 67 additions and 23 deletions

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "2.0.0-beta.2", "version": "2.0.0-beta.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "2.0.0-beta.2", "version": "2.0.0-beta.3",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@grpc/grpc-js": "~1.8.22", "@grpc/grpc-js": "~1.8.22",

View file

@ -0,0 +1,54 @@
<template>
<div class="offline-list shadow-box mb-4">
<h3>🚨 Offline Devices</h3>
<ul class="list-unstyled" v-if="offlineMonitors.length > 0">
<li
v-for="monitor in offlineMonitors"
:key="monitor.id"
class="flashing-red mb-2"
>
<router-link :to="`/dashboard/${monitor.id}`" class="text-danger">
{{ monitor.name }}
</router-link>
</li>
</ul>
<div v-else>
All devices online
</div>
</div>
</template>
<script>
export default {
computed: {
offlineMonitors() {
const monitors = Object.values(this.$root.monitorList || {});
const heartbeats = this.$root.lastHeartbeatList || {};
return monitors
.filter((monitor) => {
const status = heartbeats[monitor.id]?.status;
return status === 0;
})
.sort((a, b) => a.name.localeCompare(b.name));
},
},
};
</script>
<style scoped>
.flashing-red {
animation: flash 1.5s infinite;
font-weight: bold;
}
@keyframes flash {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
}
</style>

View file

@ -45,6 +45,9 @@
</div> </div>
</div> </div>
<!-- 🚨 Offline Devices Component -->
<OfflineList />
<div class="shadow-box table-shadow-box" style="overflow-x: hidden;"> <div class="shadow-box table-shadow-box" style="overflow-x: hidden;">
<table class="table table-borderless table-hover"> <table class="table table-borderless table-hover">
<thead> <thead>
@ -57,7 +60,11 @@
</thead> </thead>
<tbody> <tbody>
<tr v-for="(beat, index) in displayedRecords" :key="index" :class="{ 'shadow-box': $root.windowWidth <= 550}"> <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 class="name-column">
<router-link :to="`/dashboard/${beat.monitorID}`">
{{ $root.monitorList[beat.monitorID]?.name }}
</router-link>
</td>
<td><Status :status="beat.status" /></td> <td><Status :status="beat.status" /></td>
<td :class="{ 'border-0': !beat.msg }"><Datetime :value="beat.time" /></td> <td :class="{ 'border-0': !beat.msg }"><Datetime :value="beat.time" /></td>
<td class="border-0">{{ beat.msg }}</td> <td class="border-0">{{ beat.msg }}</td>
@ -89,12 +96,14 @@
import Status from "../components/Status.vue"; import Status from "../components/Status.vue";
import Datetime from "../components/Datetime.vue"; import Datetime from "../components/Datetime.vue";
import Pagination from "v-pagination-3"; import Pagination from "v-pagination-3";
import OfflineList from "../components/OfflineList.vue";
export default { export default {
components: { components: {
Datetime, Datetime,
Status, Status,
Pagination, Pagination,
OfflineList,
}, },
props: { props: {
calculatedHeight: { calculatedHeight: {
@ -140,16 +149,10 @@ export default {
beforeUnmount() { beforeUnmount() {
this.$root.emitter.off("newImportantHeartbeat", this.onNewImportantHeartbeat); this.$root.emitter.off("newImportantHeartbeat", this.onNewImportantHeartbeat);
window.removeEventListener("resize", this.updatePerPage); window.removeEventListener("resize", this.updatePerPage);
}, },
methods: { methods: {
/**
* Updates the displayed records when a new important heartbeat arrives.
* @param {object} heartbeat - The heartbeat object received.
* @returns {void}
*/
onNewImportantHeartbeat(heartbeat) { onNewImportantHeartbeat(heartbeat) {
if (this.page === 1) { if (this.page === 1) {
this.displayedRecords.unshift(heartbeat); this.displayedRecords.unshift(heartbeat);
@ -160,10 +163,6 @@ export default {
} }
}, },
/**
* Retrieves the length of the important heartbeat list for all monitors.
* @returns {void}
*/
getImportantHeartbeatListLength() { getImportantHeartbeatListLength() {
this.$root.getSocket().emit("monitorImportantHeartbeatListCount", null, (res) => { this.$root.getSocket().emit("monitorImportantHeartbeatListCount", null, (res) => {
if (res.ok) { if (res.ok) {
@ -173,10 +172,6 @@ export default {
}); });
}, },
/**
* Retrieves the important heartbeat list for the current page.
* @returns {void}
*/
getImportantHeartbeatListPaged() { getImportantHeartbeatListPaged() {
const offset = (this.page - 1) * this.perPage; const offset = (this.page - 1) * this.perPage;
this.$root.getSocket().emit("monitorImportantHeartbeatListPaged", null, offset, this.perPage, (res) => { this.$root.getSocket().emit("monitorImportantHeartbeatListPaged", null, offset, this.perPage, (res) => {
@ -186,10 +181,6 @@ export default {
}); });
}, },
/**
* Updates the number of items shown per page based on the available height.
* @returns {void}
*/
updatePerPage() { updatePerPage() {
const tableContainer = this.$refs.tableContainer; const tableContainer = this.$refs.tableContainer;
const tableContainerHeight = tableContainer.offsetHeight; const tableContainerHeight = tableContainer.offsetHeight;
@ -201,7 +192,6 @@ export default {
} else { } else {
this.perPage = this.initialPerPage; this.perPage = this.initialPerPage;
} }
}, },
}, },
}; };