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 @@ + + + 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 @@ + + + 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;