From 1dd6e61269cbad8369723a385fc0be904ed423dc Mon Sep 17 00:00:00 2001
From: Zandor Smith <info@zsinfo.nl>
Date: Thu, 18 Apr 2024 21:03:34 +0200
Subject: [PATCH 1/2] Status Page: Display offline monitors at the top of the
 statuspage.

---
 src/lang/en.json         |  4 +++-
 src/pages/StatusPage.vue | 46 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/src/lang/en.json b/src/lang/en.json
index 21b0eec70..b8baa96d2 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -910,5 +910,7 @@
     "Allow Long SMS": "Allow Long SMS",
     "cellsyntSplitLongMessages": "Split long messages into up to 6 parts. 153 x 6 = 918 characters.",
     "max 15 digits": "max 15 digits",
-    "max 11 alphanumeric characters": "max 11 alphanumeric characters"
+    "max 11 alphanumeric characters": "max 11 alphanumeric characters",
+    "offlineMonitor": "Offline Monitor",
+    "offlineMonitors": "Offline Monitors"
 }
diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue
index af2f028d7..fc9c3a2ba 100644
--- a/src/pages/StatusPage.vue
+++ b/src/pages/StatusPage.vue
@@ -313,6 +313,30 @@
                 </div>
             </div>
 
+            <div v-if="this.$root.downMonitors?.length > 0" class="mb-4">
+                <div class="mb-5">
+                    <h2 class="group-title">
+                        {{ $t(this.$root.downMonitors.length === 1 ? "offlineMonitor" : "offlineMonitors") }}
+                    </h2>
+
+                    <div class="shadow-box monitor-list mt-4 position-relative">
+                        <div v-for="monitor in this.$root.downMonitors" :key="monitor.id" class="item">
+                            <div class="row">
+                                <div class="col-9 col-md-8 small-padding">
+                                    <div class="info">
+                                        <Uptime :monitor="monitor" type="24" :pill="true" />
+                                        {{ monitor.name }}
+                                    </div>
+                                </div>
+                                <div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
+                                    <HeartbeatBar size="mid" :monitor-id="monitor.id" />
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+
             <div class="mb-4">
                 <div v-if="$root.publicGroupList.length === 0 && loadedData" class="text-center">
                     <!-- 👀 Nothing here, please add a group or a monitor. -->
@@ -374,6 +398,8 @@ import { getResBaseURL } from "../util-frontend";
 import { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_MAINTENANCE, STATUS_PAGE_PARTIAL_DOWN, UP, MAINTENANCE } from "../util.ts";
 import Tag from "../components/Tag.vue";
 import VueMultiselect from "vue-multiselect";
+import Uptime from "../components/Uptime.vue";
+import HeartbeatBar from "../components/HeartbeatBar.vue";
 
 const toast = useToast();
 dayjs.extend(duration);
@@ -390,6 +416,8 @@ const favicon = new Favico({
 export default {
 
     components: {
+        HeartbeatBar,
+        Uptime,
         PublicGroupList,
         ImageCropUpload,
         Confirm,
@@ -768,6 +796,8 @@ export default {
                     this.$root.heartbeatList = heartbeatList;
                     this.$root.uptimeList = uptimeList;
 
+                    this.$root.downMonitors = this.downMonitors(); 
+
                     const heartbeatIds = Object.keys(heartbeatList);
                     const downMonitors = heartbeatIds.reduce((downMonitorsAmount, currentId) => {
                         const monitorHeartbeats = heartbeatList[currentId];
@@ -789,6 +819,22 @@ export default {
             }
         },
 
+        downMonitors() {
+            let result = [];
+
+            for (const id in this.$root.publicMonitorList) {
+                const monitor = this.$root.publicMonitorList[id];
+                const heartbeats = this.$root.heartbeatList[monitor.id];
+                const lastHeartbeat = heartbeats.at(-1);
+                if (!lastHeartbeat || lastHeartbeat.status !== 0) {
+                    continue;
+                }
+                result.push(monitor);
+            }
+
+            return result;
+        },
+
         /**
          * Setup timer to display countdown to refresh
          * @returns {void}

From f5e1df7dae1dcc1de07976d5f4d36e8c2ce28795 Mon Sep 17 00:00:00 2001
From: Zandor Smith <info@zsinfo.nl>
Date: Thu, 18 Apr 2024 21:26:00 +0200
Subject: [PATCH 2/2] Fix linting issues.

---
 src/pages/StatusPage.vue | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue
index fc9c3a2ba..361bb0cf1 100644
--- a/src/pages/StatusPage.vue
+++ b/src/pages/StatusPage.vue
@@ -313,14 +313,14 @@
                 </div>
             </div>
 
-            <div v-if="this.$root.downMonitors?.length > 0" class="mb-4">
+            <div v-if="$root.downMonitors?.length > 0" class="mb-4">
                 <div class="mb-5">
                     <h2 class="group-title">
-                        {{ $t(this.$root.downMonitors.length === 1 ? "offlineMonitor" : "offlineMonitors") }}
+                        {{ $t($root.downMonitors.length === 1 ? "offlineMonitor" : "offlineMonitors") }}
                     </h2>
 
                     <div class="shadow-box monitor-list mt-4 position-relative">
-                        <div v-for="monitor in this.$root.downMonitors" :key="monitor.id" class="item">
+                        <div v-for="monitor in $root.downMonitors" :key="monitor.id" class="item">
                             <div class="row">
                                 <div class="col-9 col-md-8 small-padding">
                                     <div class="info">
@@ -796,7 +796,7 @@ export default {
                     this.$root.heartbeatList = heartbeatList;
                     this.$root.uptimeList = uptimeList;
 
-                    this.$root.downMonitors = this.downMonitors(); 
+                    this.$root.downMonitors = this.downMonitors();
 
                     const heartbeatIds = Object.keys(heartbeatList);
                     const downMonitors = heartbeatIds.reduce((downMonitorsAmount, currentId) => {