ui: use default filter for sessions, move around saved search actions, remove tags modal
This commit is contained in:
parent
15fe4e5994
commit
592ae5ac4d
11 changed files with 138 additions and 359 deletions
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import SessionFilters from 'Shared/SessionFilters';
|
||||
import AiSessionSearchField from 'Shared/SessionFilters/AiSessionSearchField';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
|
|
@ -9,10 +8,6 @@ const MainSearchBar = () => {
|
|||
const projectId = projectsStore.siteId;
|
||||
const currSite = React.useRef(projectId);
|
||||
|
||||
// @ts-ignore
|
||||
const originStr = window.env.ORIGIN || window.location.origin;
|
||||
const isSaas = /app\.openreplay\.com/.test(originStr);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (projectId !== currSite.current && currSite.current !== undefined) {
|
||||
console.debug('clearing filters due to project change');
|
||||
|
|
@ -22,7 +17,6 @@ const MainSearchBar = () => {
|
|||
}, [projectId]);
|
||||
return (
|
||||
<div className={'flex flex-col gap-2 w-full'}>
|
||||
{isSaas ? <AiSessionSearchField /> : null}
|
||||
<SessionFilters />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,49 +1,22 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Icon } from 'UI';
|
||||
import { Button } from 'antd';
|
||||
import cn from 'classnames';
|
||||
import stl from './SavedSearch.module.css';
|
||||
import { useModal } from 'App/components/Modal';
|
||||
import SavedSearchModal from './components/SavedSearchModal';
|
||||
import React from 'react';
|
||||
import { Dropdown } from 'antd';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
function SavedSearch() {
|
||||
const { showModal } = useModal();
|
||||
const { searchStore, customFieldStore } = useStore();
|
||||
const { searchStore } = useStore();
|
||||
const savedSearch = searchStore.savedSearch;
|
||||
const list = searchStore.list;
|
||||
const fetchedMeta = customFieldStore.fetchedMetadata;
|
||||
|
||||
// useEffect(() => {
|
||||
// if (list.size === 0 && !fetchedMeta) {
|
||||
// void searchStore.fetchSavedSearchList();
|
||||
// }
|
||||
// }, [fetchedMeta]);
|
||||
const options = searchStore.list.map((item) => ({
|
||||
key: item.searchId,
|
||||
label: item.name,
|
||||
onClick: () => searchStore.applySavedSearch(item)
|
||||
}))
|
||||
|
||||
return (
|
||||
<div className={cn('flex items-center', { [stl.disabled]: list.size === 0 })}>
|
||||
<Button
|
||||
// variant="outline"
|
||||
type="primary"
|
||||
ghost
|
||||
onClick={() => showModal(<SavedSearchModal />, { right: true, width: 450 })}
|
||||
className="flex gap-1"
|
||||
>
|
||||
<span className="mr-1">Saved Search</span>
|
||||
<span className="font-meidum">{list.size}</span>
|
||||
<Icon name="ellipsis-v" color="teal" size="14" />
|
||||
</Button>
|
||||
{savedSearch.exists() && (
|
||||
<div className="flex items-center ml-2">
|
||||
<Icon name="search" size="14" />
|
||||
<span className="color-gray-medium px-1">Viewing:</span>
|
||||
<span className="font-medium" style={{ whiteSpace: 'nowrap', width: '30%' }}>
|
||||
{savedSearch.name.length > 15 ? `${savedSearch.name.slice(0, 15)}...` : savedSearch.name}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Dropdown.Button menu={{ items: options }} className={'w-fit'}>
|
||||
{savedSearch.exists() ? 'Update' : 'Save'} Search
|
||||
</Dropdown.Button>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,10 +67,10 @@ function SavedSearchModal(props: Props) {
|
|||
<div className="bg-white box-shadow h-screen">
|
||||
<div className="p-6">
|
||||
<h1 className="text-2xl">
|
||||
Saved Search <span className="color-gray-medium">{searchStore.list.size}</span>
|
||||
Saved Search <span className="color-gray-medium">{searchStore.list.length}</span>
|
||||
</h1>
|
||||
</div>
|
||||
{searchStore.list.size > 1 && (
|
||||
{searchStore.list.length > 1 && (
|
||||
<div className="mb-6 w-full px-4">
|
||||
<Input
|
||||
icon="search"
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import React from 'react';
|
||||
import React, { useMemo } from "react";
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
import SaveFilterButton from 'Shared/SaveFilterButton';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStore } from 'App/mstore';
|
||||
import TagList from '../MainSearchBar/components/TagList';
|
||||
import SavedSearch from '../SavedSearch/SavedSearch';
|
||||
import { Button } from 'antd';
|
||||
import AiSessionSearchField from 'Shared/SessionFilters/AiSessionSearchField';
|
||||
|
||||
function SearchActions() {
|
||||
const { aiFiltersStore, searchStore, customFieldStore } = useStore();
|
||||
const { aiFiltersStore, searchStore, customFieldStore, userStore } = useStore();
|
||||
const appliedFilter = searchStore.instance;
|
||||
const activeTab = searchStore.activeTab;
|
||||
const isEnterprise = userStore.isEnterprise;
|
||||
const metaLoading = customFieldStore.isLoading;
|
||||
const hasEvents =
|
||||
appliedFilter.filters.filter((i: any) => i.isEvent).length > 0;
|
||||
|
|
@ -19,12 +20,23 @@ function SearchActions() {
|
|||
const hasSavedSearch = savedSearch && savedSearch.exists();
|
||||
const hasSearch = hasFilters || hasSavedSearch;
|
||||
|
||||
const title = useMemo(() => {
|
||||
if (activeTab && activeTab.type === 'bookmarks') {
|
||||
return isEnterprise ? 'Vault' : 'Bookmarks';
|
||||
}
|
||||
return 'Sessions';
|
||||
}, [activeTab?.type, isEnterprise]);
|
||||
|
||||
// @ts-ignore
|
||||
const originStr = window.env.ORIGIN || window.location.origin;
|
||||
const isSaas = /app\.openreplay\.com/.test(originStr);
|
||||
const showAiField = isSaas && activeTab.type === 'sessions';
|
||||
const showPanel = hasEvents || hasFilters || aiFiltersStore.isLoading;
|
||||
return !metaLoading ? (
|
||||
<div className={'mb-2'}>
|
||||
<div className={'flex items-center gap-2 w-full'}>
|
||||
<TagList />
|
||||
<SavedSearch />
|
||||
<h2 className="text-2xl capitalize mr-4">{title}</h2>
|
||||
{isSaas && showAiField ? <AiSessionSearchField /> : null}
|
||||
<div className={'ml-auto'} />
|
||||
<Button
|
||||
type="link"
|
||||
|
|
@ -32,9 +44,9 @@ function SearchActions() {
|
|||
onClick={() => searchStore.clearSearch()}
|
||||
className="font-medium"
|
||||
>
|
||||
Clear Search
|
||||
Clear
|
||||
</Button>
|
||||
<SaveFilterButton disabled={!hasEvents && !hasFilters} />
|
||||
<SavedSearch />
|
||||
</div>
|
||||
{showPanel ? (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,87 +1,8 @@
|
|||
import { CloseOutlined, EnterOutlined } from '@ant-design/icons';
|
||||
import { Tour } from 'antd';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useState } from 'react';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { assist as assistRoute, isRoute } from 'App/routes';
|
||||
import { debounce } from 'App/utils';
|
||||
import { Icon, Input } from 'UI';
|
||||
|
||||
import FilterModal from 'Shared/Filters/FilterModal';
|
||||
|
||||
import OutsideClickDetectingDiv from '../OutsideClickDetectingDiv';
|
||||
|
||||
const ASSIST_ROUTE = assistRoute();
|
||||
|
||||
interface Props {
|
||||
setFocused?: (focused: boolean) => void;
|
||||
}
|
||||
|
||||
function SessionSearchField(props: Props) {
|
||||
const { searchStore, searchStoreLive } = useStore();
|
||||
const isLive =
|
||||
isRoute(ASSIST_ROUTE, window.location.pathname) ||
|
||||
window.location.pathname.includes('multiview');
|
||||
const debounceFetchFilterSearch = React.useCallback(
|
||||
debounce(
|
||||
isLive ? searchStoreLive.fetchFilterSearch : searchStore.fetchFilterSearch,
|
||||
1000
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const onSearchChange = ({ target: { value } }: any) => {
|
||||
setSearchQuery(value);
|
||||
debounceFetchFilterSearch({ q: value });
|
||||
};
|
||||
|
||||
const onAddFilter = (filter: any) => {
|
||||
isLive
|
||||
? searchStoreLive.addFilterByKeyAndValue(filter.key, filter.value)
|
||||
: searchStore.addFilterByKeyAndValue(filter.key, filter.value);
|
||||
};
|
||||
|
||||
const onFocus = () => {
|
||||
setShowModal(true);
|
||||
props.setFocused?.(true);
|
||||
};
|
||||
const onBlur = () => {
|
||||
setTimeout(() => {
|
||||
setShowModal(false);
|
||||
props.setFocused?.(false);
|
||||
}, 200);
|
||||
};
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<Input
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
onChange={onSearchChange}
|
||||
placeholder={
|
||||
'Search sessions using any captured event (click, input, page, error...)'
|
||||
}
|
||||
style={{ minWidth: 360, height: 30 }}
|
||||
id="search"
|
||||
type="search"
|
||||
autoComplete="off"
|
||||
className="px-2 py-1 text-lg placeholder-lg !border-0 rounded-r-full nofocus"
|
||||
/>
|
||||
|
||||
{showModal && (
|
||||
<div className="absolute left-0 shadow-sm rounded-lg bg-white z-50">
|
||||
<FilterModal
|
||||
isMainSearch={true}
|
||||
onFilterClick={onAddFilter}
|
||||
isLive={isLive}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import { Input } from 'UI';
|
||||
|
||||
const AiSearchField = observer(() => {
|
||||
const { searchStore } = useStore();
|
||||
|
|
@ -129,7 +50,7 @@ const AiSearchField = observer(() => {
|
|||
value={searchQuery}
|
||||
style={{ minWidth: 360, height: 30 }}
|
||||
autoComplete="off"
|
||||
className="px-2 py-1 pe-9 text-lg placeholder-lg !border-0 rounded-e-full nofocus"
|
||||
className="px-4 py-1 text-lg placeholder-lg !border-0 nofocus"
|
||||
leadingButton={
|
||||
searchQuery !== '' ? (
|
||||
<div
|
||||
|
|
@ -148,154 +69,21 @@ const AiSearchField = observer(() => {
|
|||
}
|
||||
);
|
||||
|
||||
function AiSessionSearchField(props: Props) {
|
||||
const askTourKey = '__or__ask-tour';
|
||||
const tabKey = '__or__tab';
|
||||
function AiSessionSearchField() {
|
||||
const { aiFiltersStore } = useStore();
|
||||
const isTourShown = localStorage.getItem(askTourKey) !== null;
|
||||
const [tab, setTab] = useState(localStorage.getItem(tabKey) || 'search');
|
||||
const [touring, setTouring] = useState(!isTourShown);
|
||||
const [isFocused, setFocused] = React.useState(false);
|
||||
const askAiRef = React.useRef(null);
|
||||
|
||||
const closeTour = () => {
|
||||
setTouring(false);
|
||||
localStorage.setItem(askTourKey, 'true');
|
||||
};
|
||||
const changeValue = (v?: string) => {
|
||||
const newTab = v ? v : tab !== 'ask' ? 'ask' : 'search';
|
||||
setTab(newTab);
|
||||
localStorage.setItem(tabKey, newTab);
|
||||
};
|
||||
|
||||
const boxStyle = tab === 'ask'
|
||||
? gradientBox
|
||||
: isFocused ? regularBoxFocused : regularBoxUnfocused;
|
||||
return (
|
||||
<div className={'bg-white rounded-full shadow-sm'}>
|
||||
<div className={'bg-white rounded-full shadow-sm w-full'}>
|
||||
<div
|
||||
className={aiFiltersStore.isLoading ? 'animate-bg-spin' : ''}
|
||||
style={boxStyle}
|
||||
style={gradientBox}
|
||||
>
|
||||
<div ref={askAiRef} className={'px-2'}>
|
||||
<AskAiSwitchToggle
|
||||
enabled={tab === 'ask'}
|
||||
setEnabled={changeValue}
|
||||
loading={aiFiltersStore.isLoading}
|
||||
/>
|
||||
</div>
|
||||
{tab === 'ask' ? (
|
||||
<AiSearchField {...props} />
|
||||
) : (
|
||||
<SessionSearchField {...props} setFocused={setFocused} />
|
||||
)}
|
||||
<Tour
|
||||
open={touring}
|
||||
onClose={closeTour}
|
||||
steps={[
|
||||
{
|
||||
title: (
|
||||
<div
|
||||
className={'text-xl font-semibold flex items-center gap-2'}
|
||||
>
|
||||
<span>Introducing</span>
|
||||
<Icon name={'sparkles'} size={18} />
|
||||
<span>Ask AI</span>
|
||||
</div>
|
||||
),
|
||||
target: () => askAiRef.current,
|
||||
description:
|
||||
'Easily find sessions with our AI search. Just enable Ask AI, type in your query naturally, and the AI will swiftly and precisely display relevant sessions.',
|
||||
nextButtonProps: {
|
||||
children: (
|
||||
<OutsideClickDetectingDiv
|
||||
onClickOutside={closeTour}
|
||||
className={
|
||||
'w-full h-full text-white flex items-center gap-2'
|
||||
}
|
||||
>
|
||||
<span>Ask AI</span>
|
||||
<Icon
|
||||
name={'arrow-right-short'}
|
||||
size={16}
|
||||
color={'white'}
|
||||
/>
|
||||
</OutsideClickDetectingDiv>
|
||||
),
|
||||
onClick: () => {
|
||||
changeValue('ask');
|
||||
closeTour();
|
||||
}
|
||||
}
|
||||
}
|
||||
]}
|
||||
/>
|
||||
<AiSearchField />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const AskAiSwitchToggle = ({
|
||||
enabled,
|
||||
setEnabled,
|
||||
loading
|
||||
}: {
|
||||
enabled: boolean;
|
||||
loading: boolean;
|
||||
setEnabled: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
role="switch"
|
||||
aria-checked={enabled}
|
||||
onClick={() => setEnabled()}
|
||||
className={loading ? 'animate-bg-spin' : ''}
|
||||
style={{
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
height: 24,
|
||||
background: enabled
|
||||
? 'linear-gradient(-25deg, #394eff, #3EAAAf, #3ccf65)'
|
||||
: 'rgb(170 170 170)',
|
||||
backgroundSize: loading ? '200% 200%' : 'unset',
|
||||
borderRadius: 100,
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
border: 0,
|
||||
verticalAlign: 'middle'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
insetInlineStart: enabled ? 'calc(100% - 21px)' : '3px',
|
||||
position: 'absolute',
|
||||
top: 3,
|
||||
width: 18,
|
||||
height: 18,
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
background: '#fff',
|
||||
borderRadius: 100,
|
||||
verticalAlign: 'middle'
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
overflow: 'hidden',
|
||||
borderRadius: 100,
|
||||
height: '100%',
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
paddingInline: !enabled ? '30px 0px' : '10px 24px',
|
||||
width: 88
|
||||
}}
|
||||
>
|
||||
<div style={{ color: 'white', fontSize: 16 }}>Ask AI</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const gradientBox = {
|
||||
border: 'double 1.5px transparent',
|
||||
borderRadius: '100px',
|
||||
|
|
@ -307,27 +95,8 @@ export const gradientBox = {
|
|||
display: 'flex',
|
||||
gap: '0.25rem',
|
||||
alignItems: 'center',
|
||||
width: '100%'
|
||||
};
|
||||
|
||||
const regularBoxUnfocused = {
|
||||
borderRadius: '100px',
|
||||
border: 'solid 1.5px #BFBFBF',
|
||||
background: '#fffff',
|
||||
display: 'flex',
|
||||
gap: '0.25rem',
|
||||
alignItems: 'center',
|
||||
width: '100%'
|
||||
};
|
||||
|
||||
const regularBoxFocused = {
|
||||
borderRadius: '100px',
|
||||
border: 'solid 1.5px #394EFF',
|
||||
background: '#fffff',
|
||||
display: 'flex',
|
||||
gap: '0.25rem',
|
||||
alignItems: 'center',
|
||||
width: '100%'
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
};
|
||||
|
||||
export default observer(AiSessionSearchField);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,12 @@ function SessionFilters() {
|
|||
const saveRequestPayloads =
|
||||
projectsStore.instance?.saveRequestPayloads ?? false;
|
||||
|
||||
useEffect(() => {
|
||||
if (searchStore.instance.filters.length === 0) {
|
||||
searchStore.addFilterByKeyAndValue(FilterKey.LOCATION, '', 'isAny')
|
||||
}
|
||||
}, [])
|
||||
|
||||
useSessionSearchQueryHandler({
|
||||
appliedFilter,
|
||||
loading: metaLoading,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ function SessionsTabOverview() {
|
|||
const [query, setQuery] = React.useState('');
|
||||
const { aiFiltersStore, searchStore } = useStore();
|
||||
const appliedFilter = searchStore.instance;
|
||||
const activeTab = searchStore.activeTab;
|
||||
|
||||
const handleKeyDown = (event: any) => {
|
||||
if (event.key === 'Enter') {
|
||||
|
|
@ -41,7 +42,7 @@ function SessionsTabOverview() {
|
|||
placeholder={'ask session ai'}
|
||||
/>
|
||||
) : null}
|
||||
<SessionHeader />
|
||||
{activeTab.type !== 'bookmarks' && <SessionHeader />}
|
||||
<div className="border-b" />
|
||||
<LatestSessionsMessage />
|
||||
<SessionList />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import Period from 'Types/app/period';
|
||||
import SelectDateRange from 'Shared/SelectDateRange';
|
||||
import SessionTags from '../SessionTags';
|
||||
|
|
@ -8,20 +8,11 @@ import { useStore } from 'App/mstore';
|
|||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
function SessionHeader() {
|
||||
const { searchStore, userStore } = useStore();
|
||||
const isEnterprise = userStore.isEnterprise;
|
||||
const activeTab = searchStore.activeTab;
|
||||
const { searchStore } = useStore();
|
||||
const { startDate, endDate, rangeValue } = searchStore.instance;
|
||||
|
||||
const period = Period({ start: startDate, end: endDate, rangeName: rangeValue });
|
||||
|
||||
const title = useMemo(() => {
|
||||
if (activeTab && activeTab.type === 'bookmarks') {
|
||||
return isEnterprise ? 'Vault' : 'Bookmarks';
|
||||
}
|
||||
return 'Sessions';
|
||||
}, [activeTab?.type, isEnterprise]);
|
||||
|
||||
const onDateChange = (e: any) => {
|
||||
const dateValues = e.toJSON();
|
||||
searchStore.edit(dateValues);
|
||||
|
|
@ -30,13 +21,11 @@ function SessionHeader() {
|
|||
|
||||
return (
|
||||
<div className="flex items-center px-4 py-1 justify-between w-full">
|
||||
<h2 className="text-2xl capitalize mr-4">{title}</h2>
|
||||
<div className="flex items-center w-full justify-end">
|
||||
{activeTab.type !== 'bookmarks' && <SessionTags />}
|
||||
<SessionTags />
|
||||
<div className="mr-auto" />
|
||||
<Space>
|
||||
{activeTab.type !== 'bookmarks' &&
|
||||
<SelectDateRange isAnt period={period} onChange={onDateChange} right={true} />}
|
||||
<SelectDateRange isAnt period={period} onChange={onDateChange} right={true} />
|
||||
<SessionSort />
|
||||
</Space>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,8 +29,9 @@ const useSessionSearchQueryHandler = ({ onBeforeLoad, appliedFilter, loading }:
|
|||
const converter = JsonUrlConverter.urlParamsToJson(history.location.search);
|
||||
const json = getFilterFromJson(converter.toJSON());
|
||||
const filter = new Search(json);
|
||||
searchStore.applyFilter(filter, true);
|
||||
searchStore.setUrlParsed();
|
||||
if (filter.filters.length === 0) return;
|
||||
searchStore.applyFilter(filter, true);
|
||||
} catch (error) {
|
||||
console.error('Error applying filter from query:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
filtersMap,
|
||||
generateFilterOptions,
|
||||
liveFiltersMap,
|
||||
mobileConditionalFiltersMap
|
||||
mobileConditionalFiltersMap,
|
||||
} from 'Types/filter/newFilter';
|
||||
import { List } from 'immutable';
|
||||
import { makeAutoObservable, runInAction } from 'mobx';
|
||||
|
|
@ -28,18 +28,18 @@ export const checkValues = (key: any, value: any) => {
|
|||
};
|
||||
|
||||
export const filterMap = ({
|
||||
category,
|
||||
value,
|
||||
key,
|
||||
operator,
|
||||
sourceOperator,
|
||||
source,
|
||||
custom,
|
||||
isEvent,
|
||||
filters,
|
||||
sort,
|
||||
order
|
||||
}: any) => ({
|
||||
category,
|
||||
value,
|
||||
key,
|
||||
operator,
|
||||
sourceOperator,
|
||||
source,
|
||||
custom,
|
||||
isEvent,
|
||||
filters,
|
||||
sort,
|
||||
order,
|
||||
}: any) => ({
|
||||
value: checkValues(key, value),
|
||||
custom,
|
||||
type: category === FilterCategory.METADATA ? FilterKey.METADATA : key,
|
||||
|
|
@ -47,7 +47,7 @@ export const filterMap = ({
|
|||
source: category === FilterCategory.METADATA ? key.replace(/^_/, '') : source,
|
||||
sourceOperator,
|
||||
isEvent,
|
||||
filters: filters ? filters.map(filterMap) : []
|
||||
filters: filters ? filters.map(filterMap) : [],
|
||||
});
|
||||
|
||||
export const TAB_MAP: any = {
|
||||
|
|
@ -55,11 +55,11 @@ export const TAB_MAP: any = {
|
|||
sessions: { name: 'Sessions', type: 'sessions' },
|
||||
bookmarks: { name: 'Bookmarks', type: 'bookmarks' },
|
||||
notes: { name: 'Notes', type: 'notes' },
|
||||
recommendations: { name: 'Recommendations', type: 'recommendations' }
|
||||
recommendations: { name: 'Recommendations', type: 'recommendations' },
|
||||
};
|
||||
|
||||
class SearchStore {
|
||||
list = List();
|
||||
list: SavedSearch[] = [];
|
||||
latestRequestTime: number | null = null;
|
||||
latestList = List();
|
||||
alertMetricId: number | null = null;
|
||||
|
|
@ -107,13 +107,19 @@ class SearchStore {
|
|||
|
||||
applySavedSearch(savedSearch: ISavedSearch) {
|
||||
this.savedSearch = savedSearch;
|
||||
this.edit({ filters: savedSearch.filter ? savedSearch.filter.filters.map((i: FilterItem) => new FilterItem().fromJson(i)) : [] });
|
||||
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 = List(response.map((item: any) => new SavedSearch(item)));
|
||||
this.list = response.map((item: any) => new SavedSearch(item));
|
||||
}
|
||||
|
||||
edit(instance: Partial<Search>) {
|
||||
|
|
@ -122,7 +128,9 @@ class SearchStore {
|
|||
}
|
||||
|
||||
editSavedSearch(instance: Partial<SavedSearch>) {
|
||||
this.savedSearch = new SavedSearch(Object.assign(this.savedSearch.toData(), instance));
|
||||
this.savedSearch = new SavedSearch(
|
||||
Object.assign(this.savedSearch.toData(), instance)
|
||||
);
|
||||
}
|
||||
|
||||
apply(filter: any, fromUrl: boolean) {
|
||||
|
|
@ -145,7 +153,10 @@ class SearchStore {
|
|||
.fetchFilterSearch(params)
|
||||
.then((response: any[]) => {
|
||||
this.filterSearchList = response.reduce(
|
||||
(acc: Record<string, { projectId: number; value: string }[]>, item: any) => {
|
||||
(
|
||||
acc: Record<string, { projectId: number; value: string }[]>,
|
||||
item: any
|
||||
) => {
|
||||
const { projectId, type, value } = item;
|
||||
if (!acc[type]) acc[type] = [];
|
||||
acc[type].push({ projectId, value });
|
||||
|
|
@ -206,17 +217,19 @@ class SearchStore {
|
|||
}
|
||||
|
||||
clearList() {
|
||||
this.list = List();
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
clearSearch() {
|
||||
const instance = this.instance;
|
||||
this.edit(new Search({
|
||||
rangeValue: instance.rangeValue,
|
||||
startDate: instance.startDate,
|
||||
endDate: instance.endDate,
|
||||
filters: []
|
||||
}));
|
||||
this.edit(
|
||||
new Search({
|
||||
rangeValue: instance.rangeValue,
|
||||
startDate: instance.startDate,
|
||||
endDate: instance.endDate,
|
||||
filters: [],
|
||||
})
|
||||
);
|
||||
|
||||
this.savedSearch = new SavedSearch({});
|
||||
sessionStore.clearList();
|
||||
|
|
@ -226,7 +239,11 @@ class SearchStore {
|
|||
checkForLatestSessions() {
|
||||
const filter = this.instance.toSearch();
|
||||
if (this.latestRequestTime) {
|
||||
const period = Period({ rangeName: CUSTOM_RANGE, start: this.latestRequestTime, end: Date.now() });
|
||||
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;
|
||||
|
|
@ -242,29 +259,32 @@ class SearchStore {
|
|||
}
|
||||
|
||||
addFilter(filter: any) {
|
||||
const index = filter.isEvent ? -1 : this.instance.filters.findIndex((i: FilterItem) => i.key === filter.key);
|
||||
const index = filter.isEvent
|
||||
? -1
|
||||
: this.instance.filters.findIndex(
|
||||
(i: FilterItem) => i.key === filter.key
|
||||
);
|
||||
|
||||
console.log(filter)
|
||||
filter.value = checkFilterValue(filter.value);
|
||||
filter.filters = filter.filters
|
||||
? filter.filters.map((subFilter: any) => ({
|
||||
...subFilter,
|
||||
value: checkFilterValue(subFilter.value)
|
||||
}))
|
||||
...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)
|
||||
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.instance.toData(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -275,7 +295,13 @@ class SearchStore {
|
|||
}
|
||||
}
|
||||
|
||||
addFilterByKeyAndValue(key: any, value: any, operator?: string, sourceOperator?: string, source?: string) {
|
||||
addFilterByKeyAndValue(
|
||||
key: any,
|
||||
value: any,
|
||||
operator?: string,
|
||||
sourceOperator?: string,
|
||||
source?: string
|
||||
) {
|
||||
let defaultFilter = { ...filtersMap[key] };
|
||||
defaultFilter.value = value;
|
||||
|
||||
|
|
@ -305,7 +331,7 @@ class SearchStore {
|
|||
|
||||
this.instance = new Search({
|
||||
...this.instance.toData(),
|
||||
filters: newFilters
|
||||
filters: newFilters,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -316,7 +342,7 @@ class SearchStore {
|
|||
|
||||
this.instance = new Search({
|
||||
...this.instance.toData(),
|
||||
filters: newFilters
|
||||
filters: newFilters,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -328,12 +354,17 @@ class SearchStore {
|
|||
// TODO
|
||||
}
|
||||
|
||||
async fetchSessions(force: boolean = false, bookmarked: boolean = false): Promise<void> {
|
||||
async fetchSessions(
|
||||
force: boolean = false,
|
||||
bookmarked: boolean = false
|
||||
): Promise<void> {
|
||||
const filter = this.instance.toSearch();
|
||||
|
||||
if (this.activeTags[0] && this.activeTags[0] !== 'all') {
|
||||
const tagFilter = filtersMap[FilterKey.ISSUE];
|
||||
tagFilter.value = [issues_types.find((i: any) => i.type === this.activeTags[0])?.type];
|
||||
tagFilter.value = [
|
||||
issues_types.find((i: any) => i.type === this.activeTags[0])?.type,
|
||||
];
|
||||
delete tagFilter.operatorOptions;
|
||||
delete tagFilter.options;
|
||||
delete tagFilter.placeholder;
|
||||
|
|
@ -345,14 +376,17 @@ class SearchStore {
|
|||
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);
|
||||
};
|
||||
await sessionStore.fetchSessions(
|
||||
{
|
||||
...filter,
|
||||
page: this.currentPage,
|
||||
perPage: this.pageSize,
|
||||
tab: this.activeTab.type,
|
||||
bookmarked: bookmarked ? true : undefined,
|
||||
},
|
||||
force
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SearchStore;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
filtersMap,
|
||||
mobileConditionalFiltersMap,
|
||||
} from 'Types/filter/newFilter';
|
||||
import { action, makeAutoObservable, observable } from 'mobx';
|
||||
import { makeAutoObservable } from 'mobx';
|
||||
|
||||
import { pageUrlOperators } from '../../constants/filterOptions';
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue