import Period, { CUSTOM_RANGE } from 'Types/app/period'; import { FilterCategory, FilterKey } from 'Types/filter/filterType'; import { conditionalFiltersMap, filtersMap, generateFilterOptions, liveFiltersMap, mobileConditionalFiltersMap, } from 'Types/filter/newFilter'; import { List } from 'immutable'; import { makeAutoObservable, runInAction } from 'mobx'; import { searchService } from 'App/services'; import Search from 'App/mstore/types/search'; import { checkFilterValue } from 'App/mstore/types/filter'; import FilterItem from 'App/mstore/types/filterItem'; import { sessionStore, settingsStore } from 'App/mstore'; import SavedSearch, { ISavedSearch } from 'App/mstore/types/savedSearch'; import { iTag } from '@/services/NotesService'; import { issues_types } from 'Types/session/issue'; const PER_PAGE = 10; export const checkValues = (key: any, value: any) => { if (key === FilterKey.DURATION) { return value[0] === '' || value[0] === null ? [0, value[1]] : value; } return value.filter((i: any) => i !== '' && i !== null); }; export const filterMap = ({ category, value, key, operator, sourceOperator, source, custom, isEvent, filters, sort, order, }: any) => ({ value: checkValues(key, value), custom, type: category === FilterCategory.METADATA ? FilterKey.METADATA : key, operator, source: category === FilterCategory.METADATA ? key.replace(/^_/, '') : source, sourceOperator, isEvent, filters: filters ? filters.map(filterMap) : [], }); export const TAB_MAP: any = { all: { name: 'All', type: 'all' }, sessions: { name: 'Sessions', type: 'sessions' }, bookmarks: { name: 'Bookmarks', type: 'bookmarks' }, notes: { name: 'Notes', type: 'notes' }, recommendations: { name: 'Recommendations', type: 'recommendations' }, }; class SearchStore { list: SavedSearch[] = []; latestRequestTime: number | null = null; latestList = List(); alertMetricId: number | null = null; instance = new Search(); savedSearch: ISavedSearch = new SavedSearch(); filterSearchList: any = {}; currentPage = 1; pageSize = PER_PAGE; activeTab = { name: 'All', type: 'all' }; scrollY = 0; sessions = List(); total: number = 0; loadingFilterSearch = false; isSaving: boolean = false; activeTags: any[] = []; urlParsed: boolean = false; constructor() { makeAutoObservable(this); } setUrlParsed() { this.urlParsed = true; } get filterList() { return generateFilterOptions(filtersMap); } get filterListMobile() { return generateFilterOptions(filtersMap, true); } get filterListLive() { return generateFilterOptions(liveFiltersMap); } get filterListConditional() { return generateFilterOptions(conditionalFiltersMap); } get filterListMobileConditional() { return generateFilterOptions(mobileConditionalFiltersMap); } applySavedSearch(savedSearch: ISavedSearch) { this.savedSearch = savedSearch; this.edit({ filters: savedSearch.filter ? savedSearch.filter.filters.map((i: FilterItem) => new FilterItem().fromJson(i) ) : [], }); this.currentPage = 1; } async fetchSavedSearchList() { const response = await searchService.fetchSavedSearch(); this.list = response.map((item: any) => new SavedSearch(item)); } edit(instance: Partial) { this.instance = new Search(Object.assign({ ...this.instance }, instance)); this.currentPage = 1; } editSavedSearch(instance: Partial) { this.savedSearch = new SavedSearch( Object.assign(this.savedSearch.toData(), instance) ); } apply(filter: any, fromUrl: boolean) { if (fromUrl) { this.instance = new Search(filter); } else { this.instance = new Search({ ...this.instance.toData(), ...filter }); } this.currentPage = 1; } applyFilter(filter: any, force = false) { this.apply(filter, false); } fetchFilterSearch(params: any) { this.loadingFilterSearch = true; searchService .fetchFilterSearch(params) .then((response: any[]) => { this.filterSearchList = response.reduce( ( acc: Record, item: any ) => { const { projectId, type, value } = item; if (!acc[type]) acc[type] = []; acc[type].push({ projectId, value }); return acc; }, {} ); }) .catch((error: any) => { console.error('Error fetching filter search:', error); }) .finally(() => { this.loadingFilterSearch = false; }); } updateCurrentPage(page: number, force = false) { this.currentPage = page; void this.fetchSessions(force); } setActiveTab(tab: string) { runInAction(() => { this.activeTab = TAB_MAP[tab]; }); } toggleTag(tag?: iTag) { if (!tag) { this.activeTags = []; void this.fetchSessions(true); } else { this.activeTags = [tag]; void this.fetchSessions(true); } } async removeSavedSearch(id: string): Promise { await searchService.deleteSavedSearch(id); this.savedSearch = new SavedSearch({}); await this.fetchSavedSearchList(); } async save(id?: string | null, rename = false): Promise { const filter = this.instance.toData(); const isNew = !id; const instance = this.savedSearch.toData(); const newInstance = rename ? instance : { ...instance, filter }; newInstance.filter.filters = newInstance.filter.filters.map(filterMap); await searchService.saveSavedSearch(newInstance, id); await this.fetchSavedSearchList(); if (isNew) { const lastSavedSearch = this.list[this.list.length - 1]; this.applySavedSearch(lastSavedSearch); } } clearList() { this.list = []; } clearSearch() { const instance = this.instance; this.edit( new Search({ rangeValue: instance.rangeValue, startDate: instance.startDate, endDate: instance.endDate, filters: [], }) ); this.savedSearch = new SavedSearch({}); sessionStore.clearList(); void this.fetchSessions(true); } checkForLatestSessions() { const filter = this.instance.toSearch(); if (this.latestRequestTime) { const period = Period({ rangeName: CUSTOM_RANGE, start: this.latestRequestTime, end: Date.now(), }); const newTimestamps: any = period.toJSON(); filter.startDate = newTimestamps.startDate; filter.endDate = newTimestamps.endDate; } // TODO - dedicated API endpoint to get the count of latest sessions, or show X+ sessions delete filter.limit; delete filter.page; searchService.checkLatestSessions(filter).then((response: any) => { runInAction(() => { this.latestList = List(response); }); }); } addFilter(filter: any) { const index = filter.isEvent ? -1 : this.instance.filters.findIndex( (i: FilterItem) => i.key === filter.key ); filter.value = checkFilterValue(filter.value); filter.filters = filter.filters ? filter.filters.map((subFilter: any) => ({ ...subFilter, value: checkFilterValue(subFilter.value), })) : null; if (index > -1) { const oldFilter = new FilterItem(this.instance.filters[index]); const updatedFilter = { ...oldFilter, value: oldFilter.value.concat(filter.value), }; oldFilter.merge(updatedFilter); this.updateFilter(index, updatedFilter); } else { this.instance.filters.push(filter); this.instance = new Search({ ...this.instance.toData(), }); } this.currentPage = 1; if (filter.value && filter.value[0] && filter.value[0] !== '') { void this.fetchSessions(); } } addFilterByKeyAndValue( key: any, value: any, operator?: string, sourceOperator?: string, source?: string ) { let defaultFilter = { ...filtersMap[key] }; defaultFilter.value = value; if (operator) { defaultFilter.operator = operator; } if (defaultFilter.hasSource && source && sourceOperator) { defaultFilter.sourceOperator = sourceOperator; defaultFilter.source = source; } this.addFilter(defaultFilter); } refreshFilterOptions() { // TODO } updateSearch = (search: Partial) => { this.instance = Object.assign(this.instance, search); } updateFilter = (index: number, search: Partial) => { const newFilters = this.instance.filters.map((_filter: any, i: any) => { if (i === index) { return search; } else { return _filter; } }); this.instance = new Search({ ...this.instance.toData(), filters: newFilters, }); }; removeFilter = (index: number) => { const newFilters = this.instance.filters.filter((_filter: any, i: any) => { return i !== index; }); this.instance = new Search({ ...this.instance.toData(), filters: newFilters, }); }; setScrollPosition = (y: number) => { this.scrollY = y; }; async fetchAutoplaySessions(page: number): Promise { // TODO } async fetchSessions( force: boolean = false, bookmarked: boolean = false ): Promise { const filter = this.instance.toSearch(); if (this.activeTags[0] && this.activeTags[0] !== 'all') { const tagFilter = filtersMap[FilterKey.ISSUE]; tagFilter.type = tagFilter.type.toLowerCase(); tagFilter.value = [ issues_types.find((i: any) => i.type === this.activeTags[0])?.type, ]; delete tagFilter.operatorOptions; delete tagFilter.options; delete tagFilter.placeholder; delete tagFilter.label; delete tagFilter.icon; filter.filters = filter.filters.concat(tagFilter); } if (!filter.filters.some((f: any) => f.type === FilterKey.DURATION)) { const { durationFilter } = settingsStore.sessionSettings; if (durationFilter?.count > 0) { const multiplier = durationFilter.countType === 'sec' ? 1000 : 60000; const amount = durationFilter.count * multiplier; const value = durationFilter.operator === '<' ? [amount, 0] : [0, amount]; filter.filters.push({ type: FilterKey.DURATION, value, operator: 'is', }); } } this.latestRequestTime = Date.now(); this.latestList = List(); await sessionStore.fetchSessions( { ...filter, page: this.currentPage, perPage: this.pageSize, tab: this.activeTab.type, bookmarked: bookmarked ? true : undefined, }, force ); } } export default SearchStore;