Fixed the issue that clicking the Settings button on the edit status page was invalid

This commit is contained in:
Marshu 2025-04-13 23:41:43 +08:00
parent 436ac1b2ca
commit 853e84178e
2 changed files with 182 additions and 170 deletions

View file

@ -72,11 +72,17 @@ export default {
* @returns {void} * @returns {void}
*/ */
show(group, monitor) { show(group, monitor) {
// Check if monitor exists and has required properties
if (!monitor || !monitor.id) {
console.error("Invalid monitor object", monitor);
return;
}
this.monitor = { this.monitor = {
id: monitor.element.id, id: monitor.id,
name: monitor.element.name, name: monitor.name,
monitor_index: monitor.index, monitor_index: monitor.index || 0,
group_index: group.index, group_index: group.index || 0,
isClickAble: this.showLink(monitor), isClickAble: this.showLink(monitor),
}; };
@ -103,12 +109,18 @@ export default {
* @returns {boolean} Should the link be shown? * @returns {boolean} Should the link be shown?
*/ */
showLink(monitor, ignoreSendUrl = false) { showLink(monitor, ignoreSendUrl = false) {
if (!monitor || !monitor.id) {
return false;
}
// We must check if there are any elements in monitorList to // We must check if there are any elements in monitorList to
// prevent undefined errors if it hasn't been loaded yet // prevent undefined errors if it hasn't been loaded yet
if (this.$parent.editMode && ignoreSendUrl && Object.keys(this.$root.monitorList).length) { if (this.$parent.editMode && ignoreSendUrl && Object.keys(this.$root.monitorList).length) {
return this.$root.monitorList[monitor.element.id].type === "http" || this.$root.monitorList[monitor.element.id].type === "keyword" || this.$root.monitorList[monitor.element.id].type === "json-query"; return this.$root.monitorList[monitor.id].type === "http" ||
this.$root.monitorList[monitor.id].type === "keyword" ||
this.$root.monitorList[monitor.id].type === "json-query";
} }
return monitor.element.sendUrl && monitor.element.url && monitor.element.url !== "https://" && !this.editMode; return monitor.sendUrl && monitor.url && monitor.url !== "https://" && !this.editMode;
}, },
}, },
}; };

View file

@ -3,7 +3,7 @@
<div v-if="hasPublicGroups && !editMode" class="global-controls-container"> <div v-if="hasPublicGroups && !editMode" class="global-controls-container">
<!-- Global Controls Title --> <!-- Global Controls Title -->
<h5 class="global-controls-title">{{ $t("Global Sorting Options") }}</h5> <h5 class="global-controls-title">{{ $t("Global Sorting Options") }}</h5>
<!-- Global Sorting Bar and Search Box --> <!-- Global Sorting Bar and Search Box -->
<div class="global-controls-content"> <div class="global-controls-content">
<!-- Global Sort Buttons Group --> <!-- Global Sort Buttons Group -->
@ -45,21 +45,21 @@
</button> </button>
</div> </div>
</div> </div>
<!-- Global Search Box --> <!-- Global Search Box -->
<div class="global-search-container"> <div class="global-search-container">
<div class="input-group"> <div class="input-group">
<input <input
type="text" type="text"
class="form-control form-control-sm" class="form-control form-control-sm"
v-model="globalSearchKeyword" v-model="globalSearchKeyword"
:placeholder="$t('Search monitors across all groups')" :placeholder="$t('Search monitors across all groups')"
aria-label="Global search" aria-label="Global search"
> >
<button <button
v-if="globalSearchKeyword" v-if="globalSearchKeyword"
class="btn btn-outline-secondary btn-sm" class="btn btn-outline-secondary btn-sm"
type="button" type="button"
@click="clearGlobalSearch" @click="clearGlobalSearch"
title="Clear search" title="Clear search"
> >
@ -86,24 +86,24 @@
<div class="shadow-box"> <div class="shadow-box">
<!-- Group Title and Search Bar --> <!-- Group Title and Search Bar -->
<div class="group-header"> <div class="group-header">
<h2 class="group-title"> <h2 class="group-title">
<font-awesome-icon v-if="editMode && showGroupDrag" icon="arrows-alt-v" class="action drag me-3" /> <font-awesome-icon v-if="editMode && showGroupDrag" icon="arrows-alt-v" class="action drag me-3" />
<font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeGroup(group.index)" /> <font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeGroup(group.index)" />
<Editable v-model="group.element.name" :contenteditable="editMode" tag="span" data-testid="group-name" /> <Editable v-model="group.element.name" :contenteditable="editMode" tag="span" data-testid="group-name" />
</h2> </h2>
<!-- Group Search Box --> <!-- Group Search Box -->
<div v-if="!editMode && group.element && group.element.monitorList && group.element.monitorList.length > 0" <div v-if="!editMode && group.element && group.element.monitorList && group.element.monitorList.length > 0"
class="search-container"> class="search-container">
<div class="search-input-wrapper"> <div class="search-input-wrapper">
<input <input
type="text" type="text"
v-model="group.element.searchKeyword" v-model="group.element.searchKeyword"
:placeholder="$t('Search...')" :placeholder="$t('Search...')"
class="search-input form-control form-control-sm" class="search-input form-control form-control-sm"
@keyup.esc="clearSearch(group.element)" @keyup.esc="clearSearch(group.element)"
> >
<button <button
class="search-button btn btn-sm" class="search-button btn btn-sm"
@click="clearSearch(group.element)" @click="clearSearch(group.element)"
v-if="group.element && group.element.searchKeyword" v-if="group.element && group.element.searchKeyword"
@ -116,8 +116,8 @@
</div> </div>
<!-- Group Sort Button Bar --> <!-- Group Sort Button Bar -->
<div v-if="!editMode && group.element && group.element.monitorList && group.element.monitorList.length > 0" <div v-if="!editMode && group.element && group.element.monitorList && group.element.monitorList.length > 0"
class="sort-bar"> class="sort-bar">
<div class="sort-controls"> <div class="sort-controls">
<span class="sort-label me-2"> <span class="sort-label me-2">
{{ $t("Sort By") }}: {{ $t("Sort By") }}:
@ -132,10 +132,10 @@
@click="setSort(group.element, 'status')" @click="setSort(group.element, 'status')"
> >
{{ $t("Status") }} {{ $t("Status") }}
<font-awesome-icon <font-awesome-icon
v-if="(isGlobalSortActive && !group.element.useOwnSort && globalSortKey === 'status') || v-if="(isGlobalSortActive && !group.element.useOwnSort && globalSortKey === 'status') ||
((isGlobalSortActive && group.element.useOwnSort || !isGlobalSortActive) && group.element.sortKey === 'status')" ((isGlobalSortActive && group.element.useOwnSort || !isGlobalSortActive) && group.element.sortKey === 'status')"
:icon="(isGlobalSortActive && !group.element.useOwnSort ? globalSortDirection : group.element.sortDirection) === 'asc' ? 'arrow-up' : 'arrow-down'" :icon="(isGlobalSortActive && !group.element.useOwnSort ? globalSortDirection : group.element.sortDirection) === 'asc' ? 'arrow-up' : 'arrow-down'"
/> />
</button> </button>
<button <button
@ -147,10 +147,10 @@
@click="setSort(group.element, 'name')" @click="setSort(group.element, 'name')"
> >
{{ $t("Name") }} {{ $t("Name") }}
<font-awesome-icon <font-awesome-icon
v-if="(isGlobalSortActive && !group.element.useOwnSort && globalSortKey === 'name') || v-if="(isGlobalSortActive && !group.element.useOwnSort && globalSortKey === 'name') ||
((isGlobalSortActive && group.element.useOwnSort || !isGlobalSortActive) && group.element.sortKey === 'name')" ((isGlobalSortActive && group.element.useOwnSort || !isGlobalSortActive) && group.element.sortKey === 'name')"
:icon="(isGlobalSortActive && !group.element.useOwnSort ? globalSortDirection : group.element.sortDirection) === 'asc' ? 'arrow-up' : 'arrow-down'" :icon="(isGlobalSortActive && !group.element.useOwnSort ? globalSortDirection : group.element.sortDirection) === 'asc' ? 'arrow-up' : 'arrow-down'"
/> />
</button> </button>
<button <button
@ -162,10 +162,10 @@
@click="setSort(group.element, 'uptime')" @click="setSort(group.element, 'uptime')"
> >
{{ $t("Uptime") }} {{ $t("Uptime") }}
<font-awesome-icon <font-awesome-icon
v-if="(isGlobalSortActive && !group.element.useOwnSort && globalSortKey === 'uptime') || v-if="(isGlobalSortActive && !group.element.useOwnSort && globalSortKey === 'uptime') ||
((isGlobalSortActive && group.element.useOwnSort || !isGlobalSortActive) && group.element.sortKey === 'uptime')" ((isGlobalSortActive && group.element.useOwnSort || !isGlobalSortActive) && group.element.sortKey === 'uptime')"
:icon="(isGlobalSortActive && !group.element.useOwnSort ? globalSortDirection : group.element.sortDirection) === 'asc' ? 'arrow-up' : 'arrow-down'" :icon="(isGlobalSortActive && !group.element.useOwnSort ? globalSortDirection : group.element.sortDirection) === 'asc' ? 'arrow-up' : 'arrow-down'"
/> />
</button> </button>
<button <button
@ -178,10 +178,10 @@
@click="setSort(group.element, 'cert')" @click="setSort(group.element, 'cert')"
> >
{{ $t("Cert Exp.") }} {{ $t("Cert Exp.") }}
<font-awesome-icon <font-awesome-icon
v-if="(isGlobalSortActive && !group.element.useOwnSort && globalSortKey === 'cert') || v-if="(isGlobalSortActive && !group.element.useOwnSort && globalSortKey === 'cert') ||
((isGlobalSortActive && group.element.useOwnSort || !isGlobalSortActive) && group.element.sortKey === 'cert')" ((isGlobalSortActive && group.element.useOwnSort || !isGlobalSortActive) && group.element.sortKey === 'cert')"
:icon="(isGlobalSortActive && !group.element.useOwnSort ? globalSortDirection : group.element.sortDirection) === 'asc' ? 'arrow-up' : 'arrow-down'" :icon="(isGlobalSortActive && !group.element.useOwnSort ? globalSortDirection : group.element.sortDirection) === 'asc' ? 'arrow-up' : 'arrow-down'"
/> />
</button> </button>
</div> </div>
@ -191,70 +191,70 @@
<!-- Monitor List --> <!-- Monitor List -->
<div class="monitor-list-container"> <div class="monitor-list-container">
<div v-if="!group.element || !group.element.monitorList || group.element.monitorList.length === 0" class="text-center no-monitor-msg"> <div v-if="!group.element || !group.element.monitorList || group.element.monitorList.length === 0" class="text-center no-monitor-msg">
{{ $t("No Monitors") }} {{ $t("No Monitors") }}
</div> </div>
<div v-else-if="getFilteredMonitorList(group.element).length === 0" class="text-center no-monitor-msg"> <div v-else-if="getFilteredMonitorList(group.element).length === 0" class="text-center no-monitor-msg">
{{ $t("No services found") }} {{ $t("No services found") }}
</div> </div>
<!-- Monitor List --> <!-- Monitor List -->
<Draggable <Draggable
v-if="group.element && group.element.monitorList && group.element.monitorList.length > 0" v-if="group.element && group.element.monitorList && group.element.monitorList.length > 0"
v-model="group.element.monitorList" v-model="group.element.monitorList"
class="monitor-list" class="monitor-list"
group="same-group" group="same-group"
:disabled="!editMode" :disabled="!editMode"
:animation="100" :animation="100"
item-key="id" item-key="id"
> >
<template #item="{ element: monitor, index: monitorIndex }"> <template #item="{ element: monitor, index: monitorIndex }">
<div v-if="shouldShowMonitor(monitor, group.element)" class="item" data-testid="monitor"> <div v-if="shouldShowMonitor(monitor, group.element)" class="item" data-testid="monitor">
<div class="row"> <div class="row">
<div class="col-9 col-md-8 small-padding"> <div class="col-9 col-md-8 small-padding">
<div class="info"> <div class="info">
<font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" /> <font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" />
<font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitorIndex)" /> <font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitorIndex)" />
<Uptime :monitor="monitor" type="24" :pill="true" /> <Uptime :monitor="monitor" type="24" :pill="true" />
<a <a
v-if="showLink(monitor)" v-if="showLink(monitor)"
:href="monitor.url" :href="monitor.url"
class="item-name" class="item-name"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
data-testid="monitor-name" data-testid="monitor-name"
> >
{{ monitor.name }} {{ monitor.name }}
</a> </a>
<p v-else class="item-name" data-testid="monitor-name"> {{ monitor.name }} </p> <p v-else class="item-name" data-testid="monitor-name"> {{ monitor.name }} </p>
<span <span
title="Setting" title="Setting"
> >
<font-awesome-icon <font-awesome-icon
v-if="editMode" v-if="editMode"
:class="{'link-active': true, 'btn-link': true}" :class="{'link-active': true, 'btn-link': true}"
icon="cog" class="action me-3" icon="cog" class="action me-3"
@click="$refs.monitorSettingDialog.show(group.element, monitor)" @click="$refs.monitorSettingDialog.show(group, monitor)"
/> />
</span> </span>
</div>
<div class="extra-info">
<div v-if="showCertificateExpiry && monitor.certExpiryDaysRemaining">
<Tag :item="{name: $t('Cert Exp.'), value: formattedCertExpiryMessage(monitor), color: certExpiryColor(monitor)}" :size="'sm'" />
</div> </div>
<div v-if="showTags"> <div class="extra-info">
<div v-if="showCertificateExpiry && monitor.certExpiryDaysRemaining">
<Tag :item="{name: $t('Cert Exp.'), value: formattedCertExpiryMessage(monitor), color: certExpiryColor(monitor)}" :size="'sm'" />
</div>
<div v-if="showTags">
<Tag v-for="tag in monitor.tags" :key="tag" :item="tag" :size="'sm'" data-testid="monitor-tag" /> <Tag v-for="tag in monitor.tags" :key="tag" :item="tag" :size="'sm'" data-testid="monitor-tag" />
</div> </div>
</div>
</div>
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
<HeartbeatBar size="mid" :monitor-id="monitor.id" />
</div> </div>
</div> </div>
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
<HeartbeatBar size="mid" :monitor-id="monitor.id" />
</div>
</div> </div>
</div> </template>
</template> </Draggable>
</Draggable>
</div> </div>
</div> </div>
</div> </div>
@ -320,26 +320,26 @@ export default {
return (this.$root && this.$root.publicGroupList && this.$root.publicGroupList.length >= 2); return (this.$root && this.$root.publicGroupList && this.$root.publicGroupList.length >= 2);
}, },
hasMonitors() { hasMonitors() {
return this.groups && Array.isArray(this.groups) && this.groups.some(group => return this.groups && Array.isArray(this.groups) && this.groups.some(group =>
group && group.element && group.element.monitorList && group.element.monitorList.length > 0 group && group.element && group.element.monitorList && group.element.monitorList.length > 0
); );
}, },
hasPublicGroups() { hasPublicGroups() {
return this.$root && return this.$root &&
this.$root.publicGroupList && this.$root.publicGroupList &&
Array.isArray(this.$root.publicGroupList) && Array.isArray(this.$root.publicGroupList) &&
this.$root.publicGroupList.length > 0 && this.$root.publicGroupList.length > 0 &&
this.$root.publicGroupList.some(group => this.$root.publicGroupList.some(group =>
group && group.monitorList && group.monitorList.length > 0 group && group.monitorList && group.monitorList.length > 0
); );
}, },
filteredMonitorList() { filteredMonitorList() {
if (!this.searchText || !this.monitorList) { if (!this.searchText || !this.monitorList) {
return this.monitorList || []; return this.monitorList || [];
} }
const searchLower = this.searchText.toLowerCase(); const searchLower = this.searchText.toLowerCase();
return this.monitorList.filter(monitor => return this.monitorList.filter(monitor =>
monitor && monitor.name && monitor.name.toLowerCase().includes(searchLower) monitor && monitor.name && monitor.name.toLowerCase().includes(searchLower)
); );
}, },
@ -350,13 +350,13 @@ export default {
if (!this.globalSearchKeyword) { if (!this.globalSearchKeyword) {
return null; return null;
} }
let count = 0; let count = 0;
if (!this.groups) return count; if (!this.groups) return count;
for (const group of this.groups) { for (const group of this.groups) {
if (!group || !group.element || !group.element.monitorList) continue; if (!group || !group.element || !group.element.monitorList) continue;
for (const monitor of group.element.monitorList) { for (const monitor of group.element.monitorList) {
if (monitor && this.matchesGlobalSearch(monitor)) { if (monitor && this.matchesGlobalSearch(monitor)) {
count++; count++;
@ -369,8 +369,8 @@ export default {
if (!this.$root || !this.$root.publicGroupList) { if (!this.$root || !this.$root.publicGroupList) {
return false; return false;
} }
return this.$root.publicGroupList.some(group => return this.$root.publicGroupList.some(group =>
group && group.useOwnSort === true group && group.useOwnSort === true
); );
}, },
@ -391,7 +391,7 @@ export default {
}, },
// Watch for changes in uptimeList to re-apply sort // Watch for changes in uptimeList to re-apply sort
'$root.uptimeList': { '$root.uptimeList': {
handler() { handler() {
if (this.$root && this.$root.publicGroupList) { if (this.$root && this.$root.publicGroupList) {
this.$root.publicGroupList.forEach(group => { this.$root.publicGroupList.forEach(group => {
if (group) { if (group) {
@ -420,7 +420,7 @@ export default {
try { try {
const groupId = group.id || group.name || 'Default Group'; const groupId = group.id || group.name || 'Default Group';
const storageKey = `uptime-kuma-sort-${this.slug}-${groupId}`; const storageKey = `uptime-kuma-sort-${this.slug}-${groupId}`;
const savedSettings = localStorage.getItem(storageKey); const savedSettings = localStorage.getItem(storageKey);
if (savedSettings) { if (savedSettings) {
const settings = JSON.parse(savedSettings); const settings = JSON.parse(savedSettings);
@ -459,15 +459,15 @@ export default {
if (group.searchKeyword === undefined) { if (group.searchKeyword === undefined) {
group.searchKeyword = ''; group.searchKeyword = '';
} }
// Try to read saved sort settings from localStorage // Try to read saved sort settings from localStorage
const savedSettings = getSavedSortSettings(group); const savedSettings = getSavedSortSettings(group);
if (savedSettings) { if (savedSettings) {
// Apply saved settings if found // Apply saved settings if found
group.sortKey = savedSettings.key; group.sortKey = savedSettings.key;
group.sortDirection = savedSettings.direction; group.sortDirection = savedSettings.direction;
// Restore independent sort flag from localStorage // Restore independent sort flag from localStorage
group.useOwnSort = savedSettings.useOwnSort === undefined ? false : savedSettings.useOwnSort; group.useOwnSort = savedSettings.useOwnSort === undefined ? false : savedSettings.useOwnSort;
} else { } else {
@ -480,7 +480,7 @@ export default {
} }
group.useOwnSort = false; group.useOwnSort = false;
} }
// Apply initial sort when the component is created // Apply initial sort when the component is created
this.applySort(group); this.applySort(group);
} }
@ -500,11 +500,11 @@ export default {
if (group.searchKeyword === undefined) { if (group.searchKeyword === undefined) {
group.searchKeyword = ''; group.searchKeyword = '';
} }
if (group.sortKey === undefined) { if (group.sortKey === undefined) {
// Try to read sort settings from localStorage // Try to read sort settings from localStorage
const savedSettings = getSavedSortSettings(group); const savedSettings = getSavedSortSettings(group);
if (savedSettings) { if (savedSettings) {
// Apply saved settings if found // Apply saved settings if found
group.sortKey = savedSettings.key; group.sortKey = savedSettings.key;
@ -517,7 +517,7 @@ export default {
group.sortDirection = 'desc'; group.sortDirection = 'desc';
group.useOwnSort = false; group.useOwnSort = false;
} }
// Apply sort to newly added group // Apply sort to newly added group
this.applySort(group); this.applySort(group);
} }
@ -541,15 +541,15 @@ export default {
monitorMatchesSearch(monitor, keyword) { monitorMatchesSearch(monitor, keyword) {
if (!keyword) return true; if (!keyword) return true;
if (!monitor) return false; if (!monitor) return false;
keyword = keyword.toLowerCase().trim(); keyword = keyword.toLowerCase().trim();
// Search name, URL and description fields // Search name, URL and description fields
return (monitor.name && monitor.name.toLowerCase().includes(keyword)) || return (monitor.name && monitor.name.toLowerCase().includes(keyword)) ||
(monitor.url && monitor.url.toLowerCase().includes(keyword)) || (monitor.url && monitor.url.toLowerCase().includes(keyword)) ||
(monitor.description && monitor.description.toLowerCase().includes(keyword)); (monitor.description && monitor.description.toLowerCase().includes(keyword));
}, },
/** /**
* Check if monitor matches global search criteria * Check if monitor matches global search criteria
* @param {object} monitor Monitor object * @param {object} monitor Monitor object
@ -558,10 +558,10 @@ export default {
matchesGlobalSearch(monitor) { matchesGlobalSearch(monitor) {
if (!this.globalSearchKeyword) return true; if (!this.globalSearchKeyword) return true;
if (!monitor) return false; if (!monitor) return false;
return this.monitorMatchesSearch(monitor, this.globalSearchKeyword); return this.monitorMatchesSearch(monitor, this.globalSearchKeyword);
}, },
/** /**
* Determine if monitor should be displayed * Determine if monitor should be displayed
* @param {object} monitor Monitor object * @param {object} monitor Monitor object
@ -570,20 +570,20 @@ export default {
*/ */
shouldShowMonitor(monitor, group) { shouldShowMonitor(monitor, group) {
if (!monitor) return false; if (!monitor) return false;
// Check global search first // Check global search first
if (this.globalSearchKeyword && !this.matchesGlobalSearch(monitor)) { if (this.globalSearchKeyword && !this.matchesGlobalSearch(monitor)) {
return false; return false;
} }
// Then check group search // Then check group search
if (group && group.searchKeyword) { if (group && group.searchKeyword) {
return this.monitorMatchesSearch(monitor, group.searchKeyword); return this.monitorMatchesSearch(monitor, group.searchKeyword);
} }
return true; return true;
}, },
/** /**
* Get filtered monitor list * Get filtered monitor list
* @param {object} group Group object * @param {object} group Group object
@ -591,26 +591,26 @@ export default {
*/ */
getFilteredMonitorList(group) { getFilteredMonitorList(group) {
if (!group || !group.monitorList || !Array.isArray(group.monitorList)) return []; if (!group || !group.monitorList || !Array.isArray(group.monitorList)) return [];
let result = [...group.monitorList]; // Create a copy to avoid modifying original array let result = [...group.monitorList]; // Create a copy to avoid modifying original array
// Apply global search first // Apply global search first
if (this.globalSearchKeyword) { if (this.globalSearchKeyword) {
result = result.filter(monitor => result = result.filter(monitor =>
monitor && this.matchesGlobalSearch(monitor) monitor && this.matchesGlobalSearch(monitor)
); );
} }
// Then apply group search // Then apply group search
if (group.searchKeyword) { if (group.searchKeyword) {
result = result.filter(monitor => result = result.filter(monitor =>
monitor && this.monitorMatchesSearch(monitor, group.searchKeyword) monitor && this.monitorMatchesSearch(monitor, group.searchKeyword)
); );
} }
return result; return result;
}, },
/** /**
* Determine if group should be displayed * Determine if group should be displayed
* @param {object} group Group object * @param {object} group Group object
@ -618,20 +618,20 @@ export default {
*/ */
shouldShowGroup(group) { shouldShowGroup(group) {
if (!group) return false; if (!group) return false;
// Always show in edit mode // Always show in edit mode
if (this.editMode) return true; if (this.editMode) return true;
// If there's global search, only show groups containing matching monitors // If there's global search, only show groups containing matching monitors
if (this.globalSearchKeyword) { if (this.globalSearchKeyword) {
return group.monitorList && group.monitorList.some(monitor => return group.monitorList && group.monitorList.some(monitor =>
this.matchesGlobalSearch(monitor) this.matchesGlobalSearch(monitor)
); );
} }
return true; return true;
}, },
/** /**
* Clear group search keyword * Clear group search keyword
* @param {object} group Group object * @param {object} group Group object
@ -641,14 +641,14 @@ export default {
group.searchKeyword = ''; group.searchKeyword = '';
} }
}, },
/** /**
* Clear global search keyword * Clear global search keyword
*/ */
clearGlobalSearch() { clearGlobalSearch() {
this.globalSearchKeyword = ''; this.globalSearchKeyword = '';
}, },
/** /**
* Set sort key and direction for a group, then apply the sort * Set sort key and direction for a group, then apply the sort
* @param {object} group The group object * @param {object} group The group object
@ -662,16 +662,16 @@ export default {
group.sortKey = key; group.sortKey = key;
group.sortDirection = (key === 'status') ? 'desc' : 'asc'; group.sortDirection = (key === 'status') ? 'desc' : 'asc';
} }
// Set independent sort flag for current group when clicking on its sort buttons // Set independent sort flag for current group when clicking on its sort buttons
group.useOwnSort = true; group.useOwnSort = true;
// Save sort settings to localStorage // Save sort settings to localStorage
try { try {
// Get a unique identifier for the group, use name if id is not available // Get a unique identifier for the group, use name if id is not available
const groupId = group.id || group.name || 'Default Group'; const groupId = group.id || group.name || 'Default Group';
const storageKey = `uptime-kuma-sort-${this.slug}-${groupId}`; const storageKey = `uptime-kuma-sort-${this.slug}-${groupId}`;
// Save sort settings with the useOwnSort flag // Save sort settings with the useOwnSort flag
const sortSettings = { const sortSettings = {
key: group.sortKey, key: group.sortKey,
@ -682,7 +682,7 @@ export default {
} catch (error) { } catch (error) {
console.error('Cannot save sort settings', error); console.error('Cannot save sort settings', error);
} }
// Apply sort to this group // Apply sort to this group
this.applySortToGroup(group); this.applySortToGroup(group);
}, },
@ -695,7 +695,7 @@ export default {
if (!group || !group.monitorList || !Array.isArray(group.monitorList)) { if (!group || !group.monitorList || !Array.isArray(group.monitorList)) {
return; return;
} }
// Check if group has independent sort settings // Check if group has independent sort settings
if (group.useOwnSort || !this.isGlobalSortActive) { if (group.useOwnSort || !this.isGlobalSortActive) {
// Use group's own sort settings // Use group's own sort settings
@ -729,14 +729,14 @@ export default {
monitorList.sort((a, b) => { monitorList.sort((a, b) => {
if (!a || !b) return 0; if (!a || !b) return 0;
let comparison = 0; let comparison = 0;
let valueA, valueB; let valueA, valueB;
if (sortKey === 'status') { if (sortKey === 'status') {
const getStatusPriority = (monitor) => { const getStatusPriority = (monitor) => {
if (!monitor || !monitor.id) return 4; if (!monitor || !monitor.id) return 4;
// Ensure heartbeatList is available // Ensure heartbeatList is available
const hbList = this.$root.heartbeatList || {}; const hbList = this.$root.heartbeatList || {};
const hbArr = hbList[monitor.id]; const hbArr = hbList[monitor.id];
@ -853,14 +853,14 @@ export default {
*/ */
toggleGlobalSort() { toggleGlobalSort() {
this.isGlobalSortActive = !this.isGlobalSortActive; this.isGlobalSortActive = !this.isGlobalSortActive;
// Reset all groups' independent sort flags and save their status to localStorage // Reset all groups' independent sort flags and save their status to localStorage
if (this.$root && this.$root.publicGroupList) { if (this.$root && this.$root.publicGroupList) {
this.$root.publicGroupList.forEach(group => { this.$root.publicGroupList.forEach(group => {
if (group) { if (group) {
// When toggling global sort, reset all group's independent sort flags // When toggling global sort, reset all group's independent sort flags
group.useOwnSort = false; group.useOwnSort = false;
// Save the updated status to localStorage for each group // Save the updated status to localStorage for each group
try { try {
const groupId = group.id || group.name || 'Default Group'; const groupId = group.id || group.name || 'Default Group';
@ -877,7 +877,7 @@ export default {
} }
}); });
} }
// Save global sort toggle state // Save global sort toggle state
try { try {
const storageKey = `uptime-kuma-global-sort-${this.slug}`; const storageKey = `uptime-kuma-global-sort-${this.slug}`;
@ -890,7 +890,7 @@ export default {
} catch (error) { } catch (error) {
console.error('Cannot save global sort settings', error); console.error('Cannot save global sort settings', error);
} }
// Apply appropriate sorting based on global sort state // Apply appropriate sorting based on global sort state
if (this.isGlobalSortActive) { if (this.isGlobalSortActive) {
this.applyGlobalSort(); this.applyGlobalSort();
@ -918,17 +918,17 @@ export default {
this.globalSortKey = key; this.globalSortKey = key;
this.globalSortDirection = (key === 'status') ? 'desc' : 'asc'; this.globalSortDirection = (key === 'status') ? 'desc' : 'asc';
} }
// Activate global sort // Activate global sort
this.isGlobalSortActive = true; this.isGlobalSortActive = true;
// Clear all groups' independent sort flags and save each group's settings // Clear all groups' independent sort flags and save each group's settings
if (this.$root && this.$root.publicGroupList) { if (this.$root && this.$root.publicGroupList) {
this.$root.publicGroupList.forEach(group => { this.$root.publicGroupList.forEach(group => {
if (group) { if (group) {
// Set useOwnSort to false for all groups // Set useOwnSort to false for all groups
group.useOwnSort = false; group.useOwnSort = false;
// Save the group's sort settings with useOwnSort=false to localStorage // Save the group's sort settings with useOwnSort=false to localStorage
try { try {
const groupId = group.id || group.name || 'Default Group'; const groupId = group.id || group.name || 'Default Group';
@ -945,11 +945,11 @@ export default {
} }
}); });
} }
// Save global sort settings to localStorage // Save global sort settings to localStorage
try { try {
const storageKey = `uptime-kuma-global-sort-${this.slug}`; const storageKey = `uptime-kuma-global-sort-${this.slug}`;
// Save global sort settings // Save global sort settings
const globalSortSettings = { const globalSortSettings = {
key: this.globalSortKey, key: this.globalSortKey,
@ -960,7 +960,7 @@ export default {
} catch (error) { } catch (error) {
console.error('Cannot save global sort settings', error); console.error('Cannot save global sort settings', error);
} }
// Apply global sort // Apply global sort
this.applyGlobalSort(); this.applyGlobalSort();
}, },
@ -970,14 +970,14 @@ export default {
*/ */
disableGlobalSort() { disableGlobalSort() {
this.isGlobalSortActive = false; this.isGlobalSortActive = false;
// Save the updated useOwnSort=false status for all groups // Save the updated useOwnSort=false status for all groups
if (this.$root && this.$root.publicGroupList) { if (this.$root && this.$root.publicGroupList) {
this.$root.publicGroupList.forEach(group => { this.$root.publicGroupList.forEach(group => {
if (group) { if (group) {
// Mark all groups as not using independent sort when global sort is disabled // Mark all groups as not using independent sort when global sort is disabled
group.useOwnSort = false; group.useOwnSort = false;
// Save the updated status to localStorage // Save the updated status to localStorage
try { try {
const groupId = group.id || group.name || 'Default Group'; const groupId = group.id || group.name || 'Default Group';
@ -994,7 +994,7 @@ export default {
} }
}); });
} }
// Save global sort settings with active=false // Save global sort settings with active=false
try { try {
const storageKey = `uptime-kuma-global-sort-${this.slug}`; const storageKey = `uptime-kuma-global-sort-${this.slug}`;
@ -1007,7 +1007,7 @@ export default {
} catch (error) { } catch (error) {
console.error('Cannot save global sort settings', error); console.error('Cannot save global sort settings', error);
} }
// Restore original sorting for each group // Restore original sorting for each group
if (this.$root && this.$root.publicGroupList) { if (this.$root && this.$root.publicGroupList) {
this.$root.publicGroupList.forEach(group => { this.$root.publicGroupList.forEach(group => {
@ -1025,7 +1025,7 @@ export default {
if (!this.isGlobalSortActive || !this.$root || !this.$root.publicGroupList) { if (!this.isGlobalSortActive || !this.$root || !this.$root.publicGroupList) {
return; return;
} }
// Apply global sort settings to all groups not using independent sorting // Apply global sort settings to all groups not using independent sorting
this.$root.publicGroupList.forEach(group => { this.$root.publicGroupList.forEach(group => {
if (group && group.monitorList && !group.useOwnSort) { if (group && group.monitorList && !group.useOwnSort) {
@ -1156,11 +1156,11 @@ export default {
.monitor-list { .monitor-list {
min-height: 46px; min-height: 46px;
.item { .item {
padding: 0.75rem 1.25rem; padding: 0.75rem 1.25rem;
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0;
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }
@ -1172,7 +1172,7 @@ export default {
overflow: hidden; overflow: hidden;
background-color: #fff; background-color: #fff;
border: 1px solid #e9ecef; border: 1px solid #e9ecef;
.dark & { .dark & {
background-color: #2d3748; background-color: #2d3748;
border-color: #4a5568; border-color: #4a5568;
@ -1324,13 +1324,13 @@ export default {
.input-group { .input-group {
position: relative; position: relative;
.form-control { .form-control {
border: 1px solid #dee2e6; border: 1px solid #dee2e6;
border-radius: 4px; border-radius: 4px;
background-color: #fff; background-color: #fff;
} }
.btn { .btn {
position: absolute; position: absolute;
right: 1px; /* Position inside the input border */ right: 1px; /* Position inside the input border */
@ -1343,7 +1343,7 @@ export default {
padding: 0 0.75rem; padding: 0 0.75rem;
} }
} }
small { small {
display: none; /* Hide the 'Found X monitors' text */ display: none; /* Hide the 'Found X monitors' text */
} }
@ -1366,19 +1366,19 @@ export default {
color: $dark-font-color; color: $dark-font-color;
border-color: transparent; border-color: transparent;
} }
.sort-buttons { .sort-buttons {
border-color: #4a5568; border-color: #4a5568;
.sort-button { .sort-button {
background-color: #1a202c; background-color: #1a202c;
border-right-color: #4a5568; border-right-color: #4a5568;
color: #e2e8f0; color: #e2e8f0;
&:hover { &:hover {
background-color: #4a5568; background-color: #4a5568;
} }
&.active { &.active {
background-color: #28a745; background-color: #28a745;
color: white; color: white;
@ -1393,10 +1393,10 @@ export default {
border-color: #4a5568; border-color: #4a5568;
color: $dark-font-color; color: $dark-font-color;
} }
.btn { .btn {
color: $dark-font-color; color: $dark-font-color;
&:hover { &:hover {
color: white; color: white;
} }
@ -1409,26 +1409,26 @@ export default {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
} }
.search-container { .search-container {
margin-left: 0; margin-left: 0;
margin-top: 0.5rem; margin-top: 0.5rem;
width: 100%; width: 100%;
} }
.search-input { .search-input {
width: 100%; width: 100%;
} }
.sort-controls { .sort-controls {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
} }
.sort-buttons { .sort-buttons {
margin-top: 0.5rem; margin-top: 0.5rem;
} }
.sort-button { .sort-button {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }