From ea7bafabdd6e62fc69bf150fe9ade55ead719d13 Mon Sep 17 00:00:00 2001
From: Vivek Pandey <snapstar78@gmail.com>
Date: Sat, 19 Oct 2024 01:51:25 +0530
Subject: [PATCH 1/4] Convert interval seconds to days, hours, minutes, and
 seconds

---
 src/mixins/lang.js        | 10 ++++---
 src/pages/Details.vue     |  9 ++++--
 src/pages/EditMonitor.vue |  8 +++++-
 src/util-frontend.js      | 59 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 79 insertions(+), 7 deletions(-)

diff --git a/src/mixins/lang.js b/src/mixins/lang.js
index 9061e7d3d..f8a02415e 100644
--- a/src/mixins/lang.js
+++ b/src/mixins/lang.js
@@ -1,5 +1,5 @@
 import { currentLocale } from "../i18n";
-import { setPageLocale } from "../util-frontend";
+import { setPageLocale, rtf } from "../util-frontend";
 const langModules = import.meta.glob("../lang/*.json");
 
 export default {
@@ -28,11 +28,13 @@ export default {
          * @returns {Promise<void>}
          */
         async changeLang(lang) {
-            let message = (await langModules["../lang/" + lang + ".json"]()).default;
+            let message = (await langModules["../lang/" + lang + ".json"]())
+                .default;
             this.$i18n.setLocaleMessage(lang, message);
             this.$i18n.locale = lang;
             localStorage.locale = lang;
             setPageLocale();
-        }
-    }
+            rtf.updateLocale(lang);
+        },
+    },
 };
diff --git a/src/pages/Details.vue b/src/pages/Details.vue
index 17d32365c..3d1f0f4d5 100644
--- a/src/pages/Details.vue
+++ b/src/pages/Details.vue
@@ -76,7 +76,7 @@
                 <div class="row">
                     <div class="col-md-8">
                         <HeartbeatBar :monitor-id="monitor.id" />
-                        <span class="word">{{ $t("checkEverySecond", [ monitor.interval ]) }}</span>
+                        <span class="word">{{ $t("checkEverySecond", [ monitor.interval ]) }} ({{ secondsToHumanReadableFormat(monitor.interval) }})</span>
                     </div>
                     <div class="col-md-4 text-center">
                         <span class="badge rounded-pill" :class=" 'bg-' + status.color " style="font-size: 30px;" data-testid="monitor-status">{{ status.text }}</span>
@@ -285,7 +285,7 @@ import Tag from "../components/Tag.vue";
 import CertificateInfo from "../components/CertificateInfo.vue";
 import { getMonitorRelativeURL } from "../util.ts";
 import { URL } from "whatwg-url";
-import { getResBaseURL } from "../util-frontend";
+import { getResBaseURL, rtf } from "../util-frontend";
 import { highlight, languages } from "prismjs/components/prism-core";
 import "prismjs/components/prism-clike";
 import "prismjs/components/prism-javascript";
@@ -656,7 +656,12 @@ export default {
                     .replace("https://example.com/api/push/key?status=up&msg=OK&ping=", this.pushURL);
                 this.pushMonitor.code = code;
             });
+        },
+
+        secondsToHumanReadableFormat(seconds) {
+            return rtf.secondsToHumanReadableFormat(seconds);
         }
+    
     },
 };
 </script>
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 5d999b597..a26f023e6 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -530,6 +530,9 @@
                             <div class="my-3">
                                 <label for="interval" class="form-label">{{ $t("Heartbeat Interval") }} ({{ $t("checkEverySecond", [ monitor.interval ]) }})</label>
                                 <input id="interval" v-model="monitor.interval" type="number" class="form-control" required :min="minInterval" step="1" :max="maxInterval" @blur="finishUpdateInterval">
+                                 <div class="form-text">
+                                    {{ monitor.humanReadableInterval }}
+                                </div>
                             </div>
 
                             <div class="my-3">
@@ -1073,7 +1076,7 @@ import RemoteBrowserDialog from "../components/RemoteBrowserDialog.vue";
 import ProxyDialog from "../components/ProxyDialog.vue";
 import TagsManager from "../components/TagsManager.vue";
 import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, sleep } from "../util.ts";
-import { hostNameRegexPattern } from "../util-frontend";
+import { hostNameRegexPattern, rtf } from "../util-frontend";
 import HiddenInput from "../components/HiddenInput.vue";
 import EditMonitorConditions from "../components/EditMonitorConditions.vue";
 import { version } from "../../package.json";
@@ -1090,6 +1093,7 @@ const monitorDefaults = {
     url: "https://",
     method: "GET",
     interval: 60,
+    humanReadableInterval: rtf.secondsToHumanReadableFormat(60),
     retryInterval: 60,
     resendInterval: 0,
     maxretries: 0,
@@ -1472,6 +1476,8 @@ message HealthCheckResponse {
             if (this.monitor.retryInterval === oldValue) {
                 this.monitor.retryInterval = value;
             }
+            // Converting monitor.interval to human readable format.
+            this.monitor.humanReadableInterval = rtf.secondsToHumanReadableFormat(value);
         },
 
         "monitor.timeout"(value, oldValue) {
diff --git a/src/util-frontend.js b/src/util-frontend.js
index d9bf378e5..b6d758199 100644
--- a/src/util-frontend.js
+++ b/src/util-frontend.js
@@ -213,3 +213,62 @@ export function getToastErrorTimeout() {
 
     return errorTimeout;
 }
+
+class RelativeTimeFormatter {
+    // Default locale and options
+    constructor() {
+        this.locale = currentLocale();
+        this.options = { numeric: "auto" };
+        this.rtf = new Intl.RelativeTimeFormat(this.locale, this.options);
+    }
+
+    // Method to get the singleton instance
+    getInstance() {
+        return this.rtf;
+    }
+
+    // Method to update the locale and options
+    updateLocale(locale, options = {}) {
+        this.locale = locale;
+        this.options = { ...this.options, ...options };
+        this.rtf = new Intl.RelativeTimeFormat(this.locale, this.options);
+    }
+
+    secondsToHumanReadableFormat(seconds) {
+        const days = Math.floor(seconds / 86400);
+        const hours = Math.floor((seconds % 86400) / 3600);
+        const minutes = Math.floor(((seconds % 86400) % 3600) / 60);
+        const secs = ((seconds % 86400) % 3600) % 60;
+        const parts = [];
+        // Build the formatted string from parts
+        const toFormattedPart = (value, unitOfTime) => {
+            const res = this.getInstance().formatToParts(value, unitOfTime);
+            console.log(res)
+            let formattedString = res
+                .map((part, _idx) => {
+                    if ((part.type === "literal" || part.type === "integer") && _idx > 0) {
+                        return part.value;
+                    }
+                    return "";
+                })
+                .join("");
+            formattedString = formattedString.trim();
+            parts.push(formattedString);
+        };
+
+        if (days > 0) toFormattedPart(days, "days");
+        if (hours > 0) toFormattedPart(hours, "hour");
+        if (minutes > 0) toFormattedPart(minutes, "minute");
+        if (secs > 0) toFormattedPart(secs, "second");
+
+        const result =
+            parts.length > 0
+                ? `~ ${parts.join(" ")}`
+                : this.getInstance().format(0, "second"); // Handle case for 0 seconds
+
+        return result;
+    }
+}
+
+export const rtf = new RelativeTimeFormatter();
+

From 2438e87efa12001999799c190adcdf42872516b7 Mon Sep 17 00:00:00 2001
From: Vivek Pandey <snapstar78@gmail.com>
Date: Sat, 19 Oct 2024 14:39:39 +0530
Subject: [PATCH 2/4] ESLint Fixes

---
 src/pages/Details.vue     |  2 +-
 src/pages/EditMonitor.vue |  2 +-
 src/util-frontend.js      | 56 +++++++++++++++++++++++++++++++--------
 3 files changed, 47 insertions(+), 13 deletions(-)

diff --git a/src/pages/Details.vue b/src/pages/Details.vue
index 3d1f0f4d5..90805e80f 100644
--- a/src/pages/Details.vue
+++ b/src/pages/Details.vue
@@ -661,7 +661,7 @@ export default {
         secondsToHumanReadableFormat(seconds) {
             return rtf.secondsToHumanReadableFormat(seconds);
         }
-    
+
     },
 };
 </script>
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index a26f023e6..311f0487d 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -530,7 +530,7 @@
                             <div class="my-3">
                                 <label for="interval" class="form-label">{{ $t("Heartbeat Interval") }} ({{ $t("checkEverySecond", [ monitor.interval ]) }})</label>
                                 <input id="interval" v-model="monitor.interval" type="number" class="form-control" required :min="minInterval" step="1" :max="maxInterval" @blur="finishUpdateInterval">
-                                 <div class="form-text">
+                                <div class="form-text">
                                     {{ monitor.humanReadableInterval }}
                                 </div>
                             </div>
diff --git a/src/util-frontend.js b/src/util-frontend.js
index b6d758199..c867c446b 100644
--- a/src/util-frontend.js
+++ b/src/util-frontend.js
@@ -215,38 +215,64 @@ export function getToastErrorTimeout() {
 }
 
 class RelativeTimeFormatter {
-    // Default locale and options
+    /**
+     * Default locale and options for Relative Time Formatter
+     */
     constructor() {
         this.locale = currentLocale();
         this.options = { numeric: "auto" };
         this.rtf = new Intl.RelativeTimeFormat(this.locale, this.options);
     }
 
-    // Method to get the singleton instance
+    /**
+     * Method to get the singleton instance
+     * @returns {object} Intl.RelativeTimeFormat instance
+     */
     getInstance() {
         return this.rtf;
     }
 
-    // Method to update the locale and options
+    /**
+     * Method to update the instance locale and options
+     * @param {string} locale Localization identifier (e.g., "en", "ar-sy") to update the instance with.
+     * @param {object} options Instance options to be passed.
+     * @returns {void} No return value.
+     */
     updateLocale(locale, options = {}) {
         this.locale = locale;
-        this.options = { ...this.options, ...options };
+        this.options = {
+            ...this.options,
+            ...options,
+        };
         this.rtf = new Intl.RelativeTimeFormat(this.locale, this.options);
     }
 
+    /**
+     * Method to convert seconds into Human readable format
+     * @param {number} seconds Receive value in seconds.
+     * @returns {string} String converted to Days Mins Seconds Format
+     */
     secondsToHumanReadableFormat(seconds) {
         const days = Math.floor(seconds / 86400);
         const hours = Math.floor((seconds % 86400) / 3600);
         const minutes = Math.floor(((seconds % 86400) % 3600) / 60);
         const secs = ((seconds % 86400) % 3600) % 60;
         const parts = [];
-        // Build the formatted string from parts
+        /**
+         * Build the formatted string from parts
+         * @param {number} value Receives value in seconds.
+         * @param {string} unitOfTime Expected unit of time after conversion.
+         * @returns {void}
+         */
         const toFormattedPart = (value, unitOfTime) => {
             const res = this.getInstance().formatToParts(value, unitOfTime);
-            console.log(res)
+            console.log(res);
             let formattedString = res
                 .map((part, _idx) => {
-                    if ((part.type === "literal" || part.type === "integer") && _idx > 0) {
+                    if (
+                        (part.type === "literal" || part.type === "integer") &&
+                        _idx > 0
+                    ) {
                         return part.value;
                     }
                     return "";
@@ -256,10 +282,18 @@ class RelativeTimeFormatter {
             parts.push(formattedString);
         };
 
-        if (days > 0) toFormattedPart(days, "days");
-        if (hours > 0) toFormattedPart(hours, "hour");
-        if (minutes > 0) toFormattedPart(minutes, "minute");
-        if (secs > 0) toFormattedPart(secs, "second");
+        if (days > 0) {
+            toFormattedPart(days, "days");
+        }
+        if (hours > 0) {
+            toFormattedPart(hours, "hour");
+        }
+        if (minutes > 0) {
+            toFormattedPart(minutes, "minute");
+        }
+        if (secs > 0) {
+            toFormattedPart(secs, "second");
+        }
 
         const result =
             parts.length > 0

From b000c1d532aa51b3d1519feedb87af0688306b33 Mon Sep 17 00:00:00 2001
From: Vivek Pandey <snapstar78@gmail.com>
Date: Sat, 19 Oct 2024 19:24:41 +0530
Subject: [PATCH 3/4] Removing console log and approx sign

---
 src/util-frontend.js | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/src/util-frontend.js b/src/util-frontend.js
index c867c446b..505ae8316 100644
--- a/src/util-frontend.js
+++ b/src/util-frontend.js
@@ -235,15 +235,10 @@ class RelativeTimeFormatter {
     /**
      * Method to update the instance locale and options
      * @param {string} locale Localization identifier (e.g., "en", "ar-sy") to update the instance with.
-     * @param {object} options Instance options to be passed.
      * @returns {void} No return value.
      */
-    updateLocale(locale, options = {}) {
+    updateLocale(locale) {
         this.locale = locale;
-        this.options = {
-            ...this.options,
-            ...options,
-        };
         this.rtf = new Intl.RelativeTimeFormat(this.locale, this.options);
     }
 
@@ -266,7 +261,6 @@ class RelativeTimeFormatter {
          */
         const toFormattedPart = (value, unitOfTime) => {
             const res = this.getInstance().formatToParts(value, unitOfTime);
-            console.log(res);
             let formattedString = res
                 .map((part, _idx) => {
                     if (
@@ -297,7 +291,7 @@ class RelativeTimeFormatter {
 
         const result =
             parts.length > 0
-                ? `~ ${parts.join(" ")}`
+                ? `${parts.join(" ")}`
                 : this.getInstance().format(0, "second"); // Handle case for 0 seconds
 
         return result;

From f6342b84a1abcbf5d7ff0ce4ef5979d6dd6269ec Mon Sep 17 00:00:00 2001
From: Vivek Pandey <snapstar78@gmail.com>
Date: Sat, 19 Oct 2024 23:40:43 +0530
Subject: [PATCH 4/4] Refactoring variable names, and control flow.

---
 src/mixins/lang.js        |  4 ++--
 src/pages/Details.vue     |  4 ++--
 src/pages/EditMonitor.vue |  6 ++---
 src/util-frontend.js      | 50 +++++++++++++++------------------------
 4 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/src/mixins/lang.js b/src/mixins/lang.js
index f8a02415e..0fff8cdc8 100644
--- a/src/mixins/lang.js
+++ b/src/mixins/lang.js
@@ -1,5 +1,5 @@
 import { currentLocale } from "../i18n";
-import { setPageLocale, rtf } from "../util-frontend";
+import { setPageLocale, relativeTimeFormatter } from "../util-frontend";
 const langModules = import.meta.glob("../lang/*.json");
 
 export default {
@@ -34,7 +34,7 @@ export default {
             this.$i18n.locale = lang;
             localStorage.locale = lang;
             setPageLocale();
-            rtf.updateLocale(lang);
+            relativeTimeFormatter.updateLocale(lang);
         },
     },
 };
diff --git a/src/pages/Details.vue b/src/pages/Details.vue
index 90805e80f..995d9f2e4 100644
--- a/src/pages/Details.vue
+++ b/src/pages/Details.vue
@@ -285,7 +285,7 @@ import Tag from "../components/Tag.vue";
 import CertificateInfo from "../components/CertificateInfo.vue";
 import { getMonitorRelativeURL } from "../util.ts";
 import { URL } from "whatwg-url";
-import { getResBaseURL, rtf } from "../util-frontend";
+import { getResBaseURL, relativeTimeFormatter } from "../util-frontend";
 import { highlight, languages } from "prismjs/components/prism-core";
 import "prismjs/components/prism-clike";
 import "prismjs/components/prism-javascript";
@@ -659,7 +659,7 @@ export default {
         },
 
         secondsToHumanReadableFormat(seconds) {
-            return rtf.secondsToHumanReadableFormat(seconds);
+            return relativeTimeFormatter.secondsToHumanReadableFormat(seconds);
         }
 
     },
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 311f0487d..2d578d5a3 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -1076,7 +1076,7 @@ import RemoteBrowserDialog from "../components/RemoteBrowserDialog.vue";
 import ProxyDialog from "../components/ProxyDialog.vue";
 import TagsManager from "../components/TagsManager.vue";
 import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, sleep } from "../util.ts";
-import { hostNameRegexPattern, rtf } from "../util-frontend";
+import { hostNameRegexPattern, relativeTimeFormatter } from "../util-frontend";
 import HiddenInput from "../components/HiddenInput.vue";
 import EditMonitorConditions from "../components/EditMonitorConditions.vue";
 import { version } from "../../package.json";
@@ -1093,7 +1093,7 @@ const monitorDefaults = {
     url: "https://",
     method: "GET",
     interval: 60,
-    humanReadableInterval: rtf.secondsToHumanReadableFormat(60),
+    humanReadableInterval: relativeTimeFormatter.secondsToHumanReadableFormat(60),
     retryInterval: 60,
     resendInterval: 0,
     maxretries: 0,
@@ -1477,7 +1477,7 @@ message HealthCheckResponse {
                 this.monitor.retryInterval = value;
             }
             // Converting monitor.interval to human readable format.
-            this.monitor.humanReadableInterval = rtf.secondsToHumanReadableFormat(value);
+            this.monitor.humanReadableInterval = relativeTimeFormatter.secondsToHumanReadableFormat(value);
         },
 
         "monitor.timeout"(value, oldValue) {
diff --git a/src/util-frontend.js b/src/util-frontend.js
index 505ae8316..a3dc4c3ac 100644
--- a/src/util-frontend.js
+++ b/src/util-frontend.js
@@ -219,17 +219,8 @@ class RelativeTimeFormatter {
      * Default locale and options for Relative Time Formatter
      */
     constructor() {
-        this.locale = currentLocale();
         this.options = { numeric: "auto" };
-        this.rtf = new Intl.RelativeTimeFormat(this.locale, this.options);
-    }
-
-    /**
-     * Method to get the singleton instance
-     * @returns {object} Intl.RelativeTimeFormat instance
-     */
-    getInstance() {
-        return this.rtf;
+        this.instance = new Intl.RelativeTimeFormat(currentLocale(), this.options);
     }
 
     /**
@@ -238,8 +229,7 @@ class RelativeTimeFormatter {
      * @returns {void} No return value.
      */
     updateLocale(locale) {
-        this.locale = locale;
-        this.rtf = new Intl.RelativeTimeFormat(this.locale, this.options);
+        this.instance = new Intl.RelativeTimeFormat(locale, this.options);
     }
 
     /**
@@ -255,24 +245,24 @@ class RelativeTimeFormatter {
         const parts = [];
         /**
          * Build the formatted string from parts
+         * 1. Get the relative time formatted parts from the instance.
+         * 2. Filter out the relevant parts literal (unit of time) or integer (value).
+         * 3. Map out the required values.
          * @param {number} value Receives value in seconds.
          * @param {string} unitOfTime Expected unit of time after conversion.
          * @returns {void}
          */
         const toFormattedPart = (value, unitOfTime) => {
-            const res = this.getInstance().formatToParts(value, unitOfTime);
-            let formattedString = res
-                .map((part, _idx) => {
-                    if (
+            const partsArray = this.instance.formatToParts(value, unitOfTime);
+            const filteredParts = partsArray
+                .filter(
+                    (part, index) =>
                         (part.type === "literal" || part.type === "integer") &&
-                        _idx > 0
-                    ) {
-                        return part.value;
-                    }
-                    return "";
-                })
-                .join("");
-            formattedString = formattedString.trim();
+                        index > 0
+                )
+                .map((part) => part.value);
+
+            const formattedString = filteredParts.join("").trim();
             parts.push(formattedString);
         };
 
@@ -289,14 +279,12 @@ class RelativeTimeFormatter {
             toFormattedPart(secs, "second");
         }
 
-        const result =
-            parts.length > 0
-                ? `${parts.join(" ")}`
-                : this.getInstance().format(0, "second"); // Handle case for 0 seconds
-
-        return result;
+        if (parts.length > 0) {
+            return `${parts.join(" ")}`;
+        }
+        return this.instance.format(0, "second"); // Handle case for 0 seconds
     }
 }
 
-export const rtf = new RelativeTimeFormatter();
+export const relativeTimeFormatter = new RelativeTimeFormatter();