diff --git a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx
index 99e830466..a002346d5 100644
--- a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx
+++ b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx
@@ -11,39 +11,41 @@ import { useStore } from 'App/mstore';
import { debounce } from 'App/utils';
import useSessionSearchQueryHandler from 'App/hooks/useSessionSearchQueryHandler';
-let debounceFetch: any = () => {
-};
+let debounceFetch: () => void;
function SessionSearch() {
const { tagWatchStore, aiFiltersStore, searchStore, customFieldStore, projectsStore } = useStore();
const appliedFilter = searchStore.instance;
const metaLoading = customFieldStore.isLoading;
- const hasEvents = appliedFilter.filters.filter((i: any) => i.isEvent).length > 0;
- const hasFilters = appliedFilter.filters.filter((i: any) => !i.isEvent).length > 0;
+ const hasEvents = appliedFilter.filters.some((i: any) => i.isEvent);
+ const hasFilters = appliedFilter.filters.some((i: any) => !i.isEvent);
const saveRequestPayloads = projectsStore.instance?.saveRequestPayloads ?? false;
useSessionSearchQueryHandler({
appliedFilter,
loading: metaLoading,
onBeforeLoad: async () => {
- const tags = await tagWatchStore.getTags();
- if (tags) {
- addOptionsToFilter(
- FilterKey.TAGGED_ELEMENT,
- tags.map((tag) => ({
- label: tag.name,
- value: tag.tagId.toString()
- }))
- );
- searchStore.refreshFilterOptions();
+ try {
+ const tags = await tagWatchStore.getTags();
+ if (tags) {
+ addOptionsToFilter(
+ FilterKey.TAGGED_ELEMENT,
+ tags.map((tag) => ({
+ label: tag.name,
+ value: tag.tagId.toString()
+ }))
+ );
+ searchStore.refreshFilterOptions();
+ }
+ } catch (error) {
+ console.error('Error during onBeforeLoad:', error);
}
}
});
useEffect(() => {
debounceFetch = debounce(() => searchStore.fetchSessions(), 500);
- // void searchStore.fetchSessions(true)
- }, []);
+ }, [searchStore]);
useEffect(() => {
debounceFetch();
@@ -85,49 +87,47 @@ function SessionSearch() {
};
const showPanel = hasEvents || hasFilters || aiFiltersStore.isLoading;
- return !metaLoading ? (
- <>
- {showPanel ? (
-
-
- {aiFiltersStore.isLoading ? (
-
-
-
Translating your query into search steps...
-
- ) : null}
- {hasEvents || hasFilters ? (
-
- ) : null}
-
- {hasEvents || hasFilters ? (
-
-
-
-
-
-
-
-
-
-
- ) : null}
+ if (metaLoading) return null;
+ if (!showPanel) return null;
+
+ return (
+
+
+ {aiFiltersStore.isLoading ? (
+
+
+
Translating your query into search steps...
+
+ ) : null}
+ {hasEvents || hasFilters ? (
+
+ ) : null}
+
+
+ {hasEvents || hasFilters ? (
+
+
+
+
+
+
+
+
+
- ) : (
- <>>
- )}
- >
- ) : null;
+ ) : null}
+
+ );
}
export default observer(SessionSearch);
diff --git a/frontend/app/hooks/useSessionSearchQueryHandler.ts b/frontend/app/hooks/useSessionSearchQueryHandler.ts
index bbd26a26c..12511be38 100644
--- a/frontend/app/hooks/useSessionSearchQueryHandler.ts
+++ b/frontend/app/hooks/useSessionSearchQueryHandler.ts
@@ -6,46 +6,59 @@ import Search from '@/mstore/types/search';
import { getFilterFromJson } from 'Types/filter/newFilter';
interface Props {
- onBeforeLoad?: () => Promise
;
- appliedFilter: any;
+ onBeforeLoad?: () => Promise;
+ appliedFilter: Record;
loading: boolean;
}
-const useSessionSearchQueryHandler = (props: Props) => {
+const useSessionSearchQueryHandler = ({ onBeforeLoad, appliedFilter, loading }: Props) => {
const { searchStore } = useStore();
- const [beforeHookLoaded, setBeforeHookLoaded] = useState(!props.onBeforeLoad);
- const { appliedFilter, loading } = props;
+ const [beforeHookLoaded, setBeforeHookLoaded] = useState(!onBeforeLoad);
const history = useHistory();
+ // Apply filter from the query string when the component mounts
useEffect(() => {
const applyFilterFromQuery = async () => {
if (!loading && !searchStore.urlParsed) {
- if (props.onBeforeLoad) {
- await props.onBeforeLoad();
- setBeforeHookLoaded(true);
- }
+ try {
+ if (onBeforeLoad) {
+ await onBeforeLoad();
+ setBeforeHookLoaded(true);
+ }
- const converter = JsonUrlConverter.urlParamsToJson(history.location.search);
- const json: any = getFilterFromJson(converter.toJSON());
- const filter = new Search(json);
- searchStore.applyFilter(filter, true);
- searchStore.setUrlParsed()
+ const converter = JsonUrlConverter.urlParamsToJson(history.location.search);
+ const json = getFilterFromJson(converter.toJSON());
+ const filter = new Search(json);
+ searchStore.applyFilter(filter, true);
+ searchStore.setUrlParsed();
+ } catch (error) {
+ console.error('Error applying filter from query:', error);
+ }
}
};
void applyFilterFromQuery();
- }, [loading]);
+ }, [loading, onBeforeLoad, searchStore, history.location.search]);
+ // Update the URL whenever the appliedFilter changes
useEffect(() => {
- const generateUrlQuery = () => {
+ const updateUrlWithFilter = () => {
if (!loading && beforeHookLoaded) {
- const converter = JsonUrlConverter.jsonToUrlParams(appliedFilter);
- history.replace({ search: converter });
+ const query = JsonUrlConverter.jsonToUrlParams(appliedFilter);
+ history.replace({ search: query });
}
};
- generateUrlQuery();
- }, [appliedFilter, loading, beforeHookLoaded]);
+ updateUrlWithFilter();
+ }, [appliedFilter, loading, beforeHookLoaded, history]);
+
+ // Ensure the URL syncs on remount if already parsed
+ useEffect(() => {
+ if (searchStore.urlParsed) {
+ const query = JsonUrlConverter.jsonToUrlParams(appliedFilter);
+ history.replace({ search: query });
+ }
+ }, [appliedFilter, searchStore.urlParsed, history]);
return null;
};
diff --git a/frontend/app/mstore/types/search.ts b/frontend/app/mstore/types/search.ts
index 4ba7ca2d6..ede994d61 100644
--- a/frontend/app/mstore/types/search.ts
+++ b/frontend/app/mstore/types/search.ts
@@ -1,7 +1,8 @@
-import { DATE_RANGE_VALUES, CUSTOM_RANGE, getDateRangeFromValue } from 'App/dateRange';
-import Filter, { checkFilterValue, IFilter } from 'App/mstore/types/filter';
+import { CUSTOM_RANGE, DATE_RANGE_VALUES, getDateRangeFromValue } from 'App/dateRange';
+import Filter, { IFilter } from 'App/mstore/types/filter';
import FilterItem from 'App/mstore/types/filterItem';
-import { action, makeAutoObservable, observable } from 'mobx';
+import { makeAutoObservable, observable } from 'mobx';
+import { LAST_24_HOURS, LAST_30_DAYS, LAST_7_DAYS } from 'Types/app/period';
// @ts-ignore
const rangeValue = DATE_RANGE_VALUES.LAST_24_HOURS;
@@ -69,7 +70,7 @@ export default class Search {
constructor(initialData?: Partial) {
makeAutoObservable(this, {
- filters: observable,
+ filters: observable
});
Object.assign(this, {
name: '',
@@ -142,11 +143,48 @@ export default class Search {
return new FilterItem(filter).toJson();
});
+ const { startDate, endDate } = this.getDateRange(js.rangeName, js.startDate, js.endDate);
+ js.startDate = startDate;
+ js.endDate = endDate;
+
delete js.createdAt;
delete js.key;
return js;
}
+ private getDateRange(rangeName: string, customStartDate: number, customEndDate: number): {
+ startDate: number;
+ endDate: number
+ } {
+ let endDate = new Date().getTime();
+ let startDate: number;
+
+ switch (rangeName) {
+ case LAST_7_DAYS:
+ startDate = endDate - 7 * 24 * 60 * 60 * 1000;
+ break;
+ case LAST_30_DAYS:
+ startDate = endDate - 30 * 24 * 60 * 60 * 1000;
+ break;
+ case CUSTOM_RANGE:
+ if (!customStartDate || !customEndDate) {
+ throw new Error('Start date and end date must be provided for CUSTOM_RANGE.');
+ }
+ startDate = customStartDate;
+ endDate = customEndDate;
+ break;
+ case LAST_24_HOURS:
+ default:
+ startDate = endDate - 24 * 60 * 60 * 1000;
+ }
+
+ return {
+ startDate,
+ endDate
+ };
+ }
+
+
fromJS({ eventsOrder, filters, events, custom, ...filterData }: any) {
let startDate, endDate;
const rValue = filterData.rangeValue || rangeValue;
@@ -176,3 +214,4 @@ export default class Search {
});
}
}
+
diff --git a/frontend/app/utils/search.ts b/frontend/app/utils/search.ts
index 78f6e60b4..53357e41d 100644
--- a/frontend/app/utils/search.ts
+++ b/frontend/app/utils/search.ts
@@ -1,5 +1,4 @@
import Period, { CUSTOM_RANGE } from 'Types/app/period';
-import { filtersMap } from 'Types/filter/newFilter';
class Filter {
@@ -25,24 +24,32 @@ class Filter {
}
}
-class InputJson {
+const DEFAULT_SORT = 'startTs';
+const DEFAULT_ORDER = 'desc';
+const DEFAULT_EVENTS_ORDER = 'then';
+
+export class InputJson {
filters: Filter[];
rangeValue: string;
- startDate: number;
- endDate: number;
+ startDate?: number;
+ endDate?: number;
sort: string;
order: string;
eventsOrder: string;
- constructor(filters: Filter[], rangeValue: string, startDate: number, endDate: number, sort: string, order: string, eventsOrder: string) {
+ constructor(
+ filters: Filter[],
+ rangeValue: string,
+ sort: string,
+ order: string,
+ eventsOrder: string,
+ startDate?: string | number,
+ endDate?: string | number
+ ) {
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.startDate = startDate ? +startDate : undefined;
+ this.endDate = endDate ? +endDate : undefined;
this.sort = sort;
this.order = order;
this.eventsOrder = eventsOrder;
@@ -50,17 +57,28 @@ class InputJson {
toJSON() {
return {
- filters: this.filters.map(f => f.toJSON()),
+ filters: this.filters.map((f) => f.toJSON()),
rangeValue: this.rangeValue,
- startDate: this.startDate,
- endDate: this.endDate,
+ startDate: this.startDate ?? null,
+ endDate: this.endDate ?? null,
sort: this.sort,
order: this.order,
eventsOrder: this.eventsOrder
};
}
-}
+ fromJSON(json: Record): InputJson {
+ return new InputJson(
+ json.filters.map((f: any) => new Filter(f.key, f.operator, f.value, f.filters)),
+ json.rangeValue,
+ json.sort,
+ json.order,
+ json.eventsOrder,
+ json.startDate,
+ json.endDate
+ );
+ }
+}
export class JsonUrlConverter {
static keyMap = {
@@ -76,35 +94,46 @@ export class JsonUrlConverter {
filters: 'f'
};
- static getDateRangeValues(rangeValue: string, startDate: number | undefined, endDate: number | undefined): [number, number] {
- if (rangeValue === 'CUSTOM_RANGE') {
- return [startDate!, endDate!];
+ static getDateRangeValues(
+ rangeValue: string,
+ startDate: string | null,
+ endDate: string | null
+ ): [string, string] {
+ if (rangeValue === CUSTOM_RANGE) {
+ return [startDate || '', endDate || ''];
}
- const period = Period({ rangeName: rangeValue });
+ const period: any = Period({ rangeName: rangeValue });
return [period.start, period.end];
}
- static jsonToUrlParams(json: InputJson): string {
+ static jsonToUrlParams(json: Record): 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}].`));
- }
+ filter.value?.forEach((v, i) =>
+ params.append(`${prefix}${this.keyMap.value}[${i}]`, v || '')
+ );
+ 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);
+ json.filters.forEach((filter: any, index: number) =>
+ addFilterParams(filter, `${this.keyMap.filters}[${index}].`)
+ );
params.append(this.keyMap.rangeValue, json.rangeValue);
- params.append(this.keyMap.startDate, rangeValues[0].toString());
- params.append(this.keyMap.endDate, rangeValues[1].toString());
+ if (json.rangeValue === CUSTOM_RANGE) {
+ const rangeValues = this.getDateRangeValues(
+ json.rangeValue,
+ json.startDate?.toString() || null,
+ json.endDate?.toString() || null
+ );
+ params.append(this.keyMap.startDate, rangeValues[0]);
+ params.append(this.keyMap.endDate, rangeValues[1]);
+ }
params.append(this.keyMap.sort, json.sort);
params.append(this.keyMap.order, json.order);
params.append(this.keyMap.eventsOrder, json.eventsOrder);
@@ -130,7 +159,7 @@ export class JsonUrlConverter {
filters.push(getFilterParams(`${prefix}${this.keyMap.filters}[${index}].`));
index++;
}
- return new Filter(key, operator, value.length ? value : '', filters.length ? filters : []);
+ return new Filter(key, operator, value.length ? value : [], filters.length ? filters : []);
};
const filters: Filter[] = [];
@@ -142,21 +171,20 @@ export class JsonUrlConverter {
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.eventsOrder) || 'then'
+ params.get(this.keyMap.sort) || DEFAULT_SORT,
+ params.get(this.keyMap.order) || DEFAULT_ORDER,
+ params.get(this.keyMap.eventsOrder) || DEFAULT_EVENTS_ORDER,
+ rangeValues[0],
+ rangeValues[1]
);
}
}
+
// 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);