feat: enhance firewall log filtering with multi-select options for action and protocol, improving user experience and interaction through dynamic button states and counts
This commit is contained in:
@@ -128,30 +128,75 @@
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.firewall-filter-btn.active-blocked {
|
||||
background-color: #ef4444;
|
||||
border-color: #ef4444;
|
||||
/* DROP - Red */
|
||||
.firewall-filter-btn.active-drop {
|
||||
background-color: #dc2626;
|
||||
border-color: #dc2626;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(220, 38, 38, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-blocked .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
.firewall-filter-btn.active-drop .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.firewall-filter-btn:not(.active-blocked) .firewall-filter-btn-count.count-blocked {
|
||||
background-color: rgba(239, 68, 68, 0.1);
|
||||
color: #ef4444;
|
||||
.firewall-filter-btn:not(.active-drop) .firewall-filter-btn-count.count-drop {
|
||||
background-color: rgba(220, 38, 38, 0.1);
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.firewall-filter-btn.active-allowed {
|
||||
background-color: #22c55e;
|
||||
border-color: #22c55e;
|
||||
/* REJECT - Orange-Red */
|
||||
.firewall-filter-btn.active-reject {
|
||||
background-color: #ea580c;
|
||||
border-color: #ea580c;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(234, 88, 12, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-allowed .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
.firewall-filter-btn.active-reject .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.firewall-filter-btn:not(.active-allowed) .firewall-filter-btn-count.count-allowed {
|
||||
background-color: rgba(34, 197, 94, 0.1);
|
||||
color: #22c55e;
|
||||
.firewall-filter-btn:not(.active-reject) .firewall-filter-btn-count.count-reject {
|
||||
background-color: rgba(234, 88, 12, 0.1);
|
||||
color: #ea580c;
|
||||
}
|
||||
|
||||
/* ACCEPT - Green */
|
||||
.firewall-filter-btn.active-accept {
|
||||
background-color: #16a34a;
|
||||
border-color: #16a34a;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(22, 163, 74, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-accept .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.firewall-filter-btn:not(.active-accept) .firewall-filter-btn-count.count-accept {
|
||||
background-color: rgba(22, 163, 74, 0.1);
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
/* LOG - Cyan */
|
||||
.firewall-filter-btn.active-log {
|
||||
background-color: #0891b2;
|
||||
border-color: #0891b2;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(8, 145, 178, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-log .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.firewall-filter-btn:not(.active-log) .firewall-filter-btn-count.count-log {
|
||||
background-color: rgba(8, 145, 178, 0.1);
|
||||
color: #0891b2;
|
||||
}
|
||||
|
||||
/* Action Other - Gray */
|
||||
.firewall-filter-btn.active-action-other {
|
||||
background-color: #525252;
|
||||
border-color: #525252;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(82, 82, 82, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-action-other .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
/* Active states for Protocol filters */
|
||||
@@ -205,13 +250,90 @@
|
||||
color: #a855f7;
|
||||
}
|
||||
|
||||
.firewall-filter-btn.active-other {
|
||||
background-color: rgba(var(--color-base), 0.7);
|
||||
border-color: rgba(var(--color-base), 0.7);
|
||||
/* GRE - Teal */
|
||||
.firewall-filter-btn.active-gre {
|
||||
background-color: #0d9488;
|
||||
border-color: #0d9488;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(13, 148, 136, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-other .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
.firewall-filter-btn.active-gre .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.firewall-filter-btn:not(.active-gre) .firewall-filter-btn-count.count-gre {
|
||||
background-color: rgba(13, 148, 136, 0.1);
|
||||
color: #0d9488;
|
||||
}
|
||||
|
||||
/* ESP - Indigo */
|
||||
.firewall-filter-btn.active-esp {
|
||||
background-color: #4f46e5;
|
||||
border-color: #4f46e5;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(79, 70, 229, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-esp .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.firewall-filter-btn:not(.active-esp) .firewall-filter-btn-count.count-esp {
|
||||
background-color: rgba(79, 70, 229, 0.1);
|
||||
color: #4f46e5;
|
||||
}
|
||||
|
||||
/* AH - Violet */
|
||||
.firewall-filter-btn.active-ah {
|
||||
background-color: #7c3aed;
|
||||
border-color: #7c3aed;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(124, 58, 237, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-ah .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.firewall-filter-btn:not(.active-ah) .firewall-filter-btn-count.count-ah {
|
||||
background-color: rgba(124, 58, 237, 0.1);
|
||||
color: #7c3aed;
|
||||
}
|
||||
|
||||
/* SCTP - Pink */
|
||||
.firewall-filter-btn.active-sctp {
|
||||
background-color: #db2777;
|
||||
border-color: #db2777;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(219, 39, 119, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-sctp .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.firewall-filter-btn:not(.active-sctp) .firewall-filter-btn-count.count-sctp {
|
||||
background-color: rgba(219, 39, 119, 0.1);
|
||||
color: #db2777;
|
||||
}
|
||||
|
||||
/* IGMP - Lime */
|
||||
.firewall-filter-btn.active-igmp {
|
||||
background-color: #65a30d;
|
||||
border-color: #65a30d;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(101, 163, 13, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-igmp .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.firewall-filter-btn:not(.active-igmp) .firewall-filter-btn-count.count-igmp {
|
||||
background-color: rgba(101, 163, 13, 0.1);
|
||||
color: #65a30d;
|
||||
}
|
||||
|
||||
/* Protocol Other - Dark Gray */
|
||||
.firewall-filter-btn.active-proto-other {
|
||||
background-color: #525252;
|
||||
border-color: #525252;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(82, 82, 82, 0.3);
|
||||
}
|
||||
.firewall-filter-btn.active-proto-other .firewall-filter-btn-count {
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.firewall-filter-row {
|
||||
@@ -478,8 +600,8 @@
|
||||
sortDirection: 'desc',
|
||||
currentPage: 1,
|
||||
perPage: 10,
|
||||
actionFilter: 'all',
|
||||
protocolFilter: 'all',
|
||||
actionFilters: [],
|
||||
protocolFilters: [],
|
||||
initialize(initialLogs) {
|
||||
console.log('Initializing logs, received:', initialLogs ? initialLogs.length : 0, 'entries');
|
||||
this.logs = (initialLogs || []).map((log, index) => ({
|
||||
@@ -497,33 +619,68 @@
|
||||
}
|
||||
this.currentPage = 1;
|
||||
},
|
||||
setActionFilter(filter) {
|
||||
this.actionFilter = filter;
|
||||
toggleActionFilter(filter) {
|
||||
const idx = this.actionFilters.indexOf(filter);
|
||||
if (idx === -1) {
|
||||
this.actionFilters.push(filter);
|
||||
} else {
|
||||
this.actionFilters.splice(idx, 1);
|
||||
}
|
||||
this.currentPage = 1;
|
||||
},
|
||||
setProtocolFilter(filter) {
|
||||
this.protocolFilter = filter;
|
||||
toggleProtocolFilter(filter) {
|
||||
const idx = this.protocolFilters.indexOf(filter);
|
||||
if (idx === -1) {
|
||||
this.protocolFilters.push(filter);
|
||||
} else {
|
||||
this.protocolFilters.splice(idx, 1);
|
||||
}
|
||||
this.currentPage = 1;
|
||||
},
|
||||
clearActionFilters() {
|
||||
this.actionFilters = [];
|
||||
this.currentPage = 1;
|
||||
},
|
||||
clearProtocolFilters() {
|
||||
this.protocolFilters = [];
|
||||
this.currentPage = 1;
|
||||
},
|
||||
isActionActive(filter) {
|
||||
return this.actionFilters.includes(filter);
|
||||
},
|
||||
isProtocolActive(filter) {
|
||||
return this.protocolFilters.includes(filter);
|
||||
},
|
||||
get actionCounts() {
|
||||
const counts = { all: this.logs.length, blocked: 0, allowed: 0 };
|
||||
const counts = { all: this.logs.length, drop: 0, reject: 0, accept: 0, log: 0, other: 0 };
|
||||
this.logs.forEach(log => {
|
||||
const action = (log.action || '').toUpperCase();
|
||||
if (action === 'DROP' || action === 'REJECT' || action.includes('DROP') || action.includes('REJECT')) {
|
||||
counts.blocked++;
|
||||
if (action === 'DROP' || action.includes('DROP')) {
|
||||
counts.drop++;
|
||||
} else if (action === 'REJECT' || action.includes('REJECT')) {
|
||||
counts.reject++;
|
||||
} else if (action === 'ACCEPT' || action.includes('ACCEPT')) {
|
||||
counts.allowed++;
|
||||
counts.accept++;
|
||||
} else if (action === 'LOG' || action.includes('LOG')) {
|
||||
counts.log++;
|
||||
} else if (action) {
|
||||
counts.other++;
|
||||
}
|
||||
});
|
||||
return counts;
|
||||
},
|
||||
get protocolCounts() {
|
||||
const counts = { all: this.logs.length, tcp: 0, udp: 0, icmp: 0, other: 0 };
|
||||
const counts = { all: this.logs.length, tcp: 0, udp: 0, icmp: 0, gre: 0, esp: 0, ah: 0, sctp: 0, igmp: 0, other: 0 };
|
||||
this.logs.forEach(log => {
|
||||
const proto = (log.protocol || '').toUpperCase();
|
||||
if (proto === 'TCP') counts.tcp++;
|
||||
else if (proto === 'UDP') counts.udp++;
|
||||
else if (proto === 'ICMP' || proto === 'ICMPV6') counts.icmp++;
|
||||
else if (proto === 'GRE') counts.gre++;
|
||||
else if (proto === 'ESP') counts.esp++;
|
||||
else if (proto === 'AH') counts.ah++;
|
||||
else if (proto === 'SCTP') counts.sctp++;
|
||||
else if (proto === 'IGMP') counts.igmp++;
|
||||
else if (proto) counts.other++;
|
||||
});
|
||||
return counts;
|
||||
@@ -532,28 +689,37 @@
|
||||
const term = this.searchTerm.trim().toLowerCase();
|
||||
let entries = this.logs;
|
||||
|
||||
// Apply action filter
|
||||
if (this.actionFilter === 'blocked') {
|
||||
// Apply action filters (multi-select)
|
||||
if (this.actionFilters.length > 0) {
|
||||
entries = entries.filter(log => {
|
||||
const action = (log.action || '').toUpperCase();
|
||||
return action === 'DROP' || action === 'REJECT' || action.includes('DROP') || action.includes('REJECT');
|
||||
});
|
||||
} else if (this.actionFilter === 'allowed') {
|
||||
entries = entries.filter(log => {
|
||||
const action = (log.action || '').toUpperCase();
|
||||
return action === 'ACCEPT' || action.includes('ACCEPT');
|
||||
for (const filter of this.actionFilters) {
|
||||
if (filter === 'drop' && (action === 'DROP' || action.includes('DROP'))) return true;
|
||||
if (filter === 'reject' && (action === 'REJECT' || action.includes('REJECT'))) return true;
|
||||
if (filter === 'accept' && (action === 'ACCEPT' || action.includes('ACCEPT'))) return true;
|
||||
if (filter === 'log' && (action === 'LOG' || action.includes('LOG'))) return true;
|
||||
if (filter === 'other' && action && !['DROP', 'REJECT', 'ACCEPT', 'LOG'].some(a => action.includes(a))) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Apply protocol filter
|
||||
if (this.protocolFilter !== 'all') {
|
||||
// Apply protocol filters (multi-select)
|
||||
if (this.protocolFilters.length > 0) {
|
||||
entries = entries.filter(log => {
|
||||
const proto = (log.protocol || '').toUpperCase();
|
||||
if (this.protocolFilter === 'tcp') return proto === 'TCP';
|
||||
if (this.protocolFilter === 'udp') return proto === 'UDP';
|
||||
if (this.protocolFilter === 'icmp') return proto === 'ICMP' || proto === 'ICMPV6';
|
||||
if (this.protocolFilter === 'other') return proto && proto !== 'TCP' && proto !== 'UDP' && proto !== 'ICMP' && proto !== 'ICMPV6';
|
||||
return true;
|
||||
for (const filter of this.protocolFilters) {
|
||||
if (filter === 'tcp' && proto === 'TCP') return true;
|
||||
if (filter === 'udp' && proto === 'UDP') return true;
|
||||
if (filter === 'icmp' && (proto === 'ICMP' || proto === 'ICMPV6')) return true;
|
||||
if (filter === 'gre' && proto === 'GRE') return true;
|
||||
if (filter === 'esp' && proto === 'ESP') return true;
|
||||
if (filter === 'ah' && proto === 'AH') return true;
|
||||
if (filter === 'sctp' && proto === 'SCTP') return true;
|
||||
if (filter === 'igmp' && proto === 'IGMP') return true;
|
||||
if (filter === 'other' && proto && !['TCP', 'UDP', 'ICMP', 'ICMPV6', 'GRE', 'ESP', 'AH', 'SCTP', 'IGMP'].includes(proto)) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -662,13 +828,13 @@
|
||||
>
|
||||
<!-- Filter Tabs -->
|
||||
<div style="display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1rem;">
|
||||
<!-- Action Filter -->
|
||||
<!-- Action/Policy Filter (Multi-select) -->
|
||||
<div class="firewall-filter-row">
|
||||
<span class="firewall-filter-label">Policy:</span>
|
||||
<button
|
||||
type="button"
|
||||
@click="setActionFilter('all')"
|
||||
:class="actionFilter === 'all' ? 'active-all' : ''"
|
||||
@click="clearActionFilters()"
|
||||
:class="actionFilters.length === 0 ? 'active-all' : ''"
|
||||
class="firewall-filter-btn">
|
||||
<x-ri-list-check style="width: 14px; height: 14px;" />
|
||||
All
|
||||
@@ -676,64 +842,143 @@
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="setActionFilter('blocked')"
|
||||
:class="actionFilter === 'blocked' ? 'active-blocked' : ''"
|
||||
@click="toggleActionFilter('drop')"
|
||||
x-show="actionCounts.drop > 0"
|
||||
:class="isActionActive('drop') ? 'active-drop' : ''"
|
||||
class="firewall-filter-btn">
|
||||
<x-ri-shield-cross-line style="width: 14px; height: 14px;" />
|
||||
Blocked
|
||||
<span class="firewall-filter-btn-count count-blocked" x-text="actionCounts.blocked"></span>
|
||||
<x-ri-close-circle-line style="width: 14px; height: 14px;" />
|
||||
DROP
|
||||
<span class="firewall-filter-btn-count count-drop" x-text="actionCounts.drop"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="setActionFilter('allowed')"
|
||||
:class="actionFilter === 'allowed' ? 'active-allowed' : ''"
|
||||
@click="toggleActionFilter('reject')"
|
||||
x-show="actionCounts.reject > 0"
|
||||
:class="isActionActive('reject') ? 'active-reject' : ''"
|
||||
class="firewall-filter-btn">
|
||||
<x-ri-shield-check-line style="width: 14px; height: 14px;" />
|
||||
Allowed
|
||||
<span class="firewall-filter-btn-count count-allowed" x-text="actionCounts.allowed"></span>
|
||||
<x-ri-forbid-line style="width: 14px; height: 14px;" />
|
||||
REJECT
|
||||
<span class="firewall-filter-btn-count count-reject" x-text="actionCounts.reject"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleActionFilter('accept')"
|
||||
x-show="actionCounts.accept > 0"
|
||||
:class="isActionActive('accept') ? 'active-accept' : ''"
|
||||
class="firewall-filter-btn">
|
||||
<x-ri-checkbox-circle-line style="width: 14px; height: 14px;" />
|
||||
ACCEPT
|
||||
<span class="firewall-filter-btn-count count-accept" x-text="actionCounts.accept"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleActionFilter('log')"
|
||||
x-show="actionCounts.log > 0"
|
||||
:class="isActionActive('log') ? 'active-log' : ''"
|
||||
class="firewall-filter-btn">
|
||||
<x-ri-file-list-line style="width: 14px; height: 14px;" />
|
||||
LOG
|
||||
<span class="firewall-filter-btn-count count-log" x-text="actionCounts.log"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleActionFilter('other')"
|
||||
x-show="actionCounts.other > 0"
|
||||
:class="isActionActive('other') ? 'active-action-other' : ''"
|
||||
class="firewall-filter-btn">
|
||||
Other
|
||||
<span class="firewall-filter-btn-count" x-text="actionCounts.other"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Protocol Filter -->
|
||||
<!-- Protocol Filter (Multi-select) -->
|
||||
<div class="firewall-filter-row">
|
||||
<span class="firewall-filter-label">Protocol:</span>
|
||||
<button
|
||||
type="button"
|
||||
@click="setProtocolFilter('all')"
|
||||
:class="protocolFilter === 'all' ? 'active-proto-all' : ''"
|
||||
@click="clearProtocolFilters()"
|
||||
:class="protocolFilters.length === 0 ? 'active-proto-all' : ''"
|
||||
class="firewall-filter-btn">
|
||||
All
|
||||
<span class="firewall-filter-btn-count" x-text="protocolCounts.all"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="setProtocolFilter('tcp')"
|
||||
:class="protocolFilter === 'tcp' ? 'active-tcp' : ''"
|
||||
@click="toggleProtocolFilter('tcp')"
|
||||
x-show="protocolCounts.tcp > 0"
|
||||
:class="isProtocolActive('tcp') ? 'active-tcp' : ''"
|
||||
class="firewall-filter-btn">
|
||||
TCP
|
||||
<span class="firewall-filter-btn-count count-tcp" x-text="protocolCounts.tcp"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="setProtocolFilter('udp')"
|
||||
:class="protocolFilter === 'udp' ? 'active-udp' : ''"
|
||||
@click="toggleProtocolFilter('udp')"
|
||||
x-show="protocolCounts.udp > 0"
|
||||
:class="isProtocolActive('udp') ? 'active-udp' : ''"
|
||||
class="firewall-filter-btn">
|
||||
UDP
|
||||
<span class="firewall-filter-btn-count count-udp" x-text="protocolCounts.udp"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="setProtocolFilter('icmp')"
|
||||
:class="protocolFilter === 'icmp' ? 'active-icmp' : ''"
|
||||
@click="toggleProtocolFilter('icmp')"
|
||||
x-show="protocolCounts.icmp > 0"
|
||||
:class="isProtocolActive('icmp') ? 'active-icmp' : ''"
|
||||
class="firewall-filter-btn">
|
||||
ICMP
|
||||
<span class="firewall-filter-btn-count count-icmp" x-text="protocolCounts.icmp"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="setProtocolFilter('other')"
|
||||
@click="toggleProtocolFilter('gre')"
|
||||
x-show="protocolCounts.gre > 0"
|
||||
:class="isProtocolActive('gre') ? 'active-gre' : ''"
|
||||
class="firewall-filter-btn">
|
||||
GRE
|
||||
<span class="firewall-filter-btn-count count-gre" x-text="protocolCounts.gre"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleProtocolFilter('esp')"
|
||||
x-show="protocolCounts.esp > 0"
|
||||
:class="isProtocolActive('esp') ? 'active-esp' : ''"
|
||||
class="firewall-filter-btn">
|
||||
ESP
|
||||
<span class="firewall-filter-btn-count count-esp" x-text="protocolCounts.esp"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleProtocolFilter('ah')"
|
||||
x-show="protocolCounts.ah > 0"
|
||||
:class="isProtocolActive('ah') ? 'active-ah' : ''"
|
||||
class="firewall-filter-btn">
|
||||
AH
|
||||
<span class="firewall-filter-btn-count count-ah" x-text="protocolCounts.ah"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleProtocolFilter('sctp')"
|
||||
x-show="protocolCounts.sctp > 0"
|
||||
:class="isProtocolActive('sctp') ? 'active-sctp' : ''"
|
||||
class="firewall-filter-btn">
|
||||
SCTP
|
||||
<span class="firewall-filter-btn-count count-sctp" x-text="protocolCounts.sctp"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleProtocolFilter('igmp')"
|
||||
x-show="protocolCounts.igmp > 0"
|
||||
:class="isProtocolActive('igmp') ? 'active-igmp' : ''"
|
||||
class="firewall-filter-btn">
|
||||
IGMP
|
||||
<span class="firewall-filter-btn-count count-igmp" x-text="protocolCounts.igmp"></span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleProtocolFilter('other')"
|
||||
x-show="protocolCounts.other > 0"
|
||||
:class="protocolFilter === 'other' ? 'active-other' : ''"
|
||||
:class="isProtocolActive('other') ? 'active-proto-other' : ''"
|
||||
class="firewall-filter-btn">
|
||||
Other
|
||||
<span class="firewall-filter-btn-count" x-text="protocolCounts.other"></span>
|
||||
|
||||
Reference in New Issue
Block a user