Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/Frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"codemirror": "^6.0.1",
"hex-to-css-filter": "^6.0.0",
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.0.8",
"lossless-json": "^4.0.2",
"memoize-one": "^6.0.0",
"moment": "^2.30.1",
Expand Down
113 changes: 58 additions & 55 deletions src/Frontend/src/components/RefreshConfig.vue
Original file line number Diff line number Diff line change
@@ -1,86 +1,89 @@
<script setup lang="ts">
import { ref } from "vue";
import OnOffSwitch from "./OnOffSwitch.vue";
import { ref, watch } from "vue";
import ListFilterSelector from "@/components/audit/ListFilterSelector.vue";

const props = defineProps<{
id: string;
initialTimeout?: number;
onManualRefresh: () => void;
}>();
const props = defineProps<{ isLoading: boolean }>();
const model = defineModel<number | null>({ required: true });
const emit = defineEmits<{ (e: "manualRefresh"): Promise<void> }>();
const autoRefreshOptionsText = ["Off", "Every 5 seconds", "Every 15 seconds", "Every 30 seconds"];
let selectValue = "Off";

const emit = defineEmits<{ change: [newValue: number | null]; manualRefresh: [] }>();

const autoRefresh = ref(props.initialTimeout != null);
const refreshTimeout = ref(props.initialTimeout ?? 5);

function toggleRefresh() {
autoRefresh.value = !autoRefresh.value;
updateTimeout();
if (model.value === 5000) {
selectValue = "Every 5 seconds";
}

function updateTimeout() {
validateTimeout();
emit("change", autoRefresh.value ? refreshTimeout.value * 1000 : null);
if (model.value === 15000) {
selectValue = "Every 15 seconds";
}
if (model.value === 30000) {
selectValue = "Every 30 seconds";
}

const selectedRefresh = ref<string>(selectValue);

watch(selectedRefresh, (newValue) => {
if (newValue === autoRefreshOptionsText[0]) {
model.value = null;
}
if (newValue === autoRefreshOptionsText[1]) {
model.value = 5000;
}
if (newValue === autoRefreshOptionsText[2]) {
model.value = 15000;
}
if (newValue === autoRefreshOptionsText[3]) {
model.value = 30000;
}
});

function validateTimeout() {
refreshTimeout.value = Math.max(1, Math.min(600, refreshTimeout.value));
async function refresh() {
await emit("manualRefresh");
}
</script>

<template>
<div class="refresh-config">
<button class="fa" title="refresh" @click="() => emit('manualRefresh')">
<i class="fa fa-lg fa-refresh" />
</button>
<span>|</span>
<label>Auto-Refresh:</label>
<div>
<OnOffSwitch :id="id" @toggle="toggleRefresh" :value="autoRefresh" />
<button class="btn btn-sm" title="refresh" @click="refresh"><i class="fa fa-refresh" :class="{ spinning: props.isLoading }" /> Refresh List</button>
<div class="filter">
<div class="filter-label">Auto-Refresh:</div>
<div class="filter-component">
<ListFilterSelector :items="autoRefreshOptionsText" v-model="selectedRefresh" item-name="result" :can-clear="false" :show-clear="false" :show-filter="false" />
</div>
</div>
<input type="number" v-model="refreshTimeout" min="1" max="600" v-on:change="updateTimeout" />
<span class="unit">s</span>
</div>
</template>

<style scoped>
.refresh-config {
display: flex;
align-items: center;
gap: 0.5em;
gap: 1em;
margin-bottom: 0.5em;
}

.refresh-config .unit {
margin-left: -0.45em;
}

.refresh-config label {
margin: 0;
}

.refresh-config input {
width: 3.5em;
.filter {
display: flex;
align-items: center;
}

.refresh-config button {
background: none;
border: none;
width: 2em;
.filter-label {
font-weight: bold;
}

.refresh-config button .fa {
transition: all 0.15s ease-in-out;
transition: rotate 0.05s ease-in-out;
transform-origin: center;
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

.refresh-config button:hover .fa {
color: #00a3c4;
transform: scale(1.1);
.fa-refresh {
display: inline-block;
}

.refresh-config button:active .fa {
transform: rotate(25deg);
text-shadow: #929e9e 0.25px 0.25px;
/* You can add this class dynamically when needed */
.fa-refresh.spinning {
animation: spin 1s linear infinite;
}
</style>
32 changes: 27 additions & 5 deletions src/Frontend/src/components/audit/AuditList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,33 @@ import ResultsCount from "@/components/ResultsCount.vue";
import { dotNetTimespanToMilliseconds, formatDotNetTimespan } from "@/composables/formatUtils.ts";
import "@vuepic/vue-datepicker/dist/main.css";
import FiltersPanel from "@/components/audit/FiltersPanel.vue";
import { onBeforeMount, watch } from "vue";
import { onBeforeMount, onUnmounted, ref, watch } from "vue";
import RefreshConfig from "../RefreshConfig.vue";
import useAutoRefresh from "@/composables/autoRefresh.ts";
import throttle from "lodash/throttle";

const store = useAuditStore();
const { messages, totalCount, sortBy, messageFilterString, selectedEndpointName, itemsPerPage, dateRange } = storeToRefs(store);
const route = useRoute();
const router = useRouter();
const autoRefreshValue = ref<number | null>(null);
const isLoading = ref(false);

const dataRetriever = useAutoRefresh(
throttle(async () => {
isLoading.value = true;
try {
await store.refresh();
} finally {
isLoading.value = false;
}
}, 2000),
null
);

onUnmounted(() => {
dataRetriever.updateTimeout(null);
});

function statusToName(messageStatus: MessageStatus) {
switch (messageStatus) {
Expand Down Expand Up @@ -78,7 +98,7 @@ onBeforeMount(() => {

//without setTimeout, this happens before the store is properly initialised, and therefore the query route values aren't applied to the refresh
//TODO: is there a better way to achieve this?
setTimeout(async () => await Promise.all([store.refresh(), store.loadEndpoints()]), 0);
setTimeout(async () => await Promise.all([dataRetriever.executeAndResetTimer(), store.loadEndpoints()]), 0);

firstLoad = false;
});
Expand All @@ -87,7 +107,7 @@ watch(
() => router.currentRoute.value.query,
async () => {
setQuery();
await store.refresh();
await dataRetriever.executeAndResetTimer();
},
{ deep: true }
);
Expand All @@ -113,7 +133,7 @@ const watchHandle = watch([() => route.query, itemsPerPage, sortBy, messageFilte
},
});

await store.refresh();
await dataRetriever.executeAndResetTimer();
});

function setQuery() {
Expand All @@ -132,12 +152,14 @@ function setQuery() {

watchHandle.resume();
}

watch(autoRefreshValue, (newValue) => dataRetriever.updateTimeout(newValue));
</script>

<template>
<div>
<div class="header">
<RefreshConfig id="auditListRefresh" @change="store.updateRefreshTimer" @manual-refresh="store.refresh" />
<RefreshConfig v-model="autoRefreshValue" :isLoading="isLoading" @manual-refresh="dataRetriever.executeAndResetTimer()" />
<div class="row">
<FiltersPanel />
</div>
Expand Down
8 changes: 3 additions & 5 deletions src/Frontend/src/stores/AuditStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ref } from "vue";
import type { SortInfo } from "@/components/SortInfo";
import Message from "@/resources/Message";
import { EndpointsView } from "@/resources/EndpointView.ts";
import useAutoRefresh from "@/composables/autoRefresh";

export type DateRange = [fromDate: Date, toDate: Date] | [];

Expand Down Expand Up @@ -39,7 +38,7 @@ export const useAuditStore = defineStore("AuditStore", () => {
}
}

const dataRetriever = useAutoRefresh(async () => {
async function refresh() {
try {
const [fromDate, toDate] = dateRange.value;
const from = fromDate?.toISOString() ?? "";
Expand All @@ -53,11 +52,10 @@ export const useAuditStore = defineStore("AuditStore", () => {
messages.value = [];
throw e;
}
}, null);
}

return {
refresh: dataRetriever.executeAndResetTimer,
updateRefreshTimer: dataRetriever.updateTimeout,
refresh,
loadEndpoints,
sortBy: sortByInstances,
messages,
Expand Down