Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert interval seconds to days, hours, minutes, and seconds #5220

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/mixins/lang.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { currentLocale } from "../i18n";
import { setPageLocale } from "../util-frontend";
import { setPageLocale, relativeTimeFormatter } from "../util-frontend";
const langModules = import.meta.glob("../lang/*.json");

export default {
Expand Down Expand Up @@ -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();
}
}
relativeTimeFormatter.updateLocale(lang);
},
},
};
9 changes: 7 additions & 2 deletions src/pages/Details.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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, relativeTimeFormatter } from "../util-frontend";
import { highlight, languages } from "prismjs/components/prism-core";
import "prismjs/components/prism-clike";
import "prismjs/components/prism-javascript";
Expand Down Expand Up @@ -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 relativeTimeFormatter.secondsToHumanReadableFormat(seconds);
}

},
};
</script>
Expand Down
8 changes: 7 additions & 1 deletion src/pages/EditMonitor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down Expand Up @@ -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, relativeTimeFormatter } from "../util-frontend";
import HiddenInput from "../components/HiddenInput.vue";
import EditMonitorConditions from "../components/EditMonitorConditions.vue";
import { version } from "../../package.json";
Expand All @@ -1090,6 +1093,7 @@ const monitorDefaults = {
url: "https://",
method: "GET",
interval: 60,
humanReadableInterval: relativeTimeFormatter.secondsToHumanReadableFormat(60),
retryInterval: 60,
resendInterval: 0,
maxretries: 0,
Expand Down Expand Up @@ -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 = relativeTimeFormatter.secondsToHumanReadableFormat(value);
},

"monitor.timeout"(value, oldValue) {
Expand Down
75 changes: 75 additions & 0 deletions src/util-frontend.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,78 @@ export function getToastErrorTimeout() {

return errorTimeout;
}

class RelativeTimeFormatter {
/**
* Default locale and options for Relative Time Formatter
*/
constructor() {
this.options = { numeric: "auto" };
this.instance = new Intl.RelativeTimeFormat(currentLocale(), this.options);
}

/**
* Method to update the instance locale and options
* @param {string} locale Localization identifier (e.g., "en", "ar-sy") to update the instance with.
* @returns {void} No return value.
*/
updateLocale(locale) {
this.instance = new Intl.RelativeTimeFormat(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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a few quick unit tests to ensure that this works as expected?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm seeing e2e tests but no unit tests for frontend, do we have the setup for it?

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
* 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 partsArray = this.instance.formatToParts(value, unitOfTime);
const filteredParts = partsArray
.filter(
(part, index) =>
(part.type === "literal" || part.type === "integer") &&
index > 0
)
.map((part) => part.value);

const formattedString = filteredParts.join("").trim();
parts.push(formattedString);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The controllflow in this function is quite non-obvious. Please refactor this to not abuse scoping as heavily ^^

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have refactored the control flow, made it easy and intuitive to understand.

};

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 (parts.length > 0) {
return `${parts.join(" ")}`;
}
return this.instance.format(0, "second"); // Handle case for 0 seconds
}
}

export const relativeTimeFormatter = new RelativeTimeFormatter();

Loading