fix(ui): sessions, bookmark, notes navigation and search silters and timestamp issues

This commit is contained in:
Shekar Siri 2024-11-26 12:57:12 +01:00
parent 253feefe53
commit f5df3fb5b5
11 changed files with 137 additions and 100 deletions

View file

@ -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 (
<Switch>
<Route exact strict
path={[withSiteId(sessions(), siteId), withSiteId(notes(), siteId), withSiteId(bookmarks(), siteId)]}>
path={[withSiteId(sessions(), siteId), withSiteId(bookmarks(), siteId)]}>
<div className="mb-5 w-full mx-auto" style={{ maxWidth: '1360px' }}>
<NoSessionsMessage siteId={siteId} />
<MainSearchBar />
<SessionSearch />
<div className="my-4" />
<SessionsTabOverview />
</div>
</Route>
<Route exact strict path={withSiteId(notes(), siteId)}>
<div className="mb-5 w-full mx-auto" style={{ maxWidth: '1360px' }}>
<NotesList />
</div>
</Route>
<Route exact strict path={withSiteId(fflags(), siteId)}>
<FFlagsList siteId={siteId} />
</Route>

View file

@ -45,9 +45,10 @@ function SessionSearch() {
useEffect(() => {
debounceFetch = debounce(() => searchStore.fetchSessions(), 500);
}, [searchStore]);
}, []);
useEffect(() => {
if (searchStore.urlParsed) return;
debounceFetch();
}, [appliedFilter.filters]);

View file

@ -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 (
<div className="widget-wrapper">
{testingKey ? (
<Input
value={query}
onKeyDown={handleKeyDown}
onChange={(e) => setQuery(e.target.value)}
className={'mb-2'}
placeholder={'ask session ai'}
/>
) : null}
<SessionHeader />
<div className="border-b" />
{!isNotesRoute ? (
<>
<LatestSessionsMessage />
<SessionList />
</>
) : (
<NotesList />
)}
</div>
<>
<NoSessionsMessage />
<MainSearchBar />
<SessionSearch />
<div className="my-4" />
<div className="widget-wrapper">
{testingKey ? (
<Input
value={query}
onKeyDown={handleKeyDown}
onChange={(e) => setQuery(e.target.value)}
className={'mb-2'}
placeholder={'ask session ai'}
/>
) : null}
<SessionHeader />
<div className="border-b" />
<LatestSessionsMessage />
<SessionList />
</div>
</>
);
}

View file

@ -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 (
<Loader loading={notesStore.loading}>
<NoContent
show={list.length === 0}
title={
<div className="flex flex-col items-center justify-center">
{/* <Icon name="no-dashboard" size={80} color="figmaColors-accent-secondary" /> */}
<AnimatedSVG name={ICONS.NO_NOTES} size={60} />
<div className="text-center mt-4 text-lg font-medium">No notes yet</div>
</div>
}
subtext={
<div className="text-center flex justify-center items-center flex-col">
Note observations during session replays and share them with your team.
</div>
}
>
<div className="border-b rounded bg-white">
{list.map((note) => (
<React.Fragment key={note.noteId}>
<NoteItem note={note} />
</React.Fragment>
))}
</div>
<>
<div className="widget-wrapper">
<div className="flex items-center px-4 py-1 justify-between w-full">
<h2 className="text-2xl capitalize mr-4">Notes</h2>
<div className="w-full flex items-center justify-between py-4 px-6">
<div className="text-disabled-text">
Showing{' '}
<span className="font-semibold">{Math.min(list.length, notesStore.pageSize)}</span> out
of <span className="font-semibold">{notesStore.total}</span> notes
<div className="flex items-center justify-end w-full">
<NoteTags />
</div>
<Pagination
page={notesStore.page}
total={notesStore.total}
onPageChange={(page) => notesStore.changePage(page)}
limit={notesStore.pageSize}
debounceRequest={100}
/>
</div>
</NoContent>
</Loader>
<div className="border-b" />
<Loader loading={notesStore.loading}>
<NoContent
show={list.length === 0}
title={
<div className="flex flex-col items-center justify-center">
{/* <Icon name="no-dashboard" size={80} color="figmaColors-accent-secondary" /> */}
<AnimatedSVG name={ICONS.NO_NOTES} size={60} />
<div className="text-center mt-4 text-lg font-medium">No notes yet</div>
</div>
}
subtext={
<div className="text-center flex justify-center items-center flex-col">
Note observations during session replays and share them with your team.
</div>
}
>
<div className="border-b rounded bg-white">
{list.map((note) => (
<React.Fragment key={note.noteId}>
<NoteItem note={note} />
</React.Fragment>
))}
</div>
<div className="w-full flex items-center justify-between py-4 px-6">
<div className="text-disabled-text">
Showing{' '}
<span className="font-semibold">{Math.min(list.length, notesStore.pageSize)}</span> out
of <span className="font-semibold">{notesStore.total}</span> notes
</div>
<Pagination
page={notesStore.page}
total={notesStore.total}
onPageChange={(page) => notesStore.changePage(page)}
limit={notesStore.pageSize}
debounceRequest={100}
/>
</div>
</NoContent>
</Loader>
</div>
</>
);
}

View file

@ -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 (
<div className="flex items-center px-4 py-1 justify-between w-full">
<h2 className="text-2xl capitalize mr-4">{title}</h2>
{activeTab.type !== 'notes' ? (
<div className="flex items-center w-full justify-end">
{activeTab.type !== 'bookmarks' && (
<>
<SessionTags />
<div className="mr-auto" />
<Space>
<SelectDateRange isAnt period={period} onChange={onDateChange} right={true} />
<SessionSort />
</Space>
</>
)}
</div>
) : null}
{activeTab.type === 'notes' && (
<div className="flex items-center justify-end w-full">
<NoteTags />
</div>
)}
<div className="flex items-center w-full justify-end">
{activeTab.type !== 'bookmarks' && <SessionTags />}
<div className="mr-auto" />
<Space>
{activeTab.type !== 'bookmarks' &&
<SelectDateRange isAnt period={period} onChange={onDateChange} right={true} />}
<SessionSort />
</Space>
</div>
</div>
);
}

View file

@ -73,7 +73,6 @@ function SessionList() {
}, [isBookmark, isVault, activeTab, location.pathname]);
const [statusData, setStatusData] = React.useState<SessionStatus>({ status: 0, count: 0 });
const fetchStatus = async () => {
const response = await sessionService.getRecordingStatus();
setStatusData({

View file

@ -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
};
}

View file

@ -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) => {

View file

@ -264,6 +264,8 @@ class SearchStore {
});
}
this.currentPage = 1;
if (filter.value && filter.value[0] && filter.value[0] !== '') {
void this.fetchSessions();
}

View file

@ -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;

View file

@ -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(