mirror of
https://github.com/speatzle/nfsense.git
synced 2025-05-11 19:08:20 +00:00
add table selection, rework column definitions
This commit is contained in:
parent
0f306a7705
commit
3847a89594
2 changed files with 85 additions and 9 deletions
|
@ -1,15 +1,33 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useKeyModifier } from '@vueuse/core';
|
||||||
|
|
||||||
|
const shiftState = $(useKeyModifier('Shift'));
|
||||||
|
const ctrlState = $(useKeyModifier('Control'));
|
||||||
|
|
||||||
const props = defineModel<{
|
const props = defineModel<{
|
||||||
columns?: Record<string, string>,
|
columns?: {
|
||||||
|
heading: string,
|
||||||
|
path: string,
|
||||||
|
component?: Component,
|
||||||
|
}[],
|
||||||
data?: Record<string, any>[],
|
data?: Record<string, any>[],
|
||||||
|
sort?: boolean,
|
||||||
sortSelf?: boolean,
|
sortSelf?: boolean,
|
||||||
sortBy?: string,
|
sortBy?: string,
|
||||||
sortDesc?: boolean,
|
sortDesc?: boolean,
|
||||||
}>();
|
}>();
|
||||||
let { columns, data, sortSelf, sortBy, sortDesc } = $(props);
|
let { columns, data, sort, sortSelf, sortBy, sortDesc } = $(props);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'rowAction', index: number): void,
|
||||||
|
(event: 'selectionChanged'): void
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let selection = $ref([] as number[]);
|
||||||
|
|
||||||
const displayData = $computed(() => (sortSelf && sortBy !== '')
|
const displayData = $computed(() => (sortSelf && sortBy !== '')
|
||||||
? data?.sort((a, b) => {
|
? data?.sort((a, b) => {
|
||||||
|
selection = [];
|
||||||
let result;
|
let result;
|
||||||
if (a[sortBy ?? ''] > b[sortBy ?? '']) result = 1;
|
if (a[sortBy ?? ''] > b[sortBy ?? '']) result = 1;
|
||||||
else if (a[sortBy ?? ''] === b[sortBy ?? '']) result = 0;
|
else if (a[sortBy ?? ''] === b[sortBy ?? '']) result = 0;
|
||||||
|
@ -21,35 +39,84 @@ const displayData = $computed(() => (sortSelf && sortBy !== '')
|
||||||
: data);
|
: data);
|
||||||
|
|
||||||
function toggleSorting(columnName: string) {
|
function toggleSorting(columnName: string) {
|
||||||
|
if (!sort) return;
|
||||||
if (columnName === sortBy) sortDesc = !sortDesc;
|
if (columnName === sortBy) sortDesc = !sortDesc;
|
||||||
else {
|
else {
|
||||||
sortDesc = false;
|
sortDesc = false;
|
||||||
sortBy = columnName;
|
sortBy = columnName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rowSelection(index: number) {
|
||||||
|
if (shiftState) {
|
||||||
|
let last = selection[selection.length-1];
|
||||||
|
let start = index;
|
||||||
|
let end = last;
|
||||||
|
if (last < start) {
|
||||||
|
start = last;
|
||||||
|
end = index;
|
||||||
|
}
|
||||||
|
for (let i = start; i < end; i++ ) {
|
||||||
|
if (!selection.includes(i)) {
|
||||||
|
selection = [...selection, i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ctrlState) {
|
||||||
|
if (selection.includes(index)) {
|
||||||
|
// remove if already exists
|
||||||
|
selection.splice(selection.indexOf(index), 1);
|
||||||
|
} else {
|
||||||
|
selection = [...selection, index];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (selection.includes(index)) {
|
||||||
|
selection = [];
|
||||||
|
} else {
|
||||||
|
selection = [index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit("selectionChanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
function atPath(value: any, path: string): any {
|
||||||
|
for (const segment of path.split(".")) {
|
||||||
|
value = value[segment];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th v-for="[name, heading] in Object.entries(columns ?? {})" :key="name" @click="toggleSorting(name)">
|
<th v-for="{heading, path} of columns" :key="path" @click="() => toggleSorting(path)">
|
||||||
<div class="flex-row">
|
<div class="flex-row">
|
||||||
{{ heading }}
|
{{ heading }}
|
||||||
<i-mdi-arrow-down v-if="name === sortBy && sortDesc"/>
|
<template v-if="sort">
|
||||||
<i-mdi-arrow-up v-else-if="name === sortBy"/>
|
<i-mdi-arrow-down v-if="path === sortBy && sortDesc"/>
|
||||||
|
<i-mdi-arrow-up v-else-if="path === sortBy"/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<!-- eslint-disable-next-line vue/require-v-for-key -->
|
<tr v-for="(row, index) in displayData" :key="index"
|
||||||
<tr v-for="row of displayData">
|
@click="() => rowSelection(index)"
|
||||||
<td v-for="[column] in Object.entries(columns ?? {})" :key="column" v-text="row[column]"/>
|
@dblclick="() => emit('rowAction', index)"
|
||||||
|
:class="{'selected': selection.includes(index)}">
|
||||||
|
<td v-for="{path, component} of columns" :key="path">
|
||||||
|
{{ component ? "" : atPath(row, path) }}
|
||||||
|
<component v-if="component" :is="component"/>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.selected {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -2,6 +2,15 @@
|
||||||
import { apiCall } from "../../api";
|
import { apiCall } from "../../api";
|
||||||
|
|
||||||
let rules = $ref([]);
|
let rules = $ref([]);
|
||||||
|
const columns = [
|
||||||
|
{heading: 'Name', path: 'name'},
|
||||||
|
{heading: 'Source', path: 'match.source_addresses'},
|
||||||
|
{heading: 'Destination', path: 'match.destination_addresses'},
|
||||||
|
{heading: 'Service', path: 'match.services'},
|
||||||
|
{heading: 'Verdict', path: 'verdict'},
|
||||||
|
{heading: 'Counter', path: 'counter'},
|
||||||
|
{heading: 'Comment', path: 'comment'},
|
||||||
|
];
|
||||||
|
|
||||||
async function loadRules(){
|
async function loadRules(){
|
||||||
let res = await apiCall("Firewall.GetForwardRules", {});
|
let res = await apiCall("Firewall.GetForwardRules", {});
|
||||||
|
@ -24,6 +33,6 @@ onMounted(async() => {
|
||||||
<PageHeader title="Forward Rules">
|
<PageHeader title="Forward Rules">
|
||||||
<button @click="loadRules">Load Rules</button>
|
<button @click="loadRules">Load Rules</button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<NiceTable :columns="{name: 'Name', verdict: 'Verdict'}" :sort-self="false" v-model:data="rules"/>
|
<NiceTable :columns="columns" v-model:data="rules"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
Loading…
Add table
Reference in a new issue