diff --git a/components.d.ts b/components.d.ts
index f536d45a..72157bd5 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -12,6 +12,7 @@ declare module 'vue' {
AlertList: typeof import('./src/components/lists/AlertList.vue')['default']
AlertsExport: typeof import('./src/components/dialogs/AlertsExport.vue')['default']
AlertsFilter: typeof import('./src/components/dialogs/filters/AlertsFilter.vue')['default']
+ AlertsFilterFields: typeof import('./src/components/dialogs/filters/AlertsFilterFields.vue')['default']
AlertsHistoryExport: typeof import('./src/components/dialogs/AlertsHistoryExport.vue')['default']
AlertsHistoryFilter: typeof import('./src/components/dialogs/filters/AlertsHistoryFilter.vue')['default']
AlertSummary: typeof import('./src/components/Settings/AlertSummary.vue')['default']
@@ -25,6 +26,8 @@ declare module 'vue' {
EscalationRuleAdd: typeof import('./src/components/dialogs/add/EscalationRuleAdd.vue')['default']
EscalationRuleList: typeof import('./src/components/lists/EscalationRuleList.vue')['default']
EscalationRulesFilter: typeof import('./src/components/dialogs/filters/EscalationRulesFilter.vue')['default']
+ FilterTabAdd: typeof import('./src/components/dialogs/add/FilterTabAdd.vue')['default']
+ FilterTabs: typeof import('./src/components/dialogs/FilterTabs.vue')['default']
GCheckbox: typeof import('./src/components/GCheckbox.vue')['default']
GCombobox: typeof import('./src/components/GCombobox.vue')['default']
GroupEdit: typeof import('./src/components/GroupEdit.vue')['default']
diff --git a/src/components/dialogs/FilterTabs.vue b/src/components/dialogs/FilterTabs.vue
new file mode 100644
index 00000000..a5aa3e08
--- /dev/null
+++ b/src/components/dialogs/FilterTabs.vue
@@ -0,0 +1,226 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ d }}: {{ f.from! / -3600 }} hours
+
+
+
+ {{ d }}.{{ desc }}:
+
+
+
+
+
+ {{ d }}: {{ f.join(', ') }}
+
+
+ {{ d }}: {{ f }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Cancel') }}
+
+
+
+
+
+ {{ t('Save') }}
+
+
+
+
+
+
+
+
diff --git a/src/components/dialogs/add/FilterTabAdd.vue b/src/components/dialogs/add/FilterTabAdd.vue
new file mode 100644
index 00000000..bbd929ad
--- /dev/null
+++ b/src/components/dialogs/add/FilterTabAdd.vue
@@ -0,0 +1,189 @@
+
+
+ {{ t('AddTab') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('Cancel') }}
+
+
+
+
+
+ {{ t('Save') }}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/dialogs/filters/AlertsFilter.vue b/src/components/dialogs/filters/AlertsFilter.vue
index d720b014..fe38e874 100644
--- a/src/components/dialogs/filters/AlertsFilter.vue
+++ b/src/components/dialogs/filters/AlertsFilter.vue
@@ -23,116 +23,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -159,8 +50,7 @@
diff --git a/src/components/dialogs/filters/AlertsHistoryFilter.vue b/src/components/dialogs/filters/AlertsHistoryFilter.vue
index 9de8057c..b528acd7 100644
--- a/src/components/dialogs/filters/AlertsHistoryFilter.vue
+++ b/src/components/dialogs/filters/AlertsHistoryFilter.vue
@@ -335,8 +335,20 @@ async function validate(close?: boolean) {
}
}
+function compareDict(a: any, b: any) {
+ if (a === null) return true
+ for (const key in a) {
+ if (b[key] === undefined) return false
+ if (a[key] !== null && typeof a[key] === typeof {}) {
+ if (b[key] === null || a[key].length !== b[key].length || !compareDict(a[key], b[key])) return false
+ } else if (a[key] !== b[key]) return false
+ }
+ return true
+}
+
function apply() {
const item = editedItem.value
+ if (!compareDict(filter, filter.value)) store.dispatch('filterTabs/setCurrentTab', 'user-defined')
filter.value = {
...item,
environment: (item.environment?.length ?? 0 > 0) ? item.environment : undefined,
diff --git a/src/components/lists/AlertList.vue b/src/components/lists/AlertList.vue
index 355927c4..2e30f7a6 100644
--- a/src/components/lists/AlertList.vue
+++ b/src/components/lists/AlertList.vue
@@ -8,7 +8,7 @@
fixed-header
style="max-height: calc(99vh - calc(48px + 43px + 64px + 58px))"
:headers="customHeaders"
- :items="alerts"
+ :items="items"
:items-length="pagination.totalItems!"
:items-per-page-options="pagination.itemsPerPageOptions"
:loading="isSearching"
@@ -192,22 +192,13 @@ const headersMap = computed(() => ({
actions: {title: t('Actions'), key: 'actions', sortable: false, align: 'end'}
}))
-const alerts = computed(() => {
- const alertList = store.getters['alerts/alerts']
- // if (props.filter.text) {
- // const text = props.filter.text.toLowerCase()
- // return alertList.filter((alert: Record) =>
- // Object.keys(alert).some((k) => alert[k]?.toString().toLowerCase().includes(text))
- // )
- // } else {
- return alertList
- // }
-})
-
+const items = computed(() => store.state.alerts.alerts)
const isSearching = computed(() => (store.state.alerts.isSearching ? 'primary' : false))
const pagination = computed({
get: () => store.state.alerts.pagination,
- set: value => store.dispatch('alerts/setPagination', value)
+ set: value => {
+ store.dispatch('alerts/setPagination', value)
+ }
})
const lastNote = (item: Alert) => {
@@ -266,6 +257,9 @@ async function selectItem(item: Alert) {
if (!selected.value.length) {
await store.dispatch('alerts/getAlert', item.id)
router.push({path: `/alert/${item.id}`, query: {redirect: route.fullPath}})
+ } else {
+ const include = selected.value.includes(item.id)
+ include ? selected.value.splice(selected.value.indexOf(item.id), 1) : selected.value.push(item.id)
}
}
diff --git a/src/locales/en.ts b/src/locales/en.ts
index 43b029ef..08c1e328 100644
--- a/src/locales/en.ts
+++ b/src/locales/en.ts
@@ -145,6 +145,11 @@ export const en = {
LastReceiveId: 'Last Receive ID',
LastNote: 'Last Note',
RemoveLastNote: 'Remove Last Note',
+ AddTab: 'Add Tab',
+ EditTabs: 'Edit Tabs',
+ NewFilterTab: 'New Filter Tab',
+ EditFilterTab: 'Edit Filter Tab',
+ ClearSelected: 'Clear Selected',
// Alert list filter
Filter: 'Filter',
@@ -626,6 +631,7 @@ export const en = {
Error: 'Error',
Send: 'Send',
Required: 'Required',
+ AlreadyExists: ' already exists. Must be unique',
Cancel: 'Cancel',
Save: 'Save',
NoDisplay: 'Sorry, nothing to display here :(',
diff --git a/src/pages/Alerts.vue b/src/pages/Alerts.vue
index a5af54a7..df866be8 100644
--- a/src/pages/Alerts.vue
+++ b/src/pages/Alerts.vue
@@ -37,6 +37,9 @@
+
+
+
@@ -79,19 +82,31 @@
+
+
+
+
+ {{ tab.name }} ({{ getTabCount(tab.name) }})
+
+
- {{ env }} ({{ environmentCounts[env] || 0 }})
+ user-defined ({{ pagination.totalItems }})
@@ -126,7 +141,7 @@ import {useStore} from 'vuex'
import {useRoute, useRouter} from 'vue-router'
import {useI18n} from 'vue-i18n'
import utils from '@/common/utils'
-import type {Query, SortBy} from '@/plugins/store/types/alerts-types'
+import type {Filter, Query, SortBy} from '@/plugins/store/types/alerts-types'
import type {State, Store} from '@/plugins/store/types'
import moment from 'moment'
import type {DateRange} from '@/plugins/store/types/notificationHistory-types'
@@ -144,10 +159,14 @@ const router = useRouter()
const {t} = useI18n()
const timeout = ref | undefined>(undefined)
-const tab = ref('All')
const audio = ref(null)
const confirm = ref | null>(null)
-const currentTab = computed(() => store.state.alerts.filter.environment ?? 'All')
+const currentTab = computed({
+ get: () => {
+ return store.state.filterTabs.currentTab
+ },
+ set: val => store.dispatch('filterTabs/setCurrentTab', val)
+})
const filter = computed(() => store.state.alerts.filter)
const storeQuery = computed(() => store.state.alerts.query.q)
@@ -156,31 +175,26 @@ const routeHash = computed(() => route.hash)
const routeQuery = computed(() => route.query)
const audioUrl = computed(() => store.getters.getConfig('audio').new ?? store.getters.getPreference('audioURL'))
const showSearchBar = computed(() => store.getters.getPreference('showSearchBar'))
+const pagination = computed(() => store.state.alerts.pagination)
const isWatch = computed({
get: () => store.state.alerts.isWatch,
set: val => store.dispatch('alerts/toggle', ['isWatch', val])
})
-watch(storeQuery, val => (query.value = val))
-watch(routeHash, val => setHash(val))
-watch(isWatch, () => refreshAlerts(audio.value))
-
const isDateRange = (date: DateRange | string[]): date is DateRange => !(date instanceof Array)
-watch(routeQuery, val => setQuery(val as Query))
-
-setQuery(route.query as Query)
-
const userQueries = computed(() =>
store.getters.getUserQueries.map(q => ({title: q.q, value: q.q, props: {appendIcon: 'delete'}}))
)
-const defaultTab = computed(() => filter.value.environment || 'All')
-
const selected = computed(() => store.state.alerts.selected)
const isAlertAlarmModel = computed(() => !store.getters.getConfig('alarm_model').name.includes('ISA'))
+function clearSelected() {
+ store.dispatch('alerts/updateSelected', [])
+}
+
function haveDeleteScope() {
const scopes = store.getters['auth/scopes']
const config = store.state.config
@@ -235,17 +249,7 @@ async function removeLasNotes() {
getAlerts()
}
-const alerts = computed(() => {
- const allAlerts = store.getters['alerts/alerts']
- return allAlerts
- // const filterText = filter.value.text
- // return filterText
- // ? allAlerts.filter(alert =>{
- // const alertKeys = Object.keys(alert) as [keyof typeof alert]
- // return alertKeys.some((k: keyof typeof alert) => alert[k]?.toString().toLowerCase().includes(filterText.toLowerCase()))
- // })
- // : allAlerts.map(b => ({ ...b, service: b.service?.join(', ') }))
-})
+const alerts = computed(() => store.state.alerts.alerts)
const isNewOpenAlerts = computed(() =>
alerts.value
@@ -254,12 +258,9 @@ const isNewOpenAlerts = computed(() =>
.some(alert => !alert.repeat)
)
-const showAllowedEnvs = computed(() => store.getters.getPreference('showAllowedEnvs'))
const ackIsTimeout = computed(() => store.getters.getConfig('ack_timeout'))
-
-const environments = computed(() => ['All', ...store.getters['alerts/environments'](showAllowedEnvs.value)])
-
-const environmentCounts = computed(() => store.getters['alerts/counts'])
+const filterTabsItems = computed(() => store.state.filterTabs.items)
+const tabCounts = computed(() => store.state.filterTabs.counts)
const refreshInterval = computed(
() => store.getters.getPreference('refreshInterval') || store.getters.getConfig('refresh_interval')
@@ -274,6 +275,10 @@ function setQuery(q: Query) {
getAlerts()
}
+function getTabCount(tabname: string) {
+ return tabCounts.value[tabname] || 0
+}
+
function setSearch(q: string) {
store.dispatch('alerts/updateQuery', {q})
router.push({query: {...route.query, q}, hash: store.getters['alerts/getHash']})
@@ -294,10 +299,17 @@ function clearSearch() {
getAlerts()
}
+function setFilterTab(tab: Filter) {
+ if (JSON.stringify(tab) !== JSON.stringify(filter.value)) {
+ store.dispatch('alerts/setFilter', tab)
+ }
+ router.replace({hash: store.getters['alerts/getHash'], query: routeQuery.value})
+}
+
function setFilter(f: any) {
const val: {[key: string]: any} = {}
Object.keys(f)
- .filter(key => key && !['sb', 'asi', 'sd'].includes(key))
+ .filter(key => key && !['sb', 'asi', 'sd', 'ct'].includes(key))
.forEach(a => {
if (a.includes('dateRange')) {
const [key, child] = a.split('.')
@@ -308,6 +320,7 @@ function setFilter(f: any) {
}
})
store.dispatch('alerts/setFilter', val)
+ refreshAlerts(audio.value)
}
function setSort({sb, sd}: {sb: string; sd: string; [key: string]: string}) {
@@ -320,10 +333,6 @@ function setPage(page: number) {
store.dispatch('alerts/setPagination', {page})
}
-function setKiosk(isKiosk: boolean) {
- store.dispatch('alerts/updateKiosk', isKiosk)
-}
-
async function getAlerts() {
return await store.dispatch('alerts/getAlerts')
}
@@ -340,27 +349,24 @@ function getEnvironments() {
store.dispatch('alerts/getEnvironments')
}
-function playSound(audioRef: HTMLAudioElement | null) {
- if (!isMute.value && audioRef) audioRef.play()
+async function getFilterTabs(tab: string) {
+ await store.dispatch('filterTabs/getFilterTabs')
+ if (tab !== 'user-defined') {
+ const [filterTab] = filterTabsItems.value.filter(({name}) => name == tab)
+ setFilterTab(filterTab.filter)
+ }
}
-function setEnv(env: string) {
- store.dispatch('alerts/setFilter', {
- ...filter.value,
- environment: env === 'All' ? null : env
- })
+function playSound(audioRef: HTMLAudioElement | null) {
+ if (!isMute.value && audioRef) audioRef.play()
}
const refresh = computed(() => store.state.refresh)
-watch(refresh, val => {
- if (!val) return
- refreshAlerts(audio.value)
-})
-
async function refreshAlerts(audioRef: HTMLAudioElement | null) {
if (timeout.value) clearTimeout(timeout.value)
getEnvironments()
+ getFilterTabs(currentTab.value)
if (isLoggedIn.value) {
getQueries()
getUserPrefs()
@@ -374,46 +380,44 @@ function setHash(val: string) {
const hash = val.replace(/^#/, '')
if (hash) {
- const hashMap: {sd?: string; sb?: string; [key: string]: any} = utils.fromHash(hash)
- setFilter(hashMap)
+ const hashMap: {sd?: string; sb?: string; ct?: string; [key: string]: any} = utils.fromHash(hash)
+ if (typeof hashMap.ct === 'string') {
+ if (hashMap.ct == 'user-defined') setFilter(hashMap)
+ currentTab.value = hashMap.ct
+ } else {
+ setFilter(hashMap)
+ }
if (typeof hashMap.sd === 'string' && typeof hashMap.sb === 'string') {
const typedHashMap = {sd: hashMap.sd, sb: hashMap.sb}
setSort({sd: typedHashMap.sd ?? '', sb: typedHashMap.sb})
}
}
}
+const getPrefs = () => store.dispatch('getUserPrefs')
setHash(routeHash.value)
-// Lifecycle hooks
+router.replace({query: route.query, hash: store.getters['alerts/getHash']})
setQuery(route.query as Query)
-
-setKiosk(route.query.isKiosk === 'true')
refreshAlerts(audio.value)
+watch(refresh, val => {
+ if (!val) return
+ refreshAlerts(audio.value)
+})
+watch(routeHash, val => setHash(val))
+watch(isWatch, () => refreshAlerts(audio.value))
+watch(storeQuery, val => (query.value = val))
+watch(routeQuery, val => setQuery(val as Query))
+watch(filter, () => router.replace({hash: store.getters['alerts/getHash'], query: routeQuery.value}))
onUnmounted(() => {
clearTimeout(timeout.value)
})
-const getPrefs = () => store.dispatch('getUserPrefs')
getPrefs()
-watch(
- () => tab.value,
- () => {
- setPage(1)
- }
-)
+watch(currentTab, () => setPage(1))
-watch(
- filter,
- () => {
- router.replace({hash: store.getters['alerts/getHash'], query: routeQuery.value})
- tab.value = defaultTab.value
- refreshAlerts(null)
- },
- {deep: true}
-)
+const sortBy = computed(() => pagination.value.sortBy)
-const pagination = computed(() => store.state.alerts.pagination)
-watch(pagination, () => router.replace({hash: store.getters['alerts/getHash'], query: routeQuery.value}))
+watch(sortBy, () => router.replace({hash: store.getters['alerts/getHash'], query: routeQuery.value}))
diff --git a/src/pages/History.vue b/src/pages/History.vue
index ce1e036f..409feeab 100644
--- a/src/pages/History.vue
+++ b/src/pages/History.vue
@@ -32,9 +32,24 @@
-
-
- {{ env }} ({{ environmentCounts[env] || 0 }})
+
+
+ {{ tab.name }} ({{ getTabCount(tab.name) }})
+
+
+ user-defined ({{ pagination.totalItems }})
@@ -110,7 +125,7 @@
import utils from '@/common/utils'
import {useFilters} from '@/filters'
import type {Store} from '@/plugins/store/types'
-import type {History, Pagination, Query} from '@/plugins/store/types/alerts-types'
+import type {Filter, History, Pagination, Query} from '@/plugins/store/types/alerts-types'
import type {DateRange} from '@/plugins/store/types/notificationHistory-types'
import moment from 'moment'
import {computed, onUnmounted, ref, watch} from 'vue'
@@ -136,16 +151,20 @@ const pagination = computed({
set: value => store.dispatch('alerts/setHistoryPagination', value)
})
const filter = computed(() => store.state.alerts.historyFilter)
-const currentTab = ref(null)
+const currentTab = computed({
+ get: () => {
+ return store.state.filterTabs.currentTab
+ },
+ set: val => store.dispatch('filterTabs/setCurrentTab', val)
+})
const history = computed(() => store.state.alerts.history)
-const environmentCounts = computed(() => store.getters['alerts/historyCounts'])
-const showAllowedEnvs = computed(() => store.getters.getPreference('showAllowedEnvs'))
-const environments = computed(() => ['All', ...store.getters['alerts/environments'](showAllowedEnvs.value)])
const storeQuery = computed(() => store.state.alerts.query.q)
const interval = computed(() => store.getters.getPreference('refreshInterval'))
const routeHash = computed(() => route.hash)
const routeQuery = computed(() => route.query)
const showSearchBar = computed(() => store.getters.getPreference('showSearchBar'))
+const filterTabsItems = computed(() => store.state.filterTabs.items)
+const tabCounts = computed(() => store.state.filterTabs.historyCounts)
const query = ref('')
const timeout = ref(undefined)
@@ -186,6 +205,13 @@ const defaultHeaders = computed(() => [
{title: t('Type'), key: 'type', sortable: false}
])
+async function setFilterTab(tab: Filter) {
+ if (JSON.stringify(tab) !== JSON.stringify(filter.value)) {
+ await store.dispatch('alerts/setHistoryFilter', tab)
+ // router.replace({query: routeQuery.value, hash: store.getters['alerts/getHistoryHash']})
+ }
+}
+
const customHeaders = computed(() => {
const configHeaders = store.state.config.columns.map(
c =>
@@ -202,10 +228,14 @@ function setPagination(value: Pagination) {
refreshAll()
}
+function getTabCount(tabname: string) {
+ return tabCounts.value[tabname] || 0
+}
+
function setFilter(f: any) {
const val: {[key: string]: any} = {}
Object.keys(f)
- .filter(key => key && !['sb', 'asi', 'sd'].includes(key))
+ .filter(key => key && !['sb', 'asi', 'sd', 'ct'].includes(key))
.forEach(a => {
if (a.includes('dateRange')) {
const [key, child] = a.split('.')
@@ -222,14 +252,16 @@ function setHash(val: string) {
const hash = val.replace(/^#/, '')
if (hash) {
- const hashMap = utils.fromHash(hash)
- setFilter(hashMap)
+ const hashMap: {sd?: string; sb?: string; ct?: string; [key: string]: any} = utils.fromHash(val)
+ if (typeof hashMap.ct === 'string') {
+ if (hashMap.ct == 'user-defined') setFilter(hashMap)
+ currentTab.value = hashMap.ct
+ } else {
+ setFilter(hashMap)
+ }
}
}
-setHash(routeHash.value)
-router.replace({query: route.query, hash: store.getters['alerts/getHistoryHash']})
-
function rowProps({item}: {item: any}) {
return {
style: 'min-height: 40px',
@@ -245,25 +277,16 @@ async function selectItem(item: History) {
router.push({path: `/alert/${item.alertId}`, query: {redirect: route.fullPath}})
}
-function setEnv(env: string) {
- store.dispatch('alerts/setHistoryFilter', {
- ...filter.value,
- environment: env === 'All' ? null : env
- })
- refreshAll()
+async function getFilterTabs(tab: string) {
+ await store.dispatch('filterTabs/getFilterTabs')
+ if (tab !== 'user-defined') {
+ const [filterTab] = filterTabsItems.value.filter(({name}) => name == tab)
+ setFilterTab(filterTab.filter)
+ }
}
const refresh = computed(() => store.state.refresh)
-watch(refresh, val => {
- if (!val) return
- refreshAll()
-})
-watch(routeHash, val => setHash(val))
-watch(filter, () => router.replace({query: routeQuery.value, hash: store.getters['alerts/getHistoryHash']}))
-watch(storeQuery, val => (query.value = val))
-watch(routeQuery, val => setQuery(val as Query))
-
function setQuery(q: Query) {
store.dispatch('alerts/updateQuery', q)
query.value = q.q
@@ -298,6 +321,7 @@ const refreshAll = () => {
if (timeout.value) clearTimeout(timeout.value)
getHistory()
getEnvironments()
+ getFilterTabs(currentTab.value)
getQueries()
timeout.value = setTimeout(refreshAll, interval.value)
}
@@ -317,6 +341,16 @@ function getQueries() {
store.dispatch('getUserQueries')
}
+setHash(routeHash.value)
+router.replace({query: route.query, hash: store.getters['alerts/getHistoryHash']})
+watch(refresh, val => {
+ if (!val) return
+ refreshAll()
+})
+watch(routeHash, val => setHash(val))
+watch(filter, () => router.replace({query: routeQuery.value, hash: store.getters['alerts/getHistoryHash']}))
+watch(storeQuery, val => (query.value = val))
+watch(routeQuery, val => setQuery(val as Query))
setQuery(routeQuery.value as Query)
refreshAll()
diff --git a/src/plugins/store/index.ts b/src/plugins/store/index.ts
index 935ec3f1..0a729931 100644
--- a/src/plugins/store/index.ts
+++ b/src/plugins/store/index.ts
@@ -20,6 +20,7 @@ import reports from './modules/reports.store'
import prefs from './modules/preferences.store'
import management from './modules/management.store'
import notifications from './modules/notifications.store'
+import filterTabs from './modules/filterTabs.store'
import type {ActionContext, ActionTree} from 'vuex'
import type {OmitIfUndefined, State as RootState} from './types'
@@ -79,7 +80,8 @@ const store = createStore({
reports,
prefs,
management,
- notifications
+ notifications,
+ filterTabs
}
})
diff --git a/src/plugins/store/modules/alerts.store.ts b/src/plugins/store/modules/alerts.store.ts
index 80095869..d756b94b 100644
--- a/src/plugins/store/modules/alerts.store.ts
+++ b/src/plugins/store/modules/alerts.store.ts
@@ -196,10 +196,10 @@ const actions: Actions & ActionTree = {
try {
const {alerts, total, pageSize} = await AlertsApi.getAlerts(params)
- await commit('SET_ALERTS', [alerts, total, pageSize])
+ commit('SET_ALERTS', [alerts, total, pageSize])
return alerts
} catch {
- await commit('RESET_LOADING')
+ commit('RESET_LOADING')
return []
}
},
@@ -408,15 +408,15 @@ const getters: Getters = {
tags: state => {
return [...new Set(state.tags.map(t => t.tag).sort())]
},
- getHash: state => {
+ getHash: (state, _, rootState) => {
const filterHash = utils.toHash(state.filter)
const sortBy = state.pagination.sortBy ? state.pagination.sortBy : []
const descending = sortBy.length > 0 ? sortBy.map(o => (o.order === 'desc' ? 1 : 0)) : [0, 1]
const paginationHash = `sb:${sortBy.map(({key}) => key).join(',')};sd:${descending}`
- const asiHash = `asi:${state.showPanel ? 1 : 0}`
- return `#${filterHash};${paginationHash};${asiHash}`
+ const currentTabHash = `ct:${rootState.filterTabs.currentTab}`
+ return `#${filterHash};${paginationHash};${currentTabHash}`
},
- getHistoryHash: state => `#${utils.toHash(state.historyFilter)}`
+ getHistoryHash: (state, _, rootState) => `#${utils.toHash(state.historyFilter)};ct:${rootState.filterTabs.currentTab}`
}
export default {
diff --git a/src/plugins/store/modules/filterTabs.store.ts b/src/plugins/store/modules/filterTabs.store.ts
new file mode 100644
index 00000000..5aed4a40
--- /dev/null
+++ b/src/plugins/store/modules/filterTabs.store.ts
@@ -0,0 +1,80 @@
+import FilterTabApi from '@/services/api/filterTab.service'
+import type {State, Actions, Mutations, Getters} from '../types/filterTabs-types'
+
+const namespaced = true
+
+const state: State = {
+ isLoading: false,
+ currentTab: 'user-defined',
+ items: [],
+ counts: {},
+ historyCounts: {}
+}
+
+const mutations: Mutations = {
+ SET_LOADING(state) {
+ state.isLoading = true
+ },
+ RESET_LOADING(state) {
+ state.isLoading = false
+ },
+ SET_FILTER_TAB(state, [filterTab, counts, historyCounts]) {
+ state.items = filterTab
+ state.counts = counts
+ state.historyCounts = historyCounts
+ },
+ SET_CURRENT_TAB(state, name) {
+ state.currentTab = name
+ }
+}
+
+const actions: Actions = {
+ async createFilterTab({dispatch}, filterTab) {
+ await FilterTabApi.createFilterTab(filterTab)
+ dispatch('getFilterTabs')
+ },
+ async createFilterTabs({dispatch}, filterTabs) {
+ await FilterTabApi.createFilterTabs(filterTabs)
+ dispatch('getFilterTabs')
+ },
+ async deleteFilterTab({dispatch}, id) {
+ await FilterTabApi.deleteFilterTab(id)
+ dispatch('getFilterTabs')
+ },
+ async deleteFilterTabs({dispatch}, ids) {
+ await FilterTabApi.deleteFilterTabs(ids)
+ dispatch('getFilterTabs')
+ },
+ async getFilterTabs({commit}) {
+ commit('SET_LOADING')
+ try {
+ const {filterTabs, counts, historyCounts} = await FilterTabApi.getfilterTabs()
+ commit('SET_FILTER_TAB', [filterTabs, counts, historyCounts])
+ } catch {
+ commit('RESET_LOADING')
+ }
+ },
+ async updateFilterTabs({dispatch}, filterTabs) {
+ await FilterTabApi.updateFilterTabs(filterTabs)
+ dispatch('getFilterTabs')
+ },
+ async updateFilterTabIndexes({dispatch}, filterTabs) {
+ await FilterTabApi.updateFilterTabIndexes(filterTabs)
+ dispatch('getFilterTabs')
+ },
+ setCurrentTab({commit}, name) {
+ commit('SET_CURRENT_TAB', name)
+ }
+}
+
+const getters: Getters = {
+ getHash: state => `ct:${state.currentTab}`
+}
+
+export default {
+ namespaced,
+ state,
+ mutations,
+ actions,
+ getters
+}
diff --git a/src/plugins/store/types/alerts-types.ts b/src/plugins/store/types/alerts-types.ts
index 2d12ab0a..e34ef3fa 100644
--- a/src/plugins/store/types/alerts-types.ts
+++ b/src/plugins/store/types/alerts-types.ts
@@ -153,6 +153,16 @@ export interface Filter {
[key: string]: any
}
+export type FilterEdit = {
+ filter: Filter
+ period: {
+ startDate: string | null
+ startTime: string | null
+ endDate: string | null
+ endTime: string | null
+ }
+}
+
interface EnvironmentsCount {
[key: string]: number
}
@@ -256,6 +266,6 @@ export type Getters = {
historyCounts(state: State): EnvironmentsCount
services(state: State): string[]
tags(state: State): string[]
- getHash(state: State): string
- getHistoryHash(state: State): string
+ getHash(state: State, _: Getters, rootState: RootState): string
+ getHistoryHash(state: State, _: Getters, rootState: RootState): string
}
diff --git a/src/plugins/store/types/filterTabs-types.ts b/src/plugins/store/types/filterTabs-types.ts
new file mode 100644
index 00000000..f0c2600c
--- /dev/null
+++ b/src/plugins/store/types/filterTabs-types.ts
@@ -0,0 +1,43 @@
+import type {ActionContext} from '.'
+import type {Filter} from './alerts-types'
+
+export type FilterTab = {
+ name: string
+ index: number
+ filter: Filter
+}
+
+export interface State {
+ isLoading: boolean
+ items: FilterTab[]
+ currentTab: string
+ counts: {[key: string]: number}
+ historyCounts: {[key: string]: number}
+}
+
+export type Mutations = {
+ SET_LOADING(state: S): void
+ RESET_LOADING(state: S): void
+ SET_FILTER_TAB(
+ state: S,
+ [filterTab, counts, historyCounts]: [FilterTab[], {[key: string]: number}, {[key: string]: number}]
+ ): void
+ SET_CURRENT_TAB(state: S, name: string): void
+}
+
+type AugmentedActionContext = ActionContext
+
+export type Actions = {
+ getFilterTabs({commit, state}: AugmentedActionContext): void
+ createFilterTab({dispatch}: AugmentedActionContext, filterTab: FilterTab): void
+ createFilterTabs({dispatch}: AugmentedActionContext, filterTabs: FilterTab[]): void
+ updateFilterTabs({dispatch}: AugmentedActionContext, filterTabs: FilterTab[]): void
+ updateFilterTabIndexes({dispatch}: AugmentedActionContext, filterTabs: {name: string; index: number}[]): void
+ deleteFilterTab({dispatch}: AugmentedActionContext, id: string): void
+ deleteFilterTabs({dispatch}: AugmentedActionContext, ids: string[]): void
+ setCurrentTab({commit}: AugmentedActionContext, name: string): void
+}
+
+export type Getters = {
+ getHash(state: State): string
+}
diff --git a/src/plugins/store/types/index.ts b/src/plugins/store/types/index.ts
index 237b4b64..a8edb263 100644
--- a/src/plugins/store/types/index.ts
+++ b/src/plugins/store/types/index.ts
@@ -57,6 +57,7 @@ import type {
import type {State as ReportsState, Actions as ReportsActions} from './reports-types'
import type {State as UsersState, Getters as UsersGetters, Actions as UsersActions} from './users-types'
import type {State as IndexState, Actions as IndexActions} from '../index'
+import type {State as FilterTabState, Actions as FilterTabActions} from './filterTabs-types'
import type {ActionContext as VuexContext, Store as defaultStore} from 'vuex'
@@ -100,6 +101,7 @@ export type State = {
reports: ReportsState
users: UsersState
prefs: PreferencesState
+ filterTabs: FilterTabState
} & IndexState
type AlertModule = {
@@ -280,6 +282,10 @@ export type IndexDispatch = {
[K in keyof IndexActions]: IndexActions[K]
}
+export type FilterTabDispatch = {
+ [K in keyof FilterTabActions as `filterTabs/${K}`]: FilterTabActions[K]
+}
+
export type Actions = AlertDispatch &
AuthDispatch &
ConfigDispatch &
@@ -302,7 +308,8 @@ export type Actions = AlertDispatch &
PermsDispatch &
PreferencesDispatch &
ReportsDispatch &
- UsersDispatch
+ UsersDispatch &
+ FilterTabDispatch
type Dispatch = (
key: K,
diff --git a/src/services/api/filterTab.service.ts b/src/services/api/filterTab.service.ts
new file mode 100644
index 00000000..c961f615
--- /dev/null
+++ b/src/services/api/filterTab.service.ts
@@ -0,0 +1,29 @@
+import api from './index'
+
+export default {
+ createFilterTab(data: object) {
+ return api.post('/filtertab', data)
+ },
+ createFilterTabs(data: object) {
+ return api.post('/filtertabs', data)
+ },
+ getFilterTab(id: string) {
+ return api.get(`/filtertabs/${id}`)
+ },
+ getfilterTabs() {
+ return api.get('/filtertabs')
+ },
+ updateFilterTabs(data: object) {
+ return api.put(`/filtertabs`, data)
+ },
+ updateFilterTabIndexes(data: object) {
+ return api.put(`/filtertabs/index`, data)
+ },
+ deleteFilterTab(id: string) {
+ return api.delete(`/filtertabs/${id}`)
+ },
+ deleteFilterTabs(ids: string[]) {
+ const config = {params: {id: ids}}
+ return api.delete(`/filtertabs`, config)
+ }
+}
diff --git a/src/styles/main.scss b/src/styles/main.scss
index 94f70fc9..d5977684 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -20,6 +20,9 @@ h1 {
font-weight: 500;
}
+.table.scrollbar-offset .v-table__wrapper {
+ padding-right: 20px !important;
+}
.sidebar {
border-left: none !important;
border-top: none !important;