Compare commits

..

14 commits

16 changed files with 126 additions and 54 deletions

View file

@ -3,33 +3,25 @@ const props = withDefaults(defineProps<{
data: any[],
component?: string,
componentProp?: '',
ellipsis?: number
ellipsis?: number,
empty?: string
}>(), {
data: () => [],
component: '',
componentProp: '',
ellipsis: 2,
ellipsis: 10,
empty: '',
});
</script>
<template>
<div class="pillbar">
<div v-if="props.data.length != 0" class="pillstack">
<div v-for="(item, index) of props.data.slice(0, ellipsis)" :key="index" class="pill">
<component v-bind="{[props.componentProp]: item}" :is="props.component" v-if="props.component !== ''"/>
<template v-else>{{ item }}</template>
</div>
<div v-if="props.data.length >= props.ellipsis" class="pill">...</div>
</div>
<div v-else>
{{ props.empty }}
</div>
</template>
<style scoped>
.pillbar {
flex-direction: row;
flex-wrap: wrap;
gap: 0.25rem;
}
.pill {
border: 1px solid var(--cl-fg);
padding: 0.25rem;
}
</style>

View file

@ -0,0 +1,19 @@
<script setup lang="ts">
const props = withDefaults(defineProps<{
data: any,
component?: string,
componentProp?: '',
}>(), {
data: '',
component: '',
componentProp: '',
});
</script>
<template>
<div v-if="data" class="pillbar">
<div class="pill">
<component v-bind="{[props.componentProp]: props.data}" :is="props.component" v-if="props.component !== ''"/>
<template v-else>{{ props.data }}</template>
</div>
</div>
</template>

View file

@ -0,0 +1,12 @@
<script setup lang="ts">
import { variantOf } from '~/util';
const props = withDefaults(defineProps<{
data: object | string,
}>(), {
data: '',
});
</script>
<template>
{{ variantOf(props.data) }}
</template>

View file

@ -108,8 +108,11 @@ function dragDropRow() {
data.splice(draggedRow, 1);
data.splice(draggedOverRow, 0, row);
data = data;
// Don't emit if we are at the same spot
if (draggedRow !== draggedOverRow){
emit('draggedRow', draggedRow, draggedOverRow);
}
}
// Reset Drag & Remove Selection
draggedRow = 0;

View file

@ -1,5 +1,5 @@
<script lang="ts">
import { Index, MaybeIndex, equals } from '../../util';
import { Index, MaybeIndex, equals, variantOf } from '../../util';
import { Fields } from './NicerForm.vue';
export type Variant = {
@ -34,7 +34,7 @@ const emit = defineEmits<{
}>();
// Local Variables for Two-Way bindings
let modelValue: MaybeEnumValue = $ref(null);
let modelValue = $ref(null as MaybeEnumValue);
// Sync from v-model
onMounted(() => {
watch(() => props.modelValue, (val) => {
@ -49,8 +49,14 @@ onMounted(() => {
}, { deep: true, immediate: true });
});
// Sync to v-model
watch($$(modelValue), (val) => {
watch($$(modelValue), (val, oldVal) => {
if (equals(val, props.modelValue)) return;
const [oldVariant, newVariant] = [variantOf(oldVal), variantOf(val)];
// Wipe variant values if variant definitions change
if (typeof val === 'object'
&& val && newVariant && oldVariant
&& !equals(props.variants[newVariant].fields, props.variants[oldVariant].fields))
val[newVariant] = formValue = {};
emit('update:modelValue', typeof val === 'string' ? val : Object.assign({}, val));
}, { deep: true });

View file

@ -121,3 +121,20 @@ tbody tr:hover, th:hover {
tbody tr.selected {
background-color: var(--cl-table-sl)
}
.pillbar {
flex-direction: row;
flex-wrap: wrap;
gap: 0.25rem;
}
.pillstack {
align-content: flex-start;
align-items: flex-start;
gap: 0.25rem;
}
.pill {
border: 1px solid var(--cl-fg);
padding: 0.25rem;
}

View file

@ -1,6 +1,8 @@
<script setup lang="ts">
import { apiCall } from '../../api';
import getPlugins from '../../plugins';
import ArrayDisplay from '~/components/display/ArrayDisplay.vue';
import ElementDisplay from '~/components/display/ElementDisplay.vue';
const p = getPlugins();
let rules = $ref([]);
@ -9,11 +11,11 @@ let selection = $ref([] as number[]);
const columns = [
{ heading: 'Name', path: 'name' },
{ heading: 'Source', path: 'source_addresses' },
{ heading: 'Destination', path: 'destination_addresses' },
{ heading: 'Service', path: 'services' },
{ heading: 'Translated Address', path: 'dnat_address' },
{ heading: 'Translated Service', path: 'dnat_service' },
{ heading: 'Source', path: 'source_addresses', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Destination', path: 'destination_addresses', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Service', path: 'services', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Translated Address', path: 'dnat_address', component: markRaw(ElementDisplay), componentProp: 'data' },
{ heading: 'Translated Service', path: 'dnat_service', component: markRaw(ElementDisplay), componentProp: 'data' },
{ heading: 'Counter', path: 'counter' },
{ heading: 'Comment', path: 'comment' },
];
@ -59,7 +61,7 @@ onMounted(async() => {
<template>
<div>
<TableView v-model:selection="selection" v-model:data="rules" title="DNAT Rules" :columns="columns" :loading="loading" :table-props="{sort:true, sortSelf: true, draggable: true}" @dragged-row="draggedRow">
<TableView v-model:selection="selection" v-model:data="rules" title="DNAT Rules" :columns="columns" :loading="loading" :table-props="{draggable: true}" @dragged-row="draggedRow">
<button @click="load">Refresh</button>
<router-link class="button" to="/firewall/destination_nat_rules/edit">Create</router-link>
<router-link class="button" :class="{ disabled: selection.length != 1 }" :to="'/firewall/destination_nat_rules/edit/' + selection[0]">Edit</router-link>

View file

@ -15,6 +15,7 @@ const columns = [
{ heading: 'Service', path: 'services', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Verdict', path: 'verdict' },
{ heading: 'Counter', path: 'counter' },
{ heading: 'Log', path: 'log' },
{ heading: 'Comment', path: 'comment' },
];
@ -59,7 +60,7 @@ onMounted(async() => {
<template>
<div>
<TableView v-model:selection="selection" v-model:data="rules" title="Forward Rules" :columns="columns" :loading="loading" :table-props="{sort:true, sortSelf: true, draggable: true}" @dragged-row="draggedRow">
<TableView v-model:selection="selection" v-model:data="rules" title="Forward Rules" :columns="columns" :loading="loading" :table-props="{draggable: true}" @dragged-row="draggedRow">
<button @click="load">Refresh</button>
<router-link class="button" to="/firewall/forward_rules/edit">Create</router-link>
<router-link class="button" :class="{ disabled: selection.length != 1 }" :to="'/firewall/forward_rules/edit/' + selection[0]">Edit</router-link>

View file

@ -1,6 +1,10 @@
<script setup lang="ts">
import { apiCall } from '../../api';
import getPlugins from '../../plugins';
import ArrayDisplay from '~/components/display/ArrayDisplay.vue';
import ElementDisplay from '~/components/display/ElementDisplay.vue';
import EnumTypeDisplay from '~/components/display/EnumTypeDisplay.vue';
const p = getPlugins();
let rules = $ref([]);
@ -9,11 +13,12 @@ let selection = $ref([] as number[]);
const columns = [
{ heading: 'Name', path: 'name' },
{ heading: 'Source', path: 'source_addresses' },
{ heading: 'Destination', path: 'destination_addresses' },
{ heading: 'Service', path: 'services' },
{ heading: 'Translated Address', path: 'snat_type.snat.address' },
{ heading: 'Translated Service', path: 'snat_type.snat.service' },
{ heading: 'Source', path: 'source_addresses', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Destination', path: 'destination_addresses', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Service', path: 'services', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Type', path: 'snat_type', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
{ heading: 'Translated Address', path: 'snat_type.snat.address', component: markRaw(ElementDisplay), componentProp: 'data' },
{ heading: 'Translated Service', path: 'snat_type.snat.service', component: markRaw(ElementDisplay), componentProp: 'data' },
{ heading: 'Counter', path: 'counter' },
{ heading: 'Comment', path: 'comment' },
];
@ -59,7 +64,7 @@ onMounted(async() => {
<template>
<div>
<TableView v-model:selection="selection" v-model:data="rules" title="SNAT Rules" :columns="columns" :loading="loading" :table-props="{sort:true, sortSelf: true, draggable: true}" @dragged-row="draggedRow">
<TableView v-model:selection="selection" v-model:data="rules" title="SNAT Rules" :columns="columns" :loading="loading" :table-props="{draggable: true}" @dragged-row="draggedRow">
<button @click="load">Refresh</button>
<router-link class="button" to="/firewall/source_nat_rules/edit">Create</router-link>
<router-link class="button" :class="{ disabled: selection.length != 1 }" :to="'/firewall/source_nat_rules/edit/' + selection[0]">Edit</router-link>

View file

@ -1,6 +1,8 @@
<script setup lang="ts">
import { apiCall } from '../../api';
import getPlugins from '../../plugins';
import ArrayDisplay from '~/components/display/ArrayDisplay.vue';
import EnumTypeDisplay from '~/components/display/EnumTypeDisplay.vue';
const p = getPlugins();
let interfaces = $ref({});
@ -9,10 +11,15 @@ let selection = $ref([] as number[]);
const columns = [
{ heading: 'Name', path: 'name' },
{ heading: 'Type', path: 'type' },
{ heading: 'Members', path: 'members' },
{ heading: 'Addressing Mode', path: 'addressing_mode' },
{ heading: 'Address', path: 'address' },
{ heading: 'Alias', path: 'alias' },
{ heading: 'Type', path: 'interface_type', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
{ heading: 'Addressing Mode', path: 'addressing_mode', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
{ heading: 'Address', path: 'addressing_mode.static.address' },
{ heading: 'Vlan Parent', path: 'interface_type.vlan.parent' },
{ heading: 'Vlan ID', path: 'interface_type.vlan.id' },
{ heading: 'Bond Members', path: 'interface_type.bond.members', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Bridge Members', path: 'interface_type.bridge.members', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Comment', path: 'comment' },
];
const displayData = $computed(() => {
@ -21,7 +28,8 @@ const displayData = $computed(() => {
for (const index in interfaces) {
data.push({
name: interfaces[index].name,
type: interfaces[index].type,
alias: interfaces[index].alias,
interface_type: interfaces[index].interface_type,
addressing_mode: interfaces[index].addressing_mode,
address: interfaces[index].address,
comment: interfaces[index].comment,

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { apiCall } from '../../api';
import getPlugins from '../../plugins';
import EnumTypeDisplay from '~/components/display/EnumTypeDisplay.vue';
const p = getPlugins();
let addresses = $ref([]);
@ -9,7 +10,7 @@ let selection = $ref([] as number[]);
const columns = [
{ heading: 'Name', path: 'name' },
{ heading: 'Type', path: 'type' },
{ heading: 'Type', path: 'type', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
{ heading: 'Value', path: 'value' },
{ heading: 'Comment', path: 'comment' },
];
@ -33,7 +34,7 @@ const displayData = $computed(() => {
data.push({
name: addresses[index].name,
value: getAddressValue(addresses[index]),
type: addresses[index].type,
type: addresses[index].address_type,
comment: addresses[index].comment,
});
}
@ -42,7 +43,7 @@ const displayData = $computed(() => {
function getAddressValue(s: any): string {
let value: string;
switch (s.type) {
switch (s.address_type) {
case 'host':
value = s.host;
break;

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { apiCall } from '../../api';
import getPlugins from '../../plugins';
import EnumTypeDisplay from '~/components/display/EnumTypeDisplay.vue';
const p = getPlugins();
let services = $ref({});
@ -9,7 +10,7 @@ let selection = $ref([] as number[]);
const columns = [
{ heading: 'Name', path: 'name' },
{ heading: 'Type', path: 'type' },
{ heading: 'Type', path: 'type', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
{ heading: 'Value', path: 'value' },
{ heading: 'Comment', path: 'comment' },
];

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { apiCall } from '../../api';
import getPlugins from '../../plugins';
import ArrayDisplay from '~/components/display/ArrayDisplay.vue';
const p = getPlugins();
let servers = $ref([]);
@ -9,6 +10,7 @@ let selection = $ref([] as number[]);
const columns = [
{ heading: 'Interface', path: 'interface' },
{ heading: 'Pool', path: 'pool', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Comment', path: 'comment' },
];

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { apiCall } from '../../api';
import getPlugins from '../../plugins';
import ArrayDisplay from '~/components/display/ArrayDisplay.vue';
const p = getPlugins();
let interfaces = $ref({});
@ -10,7 +11,7 @@ let selection = $ref([] as number[]);
const columns = [
{ heading: 'Name', path: 'name' },
{ heading: 'Listen Port', path: 'listen_port' },
{ heading: 'Peers', path: 'peers' },
{ heading: 'Peers', path: 'peers', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Comment', path: 'comment' },
];

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { apiCall } from '../../api';
import getPlugins from '../../plugins';
import ArrayDisplay from '~/components/display/ArrayDisplay.vue';
const p = getPlugins();
let peers = $ref({});
@ -9,7 +10,7 @@ let selection = $ref([] as number[]);
const columns = [
{ heading: 'Name', path: 'name' },
{ heading: 'Allowed IPs', path: 'allowed_ips' },
{ heading: 'Allowed IPs', path: 'allowed_ips', component: markRaw(ArrayDisplay), componentProp: 'data' },
{ heading: 'Endpoint', path: 'endpoint' },
{ heading: 'Persistent Keepalive', path: 'persistent_keepalive' },
{ heading: 'Comment', path: 'comment' },

View file

@ -16,6 +16,7 @@ export function isNullish(value: any) {
}
export function variantOf(enumValue: any) {
if (enumValue === null) return null;
if (typeof enumValue === 'string') return enumValue;
else return Object.entries(enumValue)[0][0];
}