diff --git a/frontend/app/components/Overview/Overview.tsx b/frontend/app/components/Overview/Overview.tsx index ac5ebce64..10a6fc03d 100644 --- a/frontend/app/components/Overview/Overview.tsx +++ b/frontend/app/components/Overview/Overview.tsx @@ -12,6 +12,8 @@ import { withRouter, RouteComponentProps, useLocation } from 'react-router-dom'; import FlagView from 'Components/FFlags/FlagView/FlagView'; import { observer } from 'mobx-react-lite'; import { useStore } from '@/mstore'; +import NotesList from 'Shared/SessionsTabOverview/components/Notes/NoteList'; +import NoteTags from 'Shared/SessionsTabOverview/components/Notes/NoteTags'; // @ts-ignore interface IProps extends RouteComponentProps { @@ -36,15 +38,16 @@ function Overview({ match: { params } }: IProps) { return ( + path={[withSiteId(sessions(), siteId), withSiteId(bookmarks(), siteId)]}>
- - - -
+ +
+ +
+
diff --git a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx index a002346d5..8b782f166 100644 --- a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx +++ b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx @@ -45,9 +45,10 @@ function SessionSearch() { useEffect(() => { debounceFetch = debounce(() => searchStore.fetchSessions(), 500); - }, [searchStore]); + }, []); useEffect(() => { + if (searchStore.urlParsed) return; debounceFetch(); }, [appliedFilter.filters]); diff --git a/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx b/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx index 58c49b61a..1a0127499 100644 --- a/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx @@ -3,16 +3,17 @@ import React from 'react'; import { useStore } from 'App/mstore'; import LatestSessionsMessage from './components/LatestSessionsMessage'; -import NotesList from './components/Notes/NoteList'; import SessionHeader from './components/SessionHeader'; import SessionList from './components/SessionList'; import { observer } from 'mobx-react-lite'; +import NoSessionsMessage from 'Shared/NoSessionsMessage/NoSessionsMessage'; +import MainSearchBar from 'Shared/MainSearchBar/MainSearchBar'; +import SessionSearch from 'Shared/SessionSearch/SessionSearch'; function SessionsTabOverview() { const [query, setQuery] = React.useState(''); const { aiFiltersStore, searchStore } = useStore(); const appliedFilter = searchStore.instance; - const isNotesRoute = searchStore.activeTab.type === 'notes'; const handleKeyDown = (event: any) => { if (event.key === 'Enter') { @@ -25,27 +26,27 @@ function SessionsTabOverview() { const testingKey = localStorage.getItem('__mauricio_testing_access') === 'true'; return ( -
- {testingKey ? ( - setQuery(e.target.value)} - className={'mb-2'} - placeholder={'ask session ai'} - /> - ) : null} - -
- {!isNotesRoute ? ( - <> - - - - ) : ( - - )} -
+ <> + + + +
+
+ {testingKey ? ( + setQuery(e.target.value)} + className={'mb-2'} + placeholder={'ask session ai'} + /> + ) : null} + +
+ + +
+ ); } diff --git a/frontend/app/components/shared/SessionsTabOverview/components/Notes/NoteList.tsx b/frontend/app/components/shared/SessionsTabOverview/components/Notes/NoteList.tsx index 20c3522ce..eb5587d12 100644 --- a/frontend/app/components/shared/SessionsTabOverview/components/Notes/NoteList.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/components/Notes/NoteList.tsx @@ -5,57 +5,70 @@ import NoteItem from './NoteItem'; import { observer } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; +import NoteTags from 'Shared/SessionsTabOverview/components/Notes/NoteTags'; function NotesList() { const { notesStore } = useStore(); React.useEffect(() => { - void notesStore.fetchNotes(); + void notesStore.fetchNotes(); }, [notesStore.page]); const list = notesStore.notes; return ( - - - {/* */} - -
No notes yet
-
- } - subtext={ -
- Note observations during session replays and share them with your team. -
- } - > -
- {list.map((note) => ( - - - - ))} -
+ <> +
+
+

Notes

-
-
- Showing{' '} - {Math.min(list.length, notesStore.pageSize)} out - of {notesStore.total} notes +
+
- notesStore.changePage(page)} - limit={notesStore.pageSize} - debounceRequest={100} - />
- - +
+ + + {/* */} + +
No notes yet
+
+ } + subtext={ +
+ Note observations during session replays and share them with your team. +
+ } + > +
+ {list.map((note) => ( + + + + ))} +
+ +
+
+ Showing{' '} + {Math.min(list.length, notesStore.pageSize)} out + of {notesStore.total} notes +
+ notesStore.changePage(page)} + limit={notesStore.pageSize} + debounceRequest={100} + /> +
+ + +
+ ); } diff --git a/frontend/app/components/shared/SessionsTabOverview/components/SessionHeader/SessionHeader.tsx b/frontend/app/components/shared/SessionsTabOverview/components/SessionHeader/SessionHeader.tsx index 9a4100d67..9d284f7d3 100644 --- a/frontend/app/components/shared/SessionsTabOverview/components/SessionHeader/SessionHeader.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/components/SessionHeader/SessionHeader.tsx @@ -2,7 +2,6 @@ import React, { useMemo } from 'react'; import Period from 'Types/app/period'; import SelectDateRange from 'Shared/SelectDateRange'; import SessionTags from '../SessionTags'; -import NoteTags from '../Notes/NoteTags'; import SessionSort from '../SessionSort'; import { Space } from 'antd'; import { useStore } from 'App/mstore'; @@ -17,9 +16,6 @@ function SessionHeader() { const period = Period({ start: startDate, end: endDate, rangeName: rangeValue }); const title = useMemo(() => { - if (activeTab.type === 'notes') { - return 'Notes'; - } if (activeTab.type === 'bookmarks') { return isEnterprise ? 'Vault' : 'Bookmarks'; } @@ -35,26 +31,15 @@ function SessionHeader() { return (

{title}

- {activeTab.type !== 'notes' ? ( -
- {activeTab.type !== 'bookmarks' && ( - <> - -
- - - - - - )} -
- ) : null} - - {activeTab.type === 'notes' && ( -
- -
- )} +
+ {activeTab.type !== 'bookmarks' && } +
+ + {activeTab.type !== 'bookmarks' && + } + + +
); } diff --git a/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx b/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx index 8f8cd14aa..f818a83eb 100644 --- a/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx @@ -73,7 +73,6 @@ function SessionList() { }, [isBookmark, isVault, activeTab, location.pathname]); const [statusData, setStatusData] = React.useState({ status: 0, count: 0 }); - const fetchStatus = async () => { const response = await sessionService.getRecordingStatus(); setStatusData({ diff --git a/frontend/app/date.ts b/frontend/app/date.ts index 7a8fc7b40..96713ee77 100644 --- a/frontend/app/date.ts +++ b/frontend/app/date.ts @@ -2,6 +2,8 @@ import { DateTime, Duration } from 'luxon'; // TODO import { Timezone } from 'App/mstore/types/sessionSettings'; +import { LAST_24_HOURS, LAST_30_DAYS, LAST_7_DAYS } from 'Types/app/period'; +import { CUSTOM_RANGE } from '@/dateRange'; export function getDateFromString(date: string, format = 'yyyy-MM-dd HH:mm:ss:SSS'): string { return DateTime.fromISO(date).toFormat(format); @@ -191,3 +193,35 @@ export const countDaysFrom = (timestamp: number): number => { const d = new Date(); return Math.round(Math.abs(d.getTime() - date.toJSDate().getTime()) / (1000 * 3600 * 24)); } + +export const getDateRangeUTC = (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 + }; +} diff --git a/frontend/app/mstore/filterStore.ts b/frontend/app/mstore/filterStore.ts index 9686d9d32..3ffd9023c 100644 --- a/frontend/app/mstore/filterStore.ts +++ b/frontend/app/mstore/filterStore.ts @@ -20,7 +20,7 @@ export default class FilterStore { } setTopValues = (key: string, values: TopValue[]) => { - this.topValues[key] = values.filter((value) => value !== null && value.value !== ''); + this.topValues[key] = values?.filter((value) => value !== null && value.value !== ''); }; fetchTopValues = async (key: string, source?: string) => { diff --git a/frontend/app/mstore/searchStore.ts b/frontend/app/mstore/searchStore.ts index 3aac97058..a3e5a48ba 100644 --- a/frontend/app/mstore/searchStore.ts +++ b/frontend/app/mstore/searchStore.ts @@ -264,6 +264,8 @@ class SearchStore { }); } + this.currentPage = 1; + if (filter.value && filter.value[0] && filter.value[0] !== '') { void this.fetchSessions(); } diff --git a/frontend/app/mstore/types/search.ts b/frontend/app/mstore/types/search.ts index ede994d61..287b330e8 100644 --- a/frontend/app/mstore/types/search.ts +++ b/frontend/app/mstore/types/search.ts @@ -143,7 +143,7 @@ export default class Search { return new FilterItem(filter).toJson(); }); - const { startDate, endDate } = this.getDateRange(js.rangeName, js.startDate, js.endDate); + const { startDate, endDate } = this.getDateRange(js.rangeValue, js.startDate, js.endDate); js.startDate = startDate; js.endDate = endDate; diff --git a/frontend/app/utils/search.ts b/frontend/app/utils/search.ts index 53357e41d..47f6638c5 100644 --- a/frontend/app/utils/search.ts +++ b/frontend/app/utils/search.ts @@ -1,5 +1,8 @@ -import Period, { CUSTOM_RANGE } from 'Types/app/period'; +import Period, { CUSTOM_RANGE, LAST_24_HOURS } from 'Types/app/period'; +const DEFAULT_SORT = 'startTs'; +const DEFAULT_ORDER = 'desc'; +const DEFAULT_EVENTS_ORDER = 'then'; class Filter { key: string; @@ -24,10 +27,6 @@ class Filter { } } -const DEFAULT_SORT = 'startTs'; -const DEFAULT_ORDER = 'desc'; -const DEFAULT_EVENTS_ORDER = 'then'; - export class InputJson { filters: Filter[]; rangeValue: string; @@ -169,7 +168,7 @@ export class JsonUrlConverter { index++; } - const rangeValue = params.get(this.keyMap.rangeValue) || 'LAST_24_HOURS'; + 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)); return new InputJson(