Moved Header into separate component, added ConnectionMonitor
Some checks failed
continuous-integration/drone Build is failing
Some checks failed
continuous-integration/drone Build is failing
This commit is contained in:
parent
4452819aed
commit
9fb5d34118
85
src/components/Header/ConnectionMonitor.vue
Normal file
85
src/components/Header/ConnectionMonitor.vue
Normal file
@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<v-tooltip :text="`Last ping from server received at ${secondsFromLastPing} seconds ago`" location="bottom">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-icon :color="(secondsFromLastPing > pingThreshold || secondsFromLastPing === null) ? 'red' : 'green'" v-bind="props" class="mr-6">mdi-server</v-icon>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { VTooltip } from 'vuetify/components/VTooltip'
|
||||
import { VIcon } from 'vuetify/components/VIcon'
|
||||
import {mapGetters} from "vuex";
|
||||
|
||||
export default {
|
||||
name: "ConnectionMonitor",
|
||||
components: {
|
||||
VTooltip,
|
||||
VIcon
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pingThreshold: 10,
|
||||
pingError: false,
|
||||
secondsFromLastPing: null,
|
||||
setupStreamInterval: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'lastPingReceived'
|
||||
])
|
||||
},
|
||||
mounted() {
|
||||
// window.setInterval(() => {
|
||||
// this.monitorServerConnection()
|
||||
// }, 10000)
|
||||
window.setInterval(() => {
|
||||
this.secondsFromLastPing = Math.floor(((new Date()).getTime() - this.lastPingReceived) / 1000)
|
||||
this.pingError = this.secondsFromLastPing > this.pingThreshold
|
||||
}, 1000)
|
||||
},
|
||||
watch: {
|
||||
pingError(error) {
|
||||
if (error) {
|
||||
this.$toast.error(`Connection with server lost, attempting to reconnect...`, {
|
||||
position: "top-right",
|
||||
duration: false,
|
||||
});
|
||||
this.setupStreamInterval = window.setInterval(() => {
|
||||
this.$emit('setupStream')
|
||||
}, 3000)
|
||||
} else {
|
||||
window.clearTimeout(this.setupStreamInterval);
|
||||
this.$toast.clear();
|
||||
this.$toast.success(`Connection with server reestablished`, {
|
||||
position: "top-right",
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
monitorServerConnection () {
|
||||
if (this.secondsFromLastPing > this.pingThreshold) {
|
||||
this.pingError = true;
|
||||
this.$toast.error(`Connection with server lost, attempting to reconnect...`, {
|
||||
position: "top-right",
|
||||
});
|
||||
this.setupStream();
|
||||
} else {
|
||||
if (this.pingError) {
|
||||
this.$toast.success(`Connection with server reestablished`, {
|
||||
position: "top-right",
|
||||
});
|
||||
setTimeout(this.$toast.clear, 3000);
|
||||
}
|
||||
this.pingError = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
58
src/components/Header/Header.vue
Normal file
58
src/components/Header/Header.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<v-row no-gutters>
|
||||
<v-col cols="12" sm="5" class="d-flex justify-start flex-wrap" >
|
||||
<Datepicker class="ma-2" :refresh="refreshFilterState" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="2" class="d-flex justify-center flex-wrap">
|
||||
<h1 class="app-title"> Logmower </h1>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="5" class="d-flex justify-end flex-wrap align-center">
|
||||
<ConnectionMonitor @setup-stream="setupStream" />
|
||||
<v-btn
|
||||
color="blue-grey"
|
||||
class="ma-2"
|
||||
:prepend-icon="streaming ? 'mdi-pause' :'mdi-play'"
|
||||
@click="toggleFilterQueryStreaming"
|
||||
>
|
||||
Stream new lines
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
<script>
|
||||
import { VRow, VCol } from 'vuetify/components/VGrid'
|
||||
import { VBtn } from 'vuetify/components/VBtn'
|
||||
import Datepicker from "../Grid/Main/Filter/Datepicker.vue";
|
||||
import {mapActions, mapGetters} from "vuex";
|
||||
import ConnectionMonitor from "./ConnectionMonitor.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ConnectionMonitor,
|
||||
Datepicker,
|
||||
VRow,
|
||||
VCol,
|
||||
VBtn
|
||||
},
|
||||
props: {
|
||||
refreshFilterState: Function
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'streaming'
|
||||
]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
toggleFilterQueryStreaming: 'toggleFilterQueryStreaming',
|
||||
}),
|
||||
setupStream () {
|
||||
this.$emit('setupStream')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,23 +1,6 @@
|
||||
<template>
|
||||
<div style="height: 100%; width: 100%;" v-resize="onResize">
|
||||
<v-row no-gutters>
|
||||
<v-col cols="12" sm="5" class="d-flex justify-start flex-wrap">
|
||||
<Datepicker class="ma-2" :refresh="refreshFilterState" />
|
||||
</v-col>
|
||||
<v-col cols="12" sm="2" class="d-flex justify-center flex-wrap">
|
||||
<h1 class="app-title"> Logmower </h1>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="5" class="d-flex justify-end flex-wrap">
|
||||
<v-btn
|
||||
color="blue-grey"
|
||||
class="ma-2"
|
||||
:prepend-icon="streaming ? 'mdi-pause' :'mdi-play'"
|
||||
@click="toggleFilterQueryStreaming"
|
||||
>
|
||||
Stream new lines
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<Header :refresh-filter-state="refreshFilterState" @setup-stream="setupStream" />
|
||||
<ag-grid-vue
|
||||
style="width: 100%; height: calc(100% - 52px);"
|
||||
class="ag-theme-material"
|
||||
@ -44,8 +27,6 @@
|
||||
import { AgGridVue } from "ag-grid-vue3";
|
||||
import "ag-grid-community/styles//ag-grid.css";
|
||||
import "ag-grid-community/styles//ag-theme-material.css";
|
||||
import { VBtn } from 'vuetify/components/VBtn'
|
||||
import { VRow, VCol } from 'vuetify/components/VGrid'
|
||||
import { Resize } from 'vuetify/directives';
|
||||
import ExamineLogModal from "./Modal/ExamineLogModal.vue";
|
||||
import ComboboxFilter from "./Grid/Main/Filter/ComboboxFilter.js";
|
||||
@ -54,19 +35,16 @@ import flattenObj from "../helpers/flattenObj";
|
||||
import parseEventData from "../helpers/parseEventData";
|
||||
import {mapActions, mapGetters} from 'vuex';
|
||||
import config from "./Grid/Main/config";
|
||||
import Datepicker from "./Grid/Main/Filter/Datepicker.vue";
|
||||
import loadingOverlay from "./Grid/Main/loadingOverlay";
|
||||
import Header from "./Header/Header.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Datepicker,
|
||||
Header,
|
||||
ExamineLogModal,
|
||||
AgGridVue,
|
||||
ComboboxFilter,
|
||||
MessageWithLevelRenderer,
|
||||
VBtn,
|
||||
VRow,
|
||||
VCol,
|
||||
loadingOverlay
|
||||
},
|
||||
directives: {
|
||||
@ -110,7 +88,7 @@ export default {
|
||||
...mapActions({
|
||||
setFilterOptions: 'setFilterOptions',
|
||||
setFilterQuery: 'setFilterQuery',
|
||||
toggleFilterQueryStreaming: 'toggleFilterQueryStreaming',
|
||||
setLastPingReceived: 'setLastPingReceived',
|
||||
}),
|
||||
onResize () {
|
||||
if (this.gridApi) {
|
||||
@ -129,6 +107,7 @@ export default {
|
||||
if (url.searchParams.keys().next()) {
|
||||
let es = new EventSource(url.toString());
|
||||
es.onmessage = (e) => this.handleReceiveMessage(e)
|
||||
es.addEventListener("ping", (e) => this.handleReceivePing())
|
||||
es.addEventListener("filters", (e) => this.handleReceiveFilters(e))
|
||||
es.addEventListener("timeout", (e) => this.handleReceiveTimeout(e))
|
||||
es.addEventListener("completed", (e) => this.handleAllReceived(e))
|
||||
@ -247,6 +226,9 @@ export default {
|
||||
handleAllReceived () {
|
||||
this.gridApi.hideOverlay();
|
||||
},
|
||||
handleReceivePing () {
|
||||
this.setLastPingReceived()
|
||||
},
|
||||
doesExternalFilterPass(node) {
|
||||
if (node.data && this.filterQuery.from && this.filterQuery.to) {
|
||||
let ts = new Date(node.data['@timestamp']).getTime()
|
||||
|
@ -4,6 +4,7 @@ const store = createStore({
|
||||
state: {
|
||||
filterOptions: {},
|
||||
filterQuery: {},
|
||||
lastPingReceived: null,
|
||||
},
|
||||
getters: {
|
||||
streaming (state) {
|
||||
@ -11,7 +12,10 @@ const store = createStore({
|
||||
},
|
||||
filterQuery (state) {
|
||||
return state.filterQuery
|
||||
}
|
||||
},
|
||||
lastPingReceived (state) {
|
||||
return state.lastPingReceived
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setFilterOptions(context, payload) {
|
||||
@ -30,6 +34,9 @@ const store = createStore({
|
||||
query.streaming = false
|
||||
commit("SET_FILTER_QUERY", query);
|
||||
},
|
||||
setLastPingReceived(context) {
|
||||
context.commit("SET_LAST_PING_RECEIVED");
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
SET_FILTER_OPTIONS(state, payload) {
|
||||
@ -55,6 +62,9 @@ const store = createStore({
|
||||
|
||||
state.filterQuery = query
|
||||
},
|
||||
SET_LAST_PING_RECEIVED(state) {
|
||||
state.lastPingReceived = (new Date()).getTime()
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -22,5 +22,10 @@ export default {
|
||||
vuetify: {
|
||||
// https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader
|
||||
}
|
||||
}
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: [
|
||||
"@meforma/vue-toaster"
|
||||
]
|
||||
},
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user