change(ui): improve the url params to search
This commit is contained in:
parent
361eb0e35b
commit
8c5ef24bcc
6 changed files with 216 additions and 141 deletions
|
|
@ -75,11 +75,11 @@ function FilterOperator(props: Props) {
|
|||
<div className="mx-2">
|
||||
<Select
|
||||
name="operator"
|
||||
options={options}
|
||||
options={options || []}
|
||||
styles={dropdownStyles}
|
||||
placeholder="Select"
|
||||
isDisabled={isDisabled}
|
||||
value={value ? options.find((i: any) => i.value === value) : null}
|
||||
value={value ? options?.find((i: any) => i.value === value) : null}
|
||||
onChange={({ value }: any) => onChange(null, { name: 'operator', value: value.value })}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useHistory } from 'react-router';
|
||||
import { createUrlQuery, getFiltersFromQuery } from 'App/utils/search';
|
||||
import { JsonUrlConverter } from 'App/utils/search';
|
||||
import { useStore } from '@/mstore';
|
||||
import Search from '@/mstore/types/search';
|
||||
import { getFilterFromJson } from 'Types/filter/newFilter';
|
||||
|
||||
interface Props {
|
||||
onBeforeLoad?: () => Promise<any>;
|
||||
|
|
@ -23,7 +25,9 @@ const useSessionSearchQueryHandler = (props: Props) => {
|
|||
setBeforeHookLoaded(true);
|
||||
}
|
||||
|
||||
const filter = getFiltersFromQuery(history.location.search, appliedFilter);
|
||||
const converter = JsonUrlConverter.urlParamsToJson(history.location.search);
|
||||
const json: any = getFilterFromJson(converter.toJSON());
|
||||
const filter = new Search(json);
|
||||
searchStore.applyFilter(filter, true);
|
||||
}
|
||||
};
|
||||
|
|
@ -34,8 +38,8 @@ const useSessionSearchQueryHandler = (props: Props) => {
|
|||
useEffect(() => {
|
||||
const generateUrlQuery = () => {
|
||||
if (!loading && beforeHookLoaded) {
|
||||
const search: any = createUrlQuery(appliedFilter);
|
||||
history.replace({ search });
|
||||
const converter = JsonUrlConverter.jsonToUrlParams(appliedFilter);
|
||||
history.replace({ search: converter });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ export default class FilterItem {
|
|||
const json = {
|
||||
type: isMetadata ? FilterKey.METADATA : this.key,
|
||||
isEvent: Boolean(this.isEvent),
|
||||
value: this.value.map((i: any) => i && i.toString()),
|
||||
value: this.value.map((i: any) => i ? i.toString() : ''),
|
||||
operator: this.operator,
|
||||
source: isMetadata ? this.key.replace(/^_/, '') : this.source,
|
||||
sourceOperator: this.sourceOperator,
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ export default class Search {
|
|||
return js;
|
||||
}
|
||||
|
||||
static fromJS({ eventsOrder, filters, events, custom, ...filterData }: any) {
|
||||
fromJS({ eventsOrder, filters, events, custom, ...filterData }: any) {
|
||||
let startDate, endDate;
|
||||
const rValue = filterData.rangeValue || rangeValue;
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ export default class Search {
|
|||
eventsOrder,
|
||||
startDate,
|
||||
endDate,
|
||||
events: events.map((event: any) => new Event(event)),
|
||||
// events: events.map((event: any) => new Event(event)),
|
||||
filters: filters.map((i: any) => {
|
||||
const filter = new Filter(i).toData();
|
||||
if (Array.isArray(i.filters)) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import {
|
||||
clickSelectorOperators,
|
||||
clickSelectorOperators
|
||||
} from 'App/constants/filterOptions';
|
||||
import Record from 'Types/Record';
|
||||
import { FilterType, FilterKey, FilterCategory } from './filterType';
|
||||
import filterOptions, { countries, platformOptions } from 'App/constants';
|
||||
import { observable } from 'mobx';
|
||||
import Search from '@/mstore/types/search';
|
||||
|
||||
const countryOptions = Object.keys(countries).map(i => ({ label: countries[i], value: i }));
|
||||
const containsFilters = [{ key: 'contains', label: 'contains', text: 'contains', value: 'contains' }];
|
||||
|
|
@ -981,9 +982,9 @@ export const filterLabelMap = filters.reduce((acc, filter) => {
|
|||
return acc;
|
||||
}, {});
|
||||
|
||||
export let filtersMap = observable(mapFilters(filters))
|
||||
export let liveFiltersMap = observable(mapLiveFilters(filters))
|
||||
export let fflagsConditionsMap = observable(mapFilters(flagConditionFilters))
|
||||
export let filtersMap = observable(mapFilters(filters));
|
||||
export let liveFiltersMap = observable(mapLiveFilters(filters));
|
||||
export let fflagsConditionsMap = observable(mapFilters(flagConditionFilters));
|
||||
export let conditionalFiltersMap = observable(mapFilters(conditionalFilters));
|
||||
export let mobileConditionalFiltersMap = observable(mapFilters(mobileConditionalFilters));
|
||||
|
||||
|
|
@ -1255,3 +1256,34 @@ export const generateLiveFilterOptions = (map) => {
|
|||
});
|
||||
return filterSection;
|
||||
};
|
||||
|
||||
|
||||
export const getFilterFromJson = (json) => {
|
||||
const mapFilters = (filters) =>
|
||||
filters.map(f => {
|
||||
let filter = { ...filtersMap[f.key], ...f };
|
||||
|
||||
if (f.filters) {
|
||||
const mainFilterOptions = filtersMap[f.key]?.filters || [];
|
||||
const subFilterMap = Object.fromEntries(mainFilterOptions.map(option => [option.key, option]));
|
||||
|
||||
filter.filters = f.filters.map(subFilter => {
|
||||
const baseFilter = subFilterMap[subFilter.key] || filtersMap[subFilter.type] || {};
|
||||
return {
|
||||
...baseFilter,
|
||||
...subFilter,
|
||||
key: baseFilter.key || subFilter.key,
|
||||
type: baseFilter.type || subFilter.type,
|
||||
value: subFilter.value?.length ? subFilter.value : ['']
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return filter;
|
||||
});
|
||||
|
||||
return new Search({
|
||||
...json,
|
||||
filters: mapFilters(json.filters)
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,137 +1,176 @@
|
|||
import { getFilterKeyTypeByKey, setQueryParamKeyFromFilterkey } from 'Types/filter/filterType';
|
||||
import Period, { CUSTOM_RANGE } from 'Types/app/period';
|
||||
import { filtersMap } from 'Types/filter/newFilter';
|
||||
import Search from '@/mstore/types/search';
|
||||
|
||||
type QueryItem = {
|
||||
key: any;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export const createUrlQuery = (filter: { filters: any[]; rangeValue: string; startDate?: string; endDate?: string }) => {
|
||||
const query: QueryItem[] = [];
|
||||
class Filter {
|
||||
key: string;
|
||||
operator: string;
|
||||
value?: string[];
|
||||
filters?: Filter[];
|
||||
|
||||
filter.filters.forEach((f: any) => {
|
||||
if (!f.value.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let str = `${f.operator}|${f.value.join('|')}`;
|
||||
if (f.hasSource) {
|
||||
const sourceOperator = f.sourceOperator || '';
|
||||
const source = f.source ? f.source.join('|') : '';
|
||||
str = `${str}^${sourceOperator}|${source}`;
|
||||
}
|
||||
|
||||
let key: any = setQueryParamKeyFromFilterkey(f.key);
|
||||
if (!key) {
|
||||
key = [f.key];
|
||||
}
|
||||
|
||||
query.push({ key, value: str });
|
||||
});
|
||||
|
||||
if (query.length > 0) {
|
||||
query.push({ key: 'range', value: filter.rangeValue });
|
||||
if (filter.rangeValue === CUSTOM_RANGE) {
|
||||
query.push({ key: 'rStart', value: filter.startDate! });
|
||||
query.push({ key: 'rEnd', value: filter.endDate! });
|
||||
}
|
||||
constructor(key: string, operator: string, value?: string[], filters?: Filter[]) {
|
||||
this.key = key;
|
||||
this.operator = operator;
|
||||
this.value = value;
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
return query.map(({ key, value }) => `${key}=${value}`).join('&');
|
||||
};
|
||||
|
||||
|
||||
export const getFiltersFromQuery = (search: string, filter: any) => {
|
||||
if (!search || filter.filters.size > 0) {
|
||||
return;
|
||||
toJSON(): any {
|
||||
return {
|
||||
key: this.key,
|
||||
operator: this.operator,
|
||||
value: this.value,
|
||||
filters: this.filters ? this.filters.map(f => f.toJSON()) : undefined
|
||||
};
|
||||
}
|
||||
|
||||
const entries = getQueryObject(search);
|
||||
const period: any = getPeriodFromEntries(entries);
|
||||
const filters = getFiltersFromEntries(entries);
|
||||
|
||||
return new Search({ filters, rangeValue: period.rangeName, startDate: period.start, endDate: period.end });
|
||||
};
|
||||
|
||||
const getFiltersFromEntries = (entries: any) => {
|
||||
const _filters: any = { ...filtersMap };
|
||||
const filters: any = [];
|
||||
if (entries.length > 0) {
|
||||
entries.forEach((item: any) => {
|
||||
if (!item.key || !item.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
let filter: any = {};
|
||||
const filterKey = getFilterKeyTypeByKey(item.key);
|
||||
const tmp = item.value.split('^');
|
||||
const valueArr = tmp[0].split('|');
|
||||
const operator = valueArr.shift();
|
||||
const sourceArr = tmp[1] ? tmp[1].split('|') : [];
|
||||
const sourceOperator = sourceArr.shift();
|
||||
|
||||
if (filterKey && _filters[filterKey]) {
|
||||
filter = _filters[filterKey];
|
||||
filter.value = valueArr;
|
||||
} else {
|
||||
if (filterKey) {
|
||||
filter.type = filterKey;
|
||||
filter.key = filterKey;
|
||||
} else {
|
||||
filter = _filters[item.key];
|
||||
if (!!filter) {
|
||||
filter.type = filter.key;
|
||||
}
|
||||
}
|
||||
|
||||
if (!filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
filter.value = valueArr;
|
||||
if (filter.icon === 'filters/metadata') {
|
||||
filter.source = filter.type;
|
||||
filter.type = 'MULTIPLE';
|
||||
} else {
|
||||
filter.source = sourceArr && sourceArr.length > 0 ? sourceArr : null;
|
||||
filter.sourceOperator = !!sourceOperator ? decodeURI(sourceOperator) : null;
|
||||
}
|
||||
}
|
||||
|
||||
filter.operator = operator;
|
||||
if (!filter.filters || filter.filters.size === 0) {
|
||||
// TODO support subfilters in url
|
||||
filters.push(filter);
|
||||
}
|
||||
});
|
||||
}
|
||||
return filters;
|
||||
};
|
||||
|
||||
const getPeriodFromEntries = (entries: any) => {
|
||||
const rangeFilter = entries.find(({ key }: any) => key === 'range');
|
||||
if (!rangeFilter) {
|
||||
return Period();
|
||||
}
|
||||
|
||||
if (rangeFilter.value === CUSTOM_RANGE) {
|
||||
const start = entries.find(({ key }: any) => key === 'rStart').value;
|
||||
const end = entries.find(({ key }: any) => key === 'rEnd').value;
|
||||
return Period({ rangeName: rangeFilter.value, start, end });
|
||||
}
|
||||
|
||||
return Period({ rangeName: rangeFilter.value });
|
||||
};
|
||||
|
||||
function getQueryObject(search: any) {
|
||||
return search
|
||||
.slice(1)
|
||||
.split('&')
|
||||
.map((item: any) => {
|
||||
let [key, value] = item.split('=');
|
||||
key = key.replace('[]', '');
|
||||
return { key: key, value: decodeURI(value) };
|
||||
});
|
||||
}
|
||||
|
||||
class InputJson {
|
||||
filters: Filter[];
|
||||
rangeValue: string;
|
||||
startDate: number;
|
||||
endDate: number;
|
||||
sort: string;
|
||||
order: string;
|
||||
strict: boolean;
|
||||
eventsOrder: string;
|
||||
|
||||
constructor(filters: Filter[], rangeValue: string, startDate: number, endDate: number, sort: string, order: string, strict: boolean, eventsOrder: string) {
|
||||
this.filters = filters;
|
||||
// .map((f: any) => {
|
||||
// const subFilters = f.filters ? f.filters.map((sf: any) => new Filter(sf.key, sf.operator, sf.value, sf.filters)) : undefined;
|
||||
// return new Filter(f.key, f.operator, f.value, subFilters);
|
||||
// });
|
||||
this.rangeValue = rangeValue;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
this.sort = sort;
|
||||
this.order = order;
|
||||
this.strict = strict;
|
||||
this.eventsOrder = eventsOrder;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
filters: this.filters.map(f => f.toJSON()),
|
||||
rangeValue: this.rangeValue,
|
||||
startDate: this.startDate,
|
||||
endDate: this.endDate,
|
||||
sort: this.sort,
|
||||
order: this.order,
|
||||
strict: this.strict,
|
||||
eventsOrder: this.eventsOrder
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class JsonUrlConverter {
|
||||
static keyMap = {
|
||||
rangeValue: 'rv',
|
||||
startDate: 'sd',
|
||||
endDate: 'ed',
|
||||
sort: 's',
|
||||
order: 'o',
|
||||
strict: 'st',
|
||||
eventsOrder: 'eo',
|
||||
key: 'k',
|
||||
operator: 'op',
|
||||
value: 'v',
|
||||
filters: 'f'
|
||||
};
|
||||
|
||||
static getDateRangeValues(rangeValue: string, startDate: number | undefined, endDate: number | undefined): [number, number] {
|
||||
if (rangeValue === 'CUSTOM_RANGE') {
|
||||
return [startDate!, endDate!];
|
||||
}
|
||||
const period = Period({ rangeName: rangeValue });
|
||||
return [period.start, period.end];
|
||||
}
|
||||
|
||||
static jsonToUrlParams(json: InputJson): string {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
const addFilterParams = (filter: Filter, prefix: string) => {
|
||||
params.append(`${prefix}${this.keyMap.key}`, filter.key);
|
||||
params.append(`${prefix}${this.keyMap.operator}`, filter.operator);
|
||||
if (filter.value) {
|
||||
filter.value.forEach((v, i) => params.append(`${prefix}${this.keyMap.value}[${i}]`, v || ''));
|
||||
}
|
||||
if (filter.filters) {
|
||||
filter.filters.forEach((f, i) => addFilterParams(f, `${prefix}${this.keyMap.filters}[${i}].`));
|
||||
}
|
||||
};
|
||||
|
||||
json.filters.forEach((filter, index) => addFilterParams(filter, `${this.keyMap.filters}[${index}].`));
|
||||
|
||||
const rangeValues = this.getDateRangeValues(json.rangeValue, json.startDate, json.endDate);
|
||||
|
||||
params.append(this.keyMap.rangeValue, json.rangeValue);
|
||||
params.append(this.keyMap.startDate, rangeValues[0].toString());
|
||||
params.append(this.keyMap.endDate, rangeValues[1].toString());
|
||||
params.append(this.keyMap.sort, json.sort);
|
||||
params.append(this.keyMap.order, json.order);
|
||||
params.append(this.keyMap.strict, json.strict.toString());
|
||||
params.append(this.keyMap.eventsOrder, json.eventsOrder);
|
||||
|
||||
return decodeURIComponent(params.toString());
|
||||
}
|
||||
|
||||
static urlParamsToJson(urlParams: string): InputJson {
|
||||
const params = new URLSearchParams(urlParams);
|
||||
|
||||
const getFilterParams = (prefix: string): Filter => {
|
||||
const key = params.get(`${prefix}${this.keyMap.key}`) || '';
|
||||
const operator = params.get(`${prefix}${this.keyMap.operator}`) || '';
|
||||
const value: string[] = [];
|
||||
let index = 0;
|
||||
while (params.has(`${prefix}${this.keyMap.value}[${index}]`)) {
|
||||
value.push(params.get(`${prefix}${this.keyMap.value}[${index}]`) || '');
|
||||
index++;
|
||||
}
|
||||
const filters: Filter[] = [];
|
||||
index = 0;
|
||||
while (params.has(`${prefix}${this.keyMap.filters}[${index}].${this.keyMap.key}`)) {
|
||||
filters.push(getFilterParams(`${prefix}${this.keyMap.filters}[${index}].`));
|
||||
index++;
|
||||
}
|
||||
return new Filter(key, operator, value.length ? value : '', filters.length ? filters : []);
|
||||
};
|
||||
|
||||
const filters: Filter[] = [];
|
||||
let index = 0;
|
||||
while (params.has(`${this.keyMap.filters}[${index}].${this.keyMap.key}`)) {
|
||||
filters.push(getFilterParams(`${this.keyMap.filters}[${index}].`));
|
||||
index++;
|
||||
}
|
||||
|
||||
const rangeValue = params.get(this.keyMap.rangeValue) || 'LAST_24_HOURS';
|
||||
const rangeValues = this.getDateRangeValues(rangeValue, params.get(this.keyMap.startDate), params.get(this.keyMap.endDate));
|
||||
const startDate = rangeValues[0];
|
||||
const endDate = rangeValues[1];
|
||||
|
||||
return new InputJson(
|
||||
filters,
|
||||
rangeValue,
|
||||
startDate,
|
||||
endDate,
|
||||
params.get(this.keyMap.sort) || 'startTs',
|
||||
params.get(this.keyMap.order) || 'desc',
|
||||
params.get(this.keyMap.strict) === 'true',
|
||||
params.get(this.keyMap.eventsOrder) || 'then'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Example usage
|
||||
// const urlParams = '?f[0].k=click&f[0].op=on&f[0].v[0]=Refresh&f[1].k=fetch&f[1].op=is&f[1].v[0]=&f[1].f[0].k=fetchUrl&f[1].f[0].op=is&f[1].f[0].v[0]=/g/collect&f[1].f[1].k=fetchStatusCode&f[1].f[1].op=>=&f[1].f[1].v[0]=400&f[1].f[2].k=fetchMethod&f[1].f[2].op=is&f[1].f[2].v[0]=&f[1].f[3].k=fetchDuration&f[1].f[3].op==&f[1].f[3].v[0]=&f[1].f[4].k=fetchRequestBody&f[1].f[4].op=is&f[1].f[4].v[0]=&f[1].f[5].k=fetchResponseBody&f[1].f[5].op=is&f[1].f[5].v[0]=&rv=LAST_24_HOURS&sd=1731343412555&ed=1731429812555&s=startTs&o=desc&st=false&eo=then';
|
||||
// const parsedJson = JsonUrlConverter.urlParamsToJson(urlParams);
|
||||
// console.log(parsedJson);
|
||||
|
||||
// const inputJson: any = {"filters":[{"key":"input","operator":"is","value":["one","two"],"filters":null},{"key":"fetch","operator":"is","filters":[{"key":"fetchUrl","operator":"is","value":["PATH","another"]},{"key":"fetchStatusCode","operator":"=","value":["400","200"]},{"key":"fetchMethod","operator":"is","value":["PUT","POST"]},{"key":"fetchDuration","operator":">=","value":["1"]},{"key":"fetchRequestBody","operator":"is","value":[null]},{"key":"fetchResponseBody","operator":"is","value":[null]}],"value":[""]}],"rangeValue":"LAST_24_HOURS","startDate":1720952011000,"endDate":1721038411000,"sort":"startTs","order":"desc","strict":false,"eventsOrder":"then"};
|
||||
// const urlParams = JsonUrlConverter.jsonToUrlParams(inputJson);
|
||||
// const parsedJson = JsonUrlConverter.urlParamsToJson(urlParams);
|
||||
//
|
||||
// console.log(urlParams);
|
||||
// console.log(parsedJson);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue