mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-23 14:54:05 +00:00
Merge 9b9233f192
into 8a432ac937
This commit is contained in:
commit
01340c5e55
4 changed files with 145 additions and 54 deletions
|
@ -89,17 +89,11 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
searchText: "",
|
|
||||||
selectMode: false,
|
selectMode: false,
|
||||||
selectAll: false,
|
selectAll: false,
|
||||||
disableSelectAllWatcher: false,
|
disableSelectAllWatcher: false,
|
||||||
selectedMonitors: {},
|
selectedMonitors: {},
|
||||||
windowTop: 0,
|
windowTop: 0,
|
||||||
filterState: {
|
|
||||||
status: null,
|
|
||||||
active: null,
|
|
||||||
tags: null,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -164,12 +158,66 @@ export default {
|
||||||
return Object.keys(this.selectedMonitors).length;
|
return Object.keys(this.selectedMonitors).length;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns applied filters based on query params.
|
||||||
|
* @returns {{ status: number[], active: any[], tags: number[] }} The current filter state.
|
||||||
|
*/
|
||||||
|
filterState() {
|
||||||
|
// Since query params are always strings, convert them to the correct type
|
||||||
|
let status = this.$route.query["status"] || [];
|
||||||
|
if (!Array.isArray(status)) {
|
||||||
|
status = [ status ];
|
||||||
|
}
|
||||||
|
status = status.map(Number);
|
||||||
|
// Casting to boolean does not work here as Boolean("false") === true
|
||||||
|
let active = this.$route.query["active"] || [];
|
||||||
|
if (!Array.isArray(active)) {
|
||||||
|
active = [ active ];
|
||||||
|
}
|
||||||
|
active = active.map(val => {
|
||||||
|
if (val === "true") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (val === "false") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
});
|
||||||
|
let tags = this.$route.query["tags"] || [];
|
||||||
|
if (!Array.isArray(tags)) {
|
||||||
|
tags = [ tags ];
|
||||||
|
}
|
||||||
|
tags = tags.map(Number);
|
||||||
|
return {
|
||||||
|
status,
|
||||||
|
active,
|
||||||
|
tags,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
searchText: {
|
||||||
|
get() {
|
||||||
|
return this.$route.query.searchText || "";
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
let newQuery = { ...this.$route.query };
|
||||||
|
if (value === "") {
|
||||||
|
delete newQuery.searchText;
|
||||||
|
} else {
|
||||||
|
newQuery.searchText = value;
|
||||||
|
}
|
||||||
|
this.$router.replace({
|
||||||
|
query: newQuery,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if any filters are active.
|
* Determines if any filters are active.
|
||||||
* @returns {boolean} True if any filter is active, false otherwise.
|
* @returns {boolean} True if any filter is active, false otherwise.
|
||||||
*/
|
*/
|
||||||
filtersActive() {
|
filtersActive() {
|
||||||
return this.filterState.status != null || this.filterState.active != null || this.filterState.tags != null || this.searchText !== "";
|
return this.filterState.status.length > 0 || this.filterState.active.length > 0 || this.filterState.tags.length > 0 || this.searchText !== "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -239,11 +287,16 @@ export default {
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Update the MonitorList Filter
|
* Update the MonitorList Filter
|
||||||
* @param {object} newFilter Object with new filter
|
* @param {{ status: number[], active: any[], tags: number[] }} newFilter Object with new filter
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
updateFilter(newFilter) {
|
updateFilter(newFilter) {
|
||||||
this.filterState = newFilter;
|
this.$router.replace({
|
||||||
|
query: {
|
||||||
|
...this.$route.query,
|
||||||
|
...newFilter,
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Deselect a monitor
|
* Deselect a monitor
|
||||||
|
@ -333,7 +386,7 @@ export default {
|
||||||
|
|
||||||
// filter by status
|
// filter by status
|
||||||
let statusMatch = true;
|
let statusMatch = true;
|
||||||
if (this.filterState.status != null && this.filterState.status.length > 0) {
|
if (this.filterState.status.length > 0) {
|
||||||
if (monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[monitor.id]) {
|
if (monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[monitor.id]) {
|
||||||
monitor.status = this.$root.lastHeartbeatList[monitor.id].status;
|
monitor.status = this.$root.lastHeartbeatList[monitor.id].status;
|
||||||
}
|
}
|
||||||
|
@ -342,13 +395,13 @@ export default {
|
||||||
|
|
||||||
// filter by active
|
// filter by active
|
||||||
let activeMatch = true;
|
let activeMatch = true;
|
||||||
if (this.filterState.active != null && this.filterState.active.length > 0) {
|
if (this.filterState.active.length > 0) {
|
||||||
activeMatch = this.filterState.active.includes(monitor.active);
|
activeMatch = this.filterState.active.includes(monitor.active);
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter by tags
|
// filter by tags
|
||||||
let tagsMatch = true;
|
let tagsMatch = true;
|
||||||
if (this.filterState.tags != null && this.filterState.tags.length > 0) {
|
if (this.filterState.tags.length > 0) {
|
||||||
tagsMatch = monitor.tags.map(tag => tag.tag_id) // convert to array of tag IDs
|
tagsMatch = monitor.tags.map(tag => tag.tag_id) // convert to array of tag IDs
|
||||||
.filter(monitorTagId => this.filterState.tags.includes(monitorTagId)) // perform Array Intersaction between filter and monitor's tags
|
.filter(monitorTagId => this.filterState.tags.includes(monitorTagId)) // perform Array Intersaction between filter and monitor's tags
|
||||||
.length > 0;
|
.length > 0;
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
<font-awesome-icon v-if="numFiltersActive > 0" icon="times" />
|
<font-awesome-icon v-if="numFiltersActive > 0" icon="times" />
|
||||||
</button>
|
</button>
|
||||||
<MonitorListFilterDropdown
|
<MonitorListFilterDropdown
|
||||||
:filterActive="filterState.status?.length > 0"
|
:filterActive="filterState.status.length > 0"
|
||||||
>
|
>
|
||||||
<template #status>
|
<template #status>
|
||||||
<Status v-if="filterState.status?.length === 1" :status="filterState.status[0]" />
|
<Status v-if="filterState.status.length === 1" :status="filterState.status[0]" />
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ $t('Status') }}
|
{{ $t('Status') }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
<Status :status="1" />
|
<Status :status="1" />
|
||||||
<span class="ps-3">
|
<span class="ps-3">
|
||||||
{{ $root.stats.up }}
|
{{ $root.stats.up }}
|
||||||
<span v-if="filterState.status?.includes(1)" class="px-1 filter-active">
|
<span v-if="filterState.status.includes(1)" class="px-1 filter-active">
|
||||||
<font-awesome-icon icon="check" />
|
<font-awesome-icon icon="check" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
<Status :status="0" />
|
<Status :status="0" />
|
||||||
<span class="ps-3">
|
<span class="ps-3">
|
||||||
{{ $root.stats.down }}
|
{{ $root.stats.down }}
|
||||||
<span v-if="filterState.status?.includes(0)" class="px-1 filter-active">
|
<span v-if="filterState.status.includes(0)" class="px-1 filter-active">
|
||||||
<font-awesome-icon icon="check" />
|
<font-awesome-icon icon="check" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
<Status :status="2" />
|
<Status :status="2" />
|
||||||
<span class="ps-3">
|
<span class="ps-3">
|
||||||
{{ $root.stats.pending }}
|
{{ $root.stats.pending }}
|
||||||
<span v-if="filterState.status?.includes(2)" class="px-1 filter-active">
|
<span v-if="filterState.status.includes(2)" class="px-1 filter-active">
|
||||||
<font-awesome-icon icon="check" />
|
<font-awesome-icon icon="check" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
<Status :status="3" />
|
<Status :status="3" />
|
||||||
<span class="ps-3">
|
<span class="ps-3">
|
||||||
{{ $root.stats.maintenance }}
|
{{ $root.stats.maintenance }}
|
||||||
<span v-if="filterState.status?.includes(3)" class="px-1 filter-active">
|
<span v-if="filterState.status.includes(3)" class="px-1 filter-active">
|
||||||
<font-awesome-icon icon="check" />
|
<font-awesome-icon icon="check" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -77,11 +77,12 @@
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
</MonitorListFilterDropdown>
|
</MonitorListFilterDropdown>
|
||||||
<MonitorListFilterDropdown :filterActive="filterState.active?.length > 0">
|
<MonitorListFilterDropdown :filterActive="filterState.active.length > 0">
|
||||||
<template #status>
|
<template #status>
|
||||||
<span v-if="filterState.active?.length === 1">
|
<span v-if="filterState.active.length === 1">
|
||||||
<span v-if="filterState.active[0]">{{ $t("Running") }}</span>
|
<span v-if="filterState.active[0] === true">{{ $t("Running") }}</span>
|
||||||
<span v-else>{{ $t("filterActivePaused") }}</span>
|
<span v-else-if="filterState.active[0] === false">{{ $t("filterActivePaused") }}</span>
|
||||||
|
<span v-else>{{ $t("Unknown") }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ $t("filterActive") }}
|
{{ $t("filterActive") }}
|
||||||
|
@ -94,7 +95,7 @@
|
||||||
<span>{{ $t("Running") }}</span>
|
<span>{{ $t("Running") }}</span>
|
||||||
<span class="ps-3">
|
<span class="ps-3">
|
||||||
{{ $root.stats.active }}
|
{{ $root.stats.active }}
|
||||||
<span v-if="filterState.active?.includes(true)" class="px-1 filter-active">
|
<span v-if="filterState.active.includes(true)" class="px-1 filter-active">
|
||||||
<font-awesome-icon icon="check" />
|
<font-awesome-icon icon="check" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -107,7 +108,7 @@
|
||||||
<span>{{ $t("filterActivePaused") }}</span>
|
<span>{{ $t("filterActivePaused") }}</span>
|
||||||
<span class="ps-3">
|
<span class="ps-3">
|
||||||
{{ $root.stats.pause }}
|
{{ $root.stats.pause }}
|
||||||
<span v-if="filterState.active?.includes(false)" class="px-1 filter-active">
|
<span v-if="filterState.active.includes(false)" class="px-1 filter-active">
|
||||||
<font-awesome-icon icon="check" />
|
<font-awesome-icon icon="check" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -116,10 +117,11 @@
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
</MonitorListFilterDropdown>
|
</MonitorListFilterDropdown>
|
||||||
<MonitorListFilterDropdown :filterActive="filterState.tags?.length > 0">
|
<MonitorListFilterDropdown :filterActive="filterState.tags.length > 0">
|
||||||
<template #status>
|
<template #status>
|
||||||
|
<!-- Prevent rendering Tag component if tagsList has not been fetched or filterState contains invalid tag -->
|
||||||
<Tag
|
<Tag
|
||||||
v-if="filterState.tags?.length === 1"
|
v-if="filterState.tags.length === 1 && tagsList.find(tag => tag.id === filterState.tags[0])"
|
||||||
:item="tagsList.find(tag => tag.id === filterState.tags[0])"
|
:item="tagsList.find(tag => tag.id === filterState.tags[0])"
|
||||||
:size="'sm'"
|
:size="'sm'"
|
||||||
/>
|
/>
|
||||||
|
@ -134,7 +136,7 @@
|
||||||
<span><Tag :item="tag" :size="'sm'" /></span>
|
<span><Tag :item="tag" :size="'sm'" /></span>
|
||||||
<span class="ps-3">
|
<span class="ps-3">
|
||||||
{{ getTaggedMonitorCount(tag) }}
|
{{ getTaggedMonitorCount(tag) }}
|
||||||
<span v-if="filterState.tags?.includes(tag.id)" class="px-1 filter-active">
|
<span v-if="filterState.tags.includes(tag.id)" class="px-1 filter-active">
|
||||||
<font-awesome-icon icon="check" />
|
<font-awesome-icon icon="check" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -179,16 +181,37 @@ export default {
|
||||||
let num = 0;
|
let num = 0;
|
||||||
|
|
||||||
Object.values(this.filterState).forEach(item => {
|
Object.values(this.filterState).forEach(item => {
|
||||||
if (item != null && item.length > 0) {
|
if (item.length > 0) {
|
||||||
num += 1;
|
num += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return num;
|
return num;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Returns an array of invalid filters assuming tagsList has been fetched
|
||||||
|
* @returns {Array} Array of invalid filters
|
||||||
|
*/
|
||||||
|
invalidFilters() {
|
||||||
|
const filters = [];
|
||||||
|
if (!this.filterState.status.every((val) => val >= 0 && val <= 3)) {
|
||||||
|
filters.push(this.$t("Status"));
|
||||||
|
}
|
||||||
|
if (!this.filterState.active.every((val) => val === true || val === false)) {
|
||||||
|
filters.push(this.$t("Active"));
|
||||||
|
}
|
||||||
|
if (!this.filterState.tags.every((val) => this.tagsList.find(tag => tag.id === val))) {
|
||||||
|
filters.push(this.$t("Tags"));
|
||||||
|
}
|
||||||
|
return filters;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getExistingTags();
|
this.getExistingTags(() => {
|
||||||
|
if (this.invalidFilters.length > 0) {
|
||||||
|
this.$root.toastError(this.$t("InvalidFilters", [ this.invalidFilters.join(", ") ]));
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleStatusFilter(status) {
|
toggleStatusFilter(status) {
|
||||||
|
@ -196,15 +219,11 @@ export default {
|
||||||
...this.filterState
|
...this.filterState
|
||||||
};
|
};
|
||||||
|
|
||||||
if (newFilter.status == null) {
|
|
||||||
newFilter.status = [ status ];
|
|
||||||
} else {
|
|
||||||
if (newFilter.status.includes(status)) {
|
if (newFilter.status.includes(status)) {
|
||||||
newFilter.status = newFilter.status.filter(item => item !== status);
|
newFilter.status = newFilter.status.filter(item => item !== status);
|
||||||
} else {
|
} else {
|
||||||
newFilter.status.push(status);
|
newFilter.status.push(status);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
this.$emit("updateFilter", newFilter);
|
this.$emit("updateFilter", newFilter);
|
||||||
},
|
},
|
||||||
toggleActiveFilter(active) {
|
toggleActiveFilter(active) {
|
||||||
|
@ -212,15 +231,11 @@ export default {
|
||||||
...this.filterState
|
...this.filterState
|
||||||
};
|
};
|
||||||
|
|
||||||
if (newFilter.active == null) {
|
|
||||||
newFilter.active = [ active ];
|
|
||||||
} else {
|
|
||||||
if (newFilter.active.includes(active)) {
|
if (newFilter.active.includes(active)) {
|
||||||
newFilter.active = newFilter.active.filter(item => item !== active);
|
newFilter.active = newFilter.active.filter(item => item !== active);
|
||||||
} else {
|
} else {
|
||||||
newFilter.active.push(active);
|
newFilter.active.push(active);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
this.$emit("updateFilter", newFilter);
|
this.$emit("updateFilter", newFilter);
|
||||||
},
|
},
|
||||||
toggleTagFilter(tag) {
|
toggleTagFilter(tag) {
|
||||||
|
@ -228,27 +243,26 @@ export default {
|
||||||
...this.filterState
|
...this.filterState
|
||||||
};
|
};
|
||||||
|
|
||||||
if (newFilter.tags == null) {
|
|
||||||
newFilter.tags = [ tag.id ];
|
|
||||||
} else {
|
|
||||||
if (newFilter.tags.includes(tag.id)) {
|
if (newFilter.tags.includes(tag.id)) {
|
||||||
newFilter.tags = newFilter.tags.filter(item => item !== tag.id);
|
newFilter.tags = newFilter.tags.filter(item => item !== tag.id);
|
||||||
} else {
|
} else {
|
||||||
newFilter.tags.push(tag.id);
|
newFilter.tags.push(tag.id);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
this.$emit("updateFilter", newFilter);
|
this.$emit("updateFilter", newFilter);
|
||||||
},
|
},
|
||||||
clearFilters() {
|
clearFilters() {
|
||||||
this.$emit("updateFilter", {
|
this.$emit("updateFilter", {
|
||||||
status: null,
|
status: [],
|
||||||
|
active: [],
|
||||||
|
tags: [],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getExistingTags() {
|
getExistingTags(callback) {
|
||||||
this.$root.getSocket().emit("getTags", (res) => {
|
this.$root.getSocket().emit("getTags", (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
this.tagsList = res.tags;
|
this.tagsList = res.tags;
|
||||||
}
|
}
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getTaggedMonitorCount(tag) {
|
getTaggedMonitorCount(tag) {
|
||||||
|
|
|
@ -1051,5 +1051,6 @@
|
||||||
"RabbitMQ Password": "RabbitMQ Password",
|
"RabbitMQ Password": "RabbitMQ Password",
|
||||||
"rabbitmqHelpText": "To use the monitor, you will need to enable the Management Plugin in your RabbitMQ setup. For more information, please consult the {rabitmq_documentation}.",
|
"rabbitmqHelpText": "To use the monitor, you will need to enable the Management Plugin in your RabbitMQ setup. For more information, please consult the {rabitmq_documentation}.",
|
||||||
"SendGrid API Key": "SendGrid API Key",
|
"SendGrid API Key": "SendGrid API Key",
|
||||||
"Separate multiple email addresses with commas": "Separate multiple email addresses with commas"
|
"Separate multiple email addresses with commas": "Separate multiple email addresses with commas",
|
||||||
|
"InvalidFilters": "The following filters are invalid: {0}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,8 +192,31 @@ const routes = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const router = createRouter({
|
const router = createRouter({
|
||||||
linkActiveClass: "active",
|
linkActiveClass: "active",
|
||||||
history: createWebHistory(),
|
history: createWebHistory(),
|
||||||
routes,
|
routes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.beforeEach((to, from) => {
|
||||||
|
// Check if redirectedFrom is defined to check if this function has already been run
|
||||||
|
// Without this check, the router will be stuck in an infinite loop
|
||||||
|
if (to.redirectedFrom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user is navigating to same page but to.query is empty, they have clicked on the same link
|
||||||
|
// For example: Clicked on Add monitor twice
|
||||||
|
if (to.path === from.path && Object.keys(to.query).length !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...to,
|
||||||
|
query: {
|
||||||
|
...to.query,
|
||||||
|
...from.query,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export { router };
|
||||||
|
|
Loading…
Reference in a new issue