From beb3eeac0cc3ebbec9cfb3752ff85a1e6824a9b0 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 11 Oct 2024 16:06:10 +0200 Subject: [PATCH] fix(ui): sessions list sidemenu fix, reload properly, sessions tags filter issue --- frontend/app/components/Overview/Overview.tsx | 14 ++- .../shared/OverviewMenu/OverviewMenu.tsx | 87 ---------------- .../components/shared/OverviewMenu/index.ts | 1 - .../shared/SessionSearch/SessionSearch.tsx | 2 +- .../SessionsTabOverview.tsx | 4 +- .../SessionHeader/SessionHeader.tsx | 8 +- .../components/SessionList/SessionList.tsx | 35 ++++--- .../components/SessionTags/SessionTags.tsx | 99 ++++--------------- frontend/app/layout/SideMenu.tsx | 12 +-- frontend/app/mstore/searchStore.ts | 48 +++++++-- frontend/app/styles/general.css | 5 + 11 files changed, 113 insertions(+), 202 deletions(-) delete mode 100644 frontend/app/components/shared/OverviewMenu/OverviewMenu.tsx delete mode 100644 frontend/app/components/shared/OverviewMenu/index.ts diff --git a/frontend/app/components/Overview/Overview.tsx b/frontend/app/components/Overview/Overview.tsx index fdbdea7ad..4b720d05c 100644 --- a/frontend/app/components/Overview/Overview.tsx +++ b/frontend/app/components/Overview/Overview.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import withPageTitle from 'HOCs/withPageTitle'; import NoSessionsMessage from 'Shared/NoSessionsMessage'; import MainSearchBar from 'Shared/MainSearchBar'; @@ -8,9 +8,10 @@ import FFlagsList from 'Components/FFlags'; import NewFFlag from 'Components/FFlags/NewFFlag'; import { Switch, Route } from 'react-router'; import { sessions, fflags, withSiteId, newFFlag, fflag, notes, fflagRead, bookmarks } from 'App/routes'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; +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'; // @ts-ignore interface IProps extends RouteComponentProps { @@ -23,7 +24,16 @@ interface IProps extends RouteComponentProps { } function Overview({ match: { params } }: IProps) { + const { searchStore } = useStore(); const { siteId, fflagId } = params; + const location = useLocation(); + const tab = location.pathname.split('/')[2]; + searchStore.setActiveTab(tab); + + // useEffect(() => { + // // const tab = location.pathname.split('/')[2]; + // searchStore.setActiveTab(tab); + // }, [tab]); return ( diff --git a/frontend/app/components/shared/OverviewMenu/OverviewMenu.tsx b/frontend/app/components/shared/OverviewMenu/OverviewMenu.tsx deleted file mode 100644 index 580bf8937..000000000 --- a/frontend/app/components/shared/OverviewMenu/OverviewMenu.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import { SideMenuitem } from 'UI'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { sessions, fflags, withSiteId, notes, bookmarks } from 'App/routes'; -import { useStore } from 'App/mstore'; - -interface Props { - activeTab: string; -} - -const TabToUrlMap = { - all: sessions() as '/sessions', - bookmark: bookmarks() as '/bookmarks', - notes: notes() as '/notes', - flags: fflags() as '/feature-flags' -}; - -function OverviewMenu(props: Props & RouteComponentProps) { - // @ts-ignore - const { history, match: { params: { siteId } }, location } = props; - const { searchStore, userStore } = useStore(); - const isEnterprise = userStore.isEnterprise; - const activeTab = searchStore.activeTab.type; - - React.useEffect(() => { - const currentLocation = location.pathname; - const tab = Object.keys(TabToUrlMap).find((tab: keyof typeof TabToUrlMap) => currentLocation.includes(TabToUrlMap[tab])); - if (tab && tab !== activeTab) { - searchStore.setActiveTab({ type: tab }); - } - }, [location.pathname]); - - return ( -
-
- { - searchStore.setActiveTab({ type: 'all' }); - !location.pathname.includes(sessions()) && history.push(withSiteId(sessions(), siteId)); - }} - /> -
-
- { - // props.setActiveTab({ type: 'bookmark' }); - !location.pathname.includes(bookmarks()) && history.push(withSiteId(bookmarks(), siteId)); - }} - /> -
-
- { - searchStore.setActiveTab({ type: 'notes' }); - !location.pathname.includes(notes()) && history.push(withSiteId(notes(), siteId)); - }} - /> -
-
- { - searchStore.setActiveTab({ type: 'flags' }); - !location.pathname.includes(fflags()) && history.push(withSiteId(fflags(), siteId)); - }} - /> -
-
- ); -} - -export default withRouter(OverviewMenu); diff --git a/frontend/app/components/shared/OverviewMenu/index.ts b/frontend/app/components/shared/OverviewMenu/index.ts deleted file mode 100644 index 91599b4c8..000000000 --- a/frontend/app/components/shared/OverviewMenu/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './OverviewMenu'; \ No newline at end of file diff --git a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx index c9c0a3d5b..c3dbafbec 100644 --- a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx +++ b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx @@ -47,7 +47,7 @@ function SessionSearch(props: Props) { useEffect(() => { debounceFetch = debounce(() => searchStore.fetchSessions(), 500); - void searchStore.fetchSessions(true) + // void searchStore.fetchSessions(true) }, []); const onAddFilter = (filter: any) => { diff --git a/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx b/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx index 983305fc4..e8bbca617 100644 --- a/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx @@ -12,7 +12,7 @@ function SessionsTabOverview() { const [query, setQuery] = React.useState(''); const { aiFiltersStore, searchStore } = useStore(); const appliedFilter = searchStore.instance; - const activeTab = searchStore.activeTab.type; + const isNotesRoute = searchStore.activeTab.type === 'notes'; const handleKeyDown = (event: any) => { if (event.key === 'Enter') { @@ -38,7 +38,7 @@ function SessionsTabOverview() {
- {activeTab !== 'notes' ? ( + {!isNotesRoute ? ( ) : ( diff --git a/frontend/app/components/shared/SessionsTabOverview/components/SessionHeader/SessionHeader.tsx b/frontend/app/components/shared/SessionsTabOverview/components/SessionHeader/SessionHeader.tsx index 9bd665bc0..9a4100d67 100644 --- a/frontend/app/components/shared/SessionsTabOverview/components/SessionHeader/SessionHeader.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/components/SessionHeader/SessionHeader.tsx @@ -20,16 +20,16 @@ function SessionHeader() { if (activeTab.type === 'notes') { return 'Notes'; } - if (activeTab.type === 'bookmark') { + if (activeTab.type === 'bookmarks') { return isEnterprise ? 'Vault' : 'Bookmarks'; } return 'Sessions'; - }, [activeTab]); + }, [activeTab.type, isEnterprise]); const onDateChange = (e: any) => { const dateValues = e.toJSON(); searchStore.edit(dateValues); - searchStore.fetchSessions(); + void searchStore.fetchSessions(true); }; return ( @@ -37,7 +37,7 @@ function SessionHeader() {

{title}

{activeTab.type !== 'notes' ? (
- {activeTab.type !== 'bookmark' && ( + {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 b0e395d11..649e9f98a 100644 --- a/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { FilterKey } from 'Types/filter/filterType'; import SessionItem from 'Shared/SessionItem'; import { NoContent, Loader, Pagination, Button } from 'UI'; -import { withRouter } from 'react-router-dom'; +import { useLocation, withRouter } from 'react-router-dom'; import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; import { numberWithCommas } from 'App/utils'; import SessionDateRange from './SessionDateRange'; @@ -23,6 +23,10 @@ let sessionStatusTimeOut: any = null; const STATUS_FREQUENCY = 5000; function SessionList() { + const location = useLocation(); // Get the current URL location + const isSessionsRoute = location.pathname.includes('/sessions'); + const isBookmark = location.pathname.includes('/bookmarks'); + const { projectsStore, sessionStore, customFieldStore, userStore } = useStore(); const isEnterprise = userStore.isEnterprise; const isLoggedIn = userStore.isLoggedIn; @@ -39,12 +43,17 @@ function SessionList() { const _filterKeys = filters.map((i: any) => i.key); const hasUserFilter = _filterKeys.includes(FilterKey.USERID) || _filterKeys.includes(FilterKey.USERANONYMOUSID); - const isBookmark = activeTab.type === 'bookmark'; + // const isBookmark = activeTab.type === 'bookmark'; const isVault = isBookmark && isEnterprise; const activeSite = projectsStore.active; const hasNoRecordings = !activeSite || !activeSite.recorded; const metaList = customFieldStore.list; + useEffect(() => { + void searchStore.fetchSessions(true, isBookmark); + }, [location.pathname]); + + const NO_CONTENT = React.useMemo(() => { if (isBookmark && !isEnterprise) { return { @@ -61,7 +70,7 @@ function SessionList() { icon: ICONS.NO_SESSIONS, message: }; - }, [isBookmark, isVault, activeTab]); + }, [isBookmark, isVault, activeTab, location.pathname]); const [statusData, setStatusData] = React.useState({ status: 0, count: 0 }); @@ -101,11 +110,11 @@ function SessionList() { } }, [statusData, siteId]); - useEffect(() => { - if (siteId) { - void searchStore.fetchSessions(); - } - }, [siteId]) + // useEffect(() => { + // if (siteId) { + // void searchStore.fetchSessions(); + // } + // }, [siteId]); useEffect(() => { const id = setInterval(() => { @@ -121,11 +130,11 @@ function SessionList() { const { scrollY } = searchStore; window.scrollTo(0, scrollY); - if (total === 0 && !loading && !hasNoRecordings) { - setTimeout(() => { - void searchStore.fetchSessions(); - }, 100); - } + // if (total === 0 && !loading && !hasNoRecordings) { + // setTimeout(() => { + // void searchStore.fetchSessions(); + // }, 100); + // } return () => { searchStore.setScrollPosition(window.scrollY); diff --git a/frontend/app/components/shared/SessionsTabOverview/components/SessionTags/SessionTags.tsx b/frontend/app/components/shared/SessionsTabOverview/components/SessionTags/SessionTags.tsx index 4284e3135..773e0c12e 100644 --- a/frontend/app/components/shared/SessionsTabOverview/components/SessionTags/SessionTags.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/components/SessionTags/SessionTags.tsx @@ -1,12 +1,9 @@ import { issues_types, types } from 'Types/session/issue'; import { Segmented } from 'antd'; -import cn from 'classnames'; import { Angry, CircleAlert, Skull, WifiOff } from 'lucide-react'; import { observer } from 'mobx-react-lite'; -import React, { memo } from 'react'; - +import React from 'react'; import { useStore } from 'App/mstore'; -import { Icon } from 'UI'; interface Tag { name: string; @@ -15,7 +12,6 @@ interface Tag { } interface StateProps { - } type Props = StateProps; @@ -25,94 +21,37 @@ const tagIcons = { [types.JS_EXCEPTION]: , [types.BAD_REQUEST]: , [types.CLICK_RAGE]: , - [types.CRASH]: , + [types.CRASH]: } as Record; const SessionTags: React.FC = () => { const { projectsStore, sessionStore, searchStore } = useStore(); const total = sessionStore.total; const platform = projectsStore.active?.platform || ''; - const disable = searchStore.activeTab.type === 'all' && total === 0; - const activeTab = searchStore.activeTab; - const tags = issues_types.filter( - (tag) => - tag.type !== 'mouse_thrashing' && - (platform === 'web' - ? tag.type !== types.TAP_RAGE - : tag.type !== types.CLICK_RAGE) - ); + const activeTab = searchStore.activeTags; - const options = tags.map((tag, i) => ({ - label: ( -
- {tag.icon ? ( - tagIcons[tag.type] ? ( - tagIcons[tag.type] - ) : ( - - ) - ) : null} -
- {tag.name} -
-
- ), - value: tag.type, - disabled: disable && tag.type !== 'all', - })); - - const onPick = (tabValue: string) => { - const tab = tags.find((t) => t.type === tabValue); - if (tab) { - searchStore.setActiveTab(tab); - } - }; - return ( + return total === 0 && (activeTab.length === 0 || activeTab[0] === 'all') ? null : (
+ tag.type !== 'mouse_thrashing' && + (platform === 'web' + ? tag.type !== types.TAP_RAGE + : tag.type !== types.CLICK_RAGE) + ) + .map((tag: any) => ({ + value: tag.type, + icon: tagIcons[tag.type], + label: tag.name + }))} + value={activeTab[0]} + onChange={(value: any) => searchStore.toggleTag(value)} size={'small'} />
); }; -// Separate the TagItem into its own memoized component. -export const TagItem: React.FC<{ - isActive: boolean; - onClick: () => void; - label: string; - icon?: string; - disabled: boolean; -}> = memo(({ isActive, onClick, label, icon, disabled }) => ( - -)); - export default observer(SessionTags); diff --git a/frontend/app/layout/SideMenu.tsx b/frontend/app/layout/SideMenu.tsx index 53d256af7..6b921b436 100644 --- a/frontend/app/layout/SideMenu.tsx +++ b/frontend/app/layout/SideMenu.tsx @@ -139,12 +139,12 @@ function SideMenu(props: Props) { React.useEffect(() => { const currentLocation = location.pathname; - const tab = Object.keys(TabToUrlMap).find((tab: keyof typeof TabToUrlMap) => - currentLocation.includes(TabToUrlMap[tab]) - ); - if (tab && tab !== searchStore.activeTab && siteId) { - searchStore.setActiveTab({ type: tab }); - } + // const tab = Object.keys(TabToUrlMap).find((tab: keyof typeof TabToUrlMap) => + // currentLocation.includes(TabToUrlMap[tab]) + // ); + // if (tab && tab !== searchStore.activeTab && siteId) { + // searchStore.setActiveTab({ type: tab }); + // } }, [location.pathname]); const menuRoutes: any = { diff --git a/frontend/app/mstore/searchStore.ts b/frontend/app/mstore/searchStore.ts index 96fe1c549..2eae1cfa8 100644 --- a/frontend/app/mstore/searchStore.ts +++ b/frontend/app/mstore/searchStore.ts @@ -15,6 +15,8 @@ import Filter, { checkFilterValue, IFilter } from 'App/mstore/types/filter'; import FilterItem from 'App/mstore/types/filterItem'; import { sessionStore } from 'App/mstore'; import SavedSearch, { ISavedSearch } from 'App/mstore/types/savedSearch'; +import { iTag } from '@/services/NotesService'; +import { issues_types } from 'Types/session/issue'; const PER_PAGE = 10; @@ -48,6 +50,14 @@ export const filterMap = ({ filters: filters ? filters.map(filterMap) : [] }); +export const TAB_MAP: any = { + all: { name: 'All', type: 'all' }, + sessions: { name: 'Sessions', type: 'sessions' }, + bookmarks: { name: 'Bookmarks', type: 'bookmarks' }, + notes: { name: 'Notes', type: 'notes' }, + recommendations: { name: 'Recommendations', type: 'recommendations' } +}; + class SearchStore { filterList = generateFilterOptions(filtersMap); filterListLive = generateFilterOptions(liveFiltersMap); @@ -68,6 +78,7 @@ class SearchStore { total: number = 0; loadingFilterSearch = false; isSaving: boolean = false; + activeTags: any[] = []; constructor() { makeAutoObservable(this); @@ -79,7 +90,7 @@ class SearchStore { // filters: savedSearch.filter.filters // }); console.log('savedSearch.filter.filters', savedSearch.filter.filters); - this.edit({ filters: savedSearch.filter ? savedSearch.filter.filters.map((i: FilterItem) => new FilterItem().fromJson(i)) : []}); + this.edit({ filters: savedSearch.filter ? savedSearch.filter.filters.map((i: FilterItem) => new FilterItem().fromJson(i)) : [] }); // this.edit({ filters: savedSearch.filter ? savedSearch.filter.filters : [] }); this.currentPage = 1; } @@ -131,12 +142,23 @@ class SearchStore { void this.fetchSessions(); } - setActiveTab(tab: any) { - this.activeTab = tab; + setActiveTab(tab: string) { + this.activeTab = TAB_MAP[tab]; + // this.activeTab = tab; this.currentPage = 1; // this.fetchSessions(); } + toggleTag(tag?: iTag) { + if (!tag) { + this.activeTags = []; + void this.fetchSessions(true); + } else { + this.activeTags = [tag]; + void this.fetchSessions(true); + } + } + async removeSavedSearch(id: string): Promise { await searchService.deleteSavedSearch(id); this.savedSearch = new SavedSearch({}); @@ -174,6 +196,7 @@ class SearchStore { this.savedSearch = new SavedSearch({}); sessionStore.clearList(); + void this.fetchSessions(true); } checkForLatestSessions() { @@ -274,13 +297,26 @@ class SearchStore { // TODO } - async fetchSessions(force: boolean = false): Promise { + async fetchSessions(force: boolean = false, bookmarked: boolean = false): Promise { + const filter = this.instance.toSearch(); + + if (this.activeTags[0] && this.activeTags[0] !== 'all') { + const tagFilter = filtersMap[FilterKey.ISSUE]; + tagFilter.value = [issues_types.find((i: any) => i.type === this.activeTags[0])?.type]; + delete tagFilter.operatorOptions; + delete tagFilter.options; + delete tagFilter.placeholder; + delete tagFilter.label; + delete tagFilter.icon; + filter.filters = filter.filters.concat(tagFilter); + } + await sessionStore.fetchSessions({ - ...this.instance.toSearch(), + ...filter, page: this.currentPage, perPage: this.pageSize, tab: this.activeTab.type, - bookmarked: this.activeTab.type === 'bookmark' ? true : undefined + bookmarked: bookmarked ? true : undefined }, force); }; } diff --git a/frontend/app/styles/general.css b/frontend/app/styles/general.css index 492568607..05d022b1b 100644 --- a/frontend/app/styles/general.css +++ b/frontend/app/styles/general.css @@ -431,3 +431,8 @@ p { .ant-segmented-group{ gap:0.25rem; } + +.ant-segmented-item-label { + display: flex; + align-items: center; +}