change(ui) - search live vs recorded
This commit is contained in:
parent
c38dd87f3f
commit
70c63f7b07
3 changed files with 129 additions and 95 deletions
|
|
@ -18,7 +18,7 @@ function AssistSearchField(props: Props) {
|
|||
return (
|
||||
<div className="flex items-center w-full">
|
||||
<div style={{ width: '60%', marginRight: '10px' }}>
|
||||
<SessionSearchField fetchFilterSearch={props.fetchFilterSearch} addFilterByKeyAndValue={props.addFilterByKeyAndValue} />
|
||||
<SessionSearchField />
|
||||
</div>
|
||||
<Button
|
||||
variant="text-primary"
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ import stl from './FilterModal.module.css';
|
|||
import { filtersMap } from 'Types/filter/newFilter';
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
|
||||
function filterJson(
|
||||
jsonObj: Record<string, any>,
|
||||
excludeKeys: string[] = []
|
||||
): Record<string, any> {
|
||||
function filterJson(jsonObj: Record<string, any>, excludeKeys: string[] = []): Record<string, any> {
|
||||
let filtered: Record<string, any> = {};
|
||||
|
||||
for (const key in jsonObj) {
|
||||
|
|
@ -26,37 +23,39 @@ export const getMatchingEntries = (searchQuery: string, filters: Record<string,
|
|||
const matchingCategories: string[] = [];
|
||||
const matchingFilters: Record<string, any> = {};
|
||||
const lowerCaseQuery = searchQuery.toLowerCase();
|
||||
|
||||
if (lowerCaseQuery.length === 0) return {
|
||||
matchingCategories: Object.keys(filters),
|
||||
matchingFilters: filters,
|
||||
};
|
||||
|
||||
Object.keys(filters).forEach(name => {
|
||||
if (lowerCaseQuery.length === 0)
|
||||
return {
|
||||
matchingCategories: Object.keys(filters),
|
||||
matchingFilters: filters,
|
||||
};
|
||||
|
||||
Object.keys(filters).forEach((name) => {
|
||||
if (name.toLocaleLowerCase().includes(lowerCaseQuery)) {
|
||||
matchingCategories.push(name);
|
||||
matchingFilters[name] = filters[name];
|
||||
} else {
|
||||
const filtersQuery = filters[name]
|
||||
.filter((filterOption: any) => filterOption.label.toLocaleLowerCase().includes(lowerCaseQuery))
|
||||
const filtersQuery = filters[name].filter((filterOption: any) =>
|
||||
filterOption.label.toLocaleLowerCase().includes(lowerCaseQuery)
|
||||
);
|
||||
|
||||
if (filtersQuery.length > 0) matchingFilters[name] = filtersQuery
|
||||
filtersQuery.length > 0 && matchingCategories.push(name);
|
||||
}
|
||||
})
|
||||
if (filtersQuery.length > 0) matchingFilters[name] = filtersQuery;
|
||||
filtersQuery.length > 0 && matchingCategories.push(name);
|
||||
}
|
||||
});
|
||||
|
||||
return { matchingCategories, matchingFilters };
|
||||
}
|
||||
};
|
||||
|
||||
interface Props {
|
||||
filters: any,
|
||||
onFilterClick?: (filter: any) => void,
|
||||
filterSearchList: any,
|
||||
filters: any;
|
||||
onFilterClick?: (filter: any) => void;
|
||||
filterSearchList: any;
|
||||
// metaOptions: any,
|
||||
isMainSearch?: boolean,
|
||||
fetchingFilterSearchList: boolean,
|
||||
searchQuery?: string,
|
||||
excludeFilterKeys?: Array<string>
|
||||
isMainSearch?: boolean;
|
||||
fetchingFilterSearchList: boolean;
|
||||
searchQuery?: string;
|
||||
excludeFilterKeys?: Array<string>;
|
||||
}
|
||||
function FilterModal(props: Props) {
|
||||
const {
|
||||
|
|
@ -66,7 +65,7 @@ function FilterModal(props: Props) {
|
|||
isMainSearch = false,
|
||||
fetchingFilterSearchList,
|
||||
searchQuery = '',
|
||||
excludeFilterKeys = []
|
||||
excludeFilterKeys = [],
|
||||
} = props;
|
||||
const showSearchList = isMainSearch && searchQuery.length > 0;
|
||||
|
||||
|
|
@ -74,34 +73,51 @@ function FilterModal(props: Props) {
|
|||
const _filter = filtersMap[filter.type];
|
||||
_filter.value = [filter.value];
|
||||
onFilterClick(_filter);
|
||||
}
|
||||
};
|
||||
|
||||
const { matchingCategories, matchingFilters } = getMatchingEntries(searchQuery, filterJson(filters, excludeFilterKeys));
|
||||
const { matchingCategories, matchingFilters } = getMatchingEntries(
|
||||
searchQuery,
|
||||
filterJson(filters, excludeFilterKeys)
|
||||
);
|
||||
|
||||
const isResultEmpty = (!filterSearchList || Object.keys(filterSearchList).length === 0)
|
||||
&& matchingCategories.length === 0 && Object.keys(matchingFilters).length === 0
|
||||
const isResultEmpty =
|
||||
(!filterSearchList || Object.keys(filterSearchList).length === 0) &&
|
||||
matchingCategories.length === 0 &&
|
||||
Object.keys(matchingFilters).length === 0;
|
||||
|
||||
return (
|
||||
<div className={stl.wrapper} style={{ width: '480px', maxHeight: '380px', overflowY: 'auto'}}>
|
||||
<div className={searchQuery && !isResultEmpty ? 'mb-6' : ''} style={{ columns: matchingCategories.length > 1 ? 'auto 200px' : 1 }}>
|
||||
{matchingCategories.map((key) => {
|
||||
return (
|
||||
<div className="mb-6 flex flex-col gap-2" key={key}>
|
||||
<div className="uppercase font-medium mb-1 color-gray-medium tracking-widest text-sm">{key}</div>
|
||||
<div>
|
||||
{matchingFilters[key] && matchingFilters[key].map((filter: any) => (
|
||||
<div key={filter.label} className={cn(stl.optionItem, "flex items-center py-2 cursor-pointer -mx-2 px-2")} onClick={() => onFilterClick({ ...filter, value: [''] })}>
|
||||
<Icon name={filter.icon} size="16"/>
|
||||
<span className="ml-2">{filter.label}</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<div className={stl.wrapper} style={{ width: '480px', maxHeight: '380px', overflowY: 'auto' }}>
|
||||
<div
|
||||
className={searchQuery && !isResultEmpty ? 'mb-6' : ''}
|
||||
style={{ columns: matchingCategories.length > 1 ? 'auto 200px' : 1 }}
|
||||
>
|
||||
{matchingCategories.map((key) => {
|
||||
return (
|
||||
<div className="mb-6 flex flex-col gap-2" key={key}>
|
||||
<div className="uppercase font-medium mb-1 color-gray-medium tracking-widest text-sm">
|
||||
{key}
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
{ showSearchList && (
|
||||
<div>
|
||||
{matchingFilters[key] &&
|
||||
matchingFilters[key].map((filter: any) => (
|
||||
<div
|
||||
key={filter.label}
|
||||
className={cn(
|
||||
stl.optionItem,
|
||||
'flex items-center py-2 cursor-pointer -mx-2 px-2'
|
||||
)}
|
||||
onClick={() => onFilterClick({ ...filter, value: [''] })}
|
||||
>
|
||||
<Icon name={filter.icon} size="16" />
|
||||
<span className="ml-2">{filter.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{showSearchList && (
|
||||
<Loader size="small" loading={fetchingFilterSearchList}>
|
||||
<div className="-mx-6 px-6">
|
||||
{isResultEmpty && !fetchingFilterSearchList ? (
|
||||
|
|
@ -109,30 +125,38 @@ function FilterModal(props: Props) {
|
|||
<AnimatedSVG name={ICONS.NO_SEARCH_RESULTS} size={180} />
|
||||
<div className="color-gray-medium font-medium px-3"> No Suggestions Found </div>
|
||||
</div>
|
||||
) : Object.keys(filterSearchList).map((key, index) => {
|
||||
const filter = filterSearchList[key];
|
||||
const option = filtersMap[key];
|
||||
return option ? (
|
||||
<div
|
||||
key={index}
|
||||
className={cn('mb-3')}
|
||||
>
|
||||
<div className="font-medium uppercase color-gray-medium text-sm mb-2">{option.label}</div>
|
||||
<div>
|
||||
{filter.map((f, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={cn(stl.filterSearchItem, "cursor-pointer px-3 py-1 text-sm flex items-center")}
|
||||
onClick={() => onFilterSearchClick({ type: key, value: f.value })}
|
||||
>
|
||||
<Icon className="mr-2" name={option.icon} size="16" />
|
||||
<div className="whitespace-nowrap text-ellipsis overflow-hidden">{f.value}</div>
|
||||
</div>
|
||||
))}
|
||||
) : (
|
||||
Object.keys(filterSearchList).map((key, index) => {
|
||||
const filter = filterSearchList[key];
|
||||
const option = filtersMap[key];
|
||||
return option ? (
|
||||
<div key={index} className={cn('mb-3')}>
|
||||
<div className="font-medium uppercase color-gray-medium mb-2">
|
||||
{option.label}
|
||||
</div>
|
||||
<div>
|
||||
{filter.map((f, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={cn(
|
||||
stl.filterSearchItem,
|
||||
'cursor-pointer px-3 py-1 flex items-center'
|
||||
)}
|
||||
onClick={() => onFilterSearchClick({ type: key, value: f.value })}
|
||||
>
|
||||
<Icon className="mr-2" name={option.icon} size="16" />
|
||||
<div className="whitespace-nowrap text-ellipsis overflow-hidden">
|
||||
{f.value}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : <></>;
|
||||
})}
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</Loader>
|
||||
)}
|
||||
|
|
@ -141,14 +165,15 @@ function FilterModal(props: Props) {
|
|||
}
|
||||
|
||||
export default connect((state: any, props: any) => {
|
||||
return ({
|
||||
filters: props.isLive ? state.getIn([ 'search', 'filterListLive' ]) : state.getIn([ 'search', 'filterList' ]),
|
||||
filterSearchList: props.isLive ? state.getIn([ 'liveSearch', 'filterSearchList' ]) : state.getIn([ 'search', 'filterSearchList' ]),
|
||||
// filterSearchList: state.getIn([ 'search', 'filterSearchList' ]),
|
||||
// liveFilterSearchList: state.getIn([ 'liveSearch', 'filterSearchList' ]),
|
||||
// metaOptions: state.getIn([ 'customFields', 'list' ]),
|
||||
return {
|
||||
filters: props.isLive
|
||||
? state.getIn(['search', 'filterListLive'])
|
||||
: state.getIn(['search', 'filterList']),
|
||||
filterSearchList: props.isLive
|
||||
? state.getIn(['liveSearch', 'filterSearchList'])
|
||||
: state.getIn(['search', 'filterSearchList']),
|
||||
fetchingFilterSearchList: props.isLive
|
||||
? state.getIn(['liveSearch', 'fetchFilterSearch', 'loading'])
|
||||
: state.getIn(['search', 'fetchFilterSearch', 'loading']),
|
||||
})
|
||||
? state.getIn(['liveSearch', 'fetchFilterSearch', 'loading'])
|
||||
: state.getIn(['search', 'fetchFilterSearch', 'loading']),
|
||||
};
|
||||
})(FilterModal);
|
||||
|
|
|
|||
|
|
@ -5,16 +5,27 @@ import FilterModal from 'Shared/Filters/FilterModal';
|
|||
import { debounce } from 'App/utils';
|
||||
import { assist as assistRoute, isRoute } from 'App/routes';
|
||||
import { addFilterByKeyAndValue, fetchFilterSearch } from 'Duck/search';
|
||||
import {
|
||||
addFilterByKeyAndValue as liveAddFilterByKeyAndValue,
|
||||
fetchFilterSearch as liveFetchFilterSearch,
|
||||
} from 'Duck/liveSearch';
|
||||
const ASSIST_ROUTE = assistRoute();
|
||||
|
||||
interface Props {
|
||||
fetchFilterSearch: (query: any) => void;
|
||||
addFilterByKeyAndValue: (key: string, value: string) => void;
|
||||
filterSearchListLive: any;
|
||||
liveAddFilterByKeyAndValue: (key: string, value: string) => void;
|
||||
filterSearchList: any;
|
||||
liveFetchFilterSearch: any;
|
||||
}
|
||||
function SessionSearchField(props: Props) {
|
||||
const debounceFetchFilterSearch = React.useCallback(debounce(props.fetchFilterSearch, 1000), []);
|
||||
const isLive =
|
||||
isRoute(ASSIST_ROUTE, window.location.pathname) ||
|
||||
window.location.pathname.includes('multiview');
|
||||
const debounceFetchFilterSearch = React.useCallback(
|
||||
debounce(isLive ? props.liveFetchFilterSearch : props.fetchFilterSearch, 1000),
|
||||
[]
|
||||
);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
|
|
@ -24,7 +35,9 @@ function SessionSearchField(props: Props) {
|
|||
};
|
||||
|
||||
const onAddFilter = (filter: any) => {
|
||||
props.addFilterByKeyAndValue(filter.key, filter.value);
|
||||
isLive
|
||||
? props.liveAddFilterByKeyAndValue(filter.key, filter.value)
|
||||
: props.addFilterByKeyAndValue(filter.key, filter.value);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -47,10 +60,7 @@ function SessionSearchField(props: Props) {
|
|||
searchQuery={searchQuery}
|
||||
isMainSearch={true}
|
||||
onFilterClick={onAddFilter}
|
||||
isLive={
|
||||
isRoute(ASSIST_ROUTE, window.location.pathname) ||
|
||||
window.location.pathname.includes('multiview')
|
||||
}
|
||||
isLive={isLive}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -58,10 +68,9 @@ function SessionSearchField(props: Props) {
|
|||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state: any) => ({
|
||||
filterSearchList: state.getIn(['search', 'filterSearchList']),
|
||||
filterSearchListLive: state.getIn(['liveSearch', 'filterSearchList']),
|
||||
}),
|
||||
{ addFilterByKeyAndValue, fetchFilterSearch }
|
||||
)(SessionSearchField);
|
||||
export default connect(null, {
|
||||
addFilterByKeyAndValue,
|
||||
fetchFilterSearch,
|
||||
liveFetchFilterSearch,
|
||||
liveAddFilterByKeyAndValue,
|
||||
})(SessionSearchField);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue