ui: filter modal wip

This commit is contained in:
nick-delirium 2024-11-19 11:33:34 +01:00
parent da632304a1
commit 36feeb5ba9
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
7 changed files with 154 additions and 88 deletions

View file

@ -2,8 +2,6 @@
border-radius: .5rem;
border: solid thin $gray-light;
padding: 20px;
overflow: hidden;
overflow-y: auto;
box-shadow: 0 2px 2px 0 $gray-light;
}
.optionItem {

View file

@ -7,11 +7,14 @@ import {
CircleAlert,
Clock2,
Code,
ContactRound, CornerDownRight,
ContactRound,
CornerDownRight,
Cpu,
Earth,
FileStack, Layers,
MapPin, Megaphone,
FileStack,
Layers,
MapPin,
Megaphone,
MemoryStick,
MonitorSmartphone,
Navigation,
@ -25,11 +28,13 @@ import {
Timer,
VenetianMask,
Workflow,
Flag
Flag,
ChevronRight,
} from 'lucide-react';
import React from 'react';
import { Icon, Loader } from 'UI';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
import { Input } from 'antd';
import { FilterKey } from 'Types/filter/filterType';
import stl from './FilterModal.module.css';
@ -69,7 +74,7 @@ const IconMap = {
[FilterKey.UTM_SOURCE]: <CornerDownRight size={18} />,
[FilterKey.UTM_MEDIUM]: <Layers size={18} />,
[FilterKey.UTM_CAMPAIGN]: <Megaphone size={18} />,
[FilterKey.FEATURE_FLAG]: <Flag size={18} />
[FilterKey.FEATURE_FLAG]: <Flag size={18} />,
};
function filterJson(
@ -103,7 +108,7 @@ export const getMatchingEntries = (
if (lowerCaseQuery.length === 0)
return {
matchingCategories: Object.keys(filters),
matchingFilters: filters
matchingFilters: filters,
};
Object.keys(filters).forEach((name) => {
@ -141,19 +146,28 @@ function FilterModal(props: Props) {
isLive,
onFilterClick = () => null,
isMainSearch = false,
searchQuery = '',
excludeFilterKeys = [],
allowedFilterKeys = [],
isConditional,
} = props;
const [searchQuery, setSearchQuery] = React.useState('');
const [category, setCategory] = React.useState('ALL');
const { searchStore, searchStoreLive, projectsStore } = useStore();
const isMobile = projectsStore.active?.platform === 'ios'; // TODO - should be using mobile once the app is changed
const filters = isLive ? searchStoreLive.filterListLive : (isMobile ? searchStore.filterListMobile : searchStoreLive.filterList);
const filters = isLive
? searchStoreLive.filterListLive
: isMobile
? searchStore.filterListMobile
: searchStoreLive.filterList;
const conditionalFilters = searchStore.filterListConditional;
const mobileConditionalFilters = searchStore.filterListMobileConditional;
const showSearchList = isMainSearch && searchQuery.length > 0;
const filterSearchList = isLive ? searchStoreLive.filterSearchList : searchStore.filterSearchList;
const fetchingFilterSearchList = isLive ? searchStoreLive.loadingFilterSearch : searchStore.loadingFilterSearch;
const filterSearchList = isLive
? searchStoreLive.filterSearchList
: searchStore.filterSearchList;
const fetchingFilterSearchList = isLive
? searchStoreLive.loadingFilterSearch
: searchStore.loadingFilterSearch;
const onFilterSearchClick = (filter: any) => {
const _filter = { ...filtersMap[filter.type] };
@ -162,7 +176,9 @@ function FilterModal(props: Props) {
};
const filterJsonObj = isConditional
? isMobile ? mobileConditionalFilters : conditionalFilters
? isMobile
? mobileConditionalFilters
: conditionalFilters
: filters;
const { matchingCategories, matchingFilters } = getMatchingEntries(
searchQuery,
@ -184,44 +200,95 @@ function FilterModal(props: Props) {
return IconMap[filter.key];
} else return <Icon name={filter.icon} size={16} />;
};
const displayedFilters =
category === 'ALL'
? Object.entries(matchingFilters).flatMap(([category, filters]) =>
filters.map((f: any) => ({ ...f, category }))
)
: matchingFilters[category];
return (
<div
className={stl.wrapper}
style={{ width: '480px', maxHeight: '380px', overflowY: 'auto', borderRadius: '.5rem' }}
style={{ width: '480px', height: '380px', borderRadius: '.5rem' }}
>
<div
className={searchQuery && !isResultEmpty ? 'mb-6' : ''}
style={{ columns: matchingCategories.length > 1 ? 'auto 200px' : 1 }}
>
{matchingCategories.map((key) => {
return (
<Input
className={'mb-4'}
placeholder={'Search'}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
<div className={'flex gap-2 items-start'}>
<div className={'flex flex-col gap-1'} style={{ flex: 1 }}>
{matchingCategories.map((key) => (
<div
className="mb-6 flex flex-col gap-2 break-inside-avoid"
key={key}
className={'rounded p-4 hover:bg-active-blue capitalize'}
>
<div className="uppercase font-medium mb-1 color-gray-medium tracking-widest text-sm">
{key}
</div>
<div>
{matchingFilters[key] &&
matchingFilters[key].map((filter: Record<string, any>) => (
<div
key={filter.label}
className={cn(
stl.optionItem,
'flex items-center py-2 cursor-pointer -mx-2 px-2 gap-2 rounded-lg hover:shadow-sm'
)}
onClick={() => onFilterClick({ ...filter, value: [''] })}
>
{getNewIcon(filter)}
<span>{filter.label}</span>
</div>
))}
</div>
{key.toLowerCase()}
</div>
);
})}
))}
</div>
<div
className={'flex flex-col gap-2 overflow-y-auto w-full'}
style={{ maxHeight: 300, flex: 2 }}
>
{displayedFilters.length
? displayedFilters.map((filter: Record<string, any>) => (
<div
key={filter.label}
className={cn(
'flex items-center p-2 cursor-pointer gap-1 rounded-lg hover:bg-active-blue'
)}
onClick={() => onFilterClick({ ...filter, value: [''] })}
>
{filter.category ? <div style={{ width: 150 }} className={'text-disabled-text w-full flex justify-between items-center'}>
<span>{filter.category}</span>
<ChevronRight size={14} />
</div> : null}
<div className={'flex items-center gap-2'}>
{getNewIcon(filter)}
<span>{filter.label}</span>
</div>
</div>
))
: null}
</div>
</div>
{/*<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 break-inside-avoid"*/}
{/* 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: Record<string, any>) => (*/}
{/* <div*/}
{/* key={filter.label}*/}
{/* className={cn(*/}
{/* stl.optionItem,*/}
{/* 'flex items-center py-2 cursor-pointer -mx-2 px-2 gap-2 rounded-lg hover:shadow-sm'*/}
{/* )}*/}
{/* onClick={() => onFilterClick({ ...filter, value: [''] })}*/}
{/* >*/}
{/* {getNewIcon(filter)}*/}
{/* <span>{filter.label}</span>*/}
{/* </div>*/}
{/* ))}*/}
{/* </div>*/}
{/* </div>*/}
{/* );*/}
{/* })}*/}
{/*</div>*/}
{showSearchList && (
<Loader loading={fetchingFilterSearchList}>
<div className="-mx-6 px-6">

View file

@ -37,7 +37,7 @@ function FilterSelection(props: Props) {
<OutsideClickDetectingDiv
className="relative"
onClickOutside={() =>
setTimeout(function() {
setTimeout(function () {
setShowModal(false);
}, 200)
}
@ -49,12 +49,19 @@ function FilterSelection(props: Props) {
e.preventDefault();
setShowModal(true);
},
disabled: disabled
disabled: disabled,
})
) : (
<div
className={cn('rounded-lg py-1 px-3 flex items-center cursor-pointer bg-gray-lightest text-ellipsis hover:bg-gray-light-shade', { 'opacity-50 pointer-events-none': disabled })}
style={{ width: '150px', height: '26px', border: 'solid thin #e9e9e9' }}
className={cn(
'rounded-lg py-1 px-3 flex items-center cursor-pointer bg-gray-lightest text-ellipsis hover:bg-gray-light-shade',
{ 'opacity-50 pointer-events-none': disabled }
)}
style={{
width: '150px',
height: '26px',
border: 'solid thin #e9e9e9',
}}
onClick={() => setShowModal(true)}
>
<div
@ -66,19 +73,19 @@ function FilterSelection(props: Props) {
<Icon name="chevron-down" size="14" />
</div>
)}
{showModal && (
<div className="absolute mt-2 left-0 rounded-lg shadow bg-white z-50">
<FilterModal
isLive={isRoute(ASSIST_ROUTE, window.location.pathname)}
onFilterClick={onFilterClick}
excludeFilterKeys={excludeFilterKeys}
allowedFilterKeys={allowedFilterKeys}
isConditional={isConditional}
isMobile={isMobile}
/>
</div>
)}
</OutsideClickDetectingDiv>
{showModal && (
<div className="absolute left-0 rounded-lg shadow bg-white z-50">
<FilterModal
isLive={isRoute(ASSIST_ROUTE, window.location.pathname)}
onFilterClick={onFilterClick}
excludeFilterKeys={excludeFilterKeys}
allowedFilterKeys={allowedFilterKeys}
isConditional={isConditional}
isMobile={isMobile}
/>
</div>
)}
</div>
);
}

View file

@ -1,16 +1,12 @@
import React, { useEffect } from 'react';
import FilterList from 'Shared/Filters/FilterList';
import FilterSelection from 'Shared/Filters/FilterSelection';
import { Button } from 'UI';
import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore';
function LiveSessionSearch() {
const { projectsStore, searchStoreLive, sessionStore } = useStore();
const { projectsStore, searchStoreLive } = useStore();
const saveRequestPayloads = projectsStore.active?.saveRequestPayloads;
const appliedFilter = searchStoreLive.instance;
const hasEvents = appliedFilter.filters.filter((i) => i.isEvent).length > 0;
const hasFilters = appliedFilter.filters.filter((i) => !i.isEvent).length > 0;
useEffect(() => {
void searchStoreLive.fetchSessions();

View file

@ -73,7 +73,6 @@ function SessionSearchField(props: Props) {
{showModal && (
<div className="absolute left-0 shadow-sm rounded-lg bg-white z-50">
<FilterModal
searchQuery={searchQuery}
isMainSearch={true}
onFilterClick={onAddFilter}
isLive={isLive}

View file

@ -58,7 +58,6 @@ function SessionSearchField(props: Props) {
{showModal && (
<div className="absolute left-0 shadow-sm rounded-lg bg-white z-50">
<FilterModal
searchQuery={searchQuery}
isMainSearch={true}
onFilterClick={onAddFilter}
isLive={isLive}

View file

@ -2900,21 +2900,21 @@ __metadata:
languageName: node
linkType: hard
"@tanstack/query-core@npm:5.59.20":
version: 5.59.20
resolution: "@tanstack/query-core@npm:5.59.20"
checksum: 10c1/bfac064d0344ab32e2718c1f75552072283793780f9e22a7fb3c84d0bb560de3311c8b9c33505a20904ade136acba74f9f56f837297dbb1619f2db179b27d4da
"@tanstack/query-core@npm:5.60.5":
version: 5.60.5
resolution: "@tanstack/query-core@npm:5.60.5"
checksum: 10c1/dd9b77e7bf3073470756384574085097bb00f9682ac1621c26f1cf9089e93ba45b7e6720579187b83c49ab697665768d00643add0bd68d482610d87137a30ae1
languageName: node
linkType: hard
"@tanstack/react-query@npm:^5.56.2":
version: 5.60.2
resolution: "@tanstack/react-query@npm:5.60.2"
version: 5.60.5
resolution: "@tanstack/react-query@npm:5.60.5"
dependencies:
"@tanstack/query-core": "npm:5.59.20"
"@tanstack/query-core": "npm:5.60.5"
peerDependencies:
react: ^18 || ^19
checksum: 10c1/a3f01498078002dddb4dcca09ae21b0cb33e6923bc54a4d5c4b8426d50bff6c1e566346180f0f7b51bdd88d7ed33311936dee09c8d24258fe871591e3558a0d4
checksum: 10c1/715c0d83028741bd159e0ce6b60562d9e94b48813ab71061698808d730d0e94dc842bfa45859e116194c19f7c5cebc20fd6f0e170113c38444c203430d83d90f
languageName: node
linkType: hard
@ -4807,12 +4807,12 @@ __metadata:
linkType: hard
"bonjour-service@npm:^1.2.1":
version: 1.2.1
resolution: "bonjour-service@npm:1.2.1"
version: 1.3.0
resolution: "bonjour-service@npm:1.3.0"
dependencies:
fast-deep-equal: "npm:^3.1.3"
multicast-dns: "npm:^7.2.5"
checksum: 10c1/5a3415004efa60ce805845a18fb7b2232097b708cbba21e5432612dbe6a5912013fac9e30f27fa80d0804975e9f20fa22d61e82957d1d171b648cf0967e3336d
checksum: 10c1/c6e6c7a15df4aa4c932ec9df2eb301e38aaecd561151a384181863866d357074a69240280be8210828995ac5262fe82721af46ac1725c89c0c84db2b75a9ee06
languageName: node
linkType: hard
@ -5729,26 +5729,26 @@ __metadata:
linkType: hard
"cross-spawn@npm:^6.0.0":
version: 6.0.5
resolution: "cross-spawn@npm:6.0.5"
version: 6.0.6
resolution: "cross-spawn@npm:6.0.6"
dependencies:
nice-try: "npm:^1.0.4"
path-key: "npm:^2.0.1"
semver: "npm:^5.5.0"
shebang-command: "npm:^1.2.0"
which: "npm:^1.2.9"
checksum: 10c1/ea71fd2b6b32cb716f2fb2768377b7cf531bffec212be25b04d6826af95d6973dcb9fb8ee056932e32549a2b5b07d632f0fe9ec93d1ebf2ec66b10903b15848d
checksum: 10c1/aa1908a9201525d1fb0a67f0d0d5c1abd26fc6e052a05da243c8320d0bf5f4711174682271ea030a547c10202c7ef72af3f7db0c47dfb94132213697fb370412
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3":
version: 7.0.5
resolution: "cross-spawn@npm:7.0.5"
version: 7.0.6
resolution: "cross-spawn@npm:7.0.6"
dependencies:
path-key: "npm:^3.1.0"
shebang-command: "npm:^2.0.0"
which: "npm:^2.0.1"
checksum: 10c1/c5f8aff024602dfd280da19f91065d42c3b472cf9ef275dce1b640648b06420a110072fffd379910c07d8d18b1f81f9eb3ffbc4f069cde6586605445dc609cb9
checksum: 10c1/16d66c65e6e190a063cd75a3a90fd8396a843cb9151e862f28fd952ca4ca6d8821e4d44e0cbd455c20627993ae6c903130928d6c0e6ed2ae88534444f1c16d86
languageName: node
linkType: hard
@ -6695,9 +6695,9 @@ __metadata:
linkType: hard
"electron-to-chromium@npm:^1.5.41":
version: 1.5.60
resolution: "electron-to-chromium@npm:1.5.60"
checksum: 10c1/5677c9062d1b577afa920d7f9e401270d44d19b5791b51044f174f8218aa3cb2af3e39be422d544687110fe70edbe3da60ba832a1a930d4bf99a7ff3f7064964
version: 1.5.63
resolution: "electron-to-chromium@npm:1.5.63"
checksum: 10c1/206b19d6932d22153929a232bbabf788fbf6efad2e9455ee0f7154e2685de8eb6af2c7dd7982d3438e910acdacb76d3095ae891b12e1bd6dca5ac07cf980ba2a
languageName: node
linkType: hard
@ -7693,9 +7693,9 @@ __metadata:
linkType: hard
"flatted@npm:^3.2.9":
version: 3.3.1
resolution: "flatted@npm:3.3.1"
checksum: 10c1/b844833190688feb603d789f42c1413395cb3246abff628739a2d2b86a464a81793b09d11d6cb5a521023bc2cf657aec053793db0acabac47601ded34220977a
version: 3.3.2
resolution: "flatted@npm:3.3.2"
checksum: 10c1/e3a2065b2741881be6cf16b64c28be87b5c99d4544b3702a93e54b2aa9a792c85974fb8b485c3a26ff60359f2f85e97d124b9e56b018b2eba2a047706cefbf0a
languageName: node
linkType: hard