summaryrefslogtreecommitdiffstats
path: root/webapp/src/components/LogModule.vue
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/src/components/LogModule.vue')
-rw-r--r--webapp/src/components/LogModule.vue328
1 files changed, 328 insertions, 0 deletions
diff --git a/webapp/src/components/LogModule.vue b/webapp/src/components/LogModule.vue
new file mode 100644
index 0000000..69b1664
--- /dev/null
+++ b/webapp/src/components/LogModule.vue
@@ -0,0 +1,328 @@
+<i18n>
+{
+ "en": {
+ "systemLog": "System Log",
+ "from": "From",
+ "to": "To",
+ "today": "Today",
+ "now": "Now",
+ "log": "Log",
+ "filter": "Filter",
+ "timestamp": "Timestamp",
+ "category": "Category",
+ "categories": "Categories",
+ "description": "Description",
+ "group": "Group",
+ "groups": "Groups",
+ "client": "Client",
+ "clients": "Clients",
+ "includeSubgroups": "Include Subgroups"
+ },
+ "de": {
+ "systemLog": "System Protokoll",
+ "from": "Von",
+ "to": "Bis",
+ "today": "Heute",
+ "now": "Jetzt",
+ "log": "Protokoll",
+ "filter": "Filter",
+ "timestamp": "Zeitstempel",
+ "category": "Kategorie",
+ "categories": "Kategorien",
+ "description": "Beschreibung",
+ "group": "Gruppe",
+ "groups": "Gruppen",
+ "client": "Client",
+ "clients": "Clients",
+ "includeSubgroups": "Inklusive Untergruppen"
+ }
+}
+</i18n>
+
+<template>
+ <v-container fill-height>
+ <v-layout>
+ <v-flex xl10 offset-xl1 lg12>
+ <v-card class="tabbar-card">
+ <v-tabs v-model="tabs" grow hide-slider :dark="tabsDark" :color="tabsColor" :slider-color="tabsSliderColor">
+ <v-tab><v-icon class="tabbar-tabicon">error_outline</v-icon>{{ $t('systemLog') }}</v-tab>
+ </v-tabs>
+ </v-card>
+ <v-tabs-items v-model="tabs" style="padding-bottom: 20px">
+ <v-tab-item>
+ <v-subheader>{{ $t('filter') }}</v-subheader>
+ <v-card>
+ <v-card-text>
+ <v-layout wrap>
+ <v-flex xs12 md5 order-xs2 order-md1>
+ <select-box
+ class="select-box"
+ v-model="categoryFilter"
+ :items="categories"
+ :max-columns="selectBoxColumnCount"
+ prepend-icon="all_inbox"
+ :label="$t('categories')"
+ hide-details
+ ></select-box>
+ </v-flex>
+ <v-flex xs12 md7 order-xs1 order-md2 class="text-md-right">
+ <div style="display: inline-block; white-space: nowrap">
+ <span class="text-xs-right picker-label">{{ $t('from') }}</span>
+
+ <v-menu left offset-y :close-on-content-click="false" style="display: inline-block">
+ <template #activator="{ on }">
+ <v-btn v-on="on" small class="date-picker-button"><v-icon small class="mr-1">today</v-icon>{{ fromDate }}</v-btn>
+ </template>
+ <v-date-picker
+ v-model="fromDate"
+ color="primary"
+ :locale="locale"
+ :width="pickerWidth"
+ ></v-date-picker>
+ </v-menu>
+
+ <v-menu left offset-y :close-on-content-click="false" style="display: inline-block">
+ <template #activator="{ on }">
+ <v-btn v-on="on" small class="time-picker-button"><v-icon small class="mr-1">schedule</v-icon>{{ fromTime }}</v-btn>
+ </template>
+ <v-time-picker
+ v-model="fromTime"
+ color="primary"
+ :locale="locale"
+ format="24hr"
+ :width="pickerWidth"
+ ></v-time-picker>
+ </v-menu>
+
+ </div>
+ <div style="display: inline-block; white-space: nowrap">
+ <span class="text-xs-right picker-label">{{ $t('to') }}</span>
+
+ <v-menu v-model="toDateMenu" left offset-y :close-on-content-click="false" style="display: inline-block">
+ <template #activator="{ on }">
+ <v-btn v-on="on" small class="date-picker-button">
+ <v-icon small class="mr-1">today</v-icon>
+ {{ toDate ? toDate : $t('today') }}
+ </v-btn>
+ </template>
+ <template #default>
+ <div style="position: relative">
+ <v-btn @click="toNow" dark color="primary" small style="position: absolute; z-index: 1; top: 0; right: 0; min-width: fit-content">{{ $t('now') }}</v-btn>
+ <v-date-picker
+ v-model="toDate"
+ color="primary"
+ :locale="locale"
+ :width="pickerWidth"
+ ></v-date-picker>
+ </div>
+ </template>
+ </v-menu>
+ <v-menu v-model="toTimeMenu" left offset-y :close-on-content-click="false" style="display: inline-block">
+ <template #activator="{ on }">
+ <v-btn v-on="on" small class="time-picker-button">
+ <v-icon small class="mr-1">schedule</v-icon>
+ {{ toTime ? toTime : $t('now') }}
+ </v-btn>
+ </template>
+ <template #default>
+ <div style="position: relative">
+ <v-btn @click="toNow" dark color="primary" small style="position: absolute; z-index: 1; top: 0; left: 0; min-width: fit-content">{{ $t('now') }}</v-btn>
+ <v-time-picker
+ v-model="toTime"
+ color="primary"
+ :locale="locale"
+ format="24hr"
+ :width="pickerWidth"
+ ></v-time-picker>
+ </div>
+ </template>
+ </v-menu>
+
+ </div>
+ </v-flex>
+
+ <v-flex xs12 md5 order-xs3>
+ <select-box
+ class="select-box"
+ v-model="groupFilter"
+ :items="groupList"
+ :max-columns="selectBoxColumnCount"
+ prepend-icon="device_hub"
+ :label="$t('groups')"
+ hide-details
+ ></select-box>
+ </v-flex>
+ <v-flex xs12 md5 order-xs4>
+ <select-box
+ class="select-box"
+ v-model="clientFilter"
+ :items="clientList"
+ :max-columns="selectBoxColumnCount"
+ prepend-icon="computer"
+ :label="$t('clients')"
+ hide-details
+ ></select-box>
+ </v-flex>
+ <v-flex xs12 md2 order-xs5 style="display: flex; align-items: flex-end; justify-content: flex-end">
+ <v-btn color="primary" :loading="loading" @click="loadLog"><v-icon left>filter_list</v-icon>{{ $t('filter') }}</v-btn>
+ </v-flex>
+ </v-layout>
+ </v-card-text>
+ </v-card>
+ <v-subheader>{{ $t('log') }}</v-subheader>
+ <v-card>
+ <data-table :headers="headers" :items="log" min-width="1100px" no-select no-sort></data-table>
+ </v-card>
+ </v-tab-item>
+ </v-tabs-items>
+ </v-flex>
+ </v-layout>
+ </v-container>
+</template>
+
+<script>
+import SelectBox from '@/components/SelectBox'
+import DataTable from '@/components/DataTable'
+import { mapState, mapGetters } from 'vuex'
+
+export default {
+ name: 'LogModule',
+ components: {
+ SelectBox,
+ DataTable
+ },
+ data () {
+ return {
+ tabs: 0,
+ categories: [],
+ log: [],
+ headers: [
+ { key: 'timestamp', text: this.$t('timestamp'), width: '160px' },
+ { key: 'category', text: this.$t('category'), width: '160px' },
+ { key: 'description', text: this.$t('description') },
+ { key: 'group', text: this.$t('group'), width: '180px' },
+ { key: 'client', text: this.$t('client'), width: '180px' }
+ ],
+ loading: false,
+ fromDate: null,
+ fromTime: null,
+ toDate: null,
+ toTime: null,
+ toDateMenu: false,
+ toTimeMenu: false,
+ categoryFilter: [],
+ groupFilter: [],
+ groupRecursive: false,
+ clientFilter: []
+ }
+ },
+ computed: {
+ ...mapState('groups', ['groupList', 'clientList']),
+ ...mapGetters(['tabsDark', 'tabsColor', 'tabsSliderColor']),
+ locale () { return this.$store.state.settings.locale },
+ selectBoxColumnCount () {
+ if (this.$vuetify.breakpoint.mdOnly || this.$vuetify.breakpoint.xsOnly) return 1
+ return 2
+ },
+ pickerWidth () {
+ return this.$vuetify.breakpoint.xsOnly ? 250 : undefined
+ },
+ defaultStartDate () {
+ const date = new Date()
+ date.setDate(date.getDate() - 7)
+ return date
+ }
+ },
+ watch: {
+ toDate (value) {
+ if (value) this.toTime = this.formatDate(new Date(), { date: false, seconds: false })
+ },
+ toTime (value) {
+ if (value) this.toDate = this.formatDate(new Date(), { time: false })
+ }
+ },
+ methods: {
+ formatDate(date, options = {}) {
+ var result = ''
+ const pad = x => x < 10 ? '0' + x : x
+ if (options.date !== false) {
+ result += date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate())
+ }
+ if (options.time !== false) {
+ if (result !== '') result += ' '
+ result += pad(date.getHours()) + ':' + pad(date.getMinutes() + 1)
+ if (options.seconds !== false) result += ':' + pad(date.getSeconds())
+ }
+ return result
+ },
+ toNow () {
+ this.toDate = null
+ this.toTime = null
+ this.toDateMenu = false
+ this.toTimeMenu = false
+ },
+ loadLog () {
+ this.loading = true
+ this.$http.get('/api/log').then(response => {
+ response.data.forEach(item => {
+ item.timestamp = new Date(item.timestamp * 1000).toISOString().split('.')[0].replace('T', ' ')
+ if (item.group) item.group = item.group.name
+ if (item.client) item.client = item.client.name
+ })
+ this.log = response.data
+ this.loading = false
+ })
+ }
+ },
+ created () {
+ this.$store.dispatch('groups/loadLists')
+ this.$http.get('/api/log/categories').then(response => {
+ this.categories = response.data.map((category, index) => ({ id: index, name: category }))
+ })
+ const date = new Date()
+ date.setDate(date.getDate() - 7)
+ this.fromDate = this.formatDate(this.defaultStartDate, { time: false }),
+ this.fromTime = this.formatDate(this.defaultStartDate, { date: false, seconds: false })
+ this.loadLog()
+ }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+.select-box {
+ margin: 6px !important;
+}
+
+.picker-label {
+ display: inline-block;
+ width: 40px;
+}
+
+.date-picker-button {
+ min-width: 110px;
+ width: 110px;
+ margin-right: 0;
+}
+
+.time-picker-button {
+ min-width: 70px;
+ width: 70px;
+}
+
+.search-wrapper {
+ display: flex;
+ align-items: center;
+}
+
+.search-box {
+ flex: 1;
+}
+
+.toggle-button {
+ min-width: 36px;
+ text-transform: none;
+ font-size: 14px;
+ font-weight: 600;
+}
+</style>