diff --git a/ui/src/components/filter/KestraFilter.vue b/ui/src/components/filter/KestraFilter.vue index aeb546e55ef..48ce1ad70b0 100644 --- a/ui/src/components/filter/KestraFilter.vue +++ b/ui/src/components/filter/KestraFilter.vue @@ -217,7 +217,23 @@ }; const valueCallback = (filter, isDate = false) => { if (!isDate) { - const values = current.value[dropdowns.value.third.index].value; + const currentFilter = current.value[dropdowns.value.third.index]; + const values = currentFilter.value; + const isDuplicateInOtherFilters = current.value.some((item, index) => { + return index !== dropdowns.value.third.index && + item.label === currentFilter.label && + item.value.includes(filter.value); + }); + + if (isDuplicateInOtherFilters) { + store.dispatch("core/showMessage", { + variant: "error", + title: t("duplicate filter"), + message: t("duplicate filter message", {filterName: currentFilter.label}), + }); + return; + } + const index = values.indexOf(filter.value); if (index === -1) values.push(filter.value); @@ -376,6 +392,22 @@ if (typeof v.at(-1) === "string") { if (v.at(-2)?.label === "labels") { + const labelValue = v.at(-1); + const isDuplicateInOtherFilters = current.value.some((item) => { + return item.label === "labels" && + item !== v.at(-2) && + item.value.includes(labelValue); + }); + + if (isDuplicateInOtherFilters) { + store.dispatch("core/showMessage", { + variant: "error", + title: t("duplicate filter"), + message: t("duplicate filter message", {filterName: labelValue}), + }); + return; + } + // Adding labels to proper filter v.at(-2).value?.push(v.at(-1)); closeDropdown(); @@ -383,10 +415,26 @@ } else { // Adding text search string const label = t("filters.options.text"); - const index = current.value.findIndex((i) => i.label === label); + const newValue = v.at(-1); + + // Check if this text already exists in any other filter + const isDuplicateInOtherFilters = current.value.some(item => + item.label === label && + item.value.includes(newValue) + ); + + if (isDuplicateInOtherFilters) { + store.dispatch("core/showMessage", { + variant: "error", + title: t("duplicate filter"), + message: t("duplicate filter message", {filterName: label}), + }); + return; + } - if (index !== -1) current.value[index].value = [v.at(-1)]; - else current.value.push({label, value: [v.at(-1)]}); + const index = current.value.findIndex((i) => i.label === label); + if (index !== -1) current.value[index].value = [newValue]; + else current.value.push({label, value: [newValue]}); triggerSearch(); } diff --git a/ui/src/translations/en.json b/ui/src/translations/en.json index e8794025f2d..ce12e769527 100644 --- a/ui/src/translations/en.json +++ b/ui/src/translations/en.json @@ -523,6 +523,8 @@ "delete task confirm": "Do you want to delete the task {taskId}?", "can not save": "Can not save", "flow must have id and namespace": "Flow must have an id and a namespace.", + "duplicate filter": "Duplicate filter not allowed", + "duplicate filter message": "Duplicate filter `{filterName}` found, please remove it and choose another one.", "readonly property": "Read-only property", "namespace and id readonly": "The properties `namespace` and `id` cannot be changed — they are now set to their initial values. If you want to rename a flow or change its namespace, you can create a new flow and remove the old one.", "avg": "Average",