feat(ui) - filters - metadata and other ui changes

This commit is contained in:
Shekar Siri 2022-02-04 19:41:52 +01:00
parent 7d3103071e
commit a55b208434
14 changed files with 111 additions and 80 deletions

View file

@ -54,7 +54,7 @@ function CustomMetricWidget(props: Props) {
console.log('err', errors)
} else {
const _data = getChartFormatter(period)(data[0]);
console.log('__data', _data)
// console.log('__data', _data)
setData({ chart: _data });
}
}).finally(() => setLoading(false));

View file

@ -11,6 +11,7 @@ interface Props {
save: (metric) => void;
loading: boolean;
}
function CustomMetricForm(props: Props) {
const { metric, loading } = props;
@ -18,15 +19,16 @@ function CustomMetricForm(props: Props) {
const newSeries = {
name: `Series ${metric.series.size + 1}`,
type: '',
series: [],
// series: [],
filter: {
type: '',
value: '',
filters: [],
},
};
props.editMetric({
...metric,
series: [...metric.series, newSeries],
series: metric.series.concat(newSeries),
});
}
@ -40,10 +42,10 @@ function CustomMetricForm(props: Props) {
});
}
const write = ({ target: { value, name } }) => props.editMetric({ ...metric.toData(), [ name ]: value })
const write = ({ target: { value, name } }) => props.editMetric({ ...metric, [ name ]: value })
const changeConditionTab = (e, { name, value }) => {
props.editMetric({ ...metric.toData(), [ 'type' ]: value })
props.editMetric({ ...metric, [ 'type' ]: value })
};
return (

View file

@ -1,9 +1,9 @@
import React from 'react';
import { Icon } from 'UI';
import { Icon, Loader } from 'UI';
import { connect } from 'react-redux';
import cn from 'classnames';
import stl from './FilterModal.css';
import { filtersMap } from 'Types/filter/newFilter';
import { filtersMap, getMetaDataFilter } from 'Types/filter/newFilter';
import { FilterKey, FilterType } from 'Types/filter/filterType';
interface Props {
@ -11,21 +11,31 @@ interface Props {
onFilterClick?: (filter) => void,
filterSearchList: any,
metaOptions: any,
isMainSearch?: boolean,
fetchingFilterSearchList: boolean,
searchQuery?: string,
}
function FilterModal(props: Props) {
const { filters, metaOptions, onFilterClick = () => null, filterSearchList } = props;
const {
filters,
metaOptions,
onFilterClick = () => null,
filterSearchList,
isMainSearch = false,
fetchingFilterSearchList,
searchQuery = '',
} = props;
const hasFilerSearchList = filterSearchList && Object.keys(filterSearchList).length > 0;
const hasSearchQuery = searchQuery && searchQuery.length > 0;
const showSearchList = isMainSearch && searchQuery.length > 0;
const allFilters = Object.assign({}, filtersMap);
const allFilters = Object.assign({}, filters);
if (metaOptions.size > 0) {
allFilters['Metadata'] = [];
metaOptions.forEach((option) => {
if (option.key) {
allFilters[option.key] = {
category: FilterKey.METADATA,
key: option.key,
name: option.key,
label: option.key,
};
const _metaFilter = getMetaDataFilter(option.key, option.value);
allFilters['Metadata'].push(_metaFilter);
}
});
}
@ -36,51 +46,55 @@ function FilterModal(props: Props) {
onFilterClick(_filter);
}
return (
<div className={stl.wrapper} style={{ width: '490px', height: '400px', overflowY: 'auto'}}>
{ hasFilerSearchList && (
<div className="border-b -mx-6 px-6 mb-3">
{ filterSearchList && Object.keys(filterSearchList).map((key, index) => {
const filter = filterSearchList[key];
const option = filtersMap[key];
return (
<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>
))}
<div className={stl.wrapper} style={{ width: '490px', maxHeight: '400px', overflowY: 'auto'}}>
{ showSearchList && (
<Loader size="small" loading={fetchingFilterSearchList}>
<div className="-mx-6 px-6">
{ filterSearchList && Object.keys(filterSearchList).map((key, index) => {
const filter = filterSearchList[key];
const option = filtersMap[key];
return (
<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>
))}
</div>
</div>
</div>
);
})}
</div>
);
})}
</div>
</Loader>
)}
<div className="" style={{ columns: "100px 2" }}>
{filters && Object.keys(filters).map((key) => (
<div className="mb-6">
<div className="uppercase font-medium mb-1 color-gray-medium tracking-widest text-sm">{key}</div>
<div>
{filters[key].map((filter: any) => (
<div className={cn(stl.optionItem, "flex items-center py-2 cursor-pointer -mx-2 px-2")} onClick={() => onFilterClick(filter)}>
<Icon name={filter.icon} size="16"/>
<span className="ml-2">{filter.label}</span>
</div>
))}
{ !hasSearchQuery && (
<div className="" style={{ columns: "100px 2" }}>
{allFilters && Object.keys(allFilters).map((key) => (
<div className="mb-6" key={key}>
<div className="uppercase font-medium mb-1 color-gray-medium tracking-widest text-sm">{key}</div>
<div>
{allFilters[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)}>
<Icon name={filter.icon} size="16"/>
<span className="ml-2">{filter.label}</span>
</div>
))}
</div>
</div>
</div>
))}
</div>
))}
</div>
)}
</div>
);
}
@ -89,4 +103,5 @@ export default connect(state => ({
filters: state.getIn([ 'filters', 'filterList' ]),
filterSearchList: state.getIn([ 'search', 'filterSearchList' ]),
metaOptions: state.getIn([ 'customFields', 'list' ]),
fetchingFilterSearchList: state.getIn([ 'search', 'fetchFilterSearch', 'loading' ]),
}))(FilterModal);

View file

@ -20,7 +20,11 @@ function FilterSelection(props: Props) {
setShowModal(false)
}, 200)}
>
{ children ? React.cloneElement(children, { onClick: () => setShowModal(true)}) : (
{ children ? React.cloneElement(children, { onClick: (e) => {
e.stopPropagation();
e.preventDefault();
setShowModal(true);
}}) : (
<div
className="rounded py-1 px-3 flex items-center cursor-pointer bg-gray-lightest text-ellipsis hover:bg-gray-light-shade"
style={{ width: '140px', height: '26px', border: 'solid thin #e9e9e9' }}

View file

@ -130,7 +130,9 @@ function FilterValue(props: Props) {
renderValueFiled(filter.value, 0)
) : (
filter.value && filter.value.map((value, valueIndex) => (
renderValueFiled(value, valueIndex)
<div key={valueIndex}>
{renderValueFiled(value, valueIndex)}
</div>
))
)}
</div>

View file

@ -24,7 +24,7 @@ const MainSearchBar = (props: Props) => {
<span className="font-medium">Clear</span>
</Button>
}
content={'Clear all filters and search'}
content={'Clear Steps'}
size="tiny"
inverted
position="top right"

View file

@ -8,7 +8,7 @@ export default function SaveFunnelButton() {
<div>
<IconButton
className="mr-2"
onClick={() => setshowModal(true)} primaryText label="SAVE FUNNEL" icon="zoom-in"
onClick={() => setshowModal(true)} primaryText label="SAVE FUNNEL" icon="funnel"
/>
<FunnelSaveModal

View file

@ -74,8 +74,9 @@ function SaveSearchModal(props: Props) {
/>
</Form.Field>
</Form>
{ savedSearch && <div className="mt-2">Changes in filters will be updated.</div> }
</Modal.Content>
<Modal.Actions className="flex items-center">
<Modal.Actions className="flex items-center px-6">
<div className="mr-auto">
<Button
primary
@ -86,7 +87,9 @@ function SaveSearchModal(props: Props) {
</Button>
<Button className={ stl.cancelButton } marginRight onClick={ closeHandler }>{ 'Cancel' }</Button>
</div>
{ savedSearch && <Button className={ stl.cancelButton } marginRight onClick={ onDelete }>{ 'Delete' }</Button> }
{ savedSearch && <Button className={ stl.cancelButton } marginRight onClick={ onDelete }>
<Icon name="trash" size="18" />
</Button> }
</Modal.Actions>
</Modal>
);

View file

@ -22,7 +22,7 @@ function Row ({ name, onClick, onClickEdit, onDelete }) {
>
<div className="px-3 py-2">{name}</div>
<div className="ml-auto flex items-center">
<div className="cursor-pointer px-2 hover:bg-active-blue" onClick={onClickEdit}><Icon name="pencil" size="14" /></div>
{/* <div className="cursor-pointer px-2 hover:bg-active-blue" onClick={onClickEdit}><Icon name="pencil" size="14" /></div> */}
{/* <div className="cursor-pointer px-2 hover:bg-active-blue" onClick={onDelete}><Icon name="trash" size="14" /></div> */}
</div>
</div>
@ -53,7 +53,7 @@ function SavedSearchDropdown(props: Props) {
}
return (
<div className={stl.wrapper}>
<div className={cn(stl.wrapper, 'shadow')}>
{props.list.map(item => (
<Row
key={item.searchId}

View file

@ -9,13 +9,13 @@ import { debounce } from 'App/utils';
import { edit as editFilter } from 'Duck/search';
import {
addEvent, applyFilter, moveEvent, clearEvents,
addCustomFilter, addAttribute, setSearchQuery, setActiveFlow, setFilterOption
addCustomFilter, addAttribute, setActiveFlow, setFilterOption
} from 'Duck/filters';
interface Props {
setSearchQuery: (query: string) => void;
// setSearchQuery: (query: string) => void;
fetchFilterSearch: (query: any) => void;
searchQuery: string;
// searchQuery: string;
appliedFilter: any;
editFilter: typeof editFilter;
}
@ -23,9 +23,10 @@ function SessionSearchField(props: Props) {
const { appliedFilter } = props;
const debounceFetchFilterSearch = debounce(props.fetchFilterSearch, 1000)
const [showModal, setShowModal] = useState(false)
const [searchQuery, setSearchQuery] = useState('')
const onSearchChange = (e, { value }) => {
// props.setSearchQuery(value)
setSearchQuery(value)
debounceFetchFilterSearch({ q: value });
}
@ -58,17 +59,11 @@ function SessionSearchField(props: Props) {
autocomplete="off"
/>
{/* <FilterModal
close={ () => setShowModal(false) }
displayed={ showModal }
// displayed={ true }
// loading={ loading }
// searchedEvents={ searchedEvents }
searchQuery={ props.searchQuery }
/> */}
{ showModal && (
<div className="absolute left-0 top-20 border shadow rounded bg-white z-50">
<FilterModal
searchQuery={searchQuery}
isMainSearch={true}
onFilterClick={onAddFilter}
/>
</div>
@ -79,8 +74,7 @@ function SessionSearchField(props: Props) {
export default connect(state => ({
events: state.getIn([ 'filters', 'appliedFilter', 'events' ]),
// appliedFilter: state.getIn([ 'filters', 'appliedFilter' ]),
searchQuery: state.getIn([ 'filters', 'searchQuery' ]),
// searchQuery: state.getIn([ 'filters', 'searchQuery' ]),
appliedFilterKeys: state.getIn([ 'filters', 'appliedFilter', 'filters' ])
.map(({type}) => type).toJS(),
searchedEvents: state.getIn([ 'events', 'list' ]),
@ -88,4 +82,4 @@ export default connect(state => ({
strict: state.getIn([ 'filters', 'appliedFilter', 'strict' ]),
blink: state.getIn([ 'funnels', 'blink' ]),
appliedFilter: state.getIn(['search', 'instance']),
}), { setSearchQuery, fetchFilterSearch, editFilter })(SessionSearchField);
}), { fetchFilterSearch, editFilter })(SessionSearchField);

View file

@ -72,7 +72,6 @@ function reducer(state = initialState, action = {}) {
acc[key].push({ projectId, value });
return acc;
}, {});
console.log('groupedList', groupedList);
return state.set('filterSearchList', groupedList);
case APPLY_SAVED_SEARCH:
return state.set('savedSearch', action.filter);
@ -85,6 +84,7 @@ export default mergeReducers(
createRequestReducer({
[ ROOT_KEY ]: FETCH_LIST,
fetch: FETCH,
fetchFilterSearch: FETCH_FILTER_SEARCH
}),
);

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" class="bi bi-funnel" viewBox="0 0 16 16">
<path d="M1.5 1.5A.5.5 0 0 1 2 1h12a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.128.334L10 8.692V13.5a.5.5 0 0 1-.342.474l-3 1A.5.5 0 0 1 6 14.5V8.692L1.628 3.834A.5.5 0 0 1 1.5 3.5v-2zm1 .5v1.308l4.372 4.858A.5.5 0 0 1 7 8.5v5.306l2-.666V8.5a.5.5 0 0 1 .128-.334L13.5 3.308V2h-11z"/>
</svg>

After

Width:  |  Height:  |  Size: 361 B

View file

@ -47,6 +47,7 @@ export default Record({
return filter;
});
delete series._key
delete series.key
return series;
});

View file

@ -1,6 +1,7 @@
import Record from 'Types/Record';
import { FilterType, FilterKey, FilterCategory } from './filterType'
import filterOptions, { countries, platformOptions } from 'App/constants';
import { capitalize } from 'App/utils';
const countryOptions = Object.keys(countries).map(i => ({ text: countries[i], value: i }));
@ -55,6 +56,12 @@ export const filtersMap = {
[FilterKey.ISSUE]: { key: FilterKey.ISSUE, type: FilterType.ISSUE, category: FilterCategory.JAVASCRIPT, label: 'Issue', operator: 'is', operatorOptions: filterOptions.baseOperators, icon: 'filters/click', options: ISSUE_OPTIONS },
}
export const getMetaDataFilter = (key) => {
const METADATA_FILTER = { key: key, type: FilterType.MULTIPLE, category: FilterCategory.METADATA, label: capitalize(key), operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/metadata' }
return METADATA_FILTER;
}
export default Record({
timestamp: 0,
key: '',