Fetch new eventlogs when the user scrolls to the bottom
This commit is contained in:
@@ -1,11 +1,7 @@
|
||||
<script setup>
|
||||
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const i18n = useI18n();
|
||||
const t = i18n.t;
|
||||
|
||||
import { ref, reactive, onMounted, watch } from 'vue';
|
||||
import { Button, ButtonGroup, Dropdown, Spinner, TextInput, MultiSelect } from 'pankow';
|
||||
import { Button, Spinner, TextInput, MultiSelect } from 'pankow';
|
||||
import { useDebouncedRef, prettyDate, prettyLongDate } from 'pankow/utils';
|
||||
import AppsModel from '../models/AppsModel.js';
|
||||
import EventlogsModel from '../models/EventlogsModel.js';
|
||||
@@ -86,12 +82,6 @@ const availableActions = [
|
||||
{ id: 'volume.remove' },
|
||||
];
|
||||
|
||||
const perPageOptions = [
|
||||
{ name: t('main.pagination.perPageSelector', { n: 20 }), value: 20 },
|
||||
{ name: t('main.pagination.perPageSelector', { n: 50 }), value: 50 },
|
||||
{ name: t('main.pagination.perPageSelector', { n: 100 }), value: 100 }
|
||||
];
|
||||
|
||||
const busy = ref(false);
|
||||
const refreshBusy = ref(false);
|
||||
const apps = ref([]);
|
||||
@@ -99,7 +89,7 @@ const eventlogs = ref([]);
|
||||
const activeId = ref(null);
|
||||
const search = useDebouncedRef('');
|
||||
const page = ref(1);
|
||||
const perPage = ref(20);
|
||||
const perPage = ref(40);
|
||||
const actions = reactive([]);
|
||||
|
||||
async function onRefresh() {
|
||||
@@ -124,18 +114,23 @@ function onEventLogDetails(id) {
|
||||
else activeId.value = id;
|
||||
}
|
||||
|
||||
async function onPrevPage() {
|
||||
if (page.value > 1) page.value--;
|
||||
else page.value = 1;
|
||||
await onRefresh();
|
||||
async function onScroll(event) {
|
||||
if (event.target.scrollTop + event.target.clientHeight >= event.target.scrollHeight) {
|
||||
page.value++;
|
||||
const [error, result] = await eventlogsModel.search(actions.join(','), search.value, page.value, perPage.value);
|
||||
if (error) return console.error(error);
|
||||
|
||||
eventlogs.value = eventlogs.value.concat(result.map(e => {
|
||||
return {
|
||||
id: Symbol(),
|
||||
raw: e,
|
||||
details: eventlogDetails(e, e.appId ? getApp(e.appId) : null),
|
||||
source: eventlogSource(e, e.appId ? getApp(e.appId) : null)
|
||||
};
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
async function onNextPage() {
|
||||
page.value++;
|
||||
await onRefresh();
|
||||
}
|
||||
|
||||
watch(perPage, onRefresh);
|
||||
watch(actions, onRefresh);
|
||||
watch(search, onRefresh);
|
||||
|
||||
@@ -153,52 +148,84 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="content">
|
||||
<h1>{{ $t('eventlog.title') }}</h1>
|
||||
|
||||
<div class="eventlog-filter">
|
||||
<TextInput :placeholder="$t('main.searchPlaceholder')" style="flex-grow: 1;" v-model="search"/>
|
||||
|
||||
<MultiSelect v-model="actions" :options="availableActions" option-label="id" :selected-label="actions.length ? $t('main.multiselect.selected', { n: actions.length }) : $t('eventlog.filterAllEvents')"/>
|
||||
<Dropdown v-model="perPage" :options="perPageOptions" option-key="value" option-label="name"/>
|
||||
|
||||
<ButtonGroup>
|
||||
<Button tool secondary @click="onPrevPage()" :disabled="busy || page <= 1" icon="fa-solid fa-angle-double-left" v-tooltip="$t('main.pagination.prev')" />
|
||||
<div class="content" style="overflow: hidden; display: flex; flex-direction: column;">
|
||||
<h1 class="view-header">
|
||||
{{ $t('eventlog.title') }}
|
||||
<div style="">
|
||||
<TextInput :placeholder="$t('main.searchPlaceholder')" style="flex-grow: 1;" v-model="search"/>
|
||||
<MultiSelect v-model="actions" :options="availableActions" option-label="id" :selected-label="actions.length ? $t('main.multiselect.selected', { n: actions.length }) : $t('eventlog.filterAllEvents')"/>
|
||||
<Button tool secondary @click="onRefresh()" :loading="refreshBusy" icon="fa-solid fa-sync-alt" />
|
||||
<Button tool secondary @click="onNextPage()" :disabled="busy || perPage > eventlogs.length" icon="fa-solid fa-angle-double-right" v-tooltip="$t('main.pagination.next')" />
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<center v-show="busy"><Spinner class="pankow-spinner-large" /></center>
|
||||
<table v-show="!busy" class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('eventlog.time') }}</th>
|
||||
<th>{{ $t('eventlog.source') }}</th>
|
||||
<th>{{ $t('eventlog.details') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="eventlog in eventlogs" :key="eventlog.id">
|
||||
<tr @click="onEventLogDetails(eventlog.id)" class="hand" >
|
||||
<td style="white-space: nowrap;"><span v-tooltip="prettyLongDate(eventlog.raw.creationTime)" class="arrow">{{ prettyDate(eventlog.raw.creationTime) }}</span></td>
|
||||
<td>{{ eventlog.source }}</td>
|
||||
<td v-html="eventlog.details"></td>
|
||||
<div style="overflow: auto; position: relative; margin-bottom: 10px;" @scroll="onScroll">
|
||||
<table v-show="!busy" class="eventlog-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('eventlog.time') }}</th>
|
||||
<th>{{ $t('eventlog.source') }}</th>
|
||||
<th>{{ $t('eventlog.details') }}</th>
|
||||
</tr>
|
||||
<tr v-show="activeId === eventlog.id">
|
||||
<td colspan="4">
|
||||
<p v-show="eventlog.raw.source.ip">Source IP: <code>{{ eventlog.raw.source.ip }}</code></p>
|
||||
<pre class="eventlog-details">{{ JSON.stringify(eventlog.raw.data, null, 4) }}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="eventlog in eventlogs" :key="eventlog.id">
|
||||
<tr @click="onEventLogDetails(eventlog.id)" :class="{ 'active': activeId === eventlog.id }" >
|
||||
<td style="white-space: nowrap;"><span v-tooltip="prettyLongDate(eventlog.raw.creationTime)" class="arrow">{{ prettyDate(eventlog.raw.creationTime) }}</span></td>
|
||||
<td>{{ eventlog.source }}</td>
|
||||
<td v-html="eventlog.details"></td>
|
||||
</tr>
|
||||
<tr v-show="activeId === eventlog.id">
|
||||
<td colspan="4" class="eventlog-details">
|
||||
<p v-show="eventlog.raw.source.ip">Source IP: <code>{{ eventlog.raw.source.ip }}</code></p>
|
||||
<pre>{{ JSON.stringify(eventlog.raw.data, null, 4) }}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.eventlog-table {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
border-spacing: 0px;
|
||||
}
|
||||
|
||||
.eventlog-table thead {
|
||||
background-color: var(--pankow-body-background-color);
|
||||
top: 0;
|
||||
position: sticky;
|
||||
z-index: 1; /* avoids see-through table headers if items in the table have opacity set */
|
||||
}
|
||||
|
||||
.eventlog-table th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.eventlog-table tbody tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.eventlog-table tbody tr.active,
|
||||
.eventlog-table tbody tr:hover {
|
||||
background-color: var(--pankow-color-background-hover);
|
||||
}
|
||||
|
||||
.eventlog-table tbody tr.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.eventlog-table th,
|
||||
.eventlog-table td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.eventlog-filter {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
@@ -207,9 +234,16 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.eventlog-details {
|
||||
background-color: var(--pankow-color-background-hover);
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.eventlog-details pre {
|
||||
white-space: pre-wrap;
|
||||
color: var(--pankow-text-color);
|
||||
background-color: var(--pankow-input-background-color);
|
||||
font-size: 13px;
|
||||
padding: 6px;
|
||||
margin: 0;
|
||||
border: none;
|
||||
border-radius: var(--pankow-border-radius);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user