diff --git a/src/components/standalone/firewall/rules/FirewallRulesContent.vue b/src/components/standalone/firewall/rules/FirewallRulesContent.vue index 3fa302283..818c1a1c8 100644 --- a/src/components/standalone/firewall/rules/FirewallRulesContent.vue +++ b/src/components/standalone/firewall/rules/FirewallRulesContent.vue @@ -11,7 +11,8 @@ import { NeButton, getAxiosErrorMessage, NeEmptyState, - NeTextInput + NeTextInput, + NeDropdownFilter } from '@nethesis/vue-components' import { onMounted, ref, type PropType, watch, computed } from 'vue' import CreateOrEditFirewallRuleDrawer from './CreateOrEditFirewallRuleDrawer.vue' @@ -39,6 +40,7 @@ const isShownCreateOrEditRuleDrawer = ref(false) const isShownDeleteRuleModal = ref(false) const knownTags = ref([]) const textFilter = ref('') +const tagFilter = ref([]) const loading = ref({ listRules: false, @@ -99,13 +101,20 @@ const emptyStateTitle = computed(() => { } }) +const isFilterActive = computed(() => !!textFilter.value || tagFilter.value.length > 0) + const filteredRules = computed(() => { - if (!textFilter.value) { + if (!isFilterActive.value) { // no filter return rules.value } else { // filter rules - return rules.value.filter((rule) => searchStringInRule(rule, textFilter.value)) + return rules.value.filter((rule) => { + const matchesTextFilter = !textFilter.value || searchStringInRule(rule, textFilter.value) + const matchesTagFilter = + !tagFilter.value.length || rule.ns_tag.some((tag) => tagFilter.value.includes(tag)) + return matchesTextFilter && matchesTagFilter + }) } }) @@ -136,13 +145,14 @@ async function listRules() { const res = await ubusCall('ns.firewall', listRulesMethod.value) rules.value = res.data.rules - const tags = uniq(rules.value.map((rule) => rule.ns_tag).flat()) + const tags = uniq(rules.value.map((rule) => rule.ns_tag).flat()).sort() knownTags.value = tags.map((tag) => { return { id: tag, label: tag } }) + tagFilter.value = tagFilter.value.filter((tag) => tags.includes(tag)) } catch (err: any) { console.error(err) error.value.listRules = t(getAxiosErrorMessage(err)) @@ -217,6 +227,11 @@ function showDeleteRuleModal(rule: FirewallRule) { isShownDeleteRuleModal.value = true } +function clearFilters() { + textFilter.value = '' + tagFilter.value = [] +} + function hideCreateOrEditRuleDrawer() { isShownCreateOrEditRuleDrawer.value = false } @@ -331,7 +346,7 @@ function searchStringInRule(rule: FirewallRule, queryText: string) { @@ -376,6 +403,7 @@ function searchStringInRule(rule: FirewallRule, queryText: string) { :rules-type="rulesType" :rules="rules" :text-filter="textFilter" + :tag-filter="tagFilter" :loading-rules="loading.listRules" @reload-rules="loadData" @edit-rule="showEditRuleDrawer" diff --git a/src/components/standalone/firewall/rules/FirewallRulesTable.vue b/src/components/standalone/firewall/rules/FirewallRulesTable.vue index c0a05eeba..ce33e2cd8 100644 --- a/src/components/standalone/firewall/rules/FirewallRulesTable.vue +++ b/src/components/standalone/firewall/rules/FirewallRulesTable.vue @@ -38,6 +38,10 @@ const props = defineProps({ type: String, required: true }, + tagFilter: { + type: Array as PropType, + required: true + }, loadingRules: { type: Boolean, required: true @@ -96,13 +100,20 @@ const ruleDragged = ref() const indexOver = ref() const disabledRuleClasses = 'bg-[#fcfdfd]! dark:bg-[#18212f]!' +const isFilterActive = computed(() => !!props.textFilter || props.tagFilter.length > 0) + const filteredRules = computed(() => { - if (!props.textFilter) { + if (!isFilterActive.value) { // no filter return internalRules.value } else { // filter rules - return internalRules.value.filter((rule) => searchStringInRule(rule, props.textFilter)) + return internalRules.value.filter((rule) => { + const matchesTextFilter = !props.textFilter || searchStringInRule(rule, props.textFilter) + const matchesTagFilter = + !props.tagFilter.length || rule.ns_tag.some((tag) => props.tagFilter.includes(tag)) + return matchesTextFilter && matchesTagFilter + }) } }) @@ -309,7 +320,7 @@ function searchStringInRule(rule: FirewallRule, queryText: string) {