From 038d8decd522ac74ddf7d9cedba43ec5b1993262 Mon Sep 17 00:00:00 2001 From: nick-delirium Date: Fri, 7 Mar 2025 14:35:03 +0100 Subject: [PATCH] ui: drop deprecated router api, start moving routing --- frontend/app/IFrameRoutes.tsx | 6 +- frontend/app/PrivateRoutes.tsx | 137 ++++++++------ frontend/app/PublicRoutes.tsx | 19 +- frontend/app/Router.tsx | 23 +-- frontend/app/components/Client/Client.tsx | 66 +++---- .../components/Client/Projects/Projects.tsx | 13 +- .../components/Client/Sites/NewSiteForm.tsx | 9 +- .../app/components/Dashboard/NewDashboard.tsx | 19 +- .../CustomMetricTableErrors.tsx | 38 +--- .../AddCardSection/AddCardSection.tsx | 8 +- .../components/Alerts/AlertListItem.tsx | 9 +- .../components/Alerts/AlertsView.tsx | 14 +- .../Dashboard/components/Alerts/NewAlert.tsx | 17 +- .../components/CardUserList/CardUserList.tsx | 21 +-- .../components/CreateDashboardButton.tsx | 6 +- .../DashbaordListModal/DashbaordListModal.tsx | 18 +- .../DashboardHeader/DashboardHeader.tsx | 13 +- .../DashboardList/DashboardList.tsx | 6 +- .../DashboardList/DashboardsView.tsx | 2 +- .../DashboardList/NewDashModal/CreateCard.tsx | 178 ++++++++---------- .../DashboardList/NewDashModal/SelectCard.tsx | 9 +- .../DashboardModal/DashboardModal.tsx | 18 +- .../DashboardRouter/DashboardRouter.tsx | 25 +-- .../DashboardSideMenu/DashboardSideMenu.tsx | 19 +- .../DashboardView/DashboardView.tsx | 22 +-- .../DashboardWidgetGrid/AddMetric.tsx | 18 +- .../AddPredefinedMetric.tsx | 17 +- .../FunnelIssuesList/FunnelIssuesList.tsx | 22 +-- .../MetricListItem/MetricListItem.tsx | 8 +- .../MetricTypeList/MetricTypeList.tsx | 23 ++- .../components/MetricsList/GridView.tsx | 12 +- .../components/MetricsList/ListView.tsx | 6 +- .../components/WidgetForm/CardBuilder.tsx | 10 +- .../components/WidgetView/CardViewMenu.tsx | 6 +- .../components/WidgetView/WidgetView.tsx | 19 +- .../components/WidgetWrapper/CardMenu.tsx | 80 ++++---- .../WidgetWrapper/WidgetWrapper.tsx | 17 +- .../WidgetWrapper/WidgetWrapperNew.tsx | 14 +- .../components/Errors/Error/MainSection.js | 10 +- .../components/FFlags/FFlagsListHeader.tsx | 6 +- .../components/FFlags/FlagView/FlagView.tsx | 8 +- .../app/components/FFlags/NewFFlag/Header.tsx | 6 +- .../components/FFlags/NewFFlag/NewFFlag.tsx | 14 +- .../PreferencesView/PreferencesView.tsx | 13 +- .../Header/SettingsMenu/SettingsMenu.tsx | 29 ++- .../components/Header/UserMenu/UserMenu.tsx | 13 +- .../components/Highlights/HighlightsList.tsx | 6 +- frontend/app/components/Login/Login.tsx | 15 +- frontend/app/components/Modal/Modal.tsx | 13 +- .../app/components/Onboarding/Onboarding.tsx | 41 ++-- .../OnboardingMenu/OnboardingMenu.js | 27 +-- .../OnboardingNavButton.js | 12 +- .../Onboarding/components/withOnboarding.tsx | 31 +-- frontend/app/components/Overview/Overview.tsx | 90 +++++---- .../app/components/ScopeForm/ScopeForm.tsx | 12 +- .../Player/ClipPlayer/ClipPlayerControls.tsx | 6 +- .../AssistSessionsTabs/AssistSessionsTabs.tsx | 8 +- .../LivePlayer/LivePlayerBlockHeader.tsx | 8 +- .../Player/MobilePlayer/MobileControls.tsx | 9 +- .../MobilePlayer/MobilePlayerHeader.tsx | 6 +- .../Player/ReplayPlayer/PlayerBlockHeader.tsx | 13 +- .../BackendLogs/StatusMessages.tsx | 6 +- .../Session/Player/TagWatch/SaveModal.tsx | 6 +- .../Session_/Multiview/Multiview.tsx | 10 +- .../AssistSessionsTabs/AssistSessionsTabs.tsx | 8 +- .../Session_/Player/Controls/Controls.tsx | 9 +- .../Session_/Player/Overlay/AutoplayTimer.tsx | 17 +- .../Session_/QueueControls/QueueControls.tsx | 22 +-- frontend/app/components/Signup/Signup.tsx | 21 +-- .../Spots/SpotPlayer/SpotPlayer.tsx | 6 +- .../components/SpotPlayerHeader.tsx | 8 +- .../Spots/SpotsList/SpotListItem.tsx | 6 +- .../components/UsabilityTesting/TestEdit.tsx | 20 +- .../UsabilityTesting/TestOverview.tsx | 14 +- .../UsabilityTesting/UsabilityTesting.tsx | 10 +- .../components/hocs/withLocationHandlers.js | 102 +++++----- .../app/components/hocs/withSiteIdRouter.js | 21 --- .../app/components/hocs/withSiteIdUpdater.js | 10 +- .../shared/Breadcrumb/BackButton.tsx | 6 +- .../shared/GettingStarted/StepList.tsx | 10 +- .../IntegrateSlackButton.js | 10 +- .../NoSessionsMessage/NoSessionsMessage.js | 9 +- .../ProjectDropdown/ProjectDropdown.tsx | 11 +- .../shared/SessionItem/PlayLink/PlayLink.tsx | 6 +- .../shared/SessionItem/SessionItem.tsx | 9 +- .../components/SessionList/SessionList.tsx | 4 +- .../app/components/ui/NavPrompt/index.tsx | 46 +++++ .../NoSessionPermission.tsx | 20 +- frontend/app/components/ui/index.js | 3 +- .../app/hooks/useSessionSearchQueryHandler.ts | 16 +- frontend/app/layout/SideMenu.tsx | 18 +- .../app/layout/SpotToOpenReplayPrompt.tsx | 6 +- frontend/package.json | 4 +- frontend/yarn.lock | 112 +++-------- 94 files changed, 893 insertions(+), 1056 deletions(-) delete mode 100644 frontend/app/components/hocs/withSiteIdRouter.js create mode 100644 frontend/app/components/ui/NavPrompt/index.tsx diff --git a/frontend/app/IFrameRoutes.tsx b/frontend/app/IFrameRoutes.tsx index 8bfe3522b..f54122d72 100644 --- a/frontend/app/IFrameRoutes.tsx +++ b/frontend/app/IFrameRoutes.tsx @@ -1,5 +1,5 @@ import React, { lazy, Suspense } from 'react'; -import { Switch, Route } from 'react-router-dom'; +import { Routes, Route } from 'react-router-dom'; import { Loader } from 'UI'; import withSiteIdUpdater from 'HOCs/withSiteIdUpdater'; @@ -46,13 +46,13 @@ function IFrameRoutes(props: Props) { }> - + - + diff --git a/frontend/app/PrivateRoutes.tsx b/frontend/app/PrivateRoutes.tsx index d256df7f8..fd3c255a8 100644 --- a/frontend/app/PrivateRoutes.tsx +++ b/frontend/app/PrivateRoutes.tsx @@ -1,6 +1,7 @@ import withSiteIdUpdater from 'HOCs/withSiteIdUpdater'; import React, { Suspense, lazy } from 'react'; -import { Redirect, Route, Switch } from 'react-router-dom'; +import { useNavigate } from 'react-router' +import { Navigate, Route, Routes } from 'react-router-dom'; import { observer } from 'mobx-react-lite'; import { useStore } from './mstore'; import { GLOBAL_HAS_NO_RECORDINGS } from 'App/constants/storageKeys'; @@ -101,6 +102,7 @@ const HIGHLIGHTS_PATH = routes.highlights(); let debounceSearch: any = () => {}; function PrivateRoutes() { + const navigate = useNavigate(); const { projectsStore, userStore, integrationsStore, searchStore } = useStore(); const onboarding = userStore.onboarding; const scope = userStore.scopeState; @@ -129,34 +131,52 @@ function PrivateRoutes() { debounceSearch(); }, [searchStore.instance.filters, searchStore.instance.eventsOrder]); + React.useEffect(() => { + if (redirectToSetup) { + navigate(SCOPE_SETUP) + } + }, [redirectToSetup]) + React.useEffect(() => { + if (redirectToOnboarding) { + navigate(withSiteId(ONBOARDING_REDIRECT_PATH, siteId)) + } + }, [redirectToOnboarding]); + + console.log(withSiteId(SESSIONS_PATH, siteIdList)) return ( }> - + - {redirectToSetup ? : null} - + - {scope === 1 ? : null} + {scope === 1 + ? + + + : null + } { @@ -175,111 +195,106 @@ function PrivateRoutes() { }); break; } - return ; + return } + /> }} /> - {redirectToOnboarding && ( - - )} {/* DASHBOARD and Metrics */} - + {[ + withSiteId(ALERTS_PATH, siteIdList), + withSiteId(ALERT_EDIT_PATH, siteIdList), + withSiteId(ALERT_CREATE_PATH, siteIdList), + withSiteId(METRICS_PATH, siteIdList), + withSiteId(METRICS_DETAILS, siteIdList), + withSiteId(METRICS_DETAILS_SUB, siteIdList), + withSiteId(DASHBOARD_PATH, siteIdList), + withSiteId(DASHBOARD_SELECT_PATH, siteIdList), + withSiteId(DASHBOARD_METRIC_CREATE_PATH, siteIdList), + withSiteId(DASHBOARD_METRIC_DETAILS_PATH, siteIdList) + ].map((path) => ( + + ))} - + {[ + withSiteId(SESSIONS_PATH), + withSiteId(FFLAGS_PATH, siteIdList), + withSiteId(FFLAG_PATH, siteIdList), + withSiteId(FFLAG_READ_PATH, siteIdList), + withSiteId(FFLAG_CREATE_PATH, siteIdList), + withSiteId(NOTES_PATH, siteIdList), + withSiteId(BOOKMARKS_PATH, siteIdList) + ].map((path) => ( + } + /> + ))} - {Object.entries(routes.redirects).map(([fr, to]) => ( - - ))} - - - - + {/*} />*/} + ); } diff --git a/frontend/app/PublicRoutes.tsx b/frontend/app/PublicRoutes.tsx index 1e1cee516..4c68a40f8 100644 --- a/frontend/app/PublicRoutes.tsx +++ b/frontend/app/PublicRoutes.tsx @@ -1,6 +1,6 @@ import React, { lazy, Suspense, useEffect } from 'react'; import { Loader } from 'UI'; -import { Redirect, Route, Switch } from 'react-router-dom'; +import { Navigate, Route, Routes } from 'react-router-dom'; import Signup from 'Components/Signup/Signup'; import SupportCallout from 'Shared/SupportCallout'; import { useStore } from 'App/mstore'; @@ -35,13 +35,16 @@ function PublicRoutes() { return ( }> - - - - - - - + + } /> + } /> + } /> + + } + /> + {!hideSupport && } diff --git a/frontend/app/Router.tsx b/frontend/app/Router.tsx index 000a6d6d5..bb6fd0a56 100644 --- a/frontend/app/Router.tsx +++ b/frontend/app/Router.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useRef } from 'react'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; - +import { useLocation, useNavigate } from 'react-router' import IFrameRoutes from 'App/IFrameRoutes'; import PrivateRoutes from 'App/PrivateRoutes'; import PublicRoutes from 'App/PublicRoutes'; @@ -19,19 +18,9 @@ import { Loader } from 'UI'; import * as routes from './routes'; import { observer } from 'mobx-react-lite' -interface RouterProps extends RouteComponentProps { - match: { - params: { - siteId: string; - }; - }; -} - -const Router: React.FC = (props) => { - const { - location, - history, - } = props; +const Router = () => { + const location = useLocation() + const navigate = useNavigate() const mstore = useStore(); const { customFieldStore, projectsStore, sessionStore, searchStore, userStore } = mstore; const jwt = userStore.jwt; @@ -113,7 +102,7 @@ const Router: React.FC = (props) => { ) { const url = new URL(destinationPath, window.location.origin); checkParams(url.search); - history.push(destinationPath); + navigate(destinationPath); localStorage.removeItem(GLOBAL_DESTINATION_PATH); } }; @@ -219,4 +208,4 @@ const Router: React.FC = (props) => { ); }; -export default withRouter(observer(Router)); +export default observer(Router); diff --git a/frontend/app/components/Client/Client.tsx b/frontend/app/components/Client/Client.tsx index 06991988d..7788f6c99 100644 --- a/frontend/app/components/Client/Client.tsx +++ b/frontend/app/components/Client/Client.tsx @@ -1,13 +1,11 @@ import React from 'react'; -import { withRouter } from 'react-router-dom'; -import { Switch, Route, Redirect } from 'react-router'; +import { Routes, Route, Navigate } from 'react-router'; import { CLIENT_TABS, client as clientRoute } from 'App/routes'; import ProfileSettings from './ProfileSettings'; import Integrations from './Integrations'; import UserView from './Users/UsersView'; import AuditView from './Audit/AuditView'; -import Sites from './Sites'; import Projects from './Projects'; import CustomFields from './CustomFields'; import Webhooks from './Webhooks'; @@ -16,43 +14,27 @@ import Roles from './Roles'; import SessionsListingSettings from 'Components/Client/SessionsListingSettings'; import Modules from 'Components/Client/Modules'; -@withRouter -export default class Client extends React.PureComponent { - constructor(props) { - super(props); - } - - setTab = (tab) => { - this.props.history.push(clientRoute(tab)); - }; - - renderActiveTab = () => ( - - - - - - - - - - - - - - - ); - - render() { - const { - match: { - params: { activeTab } - } - } = this.props; - return ( -
- {activeTab && this.renderActiveTab()} -
- ); - } +function Client() { + return ( +
+ + + + + + + + + + + + + + + + +
+ ) } + +export default Client; \ No newline at end of file diff --git a/frontend/app/components/Client/Projects/Projects.tsx b/frontend/app/components/Client/Projects/Projects.tsx index 5c78068b4..f161497e1 100644 --- a/frontend/app/components/Client/Projects/Projects.tsx +++ b/frontend/app/components/Client/Projects/Projects.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import { App, Button, Card, Layout, Space, Tooltip, Typography } from 'antd'; +import { App, Button, Card, Layout, Tooltip, Typography } from 'antd'; import ProjectList from 'Components/Client/Projects/ProjectList'; import ProjectTabs from 'Components/Client/Projects/ProjectTabs'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { useStore } from '@/mstore'; import { observer } from 'mobx-react-lite'; import {PlusOutlined, KeyOutlined} from '@ant-design/icons' @@ -13,12 +13,12 @@ import Project from '@/mstore/types/project'; function Projects() { const { projectsStore, customFieldStore } = useStore(); - const history = useHistory(); + const navigate = useNavigate(); const { project, pid, tab } = projectsStore.config; const { openModal, closeModal } = useModal(); React.useEffect(() => { - const params = new URLSearchParams(history.location.search); + const params = new URLSearchParams(location.search); const pid = params.get('pid'); const tab = params.get('tab'); projectsStore.setConfigProject(pid ? parseInt(pid) : undefined); @@ -30,7 +30,7 @@ function Projects() { }, []); React.useEffect(() => { - const params = new URLSearchParams(history.location.search); + const params = new URLSearchParams(location.search); if (projectsStore.config.pid) { params.set('pid', projectsStore.config.pid + ''); } @@ -38,7 +38,8 @@ function Projects() { if (projectsStore.config.tab) { params.set('tab', projectsStore.config.tab); } - history.push({ search: params.toString() }); + const search = params.toString(); + navigate(location.pathname + '?' + search); }, [pid, tab]); const createProject = () => { diff --git a/frontend/app/components/Client/Sites/NewSiteForm.tsx b/frontend/app/components/Client/Sites/NewSiteForm.tsx index 6445fc700..b5bfee8f9 100644 --- a/frontend/app/components/Client/Sites/NewSiteForm.tsx +++ b/frontend/app/components/Client/Sites/NewSiteForm.tsx @@ -1,6 +1,6 @@ import { Segmented } from 'antd'; import React, { ChangeEvent, FormEvent, useEffect, useState } from 'react'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; +import { useLocation } from 'react-router' import { toast } from 'react-toastify'; import { useStore } from 'App/mstore'; import { confirm, Form, Icon, Input } from 'UI'; @@ -13,9 +13,10 @@ type OwnProps = { onClose: (arg: any) => void; }; -type Props = RouteComponentProps & OwnProps; +type Props = OwnProps; -const NewSiteForm = ({ location: { pathname }, onClose }: Props) => { +const NewSiteForm = ({ onClose }: Props) => { + const { pathname } = useLocation(); const mstore = useStore(); const { projectsStore } = mstore; const activeSiteId = projectsStore.active?.id; @@ -170,4 +171,4 @@ const NewSiteForm = ({ location: { pathname }, onClose }: Props) => { ); }; -export default withRouter(observer(NewSiteForm)); +export default observer(NewSiteForm); diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index cf9ce517d..f463758cf 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -1,20 +1,15 @@ import React, { useEffect } from 'react'; import { observer } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { useNavigate, useParams } from "react-router"; import { Loader } from 'UI'; import { withSiteId, dashboard, metrics } from "App/routes"; import DashboardRouter from './components/DashboardRouter'; import withPermissions from 'HOCs/withPermissions'; -interface RouterProps { - siteId: string; - dashboardId: string; - metricId: string; -} - -function NewDashboard(props: RouteComponentProps) { - const { history, match: { params: { siteId, dashboardId } } } = props; +function NewDashboard() { + const { siteId, dashboardId } = useParams() + const navigate = useNavigate(); const { dashboardStore } = useStore(); const initId = React.useRef(siteId) const loading = dashboardStore.isLoading; @@ -23,10 +18,10 @@ function NewDashboard(props: RouteComponentProps) { useEffect(() => { if (siteId !== initId.current) { if (isMetricListMetric) { - history.push(withSiteId(metrics(), siteId)) + navigate(withSiteId(metrics(), siteId)) } if (isDbMetric) { - history.push(withSiteId(dashboard(), siteId)) + navigate(withSiteId(dashboard(), siteId)) } } dashboardStore.fetchList().then((resp) => { @@ -43,4 +38,4 @@ function NewDashboard(props: RouteComponentProps) { ); } -export default withRouter(withPermissions(['METRICS'])(observer(NewDashboard))); +export default withPermissions(['METRICS'])(observer(NewDashboard)) diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors/CustomMetricTableErrors.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors/CustomMetricTableErrors.tsx index 75c70aaef..98a595de7 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors/CustomMetricTableErrors.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTableErrors/CustomMetricTableErrors.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from "react"; import { Pagination, NoContent, Icon } from "UI"; import ErrorListItem from "App/components/Dashboard/components/Errors/ErrorListItem"; -import { withRouter, RouteComponentProps } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router"; import { useModal } from "App/components/Modal"; import ErrorDetailsModal from "App/components/Dashboard/components/Errors/ErrorDetailsModal"; @@ -9,19 +9,18 @@ interface Props { metric: any; data: any; isEdit: any; - history: any; - location: any; } -function CustomMetricTableErrors(props: RouteComponentProps & Props) { +function CustomMetricTableErrors(props: Props) { const { metric, data } = props; - const errorId = new URLSearchParams(props.location.search).get("errorId"); + const location = useLocation(); + const navigate = useNavigate(); + const errorId = new URLSearchParams(location.search).get("errorId"); const { showModal, hideModal } = useModal(); const onErrorClick = (e: any, error: any) => { e.stopPropagation(); - props.history.replace({ - search: new URLSearchParams({ errorId: error.errorId }).toString(), - }); + const search = new URLSearchParams({ errorId: error.errorId }).toString() + navigate(location.pathname + "?" + search, { replace: true }); }; useEffect(() => { @@ -31,8 +30,8 @@ function CustomMetricTableErrors(props: RouteComponentProps & Props) { right: true, width: 1200, onClose: () => { - if (props.history.location.pathname.includes("/dashboard") || props.history.location.pathname.includes("/metrics/")) { - props.history.replace({ search: "" }); + if (location.pathname.includes("/dashboard") || location.pathname.includes("/metrics/")) { + navigate(location.pathname, { replace: true }); } }, }); @@ -60,7 +59,6 @@ function CustomMetricTableErrors(props: RouteComponentProps & Props) { ))} - {/*{isEdit && (*/}
- {/*)}*/} - - {/*{!isEdit && (*/} - {/* */} - {/*)}*/} ); } -export default withRouter(CustomMetricTableErrors); - -const ViewMore = ({ total, limit }: any) => - total > limit && ( -
-
-
- All {total} errors -
-
-
- ); +export default CustomMetricTableErrors; diff --git a/frontend/app/components/Dashboard/components/AddCardSection/AddCardSection.tsx b/frontend/app/components/Dashboard/components/AddCardSection/AddCardSection.tsx index 6781aeedb..9b5c65c45 100644 --- a/frontend/app/components/Dashboard/components/AddCardSection/AddCardSection.tsx +++ b/frontend/app/components/Dashboard/components/AddCardSection/AddCardSection.tsx @@ -25,7 +25,7 @@ import { USER_PATH, CATEGORIES, } from 'App/constants/card'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { dashboardMetricCreate, withSiteId, metricCreate } from 'App/routes'; import { FilterKey } from 'Types/filter/filterType'; import MetricsLibraryModal from '../MetricsLibraryModal/MetricsLibraryModal'; @@ -183,16 +183,16 @@ function CategoryTab({ }) { const items = isMobile ? mobileTabItems[tab] : tabItems[tab]; const { projectsStore, dashboardStore } = useStore(); - const history = useHistory(); + const navigate = useNavigate(); const handleCardSelection = (card: string) => { if (projectsStore.activeSiteId) { if (inCards) { - history.push( + navigate( withSiteId(metricCreate(), projectsStore.activeSiteId) + `?mk=${card}` ); } else if (dashboardStore.selectedDashboard) { - history.push( + navigate( withSiteId( dashboardMetricCreate(dashboardStore.selectedDashboard.dashboardId), projectsStore.activeSiteId diff --git a/frontend/app/components/Dashboard/components/Alerts/AlertListItem.tsx b/frontend/app/components/Dashboard/components/Alerts/AlertListItem.tsx index 1aa072d1c..39c9a3c0e 100644 --- a/frontend/app/components/Dashboard/components/Alerts/AlertListItem.tsx +++ b/frontend/app/components/Dashboard/components/Alerts/AlertListItem.tsx @@ -6,10 +6,10 @@ import { withSiteId, alertEdit } from 'App/routes'; import { numberWithCommas } from 'App/utils'; // @ts-ignore import { DateTime } from 'luxon'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import cn from 'classnames'; import Alert from 'Types/alert'; import { observer } from 'mobx-react-lite' +import { useNavigate } from 'react-router'; const getThreshold = (threshold: number) => { if (threshold === 15) return '15 Minutes'; @@ -84,7 +84,8 @@ interface Props extends RouteComponentProps { } function AlertListItem(props: Props) { - const { alert, siteId, history, init, demo, webhooks, triggerOptions } = props; + const { alert, siteId, init, demo, webhooks, triggerOptions } = props; + const navigate = useNavigate(); if (!alert) { return null; @@ -94,7 +95,7 @@ function AlertListItem(props: Props) { if (demo) return; const path = withSiteId(alertEdit(alert.alertId), siteId); init(alert || {}); - history.push(path); + navigate(path); }; const formTriggerName = () => @@ -169,4 +170,4 @@ function AlertListItem(props: Props) { ); } -export default withRouter(observer(AlertListItem)); +export default observer(AlertListItem); diff --git a/frontend/app/components/Dashboard/components/Alerts/AlertsView.tsx b/frontend/app/components/Dashboard/components/Alerts/AlertsView.tsx index 545413b0b..7a2a8a441 100644 --- a/frontend/app/components/Dashboard/components/Alerts/AlertsView.tsx +++ b/frontend/app/components/Dashboard/components/Alerts/AlertsView.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react'; -import { PageTitle, Icon, Link } from 'UI'; +import { PageTitle, Link } from 'UI'; import { Button } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; import withPageTitle from 'HOCs/withPageTitle'; @@ -7,7 +7,7 @@ import { withSiteId, alertCreate } from 'App/routes'; import AlertsList from './AlertsList'; import AlertsSearch from './AlertsSearch'; -import { useHistory } from 'react-router'; +import { useLocation } from 'react-router'; import { useStore } from 'App/mstore'; interface IAlertsView { @@ -15,18 +15,16 @@ interface IAlertsView { } function AlertsView({ siteId }: IAlertsView) { - const history = useHistory(); + const location = useLocation(); const { alertsStore } = useStore(); - useEffect(() => { - const unmount = history.listen((location) => { + return () => { if (!location.pathname.includes('/alert')) { alertsStore.updateKey('page', 1); } - }); - return unmount; - }, [history]); + } + }, [location.pathname]); return (
diff --git a/frontend/app/components/Dashboard/components/Alerts/NewAlert.tsx b/frontend/app/components/Dashboard/components/Alerts/NewAlert.tsx index aceeccf3a..6218d21d1 100644 --- a/frontend/app/components/Dashboard/components/Alerts/NewAlert.tsx +++ b/frontend/app/components/Dashboard/components/Alerts/NewAlert.tsx @@ -6,7 +6,6 @@ import { toast } from 'react-toastify'; import { SLACK, WEBHOOK, TEAMS } from 'App/constants/schedule'; import Breadcrumb from 'Shared/Breadcrumb'; import { withSiteId, alerts } from 'App/routes'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { useStore } from 'App/mstore' import { observer } from 'mobx-react-lite' import Alert from 'Types/alert' @@ -16,6 +15,7 @@ import BottomButtons from './AlertForm/BottomButtons'; import NotifyHooks from './AlertForm/NotifyHooks'; import AlertListItem from './AlertListItem'; import Condition from './AlertForm/Condition'; +import { useNavigate } from "react-router"; const Circle = ({ text }: { text: string }) => (
{ - const { alertsStore, settingsStore } = useStore(); + const navigate = useNavigate(); + const { alertsStore, settingsStore, projectsStore } = useStore(); + const siteId = projectsStore.activeSiteId const { fetchTriggerOptions, init, @@ -80,9 +82,6 @@ const NewAlert = (props: IProps) => { const deleting = loading const webhooks = settingsStore.webhooks const fetchWebhooks = settingsStore.fetchWebhooks - const { - siteId, - } = props; useEffect(() => { init({}); @@ -120,7 +119,7 @@ const NewAlert = (props: IProps) => { }) ) { remove(instance.alertId).then(() => { - props.history.push(withSiteId(alerts(), siteId)); + navigate(withSiteId(alerts(), siteId)); toast.success('Alert deleted'); }).catch(() => { toast.error('Failed to delete an alert'); @@ -133,7 +132,7 @@ const NewAlert = (props: IProps) => { save(instance).then(() => { if (!wasUpdating) { toast.success('New alert saved'); - props.history.push(withSiteId(alerts(), siteId)); + navigate(withSiteId(alerts(), siteId)); } else { toast.success('Alert updated'); } @@ -296,4 +295,4 @@ const NewAlert = (props: IProps) => { ); }; -export default withRouter(observer(NewAlert)) +export default observer(NewAlert) diff --git a/frontend/app/components/Dashboard/components/CardUserList/CardUserList.tsx b/frontend/app/components/Dashboard/components/CardUserList/CardUserList.tsx index 842184f2e..8ea081d5a 100644 --- a/frontend/app/components/Dashboard/components/CardUserList/CardUserList.tsx +++ b/frontend/app/components/Dashboard/components/CardUserList/CardUserList.tsx @@ -1,21 +1,19 @@ import { useModal } from 'App/components/Modal'; import { observer } from 'mobx-react-lite'; import React, { useEffect, useState } from 'react'; -import { RouteComponentProps, withRouter } from 'react-router'; import { Loader, Pagination } from 'UI'; import { Button } from 'antd' import SessionsModal from './SessionsModal'; import CardUserItem from './CardUserItem'; import { useStore } from 'App/mstore'; +import { useNavigate, useLocation } from 'react-router'; -interface Props { - history: any; - location: any; -} -function CardUserList(props: RouteComponentProps) { +function CardUserList() { + const navigate = useNavigate(); + const location = useLocation(); const [loading, setLoading] = useState(false); const { showModal } = useModal(); - const userId = new URLSearchParams(props.location.search).get("userId"); + const userId = new URLSearchParams(location.search).get("userId"); const { metricStore, dashboardStore } = useStore(); const [data, setData] = useState([ @@ -27,7 +25,8 @@ function CardUserList(props: RouteComponentProps) { const pageSize = data.length; const handleClick = (issue: any) => { - props.history.replace({search: (new URLSearchParams({userId : '123'})).toString()}); + const search = (new URLSearchParams({userId : '123'})).toString() + navigate(location.pathname + "?" + search, { replace: true }); // showModal(, { right: true, width: 450 }) } @@ -35,8 +34,8 @@ function CardUserList(props: RouteComponentProps) { if (!userId) return; showModal(, { right: true, width: 600, onClose: () => { - if (props.history.location.pathname.includes("/metric")) { - props.history.replace({search: ""}); + if (location.pathname.includes("/metric")) { + navigate(location.pathname, { replace: true }); } }}); }, [userId]); @@ -75,4 +74,4 @@ function CardUserList(props: RouteComponentProps) { ); } -export default withRouter(observer(CardUserList)); \ No newline at end of file +export default observer(CardUserList); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/CreateDashboardButton.tsx b/frontend/app/components/Dashboard/components/CreateDashboardButton.tsx index d768449d9..4d64d2575 100644 --- a/frontend/app/components/Dashboard/components/CreateDashboardButton.tsx +++ b/frontend/app/components/Dashboard/components/CreateDashboardButton.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { PlusOutlined } from '@ant-design/icons'; import { Button } from 'antd'; import { useStore } from 'App/mstore'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; interface Props { disabled?: boolean; @@ -12,7 +12,7 @@ function CreateDashboardButton({ disabled }: Props) { const [dashboardCreating, setDashboardCreating] = React.useState(false); const { projectsStore, dashboardStore } = useStore(); const siteId = projectsStore.siteId; - const history = useHistory(); + const navigate = useNavigate(); const createNewDashboard = async () => { setDashboardCreating(true); @@ -21,7 +21,7 @@ function CreateDashboardButton({ disabled }: Props) { .save(dashboardStore.dashboardInstance) .then(async (syncedDashboard) => { dashboardStore.selectDashboardById(syncedDashboard.dashboardId); - history.push(`/${siteId}/dashboard/${syncedDashboard.dashboardId}`); + navigate(`/${siteId}/dashboard/${syncedDashboard.dashboardId}`); }) .finally(() => { setDashboardCreating(false); diff --git a/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx b/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx index 236602493..57fdfb66d 100644 --- a/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx +++ b/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx @@ -2,23 +2,21 @@ import React from 'react'; import { useStore } from 'App/mstore'; import { SideMenuitem, Icon } from 'UI'; import { withSiteId, dashboardSelected } from 'App/routes'; -import { withRouter } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { useModal } from 'App/components/Modal'; -interface Props { - siteId: string - history: any -} -function DashbaordListModal(props: Props) { - const { dashboardStore } = useStore(); +function DashbaordListModal() { + const navigate = useNavigate(); + const { dashboardStore, projectsStore } = useStore(); + const siteId = projectsStore.activeSiteId; const { hideModal } = useModal(); const dashboards = dashboardStore.dashboards; const activeDashboardId = dashboardStore.selectedDashboard?.dashboardId; const onItemClick = (dashboard) => { dashboardStore.selectDashboardById(dashboard.dashboardId); - const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(props.siteId)); - props.history.push(path); + const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId)); + navigate(path); hideModal(); }; return ( @@ -46,4 +44,4 @@ function DashbaordListModal(props: Props) { ); } -export default withRouter(DashbaordListModal); +export default DashbaordListModal; diff --git a/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx b/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx index 033fe55da..3a72bd939 100644 --- a/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx +++ b/frontend/app/components/Dashboard/components/DashboardHeader/DashboardHeader.tsx @@ -1,7 +1,6 @@ import React from 'react'; import BackButton from 'Shared/Breadcrumb/BackButton'; import { withSiteId } from 'App/routes'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { PageTitle, confirm } from 'UI'; import { Tooltip, Popover, Button } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; @@ -12,21 +11,23 @@ import withModal from 'App/components/Modal/withModal'; import { observer } from 'mobx-react-lite'; import DashboardEditModal from '../DashboardEditModal'; import AddCardSection from '../AddCardSection/AddCardSection'; +import { useNavigate } from "react-router"; interface IProps { siteId: string; renderReport?: any; } -type Props = IProps & RouteComponentProps; +type Props = IProps; function DashboardHeader(props: Props) { - const { siteId } = props; + const navigate = useNavigate(); const [popoverOpen, setPopoverOpen] = React.useState(false); const handleOpenChange = (open: boolean) => { setPopoverOpen(open); }; - const { dashboardStore } = useStore(); + const { dashboardStore, projectsStore } = useStore(); + const siteId = projectsStore.activeSiteId const [focusTitle, setFocusedInput] = React.useState(true); const [showEditModal, setShowEditModal] = React.useState(false); const period = dashboardStore.period; @@ -48,7 +49,7 @@ function DashboardHeader(props: Props) { }) ) { dashboardStore.deleteDashboard(dashboard).then(() => { - props.history.push(withSiteId(`/dashboard`, siteId)); + navigate(withSiteId(`/dashboard`, siteId)); }); } }; @@ -118,4 +119,4 @@ function DashboardHeader(props: Props) { ); } -export default withRouter(withModal(observer(DashboardHeader))); +export default withModal(observer(DashboardHeader)); diff --git a/frontend/app/components/Dashboard/components/DashboardList/DashboardList.tsx b/frontend/app/components/Dashboard/components/DashboardList/DashboardList.tsx index 1a0ec5365..370a00c21 100644 --- a/frontend/app/components/Dashboard/components/DashboardList/DashboardList.tsx +++ b/frontend/app/components/Dashboard/components/DashboardList/DashboardList.tsx @@ -1,6 +1,6 @@ import { observer } from 'mobx-react-lite'; import React from 'react'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { Empty, Switch, @@ -32,7 +32,7 @@ function DashboardList() { const list = dashboardStore.filteredList; const dashboardsSearch = dashboardStore.filter.query; - const history = useHistory(); + const navigate = useNavigate(); // Define custom width and height for each scenario const searchImageDimensions = { width: 60, height: 'auto' }; @@ -250,7 +250,7 @@ function DashboardList() { dashboardSelected(record.dashboardId), siteId ); - history.push(path); + navigate(path); }, })} /> diff --git a/frontend/app/components/Dashboard/components/DashboardList/DashboardsView.tsx b/frontend/app/components/Dashboard/components/DashboardList/DashboardsView.tsx index 829852dd9..5f40ca824 100644 --- a/frontend/app/components/Dashboard/components/DashboardList/DashboardsView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardList/DashboardsView.tsx @@ -3,7 +3,7 @@ import withPageTitle from 'HOCs/withPageTitle'; import DashboardList from './DashboardList'; import Header from './Header'; -function DashboardsView({history, siteId}: { history: any; siteId: string }) { +function DashboardsView() { return (
diff --git a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CreateCard.tsx b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CreateCard.tsx index ef685eceb..cb413fc3b 100644 --- a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CreateCard.tsx +++ b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/CreateCard.tsx @@ -1,108 +1,96 @@ import React from 'react'; -import {Button, Space} from "antd"; -import {ArrowLeft, ArrowRight} from "lucide-react"; -import CardBuilder from "Components/Dashboard/components/WidgetForm/CardBuilder"; -import {useHistory} from "react-router"; -import {useStore} from "App/mstore"; -import { HEATMAP } from "App/constants/card"; -import {renderClickmapThumbnail} from "Components/Dashboard/components/WidgetForm/renderMap"; -import WidgetPreview from "Components/Dashboard/components/WidgetPreview/WidgetPreview"; +import { Button, Space } from 'antd'; +import { ArrowLeft, ArrowRight } from 'lucide-react'; +import { useNavigate, useLocation } from 'react-router'; +import { useStore } from 'App/mstore'; +import { HEATMAP } from 'App/constants/card'; +import { renderClickmapThumbnail } from 'Components/Dashboard/components/WidgetForm/renderMap'; +import WidgetPreview from 'Components/Dashboard/components/WidgetPreview/WidgetPreview'; import WidgetFormNew from 'Components/Dashboard/components/WidgetForm/WidgetFormNew'; -const getTitleByType = (type: string) => { - switch (type) { - case HEATMAP: - return 'Heatmap'; - default: - return 'Trend Single'; - } -} - interface Props { - // cardType: string, - onBack?: () => void - onAdded?: () => void - extra?: React.ReactNode + onBack?: () => void; + onAdded?: () => void; + extra?: React.ReactNode; } function CreateCard(props: Props) { - const history = useHistory(); - const {metricStore, dashboardStore, aiFiltersStore} = useStore(); - const metric = metricStore.instance; - const siteId: string = history.location.pathname.split('/')[1]; - const dashboardId: string = history.location.pathname.split('/')[3]; - const isItDashboard = history.location.pathname.includes('dashboard') - // const title = getTitleByType(metric.metricType) + const location = useLocation(); + const navigate = useNavigate(); + const { metricStore, dashboardStore } = useStore(); + const metric = metricStore.instance; + const dashboardId: string = location.pathname.split('/')[3]; + const isItDashboard = location.pathname.includes('dashboard'); - const createNewDashboard = async () => { - dashboardStore.initDashboard(); - return await dashboardStore - .save(dashboardStore.dashboardInstance) - .then(async (syncedDashboard) => { - dashboardStore.selectDashboardById(syncedDashboard.dashboardId); - return syncedDashboard.dashboardId; - }); - } + const createNewDashboard = async () => { + dashboardStore.initDashboard(); + return await dashboardStore + .save(dashboardStore.dashboardInstance) + .then(async (syncedDashboard) => { + dashboardStore.selectDashboardById(syncedDashboard.dashboardId); + return syncedDashboard.dashboardId; + }); + }; - const addCardToDashboard = async (dashboardId: string, metricId: string) => { - return dashboardStore.addWidgetToDashboard( - dashboardStore.getDashboard(parseInt(dashboardId, 10))!, [metricId] - ); - } - - const createCard = async () => { - const isClickMap = metric.metricType === HEATMAP; - if (isClickMap) { - try { - metric.thumbnail = await renderClickmapThumbnail(); - } catch (e) { - console.error(e); - } - } - - const savedMetric = await metricStore.save(metric); - return savedMetric.metricId; - } - - const createDashboardAndAddCard = async () => { - const cardId = await createCard(); - - if (dashboardId) { - await addCardToDashboard(dashboardId, cardId); - void dashboardStore.fetch(dashboardId); - props.onAdded?.(); - } else if (isItDashboard) { - const dashboardId = await createNewDashboard(); - await addCardToDashboard(dashboardId, cardId); - history.replace(`${history.location.pathname}/${dashboardId}`); - } else { - history.replace(`${history.location.pathname}/${cardId}`); - } - } - - return ( -
-
- - {props.onBack ? : null} -
- {metric.name} -
-
- -
- {props.extra} - {/**/} - - -
+ const addCardToDashboard = async (dashboardId: string, metricId: string) => { + return dashboardStore.addWidgetToDashboard( + dashboardStore.getDashboard(parseInt(dashboardId, 10))!, + [metricId] ); + }; + + const createCard = async () => { + const isClickMap = metric.metricType === HEATMAP; + if (isClickMap) { + try { + metric.thumbnail = await renderClickmapThumbnail(); + } catch (e) { + console.error(e); + } + } + + const savedMetric = await metricStore.save(metric); + return savedMetric.metricId; + }; + + const createDashboardAndAddCard = async () => { + const cardId = await createCard(); + + if (dashboardId) { + await addCardToDashboard(dashboardId, cardId); + void dashboardStore.fetch(dashboardId); + props.onAdded?.(); + } else if (isItDashboard) { + const dashboardId = await createNewDashboard(); + await addCardToDashboard(dashboardId, cardId); + navigate(`${location.pathname}/${dashboardId}`, { replace: true }); + } else { + navigate(`${location.pathname}/${cardId}`, { replace: true }); + } + }; + + return ( +
+
+ + {props.onBack ? ( + + ) : null} +
{metric.name}
+
+ +
+ {props.extra} + + +
+ ); } export default CreateCard; diff --git a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/SelectCard.tsx b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/SelectCard.tsx index 1804e029f..05c385d36 100644 --- a/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/SelectCard.tsx +++ b/frontend/app/components/Dashboard/components/DashboardList/NewDashModal/SelectCard.tsx @@ -1,13 +1,13 @@ import React, { useMemo, useState, useEffect } from 'react'; import { Button, Input, Segmented, Space } from 'antd'; import { RightOutlined } from '@ant-design/icons'; -import { ArrowRight, Info } from 'lucide-react'; +import { Info } from 'lucide-react'; import { CARD_LIST, CARD_CATEGORIES, CardType } from './ExampleCards'; import { useStore } from 'App/mstore'; import Option from './Option'; import CardsLibrary from 'Components/Dashboard/components/DashboardList/NewDashModal/CardsLibrary'; import { FUNNEL } from 'App/constants/card'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { FilterKey } from 'Types/filter/filterType'; import FilterSeries from '@/mstore/types/filterSeries'; @@ -32,7 +32,7 @@ const SelectCard: React.FC = (props: SelectCardProps) => { const isCreatingDashboard = !dashboardId && location.pathname.includes('dashboard'); const [dashboardCreating, setDashboardCreating] = useState(false); const [dashboardUpdating, setDashboardUpdating] = useState(false); - const history = useHistory(); + const navigate = useNavigate(); useEffect(() => { if (dashboardId) { @@ -49,8 +49,7 @@ const SelectCard: React.FC = (props: SelectCardProps) => { .save(dashboardStore.dashboardInstance) .then(async (syncedDashboard) => { dashboardStore.selectDashboardById(syncedDashboard.dashboardId); - history.push(`/${siteId}/dashboard/${syncedDashboard.dashboardId}`); - //return syncedDashboard.dashboardId; + navigate(`/${siteId}/dashboard/${syncedDashboard.dashboardId}`); }).finally(() => { setDashboardCreating(false); }); diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx index 20429e74a..cfd79f4d4 100644 --- a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -3,20 +3,20 @@ import { useObserver } from 'mobx-react-lite'; import DashboardMetricSelection from '../DashboardMetricSelection'; import DashboardForm from '../DashboardForm'; import { Button } from 'antd'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { useStore } from 'App/mstore'; import { useModal } from 'App/components/Modal'; import { dashboardMetricCreate, withSiteId } from 'App/routes'; +import { useNavigate } from "react-router"; -interface Props extends RouteComponentProps { - history: any - siteId?: string +interface Props { dashboardId?: string onMetricAdd?: () => void; } function DashboardModal(props: Props) { - const { history, siteId, dashboardId } = props; - const { dashboardStore } = useStore(); + const navigate = useNavigate(); + const { dashboardId } = props; + const { dashboardStore, projectsStore } = useStore(); + const siteId = projectsStore.activeSiteId const selectedWidgetsCount = useObserver(() => dashboardStore.selectedWidgets.length); const { hideModal } = useModal(); const dashboard = useObserver(() => dashboardStore.dashboardInstance); @@ -28,7 +28,7 @@ function DashboardModal(props: Props) { await dashboardStore.fetch(dashboard.dashboardId) } dashboardStore.selectDashboardById(syncedDashboard.dashboardId); - history.push(withSiteId(`/dashboard/${syncedDashboard.dashboardId}`, siteId)) + navigate(withSiteId(`/dashboard/${syncedDashboard.dashboardId}`, siteId)) }) .then(hideModal) } @@ -36,7 +36,7 @@ function DashboardModal(props: Props) { const handleCreateNew = () => { const path = withSiteId(dashboardMetricCreate(dashboardId), siteId); props.onMetricAdd(); - history.push(path); + navigate(path); hideModal(); } const isDashboardExists = dashboard.exists() @@ -81,4 +81,4 @@ function DashboardModal(props: Props) { )); } -export default withRouter(DashboardModal); +export default DashboardModal; diff --git a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx index 419ac64b2..998660df4 100644 --- a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx +++ b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { Switch, Route } from 'react-router'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { Routes, Route } from 'react-router'; import { metrics, @@ -22,26 +21,18 @@ import WidgetSubDetailsView from '../WidgetSubDetailsView'; import DashboardsView from '../DashboardList'; import Alerts from '../Alerts'; import CreateAlert from '../Alerts/NewAlert' +import { useParams, useNavigate } from "react-router"; function DashboardViewSelected({ siteId, dashboardId }: { siteId: string; dashboardId: string }) { return ; } -interface Props extends RouteComponentProps { - match: any; -} - -function DashboardRouter(props: Props) { - const { - match: { - params: { siteId, dashboardId }, - }, - history, - } = props; +function DashboardRouter(props: any) { + const { siteId, dashboardId } = useParams(); return (
- + @@ -55,7 +46,7 @@ function DashboardRouter(props: Props) { - + @@ -83,9 +74,9 @@ function DashboardRouter(props: Props) { {/* @ts-ignore */} - +
); } -export default withRouter(DashboardRouter); +export default DashboardRouter; diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index af8ecf16e..66c2d6b2a 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -1,20 +1,21 @@ import React from 'react'; import { SideMenuitem } from 'UI'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { withSiteId, metrics, dashboard, alerts } from 'App/routes'; +import { useLocation, useNavigate } from "react-router"; -interface Props extends RouteComponentProps { +interface Props { siteId: string; - history: any; } function DashboardSideMenu(props: Props) { - const { history, siteId } = props; - const isMetric = history.location.pathname.includes('metrics'); - const isDashboards = history.location.pathname.includes('dashboard'); - const isAlerts = history.location.pathname.includes('alerts'); + const { siteId } = props; + const navigate = useNavigate(); + const location = useLocation(); + const isMetric = location.pathname.includes('metrics'); + const isDashboards = location.pathname.includes('dashboard'); + const isAlerts = location.pathname.includes('alerts'); const redirect = (path: string) => { - history.push(path); + navigate(path); }; return ( @@ -53,4 +54,4 @@ function DashboardSideMenu(props: Props) { ); } -export default withRouter(DashboardSideMenu); +export default DashboardSideMenu; diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index 7b499ecd7..20fd72758 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -5,29 +5,28 @@ import {Loader} from 'UI'; import {withSiteId} from 'App/routes'; import withModal from 'App/components/Modal/withModal'; import DashboardWidgetGrid from '../DashboardWidgetGrid'; -import {withRouter, RouteComponentProps} from 'react-router-dom'; import {useModal} from 'App/components/Modal'; import DashboardModal from '../DashboardModal'; import AlertFormModal from 'App/components/Alerts/AlertFormModal'; import withPageTitle from 'HOCs/withPageTitle'; import withReport from 'App/components/hocs/withReport'; import DashboardHeader from '../DashboardHeader'; -import {useHistory} from "react-router"; +import { useNavigate } from "react-router"; import AiQuery from "./AiQuery"; interface IProps { - siteId: string; dashboardId: any; renderReport?: any; } -type Props = IProps & RouteComponentProps; +type Props = IProps; function DashboardView(props: Props) { - const {siteId, dashboardId} = props; - const {dashboardStore} = useStore(); + const { dashboardId } = props; + const { dashboardStore, projectsStore } = useStore(); + const siteId = projectsStore.activeSiteId; const {showModal, hideModal} = useModal(); - const history = useHistory(); + const navigate = useNavigate(); const showAlertModal = dashboardStore.showAlertModal; const loading = dashboardStore.fetchingDashboard; @@ -38,9 +37,8 @@ function DashboardView(props: Props) { const trimQuery = () => { if (!queryParams.has('modal')) return; queryParams.delete('modal'); - history.replace({ - search: queryParams.toString(), - }); + const search = queryParams.toString(); + navigate(location.pathname + "?" + search, { replace: true }); }; useEffect(() => { @@ -60,7 +58,7 @@ function DashboardView(props: Props) { }, [showAlertModal]) const pushQuery = () => { - if (!queryParams.has('modal')) history.push('?modal=addMetric'); + if (!queryParams.has('modal')) navigate('?modal=addMetric'); }; useEffect(() => { @@ -75,7 +73,7 @@ function DashboardView(props: Props) { useEffect(() => { const isExists = dashboardStore.getDashboardById(dashboardId); if (!isExists) { - history.push(withSiteId(`/dashboard`, siteId)); + navigate(withSiteId(`/dashboard`, siteId)); } }, [dashboardId]); diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetric.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetric.tsx index 54b7c1e05..210a7ed24 100644 --- a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetric.tsx +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddMetric.tsx @@ -6,18 +6,18 @@ import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper'; import { useStore } from 'App/mstore'; import { useModal } from 'App/components/Modal'; import { dashboardMetricCreate, withSiteId } from 'App/routes'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { useNavigate } from "react-router"; -interface IProps extends RouteComponentProps { - siteId: string; +interface IProps { title: string; description: string; } -function AddMetric({ history, siteId, title, description }: IProps) { +function AddMetric({ title, description }: IProps) { const [metrics, setMetrics] = React.useState[]>([]); - - const { dashboardStore } = useStore(); + const navigate = useNavigate(); + const { dashboardStore, projectsStore } = useStore(); + const siteId = projectsStore.activeSiteId const { hideModal } = useModal(); React.useEffect(() => { @@ -47,8 +47,8 @@ function AddMetric({ history, siteId, title, description }: IProps) { const onCreateNew = () => { const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId); - if (!queryParams.has('modal')) history.push('?modal=addMetric'); - history.push(path); + if (!queryParams.has('modal')) navigate('?modal=addMetric'); + navigate(path); hideModal(); }; @@ -110,4 +110,4 @@ function AddMetric({ history, siteId, title, description }: IProps) { ); } -export default withRouter(observer(AddMetric)); +export default observer(AddMetric); diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddPredefinedMetric.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddPredefinedMetric.tsx index 70fbf4040..420364fce 100644 --- a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddPredefinedMetric.tsx +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/AddPredefinedMetric.tsx @@ -6,18 +6,19 @@ import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper'; import { useStore } from 'App/mstore'; import { useModal } from 'App/components/Modal'; import { dashboardMetricCreate, withSiteId } from 'App/routes'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { WidgetCategoryItem } from 'App/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection'; +import { useNavigate } from "react-router"; -interface IProps extends RouteComponentProps { - siteId: string; +interface IProps { title: string; description: string; } -function AddPredefinedMetric({ history, siteId, title, description }: IProps) { +function AddPredefinedMetric({ title, description }: IProps) { + const navigate = useNavigate(); const [categories, setCategories] = React.useState([]); - const { dashboardStore } = useStore(); + const { dashboardStore, projectsStore } = useStore(); + const siteId = projectsStore.activeSiteId const { hideModal } = useModal(); const [activeCategory, setActiveCategory] = React.useState>(); @@ -62,8 +63,8 @@ function AddPredefinedMetric({ history, siteId, title, description }: IProps) { const onCreateNew = () => { const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId); - if (!queryParams.has('modal')) history.push('?modal=addMetric'); - history.push(path); + if (!queryParams.has('modal')) navigate('?modal=addMetric'); + navigate(path); hideModal(); }; @@ -155,4 +156,4 @@ function AddPredefinedMetric({ history, siteId, title, description }: IProps) { ); } -export default withRouter(observer(AddPredefinedMetric)); +export default observer(AddPredefinedMetric); diff --git a/frontend/app/components/Dashboard/components/Funnels/FunnelIssuesList/FunnelIssuesList.tsx b/frontend/app/components/Dashboard/components/Funnels/FunnelIssuesList/FunnelIssuesList.tsx index 224654d5f..d8d704718 100644 --- a/frontend/app/components/Dashboard/components/Funnels/FunnelIssuesList/FunnelIssuesList.tsx +++ b/frontend/app/components/Dashboard/components/Funnels/FunnelIssuesList/FunnelIssuesList.tsx @@ -2,7 +2,6 @@ import { Table, Typography } from 'antd'; import type { TableProps } from 'antd'; import { useObserver } from 'mobx-react-lite'; import React, { useEffect } from 'react'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; import { useModal } from 'App/components/Modal'; import { useStore } from 'App/mstore'; @@ -10,8 +9,8 @@ import { NoContent } from 'UI'; import { InfoCircleOutlined } from '@ant-design/icons'; import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; import FunnelIssueModal from '../FunnelIssueModal'; -import FunnelIssuesListItem from '../FunnelIssuesListItem'; const { Text } = Typography; +import { useLocation, useNavigate } from "react-router"; interface Issue { issueId: string; @@ -70,26 +69,25 @@ const columns: TableProps['columns'] = [ }, ]; -interface Props extends RouteComponentProps { +interface Props { loading?: boolean; issues: Issue[]; - history: any; - location: any; } function FunnelIssuesList(props: Props) { const { issues, loading } = props; + const location = useLocation(); + const navigate = useNavigate(); const { funnelStore } = useStore(); const issuesSort = useObserver(() => funnelStore.issuesSort); const issuesFilter = useObserver(() => funnelStore.issuesFilter.map((issue: any) => issue.value) ); const { showModal } = useModal(); - const issueId = new URLSearchParams(props.location.search).get('issueId'); + const issueId = new URLSearchParams(location.search).get('issueId'); const onIssueClick = (issue: any) => { - props.history.replace({ - search: new URLSearchParams({ issueId: issue.issueId }).toString(), - }); + const search = new URLSearchParams({ issueId: issue.issueId }).toString(); + navigate(location.pathname + '?' + search, { replace: true }); }; useEffect(() => { @@ -99,8 +97,8 @@ function FunnelIssuesList(props: Props) { right: true, width: 1000, onClose: () => { - if (props.history.location.pathname.includes('/metric')) { - props.history.replace({ search: '' }); + if (location.pathname.includes('/metric')) { + navigate(location.pathname, { replace: true }); } }, }); @@ -147,4 +145,4 @@ function FunnelIssuesList(props: Props) { )); } -export default withRouter(FunnelIssuesList); +export default FunnelIssuesList; diff --git a/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx b/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx index 8cb522b24..23def70cb 100644 --- a/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx +++ b/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx @@ -21,7 +21,7 @@ import { TYPE_ICONS, TYPE_NAMES } from 'App/constants/card'; import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; import { toast } from 'react-toastify'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { EllipsisVertical } from 'lucide-react'; import cn from 'classnames' @@ -61,7 +61,7 @@ const MetricListItem: React.FC = ({ renderColumn, inLibrary, }) => { - const history = useHistory(); + const navigate = useNavigate(); const { metricStore } = useStore(); const [isEdit, setIsEdit] = useState(false); const [newName, setNewName] = useState(metric.name); @@ -75,7 +75,7 @@ const MetricListItem: React.FC = ({ return toggleSelection(e); } const path = withSiteId(`/metrics/${metric.metricId}`, siteId); - history.push(path); + navigate(path); }; const onMenuClick = async ({ key }: { key: string }) => { @@ -99,7 +99,7 @@ const MetricListItem: React.FC = ({ try { metric.update({ name: newName }); await metricStore.save(metric); - metricStore.fetchList(); + void metricStore.fetchList(); setIsEdit(false); } catch (e) { toast.error('Failed to rename card'); diff --git a/frontend/app/components/Dashboard/components/MetricTypeList/MetricTypeList.tsx b/frontend/app/components/Dashboard/components/MetricTypeList/MetricTypeList.tsx index b2cc50836..fe5222925 100644 --- a/frontend/app/components/Dashboard/components/MetricTypeList/MetricTypeList.tsx +++ b/frontend/app/components/Dashboard/components/MetricTypeList/MetricTypeList.tsx @@ -3,21 +3,22 @@ import React from 'react'; import MetricsLibraryModal from '../MetricsLibraryModal'; import MetricTypeItem, { MetricType } from '../MetricTypeItem/MetricTypeItem'; import { TYPES, LIBRARY, INSIGHTS } from 'App/constants/card'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { dashboardMetricCreate, metricCreate, withSiteId } from 'App/routes'; import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; import { ENTERPRISE_REQUEIRED } from 'App/constants'; +import { useNavigate } from "react-router"; -interface Props extends RouteComponentProps { +interface Props { dashboardId?: number; - siteId: string; isList?: boolean; } function MetricTypeList(props: Props) { - const { dashboardId, siteId, history, isList = false } = props; - const { metricStore, userStore } = useStore(); + const navigate = useNavigate(); + const { dashboardId, isList = false } = props; + const { metricStore, userStore, projectsStore } = useStore(); + const siteId = projectsStore.activeSiteId; const { showModal, hideModal } = useModal(); const isEnterprise = userStore.isEnterprise; @@ -49,13 +50,11 @@ function MetricTypeList(props: Props) { }); } - const path = dashboardId ? withSiteId(dashboardMetricCreate(dashboardId + ''), siteId) : - withSiteId(metricCreate(), siteId); + const path = dashboardId + ? withSiteId(dashboardMetricCreate(dashboardId + ''), siteId) + : withSiteId(metricCreate(), siteId); const queryString = new URLSearchParams({ type: slug }).toString(); - history.push({ - pathname: path, - search: `?${queryString}` - }); + navigate(path + `?${queryString}`); }; return ( @@ -67,4 +66,4 @@ function MetricTypeList(props: Props) { ); } -export default withRouter(observer(MetricTypeList)); +export default observer(MetricTypeList); diff --git a/frontend/app/components/Dashboard/components/MetricsList/GridView.tsx b/frontend/app/components/Dashboard/components/MetricsList/GridView.tsx index 195a6f65f..0bca9048d 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/GridView.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/GridView.tsx @@ -1,18 +1,20 @@ import React from 'react'; import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { withSiteId } from 'App/routes'; -interface Props extends RouteComponentProps { +import { useNavigate } from "react-router"; + +interface Props { list: any; siteId: any; selectedList: any; } function GridView(props: Props) { - const { siteId, list, selectedList, history } = props; + const { siteId, list, selectedList } = props; + const navigate = useNavigate(); const onItemClick = (metricId: number) => { const path = withSiteId(`/metrics/${metricId}`, siteId); - history.push(path); + navigate(path); }; return ( @@ -33,4 +35,4 @@ function GridView(props: Props) { ); } -export default withRouter(GridView); +export default GridView; diff --git a/frontend/app/components/Dashboard/components/MetricsList/ListView.tsx b/frontend/app/components/Dashboard/components/MetricsList/ListView.tsx index 706079811..c685504ed 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/ListView.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/ListView.tsx @@ -15,7 +15,7 @@ import { EllipsisVertical } from 'lucide-react'; import { TablePaginationConfig, SorterResult } from 'antd/lib/table/interface'; import { useStore } from 'App/mstore'; import { toast } from 'react-toastify'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { withSiteId } from 'App/routes'; import { Icon } from 'UI'; import cn from 'classnames'; @@ -49,7 +49,7 @@ const ListView: React.FC = ({ const [editingMetricId, setEditingMetricId] = useState(null); const [newName, setNewName] = useState(''); const { metricStore } = useStore(); - const history = useHistory(); + const navigate = useNavigate(); const sortedData = useMemo(() => { return [...list].sort((a, b) => { @@ -142,7 +142,7 @@ const ListView: React.FC = ({ const onItemClick = (metric: Widget) => { if (disableSelection) { const path = withSiteId(`/metrics/${metric.metricId}`, siteId); - history.push(path); + navigate(path); } else { toggleSelection?.(metric.metricId); } diff --git a/frontend/app/components/Dashboard/components/WidgetForm/CardBuilder.tsx b/frontend/app/components/Dashboard/components/WidgetForm/CardBuilder.tsx index 65009e97c..c2e151f4f 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/CardBuilder.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/CardBuilder.tsx @@ -17,7 +17,7 @@ import FilterItem from 'Shared/Filters/FilterItem'; import { TIMESERIES, TABLE, HEATMAP, FUNNEL, ERRORS, INSIGHTS, USER_PATH, RETENTION } from 'App/constants/card'; -import {useHistory} from "react-router"; +import { useNavigate } from "react-router"; const tableOptions = metricOf.filter((i) => i.type === 'table'); @@ -217,8 +217,8 @@ interface CardBuilderProps { } const CardBuilder = observer((props: CardBuilderProps) => { - const history = useHistory(); - const {siteId, dashboardId, metricId} = props; + const navigate = useNavigate(); + const { siteId, dashboardId, metricId } = props; const {metricStore, dashboardStore, aiFiltersStore} = useStore(); const [aiQuery, setAiQuery] = useState(''); const [aiAskChart, setAiAskChart] = useState(''); @@ -259,7 +259,7 @@ const CardBuilder = observer((props: CardBuilderProps) => { const route = parseInt(dashboardId, 10) > 0 ? withSiteId(dashboardMetricDetails(dashboardId, savedMetric.metricId), siteId) : withSiteId(metricDetails(savedMetric.metricId), siteId); - history.replace(route); + navigate(route, { replace: true }); if (parseInt(dashboardId, 10) > 0) { dashboardStore.addWidgetToDashboard( dashboardStore.getDashboard(parseInt(dashboardId, 10)), @@ -267,7 +267,7 @@ const CardBuilder = observer((props: CardBuilderProps) => { ); } } - }, [dashboardId, dashboardStore, history, metric, metricStore, siteId]); + }, [dashboardId, dashboardStore, metric, metricStore, siteId]); const onDelete = useCallback(async () => { if (await confirm({ diff --git a/frontend/app/components/Dashboard/components/WidgetView/CardViewMenu.tsx b/frontend/app/components/Dashboard/components/WidgetView/CardViewMenu.tsx index 05502ebcd..61aa71181 100644 --- a/frontend/app/components/Dashboard/components/WidgetView/CardViewMenu.tsx +++ b/frontend/app/components/Dashboard/components/WidgetView/CardViewMenu.tsx @@ -1,4 +1,4 @@ -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; import { Button, Dropdown, MenuProps, Modal } from 'antd'; @@ -10,7 +10,7 @@ import AlertFormModal from 'Components/Alerts/AlertFormModal/AlertFormModal'; import { showAddToDashboardModal } from 'Components/Dashboard/components/AddToDashboardButton'; const CardViewMenu = () => { - const history = useHistory(); + const navigate = useNavigate(); const { alertsStore, metricStore, dashboardStore } = useStore(); const widget = metricStore.instance; const { openModal, closeModal } = useModal(); @@ -60,7 +60,7 @@ const CardViewMenu = () => { metricStore .delete(widget) .then(() => { - history.goBack(); + navigate(-1) }) .catch(() => { toast.error('Failed to remove card'); diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx index 480b59c52..6fffedbe8 100644 --- a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx +++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx @@ -1,13 +1,13 @@ import React, { useState, useEffect } from 'react'; import { useStore } from 'App/mstore'; -import { Loader } from 'UI'; +import { Loader, NavPrompt } from 'UI'; import WidgetPreview from '../WidgetPreview'; import WidgetSessions from '../WidgetSessions'; import { observer } from 'mobx-react-lite'; import { dashboardMetricDetails, metricDetails, withSiteId } from 'App/routes'; import Breadcrumb from 'Shared/Breadcrumb'; import { FilterKey } from 'Types/filter/filterType'; -import { Prompt, useHistory, useLocation } from 'react-router'; +import { useNavigate, useLocation } from 'react-router'; import { TIMESERIES, TABLE, @@ -29,7 +29,6 @@ import { CARD_LIST, CardType } from 'Components/Dashboard/components/DashboardLi import FilterSeries from '@/mstore/types/filterSeries'; interface Props { - history: any; match: any; siteId: any; } @@ -50,7 +49,7 @@ function WidgetView({ match: { params: { siteId, dashboardId, metricId } } }: Pr const dashboard = dashboardStore.dashboards.find((d: any) => d.dashboardId == dashboardId); const dashboardName = dashboard ? dashboard.name : null; const [metricNotFound, setMetricNotFound] = useState(false); - const history = useHistory(); + const navigate = useNavigate(); const location = useLocation(); const [initialInstance, setInitialInstance] = useState(); const isClickMap = widget.metricType === HEATMAP; @@ -112,9 +111,9 @@ function WidgetView({ match: { params: { siteId, dashboardId, metricId } } }: Pr useEffect(() => { if (metricNotFound) { - history.replace(withSiteId('/metrics', siteId)); + navigate(withSiteId('/metrics', siteId), { replace: true }); } - }, [metricNotFound, history, siteId]); + }, [metricNotFound, siteId]); const undoChanges = () => { const w = new Widget(); @@ -134,15 +133,15 @@ function WidgetView({ match: { params: { siteId, dashboardId, metricId } } }: Pr setInitialInstance(widget.toJson()); if (wasCreating) { if (parseInt(dashboardId, 10) > 0) { - history.replace( + navigate( withSiteId(dashboardMetricDetails(dashboardId, savedMetric.metricId), siteId) - ); + , { replace: true }); void dashboardStore.addWidgetToDashboard( dashboardStore.getDashboard(parseInt(dashboardId, 10))!, [savedMetric.metricId] ); } else { - history.replace(withSiteId(metricDetails(savedMetric.metricId), siteId)); + navigate(withSiteId(metricDetails(savedMetric.metricId), siteId), { replace: true }); } } }; @@ -154,7 +153,7 @@ function WidgetView({ match: { params: { siteId, dashboardId, metricId } } }: Pr return ( - loc.pathname.includes('/metrics/') || loc.pathname.includes('/metric/') diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/CardMenu.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/CardMenu.tsx index 39e22ad6e..14f72da83 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/CardMenu.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/CardMenu.tsx @@ -1,48 +1,48 @@ import React from 'react'; -import {useHistory} from "react-router"; -import {useStore} from "App/mstore"; -import {useObserver} from "mobx-react-lite"; -import {Button, Dropdown, MenuProps, message, Modal} from "antd"; -import {BellIcon, EllipsisVertical, EyeOffIcon, PencilIcon, TrashIcon} from "lucide-react"; -import {toast} from "react-toastify"; -import {dashboardMetricDetails, withSiteId} from "App/routes"; +import { useNavigate } from 'react-router'; +import { useStore } from 'App/mstore'; +import { Button, Dropdown, MenuProps } from 'antd'; +import { EllipsisVertical, EyeOffIcon, PencilIcon } from 'lucide-react'; +import { dashboardMetricDetails, withSiteId } from 'App/routes'; -function CardMenu({card}: any) { - const siteId = location.pathname.split('/')[1]; - const history = useHistory(); - const {dashboardStore, metricStore} = useStore(); - const dashboardId = dashboardStore.selectedDashboard?.dashboardId; +function CardMenu({ card }: any) { + const siteId = location.pathname.split('/')[1]; + const navigate = useNavigate(); + const { dashboardStore } = useStore(); + const dashboardId = dashboardStore.selectedDashboard?.dashboardId; - const items: MenuProps['items'] = [ - { - key: 'edit', - label: "Edit", - icon: , - }, - { - key: 'hide', - label: 'Remove from Dashboard', - icon: , - }, - ]; + const items: MenuProps['items'] = [ + { + key: 'edit', + label: 'Edit', + icon: , + }, + { + key: 'hide', + label: 'Remove from Dashboard', + icon: , + }, + ]; - const onClick: MenuProps['onClick'] = ({key}) => { - if (key === 'edit') { - history.push( - withSiteId(dashboardMetricDetails(dashboardId, card.metricId), siteId) - ) - } else if (key === 'hide') { - dashboardStore.deleteDashboardWidget(dashboardId!, card.widgetId).then(r => null); - } - }; + const onClick: MenuProps['onClick'] = ({ key }) => { + if (key === 'edit') { + navigate( + withSiteId(dashboardMetricDetails(dashboardId, card.metricId), siteId) + ); + } else if (key === 'hide') { + dashboardStore + .deleteDashboardWidget(dashboardId!, card.widgetId) + .then((r) => null); + } + }; - return ( -
- -
- ); + return ( +
+ +
+ ); } export default CardMenu; diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index 3cb78e8e1..308ea645b 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -1,14 +1,14 @@ import React, { useRef, lazy } from 'react'; import cn from 'classnames'; -import { ItemMenu, TextEllipsis } from 'UI'; +import { TextEllipsis } from 'UI'; import { useDrag, useDrop } from 'react-dnd'; import { observer } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { withSiteId, dashboardMetricDetails } from 'App/routes'; import TemplateOverlay from './TemplateOverlay'; import { FilterKey } from 'App/types/filter/filterType'; import { TIMESERIES } from 'App/constants/card'; +import { useNavigate } from "react-router"; const WidgetChart = lazy( () => import('Components/Dashboard/components/WidgetChart') @@ -22,17 +22,17 @@ interface Props { isPreview?: boolean; isTemplate?: boolean; dashboardId?: string; - siteId?: string; active?: boolean; - history?: any; onClick?: () => void; isSaved?: boolean; hideName?: boolean; grid?: string; isGridView?: boolean; } -function WidgetWrapper(props: Props & RouteComponentProps) { - const { dashboardStore } = useStore(); +function WidgetWrapper(props: Props) { + const { dashboardStore, projectsStore } = useStore(); + const siteId = projectsStore.activeSiteId; + const navigate = useNavigate(); const { isSaved = false, active = false, @@ -40,7 +40,6 @@ function WidgetWrapper(props: Props & RouteComponentProps) { moveListItem = null, isPreview = false, isTemplate = false, - siteId, grid = '', isGridView = false, } = props; @@ -83,7 +82,7 @@ function WidgetWrapper(props: Props & RouteComponentProps) { const onChartClick = () => { if (!isSaved || isPredefined) return; - props.history.push( + navigate( withSiteId( dashboardMetricDetails(dashboard?.dashboardId, widget.metricId), siteId @@ -150,4 +149,4 @@ function WidgetWrapper(props: Props & RouteComponentProps) { ); } -export default withRouter(observer(WidgetWrapper)); +export default observer(WidgetWrapper); diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapperNew.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapperNew.tsx index d09ca999c..bd5bb7d02 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapperNew.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapperNew.tsx @@ -4,8 +4,8 @@ import { Card, Tooltip } from 'antd'; import { useDrag, useDrop } from 'react-dnd'; import { observer } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { withSiteId, dashboardMetricDetails } from 'App/routes'; +import { useNavigate } from "react-router"; import TemplateOverlay from './TemplateOverlay'; import stl from './widgetWrapper.module.css'; import { FilterKey } from 'App/types/filter/filterType'; @@ -25,7 +25,6 @@ interface Props { dashboardId?: string; siteId?: string; active?: boolean; - history?: any; onClick?: () => void; isWidget?: boolean; hideName?: boolean; @@ -35,8 +34,10 @@ interface Props { isSaved?: boolean; } -function WidgetWrapperNew(props: Props & RouteComponentProps) { - const { dashboardStore, metricStore } = useStore(); +function WidgetWrapperNew(props: Props) { + const navigate = useNavigate() + const { dashboardStore, metricStore, projectsStore } = useStore(); + const siteId = projectsStore.activeSiteId; const { isWidget = false, active = false, @@ -44,7 +45,6 @@ function WidgetWrapperNew(props: Props & RouteComponentProps) { moveListItem = null, isPreview = false, isTemplate = false, - siteId, grid = '', isGridView = false, showMenu = false, @@ -80,7 +80,7 @@ function WidgetWrapperNew(props: Props & RouteComponentProps) { const onChartClick = () => { // if (!isWidget || isPredefined) return; - props.history.push( + navigate( withSiteId(dashboardMetricDetails(dashboard?.dashboardId, widget.metricId), siteId) ); }; @@ -164,4 +164,4 @@ function WidgetWrapperNew(props: Props & RouteComponentProps) { ); } -export default withRouter(observer(WidgetWrapperNew)); +export default observer(WidgetWrapperNew); diff --git a/frontend/app/components/Errors/Error/MainSection.js b/frontend/app/components/Errors/Error/MainSection.js index 9c3e9a76a..205cc0f4c 100644 --- a/frontend/app/components/Errors/Error/MainSection.js +++ b/frontend/app/components/Errors/Error/MainSection.js @@ -3,8 +3,7 @@ import { FilterKey } from 'Types/filter/filterType'; import cn from 'classnames'; import { observer } from 'mobx-react-lite'; import React from 'react'; -import { withRouter } from 'react-router-dom'; - +import { useNavigate } from 'react-router' import { resentOrDate } from 'App/date'; import { useStore } from 'App/mstore'; import { sessions as sessionsRoute } from 'App/routes'; @@ -17,6 +16,7 @@ import { Button } from 'antd' import SessionBar from './SessionBar'; function MainSection(props) { + const navigate = useNavigate(); const { errorStore, searchStore } = useStore(); const error = errorStore.instance; const trace = errorStore.instanceTrace; @@ -26,7 +26,7 @@ function MainSection(props) { const findSessions = () => { searchStore.addFilterByKeyAndValue(FilterKey.ERROR, error.message); - props.history.push(sessionsRoute()); + navigate(sessionsRoute()); }; return (
@@ -14,7 +14,7 @@ function FFlagsListHeader({ siteId }: { siteId: string }) {
-
diff --git a/frontend/app/components/FFlags/FlagView/FlagView.tsx b/frontend/app/components/FFlags/FlagView/FlagView.tsx index 9e6912be8..9fdc47044 100644 --- a/frontend/app/components/FFlags/FlagView/FlagView.tsx +++ b/frontend/app/components/FFlags/FlagView/FlagView.tsx @@ -4,7 +4,7 @@ import { useStore } from 'App/mstore'; import { Loader, NoContent, ItemMenu } from 'UI'; import { Button, Switch } from 'antd' import Breadcrumb from 'Shared/Breadcrumb'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { withSiteId, fflag, fflags } from 'App/routes'; import Multivariant from "Components/FFlags/NewFFlag/Multivariant"; import { toast } from 'react-toastify'; @@ -12,7 +12,7 @@ import RolloutCondition from "Shared/ConditionSet"; function FlagView({ siteId, fflagId }: { siteId: string; fflagId: string }) { const { featureFlagsStore } = useStore(); - const history = useHistory(); + const navigate = useNavigate(); React.useEffect(() => { if (fflagId) { @@ -27,7 +27,7 @@ function FlagView({ siteId, fflagId }: { siteId: string; fflagId: string }) { const deleteHandler = () => { featureFlagsStore.deleteFlag(current.featureFlagId).then(() => { toast.success('Feature flag deleted.'); - history.push(withSiteId(fflags(), siteId)); + navigate(withSiteId(fflags(), siteId)); }); }; @@ -63,7 +63,7 @@ function FlagView({ siteId, fflagId }: { siteId: string; fflagId: string }) { className={'ml-auto text-main'} type={'text'} onClick={() => - history.push( + navigate( withSiteId( fflag( featureFlagsStore.currentFflag?.featureFlagId.toString() diff --git a/frontend/app/components/FFlags/NewFFlag/Header.tsx b/frontend/app/components/FFlags/NewFFlag/Header.tsx index 48c68da90..6a618b3f8 100644 --- a/frontend/app/components/FFlags/NewFFlag/Header.tsx +++ b/frontend/app/components/FFlags/NewFFlag/Header.tsx @@ -3,19 +3,19 @@ import { observer } from 'mobx-react-lite'; import cn from 'classnames'; import { ItemMenu } from 'UI'; import { useStore } from 'App/mstore'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { toast } from 'react-toastify'; import { fflags, withSiteId } from "App/routes"; import { Button } from 'antd'; function Header({ current, onCancel, onSave, isNew, siteId }: any) { const { featureFlagsStore } = useStore(); - const history = useHistory(); + const navigate = useNavigate() const deleteHandler = () => { featureFlagsStore.deleteFlag(current.featureFlagId).then(() => { toast.success('Feature flag deleted.'); - history.push(withSiteId(fflags(), siteId)); + navigate(withSiteId(fflags(), siteId)); }); }; diff --git a/frontend/app/components/FFlags/NewFFlag/NewFFlag.tsx b/frontend/app/components/FFlags/NewFFlag/NewFFlag.tsx index b4c1b57cb..29c8466c2 100644 --- a/frontend/app/components/FFlags/NewFFlag/NewFFlag.tsx +++ b/frontend/app/components/FFlags/NewFFlag/NewFFlag.tsx @@ -1,12 +1,12 @@ import React from 'react'; import { observer } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; -import { Input, SegmentSelection, Loader, NoContent } from 'UI'; +import { Input, SegmentSelection, Loader, NoContent, NavPrompt } from 'UI'; import Breadcrumb from 'Shared/Breadcrumb'; import { Button, Switch } from 'antd' import { useModal } from 'App/components/Modal'; import HowTo from 'Components/FFlags/NewFFlag/HowTo'; -import {Prompt, useHistory} from 'react-router'; +import { useNavigate } from 'react-router'; import {withSiteId, fflags, fflagRead} from 'App/routes'; import Description from './Description'; import Header from './Header'; @@ -32,7 +32,7 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) { const current = featureFlagsStore.currentFflag; const { showModal } = useModal(); - const history = useHistory(); + const navigate = useNavigate() if (featureFlagsStore.isLoading) return ; if (!current) return ( @@ -52,7 +52,7 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) { }; const onCancel = () => { - history.goBack() + navigate(-1) }; const onSave = () => { @@ -61,7 +61,7 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) { if (fflagId) { featureFlagsStore.updateFlag().then(() => { toast.success('Feature flag updated.'); - history.push(withSiteId(fflagRead(fflagId), siteId)); + navigate(withSiteId(fflagRead(fflagId), siteId)); }) .catch(() => { toast.error(`Failed to update flag, check your data and try again.`) @@ -69,7 +69,7 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) { } else { featureFlagsStore.createFlag().then(() => { toast.success('Feature flag created.'); - history.push(withSiteId(fflags(), siteId)); + navigate(withSiteId(fflags(), siteId)); }).catch(() => { toast.error('Failed to create flag.'); }) @@ -79,7 +79,7 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) { const showDescription = Boolean(current.description?.length); return (
- { return 'You have unsaved changes. Are you sure you want to leave?'; diff --git a/frontend/app/components/Header/PreferencesView/PreferencesView.tsx b/frontend/app/components/Header/PreferencesView/PreferencesView.tsx index 4087475c0..7e7b479b9 100644 --- a/frontend/app/components/Header/PreferencesView/PreferencesView.tsx +++ b/frontend/app/components/Header/PreferencesView/PreferencesView.tsx @@ -1,13 +1,12 @@ import React from 'react'; import { Icon } from 'UI'; -import { withRouter } from 'react-router-dom'; +import { useNavigate } from "react-router"; -interface Props { - history: any; -} -function PreferencesView(props: Props) { + +function PreferencesView() { + const navigate = useNavigate(); const onExit = () => { - props.history.push('/'); + navigate('/'); }; return ( <> @@ -24,4 +23,4 @@ function PreferencesView(props: Props) { ); } -export default withRouter(PreferencesView); +export default PreferencesView; diff --git a/frontend/app/components/Header/SettingsMenu/SettingsMenu.tsx b/frontend/app/components/Header/SettingsMenu/SettingsMenu.tsx index bd097390f..6d6988e55 100644 --- a/frontend/app/components/Header/SettingsMenu/SettingsMenu.tsx +++ b/frontend/app/components/Header/SettingsMenu/SettingsMenu.tsx @@ -1,39 +1,38 @@ import React from 'react'; -import cn from 'classnames'; import { Icon } from 'UI'; import { CLIENT_TABS, client as clientRoute } from 'App/routes'; -import { withRouter, RouteComponentProps } from 'react-router'; +import { useNavigate } from "react-router"; interface Props { - history: any; className: string; account: any; } -function SettingsMenu(props: RouteComponentProps) { - const { history, account, className }: any = props; +function SettingsMenu(props: Props) { + const navigate = useNavigate(); + const { account, className }: any = props; const isAdmin = account.admin || account.superAdmin; const isEnterprise = account.edition === 'ee'; const navigateTo = (path: any) => { switch (path) { case 'sessions-listing': - return history.push(clientRoute(CLIENT_TABS.SESSIONS_LISTING)); + return navigate(clientRoute(CLIENT_TABS.SESSIONS_LISTING)); case 'projects': - return history.push(clientRoute(CLIENT_TABS.SITES)); + return navigate(clientRoute(CLIENT_TABS.SITES)); case 'team': - return history.push(clientRoute(CLIENT_TABS.MANAGE_USERS)); + return navigate(clientRoute(CLIENT_TABS.MANAGE_USERS)); case 'metadata': - return history.push(clientRoute(CLIENT_TABS.CUSTOM_FIELDS)); + return navigate(clientRoute(CLIENT_TABS.CUSTOM_FIELDS)); case 'webhooks': - return history.push(clientRoute(CLIENT_TABS.WEBHOOKS)); + return navigate(clientRoute(CLIENT_TABS.WEBHOOKS)); case 'integrations': - return history.push(clientRoute(CLIENT_TABS.INTEGRATIONS)); + return navigate(clientRoute(CLIENT_TABS.INTEGRATIONS)); case 'notifications': - return history.push(clientRoute(CLIENT_TABS.NOTIFICATIONS)); + return navigate(clientRoute(CLIENT_TABS.NOTIFICATIONS)); case 'roles': - return history.push(clientRoute(CLIENT_TABS.MANAGE_ROLES)); + return navigate(clientRoute(CLIENT_TABS.MANAGE_ROLES)); case 'audit': - return history.push(clientRoute(CLIENT_TABS.AUDIT)); + return navigate(clientRoute(CLIENT_TABS.AUDIT)); } }; return ( @@ -64,7 +63,7 @@ function SettingsMenu(props: RouteComponentProps) { ); } -export default withRouter(SettingsMenu); +export default SettingsMenu; function MenuItem({ onClick, label, icon }: any) { return ( diff --git a/frontend/app/components/Header/UserMenu/UserMenu.tsx b/frontend/app/components/Header/UserMenu/UserMenu.tsx index 9b642593f..59cbd8362 100644 --- a/frontend/app/components/Header/UserMenu/UserMenu.tsx +++ b/frontend/app/components/Header/UserMenu/UserMenu.tsx @@ -1,24 +1,21 @@ import React from 'react'; -import { withRouter } from 'react-router-dom'; import { client, CLIENT_DEFAULT_TAB } from 'App/routes'; import { Icon } from 'UI'; import { getInitials } from 'App/utils'; import { useStore } from "App/mstore"; import { observer } from 'mobx-react-lite'; +import { useNavigate } from "react-router"; const CLIENT_PATH = client(CLIENT_DEFAULT_TAB); -interface Props { - history: any; -} -function UserMenu(props: Props) { - const { history }: any = props; +function UserMenu() { + const navigate = useNavigate(); const { loginStore, userStore } = useStore(); const account = userStore.account; const onLogoutClick = userStore.logout; const onAccountClick = () => { - history.push(CLIENT_PATH); + navigate(CLIENT_PATH); }; const onLogout = () => { @@ -59,4 +56,4 @@ function UserMenu(props: Props) { ); } -export default withRouter(observer(UserMenu)) +export default observer(UserMenu) diff --git a/frontend/app/components/Highlights/HighlightsList.tsx b/frontend/app/components/Highlights/HighlightsList.tsx index 0f6fa9c6b..a1ff74cea 100644 --- a/frontend/app/components/Highlights/HighlightsList.tsx +++ b/frontend/app/components/Highlights/HighlightsList.tsx @@ -12,11 +12,11 @@ import { toast } from 'react-toastify'; import EditHlModal from './EditHlModal'; import HighlightsListHeader from './HighlightsListHeader'; import withPermissions from 'HOCs/withPermissions'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { highlights, withSiteId } from 'App/routes' function HighlightsList() { - const history = useHistory(); + const navigate = useNavigate() const params = new URLSearchParams(window.location.search); const hlId = params.get('highlight'); const { notesStore, projectsStore, userStore } = useStore(); @@ -31,7 +31,7 @@ function HighlightsList() { React.useEffect(() => { if (hlId) { setActiveId(hlId); - history.replace(withSiteId(highlights(), projectsStore.siteId)); + navigate(withSiteId(highlights(), projectsStore.siteId), { replace: true }); } }, [hlId]) diff --git a/frontend/app/components/Login/Login.tsx b/frontend/app/components/Login/Login.tsx index 559f1bd9c..58efe559c 100644 --- a/frontend/app/components/Login/Login.tsx +++ b/frontend/app/components/Login/Login.tsx @@ -3,7 +3,7 @@ import cn from 'classnames'; import React, { useEffect, useRef, useState } from 'react'; // Consider using a different approach for titles in functional components import ReCAPTCHA from 'react-google-recaptcha'; -import { useHistory } from 'react-router-dom'; +import { useNavigate, useLocation } from 'react-router'; import { observer } from 'mobx-react-lite'; import { toast } from 'react-toastify'; @@ -20,13 +20,8 @@ import stl from './login.module.css'; const FORGOT_PASSWORD = forgotPassword(); const SIGNUP_ROUTE = signup(); -interface LoginProps { - location: Location; -} - -const Login = ({ - location - }: LoginProps) => { +const Login = () => { + const location = useLocation() const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const CAPTCHA_ENABLED = React.useMemo(() => { @@ -38,12 +33,12 @@ const Login = ({ const loading = loginStore.loading; const authDetails = userStore.authStore.authDetails; const setJwt = userStore.updateJwt; - const history = useHistory(); + const navigate = useNavigate() const params = new URLSearchParams(location.search); useEffect(() => { if (authDetails && !authDetails.tenants) { - history.push(SIGNUP_ROUTE); + navigate(SIGNUP_ROUTE); } }, [authDetails]); diff --git a/frontend/app/components/Modal/Modal.tsx b/frontend/app/components/Modal/Modal.tsx index d2e8a838d..2c42ce6a1 100644 --- a/frontend/app/components/Modal/Modal.tsx +++ b/frontend/app/components/Modal/Modal.tsx @@ -1,8 +1,7 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import ReactDOM from 'react-dom'; import ModalOverlay from './ModalOverlay'; import cn from 'classnames'; -import { useHistory } from 'react-router'; const DEFAULT_WIDTH = 350; interface Props { @@ -13,16 +12,6 @@ interface Props { width?: number; } function Modal({ component, className = 'bg-white', props, hideModal }: Props) { - const history = useHistory(); - - useEffect(() => { - return history.listen((location) => { - if (history.action === 'POP') { - document.querySelector('body').style.overflow = 'visible'; - } - }); - }); - return component ? ( ReactDOM.createPortal( diff --git a/frontend/app/components/Onboarding/Onboarding.tsx b/frontend/app/components/Onboarding/Onboarding.tsx index d2fb8fc4f..ec7b830a0 100644 --- a/frontend/app/components/Onboarding/Onboarding.tsx +++ b/frontend/app/components/Onboarding/Onboarding.tsx @@ -1,7 +1,6 @@ import React from 'react'; -import { Redirect, Route, RouteComponentProps, Switch } from 'react-router'; -import { withRouter } from 'react-router-dom'; - +import { Navigate, Route, Routes } from 'react-router'; +import { useParams, useNavigate } from "react-router"; import { OB_TABS, onboarding as onboardingRoute } from 'App/routes'; import { withSiteId } from 'App/routes'; import { Icon } from 'UI'; @@ -12,22 +11,14 @@ import IntegrationsTab from './components/IntegrationsTab'; import ManageUsersTab from './components/ManageUsersTab'; import SideMenu from './components/SideMenu'; -interface Props { - match: { - params: { - activeTab: string; - siteId: string; - }; - }; - history: RouteComponentProps['history']; -} - const platformMap = { ios: 'mobile', web: 'web', }; -const Onboarding = (props: Props) => { +const Onboarding = (props) => { + const navigate = useNavigate(); + const { activeTab, siteId } = useParams(); const platforms = [ { label: ( @@ -47,18 +38,13 @@ const Onboarding = (props: Props) => { } as const, ] as const; const [platform, setPlatform] = React.useState(platforms[0]); - const { - match: { - params: { activeTab, siteId }, - }, - } = props; const route = (path: string) => { return withSiteId(onboardingRoute(path)); }; const onMenuItemClick = (tab: string) => { - props.history.push(withSiteId(onboardingRoute(tab), siteId)); + navigate(withSiteId(onboardingRoute(tab), siteId)); }; return ( @@ -69,7 +55,7 @@ const Onboarding = (props: Props) => { className="bg-white w-full rounded-lg mx-auto mb-8 border" style={{ maxWidth: '1360px' }} > - + { path={route(OB_TABS.INTEGRATIONS)} component={IntegrationsTab} /> - - + + + +
- {/*
-
- -
-
*/}
); }; -export default withRouter(Onboarding); +export default Onboarding; diff --git a/frontend/app/components/Onboarding/components/OnboardingMenu/OnboardingMenu.js b/frontend/app/components/Onboarding/components/OnboardingMenu/OnboardingMenu.js index 9e5bfa049..5b3ceeb2d 100644 --- a/frontend/app/components/Onboarding/components/OnboardingMenu/OnboardingMenu.js +++ b/frontend/app/components/Onboarding/components/OnboardingMenu/OnboardingMenu.js @@ -1,10 +1,10 @@ import React from 'react'; -import { Icon, SideMenuitem } from 'UI'; +import { SideMenuitem } from 'UI'; import cn from 'classnames'; import stl from './onboardingMenu.module.css'; import { OB_TABS, onboarding as onboardingRoute } from 'App/routes'; -import { withRouter } from 'react-router-dom'; -import * as routes from '../../../../routes'; +import * as routes from 'App/routes'; +import { useParams, useNavigate } from "react-router"; const withSiteId = routes.withSiteId; @@ -15,7 +15,7 @@ const MENU_ITEMS = [ OB_TABS.INTEGRATIONS, ]; -const Item = ({ icon, text, completed, active, onClick }) => ( +const Item = ({ text, completed, active, onClick }) => (
(
); -const OnboardingMenu = (props) => { - const { - match: { - params: { activeTab, siteId }, - }, - history, - } = props; +const OnboardingMenu = () => { + const { siteId, activeTab } = useParams(); + const navigate = useNavigate(); + const activeIndex = MENU_ITEMS.findIndex((i) => i === activeTab); const setTab = (tab) => { - history.push(withSiteId(onboardingRoute(tab), siteId)); + navigate(withSiteId(onboardingRoute(tab), siteId)); }; return ( @@ -61,28 +58,24 @@ const OnboardingMenu = (props) => { <> = 0} active={activeIndex === 0} onClick={() => setTab(MENU_ITEMS[0])} /> = 1} active={activeIndex === 1} onClick={() => setTab(MENU_ITEMS[1])} /> = 2} active={activeIndex === 2} onClick={() => setTab(MENU_ITEMS[2])} /> = 3} active={activeIndex === 3} @@ -93,4 +86,4 @@ const OnboardingMenu = (props) => { ); }; -export default withRouter(OnboardingMenu); +export default OnboardingMenu; diff --git a/frontend/app/components/Onboarding/components/OnboardingNavButton/OnboardingNavButton.js b/frontend/app/components/Onboarding/components/OnboardingNavButton/OnboardingNavButton.js index d6db642fe..9056ae1ff 100644 --- a/frontend/app/components/Onboarding/components/OnboardingNavButton/OnboardingNavButton.js +++ b/frontend/app/components/Onboarding/components/OnboardingNavButton/OnboardingNavButton.js @@ -1,9 +1,9 @@ import React from 'react' -import { withRouter } from 'react-router' import { Button } from 'antd' import { OB_TABS, onboarding as onboardingRoute, withSiteId } from 'App/routes' import { sessions } from 'App/routes'; import { useStore } from 'App/mstore' +import { useParams, useNavigate } from "react-router"; const MENU_ITEMS = [OB_TABS.INSTALLING, OB_TABS.IDENTIFY_USERS, OB_TABS.MANAGE_USERS, OB_TABS.INTEGRATIONS] const BTN_MSGS = [ @@ -13,7 +13,9 @@ const BTN_MSGS = [ 'See Recorded Sessions' ] -const OnboardingNavButton = ({ match: { params: { activeTab, siteId } }, history }) => { +const OnboardingNavButton = () => { + const { activeTab, siteId } = useParams(); + const navigate = useNavigate(); const { userStore } = useStore(); const activeIndex = MENU_ITEMS.findIndex(i => i === activeTab); const completed = activeIndex == MENU_ITEMS.length - 1; @@ -21,7 +23,7 @@ const OnboardingNavButton = ({ match: { params: { activeTab, siteId } }, history const setTab = () => { if (!completed) { const tab = MENU_ITEMS[activeIndex+1] - history.push(withSiteId(onboardingRoute(tab), siteId)); + navigate(withSiteId(onboardingRoute(tab), siteId)); } else { onDone() } @@ -29,7 +31,7 @@ const OnboardingNavButton = ({ match: { params: { activeTab, siteId } }, history const onDone = () => { userStore.setOnboarding(true); - history.push(sessions()); + navigate(sessions()); } return ( @@ -53,4 +55,4 @@ const OnboardingNavButton = ({ match: { params: { activeTab, siteId } }, history ) } -export default withRouter(OnboardingNavButton) \ No newline at end of file +export default OnboardingNavButton \ No newline at end of file diff --git a/frontend/app/components/Onboarding/components/withOnboarding.tsx b/frontend/app/components/Onboarding/components/withOnboarding.tsx index 054f59265..8e4e5936d 100644 --- a/frontend/app/components/Onboarding/components/withOnboarding.tsx +++ b/frontend/app/components/Onboarding/components/withOnboarding.tsx @@ -1,53 +1,38 @@ import React, { useMemo } from 'react'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { sessions, withSiteId, onboarding as onboardingRoute } from 'App/routes'; import { observer } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; +import { useParams, useNavigate } from "react-router"; export interface WithOnboardingProps { - history: RouteComponentProps['history']; skip?: () => void; navTo?: (tab: string) => void; site?: any; - match: { - params: { - activeTab: string; - siteId: string; - }; - }; } - -const withOnboarding =

( - Component: React.ComponentType

+const withOnboarding =

>( + Component: React.ComponentType

) => { const WithOnboarding: React.FC

= (props) => { + const { siteId } = useParams(); + const navigate = useNavigate(); const { projectsStore, userStore } = useStore(); const sites = projectsStore.list; - const { - match: { - params: { siteId }, - }, - } = props; const site = useMemo(() => sites.find((s: any) => s.id === siteId), [sites, siteId]); const skip = () => { userStore.setOnboarding(true); - props.history.push(withSiteId(sessions(), siteId)); + navigate(withSiteId(sessions(), siteId)); }; const navTo = (tab: string) => { - props.history.push(withSiteId(onboardingRoute(tab), siteId)); + navigate(withSiteId(onboardingRoute(tab), siteId)); }; return ; }; - return withRouter( - observer( - WithOnboarding - ) - ); + return observer(WithOnboarding) }; export default withOnboarding; diff --git a/frontend/app/components/Overview/Overview.tsx b/frontend/app/components/Overview/Overview.tsx index eec1ce254..1f6dad773 100644 --- a/frontend/app/components/Overview/Overview.tsx +++ b/frontend/app/components/Overview/Overview.tsx @@ -3,27 +3,25 @@ import withPageTitle from 'HOCs/withPageTitle'; import SessionsTabOverview from 'Shared/SessionsTabOverview/SessionsTabOverview'; import FFlagsList from 'Components/FFlags'; import NewFFlag from 'Components/FFlags/NewFFlag'; -import { Switch, Route } from 'react-router'; -import { sessions, fflags, withSiteId, newFFlag, fflag, fflagRead, bookmarks } from 'App/routes'; -import { withRouter, RouteComponentProps, useLocation } from 'react-router-dom'; +import { Routes, Route } from 'react-router'; +import { + sessions, + fflags, + withSiteId, + newFFlag, + fflag, + fflagRead, + bookmarks, +} from 'App/routes'; +import { useLocation, useParams, useNavigate } from 'react-router'; import FlagView from 'Components/FFlags/FlagView/FlagView'; import { observer } from 'mobx-react-lite'; import { useStore } from '@/mstore'; import Bookmarks from 'Shared/SessionsTabOverview/components/Bookmarks/Bookmarks'; -// @ts-ignore -interface IProps extends RouteComponentProps { - match: { - params: { - siteId: string; - fflagId?: string; - }; - }; -} -// TODO should move these routes to the Routes file -function Overview({ match: { params } }: IProps) { +function Overview() { const { searchStore } = useStore(); - const { siteId, fflagId } = params; + const { siteId, fflagId } = useParams(); const location = useLocation(); const tab = location.pathname.split('/')[2]; @@ -31,34 +29,50 @@ function Overview({ match: { params } }: IProps) { searchStore.setActiveTab(tab); }, [tab]); - return ( - - + console.log(location.pathname) + const renderContent = () => { + const path = location.pathname; + + if (path.startsWith(withSiteId(sessions()), siteId)) { + return (

- - + ); + } + + if (path.startsWith(withSiteId(bookmarks()), siteId)) { + return (
-
- - - - - - - - - - - - - - ); + ); + } + + if (path.startsWith(withSiteId(fflags()), siteId)) { + return ; + } + + if (path.startsWith(withSiteId(newFFlag()), siteId)) { + return ; + } + + if (path.match(new RegExp(`^${withSiteId(fflag(), siteId).replace(':fflagId', '\\d+')}$`))) { + return ; + } + + if (path.match(new RegExp(`^${withSiteId(fflagRead(), siteId).replace(':fflagId', '\\d+')}$`))) { + return ; + } + + return ( +
+ +
+ ); + }; + + return renderContent(); } -export default withPageTitle('Sessions - OpenReplay')(withRouter(observer(Overview))); +export default withPageTitle('Sessions - OpenReplay')(observer(Overview)); \ No newline at end of file diff --git a/frontend/app/components/ScopeForm/ScopeForm.tsx b/frontend/app/components/ScopeForm/ScopeForm.tsx index deabf9879..c7d14f497 100644 --- a/frontend/app/components/ScopeForm/ScopeForm.tsx +++ b/frontend/app/components/ScopeForm/ScopeForm.tsx @@ -1,7 +1,7 @@ import { ArrowRightOutlined } from '@ant-design/icons'; import { Button, Card, Radio } from 'antd'; import React from 'react'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import * as routes from 'App/routes' import { SPOT_ONBOARDING } from "App/constants/storageKeys"; import { useStore } from 'App/mstore'; @@ -27,24 +27,24 @@ function ScopeForm() { const downgradeScope = userStore.downgradeScope const scopeState = userStore.scopeState const [scope, setScope] = React.useState(getDefaultSetup); + const navigate = useNavigate() React.useEffect(() => { if (scopeState !== 0) { if (scopeState === 2) { - history.replace(routes.onboarding()) + navigate(routes.onboarding(), { replace: true }) } else { - history.replace(routes.spotsList()) + navigate(routes.spotsList(), { replace: true }) } } }, [scopeState]) - const history = useHistory(); const onContinue = () => { if (scope === Scope.FULL) { void upgradeScope(); - history.replace(routes.onboarding()) + navigate(routes.onboarding(), { replace: true }) } else { void downgradeScope(); - history.replace(routes.spotsList()) + navigate(routes.spotsList(), { replace: true }) } }; return ( diff --git a/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerControls.tsx b/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerControls.tsx index caa4faf1a..a74f5b939 100644 --- a/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerControls.tsx +++ b/frontend/app/components/Session/Player/ClipPlayer/ClipPlayerControls.tsx @@ -4,7 +4,7 @@ import { PlayButton, PlayingState } from '@/player-ui'; import { PlayerContext } from 'Components/Session/playerContext'; import { observer } from 'mobx-react-lite'; import { Button } from 'antd'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { CirclePlay } from 'lucide-react'; import { withSiteId } from '@/routes'; import * as routes from '@/routes'; @@ -20,7 +20,7 @@ function ClipPlayerControls({ }) { const { projectsStore } = useStore(); const { player, store } = React.useContext(PlayerContext); - const history = useHistory(); + const navigate = useNavigate(); const siteId = projectsStore.siteId; const { playing, completed } = store.get(); @@ -37,7 +37,7 @@ function ClipPlayerControls({ const showFullSession = () => { const path = withSiteId(routes.session(session.sessionId), siteId); - history.push(path + '?jumpto=' + Math.round(range[0])); + navigate(path + '?jumpto=' + Math.round(range[0])); }; return ( diff --git a/frontend/app/components/Session/Player/LivePlayer/AssistSessionsTabs/AssistSessionsTabs.tsx b/frontend/app/components/Session/Player/LivePlayer/AssistSessionsTabs/AssistSessionsTabs.tsx index 28b3945dd..d6bea2c96 100644 --- a/frontend/app/components/Session/Player/LivePlayer/AssistSessionsTabs/AssistSessionsTabs.tsx +++ b/frontend/app/components/Session/Player/LivePlayer/AssistSessionsTabs/AssistSessionsTabs.tsx @@ -3,7 +3,7 @@ import cn from 'classnames'; import { Icon } from 'UI'; import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { multiview, liveSession, withSiteId } from 'App/routes'; import { PlayerContext, ILivePlayerContext } from 'App/components/Session/playerContext'; @@ -44,7 +44,7 @@ const CurrentTab = React.memo(() => ( )); function AssistTabs({ session }: { session: Record }) { - const history = useHistory(); + const navigate = useNavigate(); const { store } = React.useContext(PlayerContext) as unknown as ILivePlayerContext const { recordingState, calling, remoteControl } = store.get() const isDisabled = recordingState !== 0 || calling !== 0 || remoteControl !== 0 @@ -63,12 +63,12 @@ function AssistTabs({ session }: { session: Record }) { const openGrid = () => { if (isDisabled) return; const sessionIdQuery = encodeURIComponent(assistMultiviewStore.sessions.map((s) => s.sessionId).join(',')); - return history.push(withSiteId(multiview(sessionIdQuery), siteId)); + return navigate(withSiteId(multiview(sessionIdQuery), siteId)); }; const openLiveSession = (sessionId: string) => { if (isDisabled) return; assistMultiviewStore.setActiveSession(sessionId); - history.push(withSiteId(liveSession(sessionId), siteId)); + navigate(withSiteId(liveSession(sessionId), siteId)); }; return ( diff --git a/frontend/app/components/Session/Player/LivePlayer/LivePlayerBlockHeader.tsx b/frontend/app/components/Session/Player/LivePlayer/LivePlayerBlockHeader.tsx index afde85b68..9892f0546 100644 --- a/frontend/app/components/Session/Player/LivePlayer/LivePlayerBlockHeader.tsx +++ b/frontend/app/components/Session/Player/LivePlayer/LivePlayerBlockHeader.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { withSiteId, multiview, @@ -24,7 +24,7 @@ function LivePlayerBlockHeader({ const session = sessionStore.current; const closedLive = sessionStore.fetchFailed || (isAssist && !session.live); const siteId = projectsStore.siteId; - const history = useHistory(); + const navigate = useNavigate() const { width, height } = store.get(); const metaList = customFieldStore.list.map((i: any) => i.key) @@ -35,7 +35,7 @@ function LivePlayerBlockHeader({ }, []); const backHandler = () => { - history.goBack(); + navigate(-1) }; const { userId, metadata, isCallActive, agentIds } = session; @@ -48,7 +48,7 @@ function LivePlayerBlockHeader({ const openGrid = () => { const sessionIdQuery = encodeURIComponent(assistMultiviewStore.sessions.map((s) => s?.sessionId).join(',')); - return history.push(withSiteId(multiview(sessionIdQuery), siteId)); + return navigate(withSiteId(multiview(sessionIdQuery), siteId)); }; return ( diff --git a/frontend/app/components/Session/Player/MobilePlayer/MobileControls.tsx b/frontend/app/components/Session/Player/MobilePlayer/MobileControls.tsx index 275f3e6e2..fc8badff0 100644 --- a/frontend/app/components/Session/Player/MobilePlayer/MobileControls.tsx +++ b/frontend/app/components/Session/Player/MobilePlayer/MobileControls.tsx @@ -1,7 +1,7 @@ import cn from 'classnames'; import { observer } from 'mobx-react-lite'; import React from 'react'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { MobilePlayerContext } from 'App/components/Session/playerContext'; import { FullScreenButton, PlayButton, PlayingState } from 'App/player-ui'; @@ -28,7 +28,6 @@ import { import { useStore } from 'App/mstore'; import { session as sessionRoute, withSiteId } from 'App/routes'; import { SummaryButton } from 'Components/Session_/Player/Controls/Controls'; -import { MobEventsList, WebEventsList } from "../../../Session_/Player/Controls/EventsList"; import useShortcuts from '../ReplayPlayer/useShortcuts'; export const SKIP_INTERVALS = { @@ -46,7 +45,7 @@ function Controls(props: any) { const permissions = userStore.account.permissions || []; const disableDevtools = userStore.isEnterprise && !(permissions.includes('DEV_TOOLS') || permissions.includes('SERVICE_DEV_TOOLS')); const { player, store } = React.useContext(MobilePlayerContext); - const history = useHistory(); + const navigate = useNavigate() const { playing, completed, skip, speed, messagesLoading } = store.get(); const { uiPlayerStore, projectsStore } = useStore(); const fullscreen = uiPlayerStore.fullscreen; @@ -68,11 +67,11 @@ function Controls(props: any) { const sessionTz = session?.timezone; const nextHandler = () => { - history.push(withSiteId(sessionRoute(nextSessionId), siteId)); + navigate(withSiteId(sessionRoute(nextSessionId), siteId)); }; const prevHandler = () => { - history.push(withSiteId(sessionRoute(previousSessionId), siteId)); + navigate(withSiteId(sessionRoute(previousSessionId), siteId)); }; useShortcuts({ diff --git a/frontend/app/components/Session/Player/MobilePlayer/MobilePlayerHeader.tsx b/frontend/app/components/Session/Player/MobilePlayer/MobilePlayerHeader.tsx index 3ed4b29b4..9ff703f12 100644 --- a/frontend/app/components/Session/Player/MobilePlayer/MobilePlayerHeader.tsx +++ b/frontend/app/components/Session/Player/MobilePlayer/MobilePlayerHeader.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { sessions as sessionsRoute, withSiteId } from 'App/routes'; import { BackLink } from 'UI'; import cn from 'classnames'; @@ -30,7 +30,7 @@ function PlayerBlockHeader(props: Props) { const { customFieldStore, projectsStore, sessionStore } = useStore(); const session = sessionStore.current; const siteId = projectsStore.siteId!; - const history = useHistory(); + const navigate = useNavigate(); const { fullscreen, setActiveTab, @@ -46,7 +46,7 @@ function PlayerBlockHeader(props: Props) { }, []); const backHandler = () => { - history.push(withSiteId(SESSIONS_ROUTE, siteId)); + navigate(withSiteId(SESSIONS_ROUTE, siteId)); }; const { metadata } = session; diff --git a/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlockHeader.tsx b/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlockHeader.tsx index 03ccfcfda..10119104d 100644 --- a/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlockHeader.tsx +++ b/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlockHeader.tsx @@ -1,6 +1,5 @@ import { useStore } from "App/mstore"; import React from 'react'; -import { withRouter } from 'react-router-dom'; import { sessions as sessionsRoute, liveSession as liveSessionRoute, @@ -15,6 +14,7 @@ import { PlayerContext } from 'App/components/Session/playerContext'; import { observer } from 'mobx-react-lite'; import stl from './playerBlockHeader.module.css'; import { IFRAME } from 'App/constants/storageKeys'; +import { useNavigate } from "react-router"; const SESSIONS_ROUTE = sessionsRoute(); @@ -28,13 +28,12 @@ function PlayerBlockHeader(props: any) { const playerState = store?.get?.() || { width: 0, height: 0, showEvents: false } const { width = 0, height = 0, showEvents = false } = playerState const metaList = customFieldStore.list.map((i: any) => i.key) - + const navigate = useNavigate(); const { fullscreen, closedLive = false, setActiveTab, activeTab, - history, } = props; React.useEffect(() => { @@ -46,12 +45,12 @@ function PlayerBlockHeader(props: any) { const backHandler = () => { if ( - sessionPath.pathname === history.location.pathname || + sessionPath.pathname === location.pathname || sessionPath.pathname.includes('/session/') || sessionPath.pathname.includes('/assist/') ) { - history.push(withSiteId(SESSIONS_ROUTE, siteId)); + navigate(withSiteId(SESSIONS_ROUTE, siteId)); } else { - history.push( + navigate( sessionPath ? sessionPath.pathname + sessionPath.search : withSiteId(SESSIONS_ROUTE, siteId) ); } @@ -127,4 +126,4 @@ function PlayerBlockHeader(props: any) { const PlayerHeaderCont = observer(PlayerBlockHeader); -export default withRouter(PlayerHeaderCont); +export default PlayerHeaderCont; diff --git a/frontend/app/components/Session/Player/SharedComponents/BackendLogs/StatusMessages.tsx b/frontend/app/components/Session/Player/SharedComponents/BackendLogs/StatusMessages.tsx index 1f22dc589..8a5847de3 100644 --- a/frontend/app/components/Session/Player/SharedComponents/BackendLogs/StatusMessages.tsx +++ b/frontend/app/components/Session/Player/SharedComponents/BackendLogs/StatusMessages.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { client as settingsPath, CLIENT_TABS } from 'App/routes'; import { Icon } from 'UI'; import { LoadingOutlined } from '@ant-design/icons'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { Button } from 'antd'; export function LoadingFetch({ provider }: { provider: string }) { @@ -25,7 +25,7 @@ export function FailedFetch({ provider: string; onRetry: () => void; }) { - const history = useHistory(); + const navigate = useNavigate(); const intPath = settingsPath(CLIENT_TABS.INTEGRATIONS); return (
-
diff --git a/frontend/app/components/Session/Player/TagWatch/SaveModal.tsx b/frontend/app/components/Session/Player/TagWatch/SaveModal.tsx index 1a7349a1f..055e4542d 100644 --- a/frontend/app/components/Session/Player/TagWatch/SaveModal.tsx +++ b/frontend/app/components/Session/Player/TagWatch/SaveModal.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Button, Checkbox, Input } from 'antd'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { withSiteId, sessions } from 'App/routes'; import { useStore } from 'App/mstore'; @@ -14,7 +14,7 @@ interface Props { } function SaveModal({ onSave, hideModal }: Props) { - const history = useHistory(); + const navigate = useNavigate(); const { projectsStore, searchStore } = useStore(); const [name, setName] = React.useState(''); const [ignoreClRage, setIgnoreClRage] = React.useState(false); @@ -32,7 +32,7 @@ function SaveModal({ onSave, hideModal }: Props) { 'tag', tagId.toString(), ) - history.push( + navigate( withSiteId( sessions(), siteId diff --git a/frontend/app/components/Session_/Multiview/Multiview.tsx b/frontend/app/components/Session_/Multiview/Multiview.tsx index 695d9ad1c..b873e8bed 100644 --- a/frontend/app/components/Session_/Multiview/Multiview.tsx +++ b/frontend/app/components/Session_/Multiview/Multiview.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useStore } from 'App/mstore'; import { BackLink } from 'UI'; import { observer } from 'mobx-react-lite'; -import { useHistory, useParams } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router'; import { liveSession, assist, withSiteId, multiview } from 'App/routes'; import AssistSessionsModal from 'App/components/Session_/Player/Controls/AssistSessionsModal'; import { useModal } from 'App/components/Modal'; @@ -19,14 +19,14 @@ function Multiview({ const { showModal, hideModal } = useModal(); const { assistMultiviewStore, projectsStore, searchStoreLive, sessionStore } = useStore(); const siteId = projectsStore.siteId!; - const history = useHistory(); + const navigate = useNavigate() // @ts-ignore const { sessionsquery } = useParams(); const total = sessionStore.totalLiveSessions; const onSessionsChange = (sessions: Array | undefined>) => { const sessionIdQuery = encodeURIComponent(sessions.map((s) => s && s.sessionId).join(',')); - return history.replace(withSiteId(multiview(sessionIdQuery), siteId)); + return navigate(withSiteId(multiview(sessionIdQuery), siteId), { replace: true }); }; React.useEffect(() => { @@ -46,12 +46,12 @@ function Multiview({ const openLiveSession = (e: React.MouseEvent, sessionId: string) => { e.stopPropagation(); assistMultiviewStore.setActiveSession(sessionId); - history.push(withSiteId(liveSession(sessionId) + '?multi=true', siteId)); + navigate(withSiteId(liveSession(sessionId) + '?multi=true', siteId)); }; const returnToList = () => { assistMultiviewStore.reset(); - history.push(withSiteId(assist(), siteId)); + navigate(withSiteId(assist(), siteId)); }; const openListModal = () => { diff --git a/frontend/app/components/Session_/Player/Controls/AssistSessionsTabs/AssistSessionsTabs.tsx b/frontend/app/components/Session_/Player/Controls/AssistSessionsTabs/AssistSessionsTabs.tsx index bd62eba45..462c189a4 100644 --- a/frontend/app/components/Session_/Player/Controls/AssistSessionsTabs/AssistSessionsTabs.tsx +++ b/frontend/app/components/Session_/Player/Controls/AssistSessionsTabs/AssistSessionsTabs.tsx @@ -3,7 +3,7 @@ import cn from 'classnames'; import { Icon } from 'UI'; import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { multiview, liveSession, withSiteId } from 'App/routes'; interface ITab { @@ -42,7 +42,7 @@ const CurrentTab = React.memo(() => ( )); function AssistTabs({ session }: { session: Record }) { - const history = useHistory(); + const navigate = useNavigate() const { assistMultiviewStore, projectsStore } = useStore(); const siteId = projectsStore.siteId!; @@ -56,11 +56,11 @@ function AssistTabs({ session }: { session: Record }) { const openGrid = () => { const sessionIdQuery = encodeURIComponent(assistMultiviewStore.sessions.map((s) => s.sessionId).join(',')); - return history.push(withSiteId(multiview(sessionIdQuery), siteId)); + return navigate(withSiteId(multiview(sessionIdQuery), siteId)); }; const openLiveSession = (sessionId: string) => { assistMultiviewStore.setActiveSession(sessionId); - history.push(withSiteId(liveSession(sessionId), siteId)); + navigate(withSiteId(liveSession(sessionId), siteId)); }; return ( diff --git a/frontend/app/components/Session_/Player/Controls/Controls.tsx b/frontend/app/components/Session_/Player/Controls/Controls.tsx index d2dd042c0..90eab6012 100644 --- a/frontend/app/components/Session_/Player/Controls/Controls.tsx +++ b/frontend/app/components/Session_/Player/Controls/Controls.tsx @@ -3,7 +3,7 @@ import { Switch } from 'antd'; import cn from 'classnames'; import { observer } from 'mobx-react-lite'; import React from 'react'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { PlayerContext } from 'App/components/Session/playerContext'; import { useStore } from 'App/mstore'; import { FullScreenButton, PlayButton, PlayingState } from 'App/player-ui'; @@ -34,7 +34,6 @@ import { Icon } from 'UI'; import LogsButton from 'App/components/Session/Player/SharedComponents/BackendLogs/LogsButton'; import ControlButton from './ControlButton'; -import { WebEventsList } from "./EventsList"; import Timeline from './Timeline'; import PlayerControls from './components/PlayerControls'; import styles from './controls.module.css'; @@ -92,7 +91,7 @@ function Controls({ setActiveTab }: any) { const changeSkipInterval = uiPlayerStore.changeSkipInterval; const skipInterval = uiPlayerStore.skipInterval; const showStorageRedux = !uiPlayerStore.hiddenHints.storage; - const history = useHistory(); + const navigate = useNavigate() const siteId = projectsStore.siteId; const { playing, @@ -113,11 +112,11 @@ function Controls({ setActiveTab }: any) { const sessionTz = session?.timezone; const nextHandler = () => { - history.push(withSiteId(sessionRoute(nextSessionId), siteId)); + navigate(withSiteId(sessionRoute(nextSessionId), siteId)); }; const prevHandler = () => { - history.push(withSiteId(sessionRoute(previousSessionId), siteId)); + navigate(withSiteId(sessionRoute(previousSessionId), siteId)); }; useShortcuts({ diff --git a/frontend/app/components/Session_/Player/Overlay/AutoplayTimer.tsx b/frontend/app/components/Session_/Player/Overlay/AutoplayTimer.tsx index e780a4820..41170275e 100644 --- a/frontend/app/components/Session_/Player/Overlay/AutoplayTimer.tsx +++ b/frontend/app/components/Session_/Player/Overlay/AutoplayTimer.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import cn from 'classnames'; import { observer } from 'mobx-react-lite'; -import { withRouter } from 'react-router-dom'; +import { useNavigate } from "react-router"; import { Link } from 'UI'; import { Button } from 'antd' import { session as sessionRoute, withSiteId } from 'App/routes'; @@ -10,7 +10,8 @@ import clsOv from './overlay.module.css'; import AutoplayToggle from 'Shared/AutoplayToggle'; import { useStore } from 'App/mstore'; -function AutoplayTimer({ history }: any) { +function AutoplayTimer() { + const navigate = useNavigate(); let timer: NodeJS.Timer; const [cancelled, setCancelled] = useState(false); const [counter, setCounter] = useState(5); @@ -25,8 +26,8 @@ function AutoplayTimer({ history }: any) { } if (counter === 0) { - const siteId = projectsStore.getSiteId().siteId; - history.push(withSiteId(sessionRoute(nextId), siteId)); + const siteId = projectsStore.activeSiteId; + navigate(withSiteId(sessionRoute(nextId), siteId)); } return () => clearTimeout(timer); @@ -60,14 +61,10 @@ function AutoplayTimer({ history }: any) {
- {/*
- Turn on/off auto-replay in More options -
*/}
); } -export default withRouter( - observer(AutoplayTimer) -); +export default observer(AutoplayTimer) + diff --git a/frontend/app/components/Session_/QueueControls/QueueControls.tsx b/frontend/app/components/Session_/QueueControls/QueueControls.tsx index 755982cd7..a56e8300a 100644 --- a/frontend/app/components/Session_/QueueControls/QueueControls.tsx +++ b/frontend/app/components/Session_/QueueControls/QueueControls.tsx @@ -1,16 +1,16 @@ import React, { useEffect } from 'react'; import { withSiteId, session as sessionRoute } from 'App/routes'; import AutoplayToggle from 'Shared/AutoplayToggle/AutoplayToggle'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import cn from 'classnames'; import { LeftOutlined, RightOutlined } from '@ant-design/icons'; import { Button, Popover } from 'antd'; import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; +import { useParams, useNavigate } from 'react-router'; const PER_PAGE = 10; -interface Props extends RouteComponentProps { +interface Props { defaultList: any; currentPage: number; latestRequestTime: any; @@ -24,12 +24,8 @@ function QueueControls(props: Props) { const total = sessionStore.total; const sessionIds = sessionStore.sessionIds ?? []; const setAutoplayValues = sessionStore.setAutoplayValues; - const { - match: { - // @ts-ignore - params: { sessionId }, - }, - } = props; + const { sessionId } = useParams(); + const navigate = useNavigate(); const currentPage = searchStore.currentPage; @@ -46,13 +42,13 @@ function QueueControls(props: Props) { }, []); const nextHandler = () => { - const siteId = projectsStore.getSiteId().siteId!; - props.history.push(withSiteId(sessionRoute(nextId), siteId)); + const siteId = projectsStore.activeSiteId!; + navigate(withSiteId(sessionRoute(nextId), siteId)); }; const prevHandler = () => { - const siteId = projectsStore.getSiteId().siteId!; - props.history.push(withSiteId(sessionRoute(previousId), siteId)); + const siteId = projectsStore.activeSiteId!; + navigate(withSiteId(sessionRoute(previousId), siteId)); }; return ( @@ -108,4 +104,4 @@ function QueueControls(props: Props) { ); } -export default withRouter(observer(QueueControls)); +export default observer(QueueControls); diff --git a/frontend/app/components/Signup/Signup.tsx b/frontend/app/components/Signup/Signup.tsx index f8704c54b..c7d1137ae 100644 --- a/frontend/app/components/Signup/Signup.tsx +++ b/frontend/app/components/Signup/Signup.tsx @@ -1,30 +1,19 @@ import React, { useEffect, useState } from 'react'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; import { observer } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; -import { Icon } from 'UI'; import SignupForm from './SignupForm'; import HealthModal from 'Components/Header/HealthStatus/HealthModal/HealthModal'; import { getHealthRequest } from 'Components/Header/HealthStatus/getHealth'; import withPageTitle from 'HOCs/withPageTitle'; import { login } from 'App/routes'; import Copyright from 'Shared/Copyright'; +import { useNavigate } from "react-router"; const LOGIN_ROUTE = login(); -const BulletItem: React.FC<{ text: string }> = ({ text }) => ( -
-
- -
-
{text}
-
-); - const healthStatusCheck_key = '__or__healthStatusCheck_key'; -type SignupProps = RouteComponentProps; - -const Signup: React.FC = ({ history }) => { +const Signup = () => { + const navigate = useNavigate(); const { userStore } = useStore(); const authDetails = userStore.authStore.authDetails; const [healthModalPassed, setHealthModalPassed] = useState(localStorage.getItem(healthStatusCheck_key) === 'true'); @@ -47,7 +36,7 @@ const Signup: React.FC = ({ history }) => { if (!authDetails) return if (authDetails) { if (authDetails.tenants) { - history.push(LOGIN_ROUTE); + navigate(LOGIN_ROUTE); } else { void getHealth(); } @@ -79,4 +68,4 @@ const Signup: React.FC = ({ history }) => { ); }; -export default withRouter(withPageTitle('Signup - OpenReplay')(observer(Signup))); +export default withPageTitle('Signup - OpenReplay')(observer(Signup)); diff --git a/frontend/app/components/Spots/SpotPlayer/SpotPlayer.tsx b/frontend/app/components/Spots/SpotPlayer/SpotPlayer.tsx index 3c6857e05..c58641eec 100644 --- a/frontend/app/components/Spots/SpotPlayer/SpotPlayer.tsx +++ b/frontend/app/components/Spots/SpotPlayer/SpotPlayer.tsx @@ -1,7 +1,7 @@ import cn from 'classnames'; import { observer } from 'mobx-react-lite'; import React from 'react'; -import { useHistory, useParams } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router'; import { useStore } from 'App/mstore'; import { @@ -27,7 +27,7 @@ import spotPlayerStore, { PANELS } from './spotPlayerStore'; function SpotPlayer() { const defaultHeight = getDefaultPanelHeight(); - const history = useHistory(); + const navigate = useNavigate() const [panelHeight, setPanelHeight] = React.useState(defaultHeight); const { spotStore, userStore } = useStore(); const userEmail = userStore.account.name; @@ -47,7 +47,7 @@ function SpotPlayer() { if (pubKey) { spotStore.setAccessKey(pubKey); } else { - history.push('/'); + navigate('/'); } } }, [loggedIn]); diff --git a/frontend/app/components/Spots/SpotPlayer/components/SpotPlayerHeader.tsx b/frontend/app/components/Spots/SpotPlayer/components/SpotPlayerHeader.tsx index e46a5547b..464bba0e7 100644 --- a/frontend/app/components/Spots/SpotPlayer/components/SpotPlayerHeader.tsx +++ b/frontend/app/components/Spots/SpotPlayer/components/SpotPlayerHeader.tsx @@ -20,7 +20,7 @@ import { import copy from 'copy-to-clipboard'; import { observer } from 'mobx-react-lite'; import React, { useState } from 'react'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import Tabs from 'App/components/Session/Tabs'; import { useStore } from 'App/mstore'; @@ -58,7 +58,7 @@ function SpotPlayerHeader({ const comments = spotStore.currentSpot?.comments ?? []; const [dropdownOpen, setDropdownOpen] = useState(false); - const history = useHistory(); + const navigate = useNavigate() const onCopy = () => { copy(window.location.href); @@ -66,7 +66,7 @@ function SpotPlayerHeader({ }; const navigateToSpotsList = () => { - history.push(spotLink); + navigate(spotLink); }; const items: MenuProps['items'] = [ @@ -88,7 +88,7 @@ function SpotPlayerHeader({ await downloadFile(url, `${spotStore.currentSpot!.title}.webm`); } else if (key === '2') { spotStore.deleteSpot([spotStore.currentSpot!.spotId]).then(() => { - history.push(spotsList()); + navigate(spotsList()); message.success('Spot successfully deleted'); }); } diff --git a/frontend/app/components/Spots/SpotsList/SpotListItem.tsx b/frontend/app/components/Spots/SpotsList/SpotListItem.tsx index 381453b87..981bc5d8b 100644 --- a/frontend/app/components/Spots/SpotsList/SpotListItem.tsx +++ b/frontend/app/components/Spots/SpotsList/SpotListItem.tsx @@ -12,7 +12,7 @@ import { Button, Checkbox, Dropdown, Tooltip } from 'antd'; import copy from 'copy-to-clipboard'; import { Link2 } from 'lucide-react'; import React, { useState } from 'react'; -import { useHistory, useParams } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router'; import { toast } from 'react-toastify'; import { TextEllipsis } from "UI"; @@ -45,7 +45,7 @@ function SpotListItem({ const [isEdit, setIsEdit] = useState(false); const [loading, setLoading] = useState(true); const [tooltipText, setTooltipText] = useState('Copy link to clipboard'); - const history = useHistory(); + const navigate = useNavigate(); const { siteId } = useParams<{ siteId: string }>(); const menuItems = [ @@ -104,7 +104,7 @@ function SpotListItem({ const fullLink = `${window.location.origin}${spotLink}`; window.open(fullLink, '_blank'); } else { - history.push(withSiteId(spotUrl(spot.spotId.toString()), siteId)); + navigate(withSiteId(spotUrl(spot.spotId.toString()), siteId)); } }; diff --git a/frontend/app/components/UsabilityTesting/TestEdit.tsx b/frontend/app/components/UsabilityTesting/TestEdit.tsx index 22fcd396d..ca4cc8bbf 100644 --- a/frontend/app/components/UsabilityTesting/TestEdit.tsx +++ b/frontend/app/components/UsabilityTesting/TestEdit.tsx @@ -7,14 +7,14 @@ import { usabilityTestingView, usabilityTestingEdit, } from 'App/routes'; -import { useParams, useHistory, Prompt } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router'; import Breadcrumb from 'Shared/Breadcrumb'; import { EditOutlined, DeleteOutlined, MoreOutlined } from '@ant-design/icons'; import {Power, Info, ListTodo} from 'lucide-react'; import { useModal } from 'App/components/Modal'; import { observer } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; -import { confirm } from 'UI'; +import { confirm, NavPrompt } from 'UI'; import StepsModal from './StepsModal'; import SidePanel from './SidePanel'; import usePageTitle from 'App/hooks/usePageTitle'; @@ -48,12 +48,12 @@ function TestEdit() { const [isOverviewEditing, setIsOverviewEditing] = React.useState(false); const { showModal, hideModal } = useModal(); - const history = useHistory(); + const navigate = useNavigate() usePageTitle(`Usability Tests | ${uxtestingStore.instance ? 'Edit' : 'Create'}`); React.useEffect(() => { if (uxtestingStore.instanceCreationSiteId && siteId !== uxtestingStore.instanceCreationSiteId) { - history.push(withSiteId(usabilityTesting(), siteId)); + navigate(withSiteId(usabilityTesting(), siteId)); } }, [siteId]); React.useEffect(() => { @@ -66,7 +66,7 @@ function TestEdit() { }); } else { if (!uxtestingStore.instance) { - history.push(withSiteId(usabilityTesting(), siteId)); + navigate(withSiteId(usabilityTesting(), siteId)); } else { setConclusion(uxtestingStore.instance!.conclusionMessage); setGuidelines(uxtestingStore.instance!.guidelines); @@ -89,16 +89,16 @@ function TestEdit() { ); } else { toast.success('The usability test is now live and accessible to participants.'); - history.push(withSiteId(usabilityTestingView(testId!.toString()), siteId)); + navigate(withSiteId(usabilityTestingView(testId!.toString()), siteId)); } }); } else { uxtestingStore.createNewTest(isPreview).then((test) => { if (isPreview) { window.open(`${test.startingPath}?oruxt=${test.testId}`, '_blank', 'noopener,noreferrer'); - history.replace(withSiteId(usabilityTestingEdit(test.testId), siteId)); + navigate(withSiteId(usabilityTestingEdit(test.testId), siteId), { replace: true }); } else { - history.push(withSiteId(usabilityTestingView(test.testId), siteId)); + navigate(withSiteId(usabilityTestingView(test.testId), siteId)); } }); } @@ -128,7 +128,7 @@ function TestEdit() { }) ) { uxtestingStore.deleteTest(testId).then(() => { - history.push(withSiteId(usabilityTesting(), siteId)); + navigate(withSiteId(usabilityTesting(), siteId)); }); } } @@ -155,7 +155,7 @@ function TestEdit() { }, ]} /> - { return 'You have unsaved changes. Are you sure you want to leave?'; diff --git a/frontend/app/components/UsabilityTesting/TestOverview.tsx b/frontend/app/components/UsabilityTesting/TestOverview.tsx index 2dd8cc565..394813c77 100644 --- a/frontend/app/components/UsabilityTesting/TestOverview.tsx +++ b/frontend/app/components/UsabilityTesting/TestOverview.tsx @@ -4,10 +4,10 @@ import { getPdf2 } from 'Components/AssistStats/pdfGenerator'; import { useModal } from 'Components/Modal'; import LiveTestsModal from 'Components/UsabilityTesting/LiveTestsModal'; import React from 'react'; -import { Button, Typography, Select, Space, Popover, Dropdown, Tooltip } from 'antd'; +import { Button, Typography, Select, Space, Popover, Dropdown } from 'antd'; import { InfoCircleOutlined } from '@ant-design/icons'; import { withSiteId, usabilityTesting, usabilityTestingEdit } from 'App/routes'; -import { useParams, useHistory } from 'react-router-dom'; +import { useParams, useNavigate } from 'react-router'; import Breadcrumb from 'Shared/Breadcrumb'; import { observer } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; @@ -91,7 +91,7 @@ function TestOverview() { // @ts-ignore const { siteId, testId } = useParams(); const { showModal, hideModal } = useModal(); - const history = useHistory(); + const navigate = useNavigate() const { uxtestingStore } = useStore(); React.useEffect(() => { @@ -99,7 +99,7 @@ function TestOverview() { try { await uxtestingStore.getTest(testId); } catch { - history.push(withSiteId(usabilityTesting(), siteId)); + navigate(withSiteId(usabilityTesting(), siteId)); } }; @@ -375,7 +375,7 @@ const TaskSummary = observer(() => { const Title = observer(({ testId, siteId }: any) => { const [truncate, setTruncate] = React.useState(true); const { uxtestingStore } = useStore(); - const history = useHistory(); + const navigate = useNavigate() const handleChange = (value: string) => { uxtestingStore.updateTestStatus(value); @@ -411,7 +411,7 @@ const Title = observer(({ testId, siteId }: any) => { }) ) { uxtestingStore.deleteTest(testId).then(() => { - history.push(withSiteId(usabilityTesting(), siteId)); + navigate(withSiteId(usabilityTesting(), siteId)); }); } } @@ -441,7 +441,7 @@ const Title = observer(({ testId, siteId }: any) => { confirmButton: 'Edit' }) ) { - history.push(withSiteId(usabilityTestingEdit(testId), siteId)); + navigate(withSiteId(usabilityTestingEdit(testId), siteId)); } }; diff --git a/frontend/app/components/UsabilityTesting/UsabilityTesting.tsx b/frontend/app/components/UsabilityTesting/UsabilityTesting.tsx index 9d60224af..3d8d25390 100644 --- a/frontend/app/components/UsabilityTesting/UsabilityTesting.tsx +++ b/frontend/app/components/UsabilityTesting/UsabilityTesting.tsx @@ -9,7 +9,7 @@ import { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; import { Loader, NoContent, Pagination, Link, Icon } from 'UI'; import { checkForRecent, getDateFromMill } from 'App/date'; import { ArrowRightOutlined, PlusOutlined } from '@ant-design/icons'; -import { useHistory, useParams } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router'; import { withSiteId, usabilityTestingEdit, usabilityTestingView } from 'App/routes'; import { debounce } from 'App/utils'; import withPageTitle from 'HOCs/withPageTitle'; @@ -45,7 +45,7 @@ function TestsTable() { // @ts-ignore const { siteId } = useParams(); - const history = useHistory(); + const navigate = useNavigate() const onClose = (confirmed: boolean) => { if (confirmed) { @@ -65,7 +65,7 @@ function TestsTable() { }; const redirect = (path: string) => { - history.push(withSiteId(usabilityTestingEdit(path), siteId)); + navigate(withSiteId(usabilityTestingEdit(path), siteId)); }; return ( @@ -197,10 +197,10 @@ const statusMap = { function Row({ test, siteId }: { test: UxTListEntry; siteId: string }) { const link = usabilityTestingView(test.testId.toString()); const editLink = usabilityTestingEdit(test.testId.toString()); - const history = useHistory(); + const navigate = useNavigate() const redirect = () => { - history.push(withSiteId(test.status === 'preview' ? editLink : link, siteId)); + navigate(withSiteId(test.status === 'preview' ? editLink : link, siteId)); }; return (
(BaseComponent) => { - @withRouter - class WrapperClass extends React.Component { - getQuery = (names) => parseQuery(this.props.location, names); - getParam = (name) => parseQuery(this.props.location)[name]; - addQuery = (params) => { - const { location, history } = this.props; - history.push(addQueryParams(location, params)); - }; - removeQuery = (names = [], replace = false) => { - const { location, history } = this.props; + const WrapperComponent = (props) => { + const location = useLocation(); + const navigate = useNavigate(); + const getQuery = (names) => parseQuery(location, names); + const getParam = (name) => parseQuery(location)[name]; + + const addQuery = (params) => { + navigate(addQueryParams(location, params)); + }; + + const removeQuery = (names = [], replace = false) => { const namesArray = Array.isArray(names) ? names : [names]; /* to avoid update stack overflow */ - const actualNames = Object.keys(this.getQuery(namesArray)); + const actualNames = Object.keys(getQuery(namesArray)); if (actualNames.length > 0) { - history[replace ? 'replace' : 'push'](removeQueryParams(location, actualNames)); + const path = removeQueryParams(location, actualNames); + navigate(path, { replace }); } }; - setQuery = (params, replace = false) => { - const { location, history } = this.props; - history[replace ? 'replace' : 'push'](setQueryParams(location, params)); - }; - query = { - all: this.getQuery, - get: this.getParam, - add: this.addQuery, - remove: this.removeQuery, - set: this.setQuery, // TODO: use namespaces + + const setQuery = (params, replace = false) => { + navigate(setQueryParams(location, params), { replace }); }; - getHash = () => this.props.location.hash.substring(1); - setHash = (hash) => { - const { location, history } = this.props; - history.push({ ...location, hash: `#${hash}` }); - }; - removeHash = () => { - const { location, history } = this.props; - history.push({ ...location, hash: '' }); - }; - hash = { - get: this.getHash, - set: this.setHash, - remove: this.removeHash, + const query = { + all: getQuery, + get: getParam, + add: addQuery, + remove: removeQuery, + set: setQuery, // TODO: use namespaces }; - getQueryProps() { - if (Array.isArray(propNames)) return this.getQuery(propNames); + const getHash = () => location.hash.substring(1); + + const setHash = (hash) => { + navigate({ ...location, hash: `#${hash}` }); + }; + + const removeHash = () => { + navigate({ ...location, hash: '' }); + }; + + const hash = { + get: getHash, + set: setHash, + remove: removeHash, + }; + + const getQueryProps = () => { + if (Array.isArray(propNames)) return getQuery(propNames); if (propNames !== null && typeof propNames === 'object') { const values = Object.values(propNames); - const query = this.getQuery(values); + const query = getQuery(values); const queryProps = {}; - Object.keys(propNames).map((key) => { + Object.keys(propNames).forEach((key) => { queryProps[key] = query[propNames[key]]; }); return queryProps; } return {}; - } + }; - render() { - const queryProps = this.getQueryProps(); - return ; - } - } - return WrapperClass; + const queryProps = getQueryProps(); + return ; + }; + + return WrapperComponent; }; -export default withLocationHandlers; + +export default withLocationHandlers; \ No newline at end of file diff --git a/frontend/app/components/hocs/withSiteIdRouter.js b/frontend/app/components/hocs/withSiteIdRouter.js deleted file mode 100644 index 756a5d280..000000000 --- a/frontend/app/components/hocs/withSiteIdRouter.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { withRouter } from 'react-router-dom'; -import { withSiteId } from 'App/routes'; -import { observer } from 'mobx-react-lite' -import { useStore } from "App/mstore"; - -export default BaseComponent => withRouter(observer((props) => { - const { history, ...other } = props - const { projectsStore } = useStore(); - const siteId = projectsStore.siteId - - const push = (location) => { - if (typeof location === 'string') { - history.push(withSiteId(location, siteId)); - } else if (typeof location === 'object') { - history.push({ ...location, pathname: withSiteId(location.pathname, siteId) }); - } - } - - return -})) \ No newline at end of file diff --git a/frontend/app/components/hocs/withSiteIdUpdater.js b/frontend/app/components/hocs/withSiteIdUpdater.js index 8bc3f533e..ee52b51f1 100644 --- a/frontend/app/components/hocs/withSiteIdUpdater.js +++ b/frontend/app/components/hocs/withSiteIdUpdater.js @@ -1,13 +1,15 @@ import React, { useEffect, useRef } from 'react'; import { useStore } from "App/mstore"; import { observer } from 'mobx-react-lite' +import { useParams, useNavigate } from 'react-router'; const withSiteIdUpdater = (BaseComponent) => { const WrapperComponent = (props) => { + const { siteId: urlSiteId } = useParams(); + const navigate = useNavigate(); const { projectsStore } = useStore(); const siteId = projectsStore.siteId; const setSiteId = projectsStore.setSiteId; - const urlSiteId = props.match.params.siteId const prevSiteIdRef = useRef(siteId); useEffect(() => { @@ -17,15 +19,15 @@ const withSiteIdUpdater = (BaseComponent) => { }, []); useEffect(() => { - const { location: { pathname }, history } = props; + const pathname = location.pathname; const shouldUrlUpdate = urlSiteId && parseInt(urlSiteId, 10) !== parseInt(siteId, 10); if (shouldUrlUpdate) { const path = ['', siteId].concat(pathname.split('/').slice(2)).join('/'); - history.push(path); + navigate(path); } prevSiteIdRef.current = siteId; - }, [urlSiteId, siteId, props.location.pathname, props.history]); + }, [urlSiteId, siteId, location.pathname]); const key = siteId; diff --git a/frontend/app/components/shared/Breadcrumb/BackButton.tsx b/frontend/app/components/shared/Breadcrumb/BackButton.tsx index ef046d5a0..43c9d25d2 100644 --- a/frontend/app/components/shared/Breadcrumb/BackButton.tsx +++ b/frontend/app/components/shared/Breadcrumb/BackButton.tsx @@ -1,14 +1,14 @@ import React from 'react'; import { Button } from 'antd'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { LeftOutlined, ArrowLeftOutlined } from '@ant-design/icons'; function BackButton({ compact }: { compact?: boolean }) { - const history = useHistory(); + const navigate = useNavigate() const siteId = location.pathname.split('/')[1]; const handleBackClick = () => { - history.push(`/${siteId}/dashboard`); + navigate(`/${siteId}/dashboard`); }; if (compact) { return ( diff --git a/frontend/app/components/shared/GettingStarted/StepList.tsx b/frontend/app/components/shared/GettingStarted/StepList.tsx index 178cb4aa1..431a51e98 100644 --- a/frontend/app/components/shared/GettingStarted/StepList.tsx +++ b/frontend/app/components/shared/GettingStarted/StepList.tsx @@ -1,13 +1,13 @@ import React from 'react'; +import { useNavigate } from "react-router"; import { Icon } from 'UI'; import cn from 'classnames'; import { Step } from 'App/mstore/types/gettingStarted'; import { useStore } from 'App/mstore'; import { onboarding as onboardingRoute, withSiteId } from 'App/routes'; -import { RouteComponentProps, withRouter } from 'react-router'; import { useModal } from 'App/components/Modal'; -interface StepListProps extends RouteComponentProps { +interface StepListProps { title: string; steps: Step[]; status: 'pending' | 'completed'; @@ -61,6 +61,7 @@ const StepItem = React.memo( ); const StepList = React.memo((props: StepListProps) => { + const navigate = useNavigate() const { title, steps } = props; const { hideModal } = useModal(); @@ -79,10 +80,9 @@ const StepList = React.memo((props: StepListProps) => { } const onClick = (step: any) => { - const { history } = props; const siteId = projectsStore.getSiteId().siteId!; hideModal(); - history.push(withSiteId(onboardingRoute(step.url), siteId)); + navigate(withSiteId(onboardingRoute(step.url), siteId)); }; return ( @@ -97,4 +97,4 @@ const StepList = React.memo((props: StepListProps) => { ); }); -export default withRouter(StepList); +export default StepList; diff --git a/frontend/app/components/shared/IntegrateSlackButton/IntegrateSlackButton.js b/frontend/app/components/shared/IntegrateSlackButton/IntegrateSlackButton.js index bc1900336..d87b78ed4 100644 --- a/frontend/app/components/shared/IntegrateSlackButton/IntegrateSlackButton.js +++ b/frontend/app/components/shared/IntegrateSlackButton/IntegrateSlackButton.js @@ -2,12 +2,12 @@ import React from 'react' import { Icon } from 'UI' import { Button } from 'antd' import { CLIENT_TABS, client as clientRoute } from 'App/routes'; -import { withRouter } from 'react-router-dom'; - -function IntegrateSlackTeamsButton({ history }) { +import { useNavigate } from "react-router"; +function IntegrateSlackTeamsButton() { + const navigate = useNavigate(); const gotoPreferencesIntegrations = () => { - history.push(clientRoute(CLIENT_TABS.INTEGRATIONS)); + navigate(clientRoute(CLIENT_TABS.INTEGRATIONS)); } return ( @@ -26,4 +26,4 @@ function IntegrateSlackTeamsButton({ history }) { ) } -export default withRouter(IntegrateSlackTeamsButton) +export default IntegrateSlackTeamsButton diff --git a/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js b/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js index c8b1a626d..ebdbe6587 100644 --- a/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js +++ b/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js @@ -3,17 +3,16 @@ import { Alert, Space, Button } from 'antd'; import { observer } from 'mobx-react-lite' import { useStore } from "App/mstore"; import { onboarding as onboardingRoute } from 'App/routes'; -import { withRouter } from 'react-router-dom'; import * as routes from '../../../routes'; import { SquareArrowOutUpRight } from 'lucide-react'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; const withSiteId = routes.withSiteId; const NoSessionsMessage = () => { const { projectsStore } = useStore(); const siteId = projectsStore.siteId; - const history = useHistory(); + const navigate = useNavigate(); const activeSite = projectsStore.active; const showNoSessions = !!activeSite && !activeSite.recorded; const onboardingPath = withSiteId(onboardingRoute('installing'), siteId); @@ -41,7 +40,7 @@ const NoSessionsMessage = () => { @@ -55,4 +54,4 @@ const NoSessionsMessage = () => { ); }; -export default withRouter(observer(NoSessionsMessage)); +export default observer(NoSessionsMessage) diff --git a/frontend/app/components/shared/ProjectDropdown/ProjectDropdown.tsx b/frontend/app/components/shared/ProjectDropdown/ProjectDropdown.tsx index 72d9446cb..3514315e8 100644 --- a/frontend/app/components/shared/ProjectDropdown/ProjectDropdown.tsx +++ b/frontend/app/components/shared/ProjectDropdown/ProjectDropdown.tsx @@ -5,11 +5,10 @@ import { import { Button, Dropdown, MenuProps, Space, Typography } from 'antd'; import cn from 'classnames'; import React from 'react'; -import { withRouter } from 'react-router-dom'; +import { useLocation } from 'react-router' import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; import { hasSiteId, siteChangeAvailable } from 'App/routes'; -import NewSiteForm from 'Components/Client/Sites/NewSiteForm'; import { Icon } from 'UI'; import { useModal } from 'Components/ModalContext'; import ProjectForm from 'Components/Client/Projects/ProjectForm'; @@ -17,15 +16,15 @@ import Project from '@/mstore/types/project'; const { Text } = Typography; -function ProjectDropdown(props: { location: any }) { +function ProjectDropdown() { const mstore = useStore(); + const location = useLocation(); const { projectsStore, searchStore, searchStoreLive, userStore } = mstore; const account = userStore.account; const sites = projectsStore.list; const siteId = projectsStore.siteId; const setSiteId = projectsStore.setSiteId; const initProject = projectsStore.initProject; - const { location } = props; const isAdmin = account.admin || account.superAdmin; const activeSite = sites.find((s) => s.id === siteId); const showCurrent = @@ -139,6 +138,4 @@ function ProjectDropdown(props: { location: any }) { ); } -export default withRouter( - observer(ProjectDropdown) -); +export default observer(ProjectDropdown) \ No newline at end of file diff --git a/frontend/app/components/shared/SessionItem/PlayLink/PlayLink.tsx b/frontend/app/components/shared/SessionItem/PlayLink/PlayLink.tsx index f9d322606..f71913d10 100644 --- a/frontend/app/components/shared/SessionItem/PlayLink/PlayLink.tsx +++ b/frontend/app/components/shared/SessionItem/PlayLink/PlayLink.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { liveSession as liveSessionRoute, @@ -31,7 +31,7 @@ interface Props { function PlayLink(props: Props) { const { projectsStore } = useStore(); const { isAssist, viewed, sessionId, onClick = null, queryParams } = props; - const history = useHistory(); + const navigate = useNavigate() const defaultIconName = getIconName(viewed); const [iconName, setIconName] = useState(defaultIconName); @@ -58,7 +58,7 @@ function PlayLink(props: Props) { } else { e.preventDefault(); props.beforeOpen(); - history.push(replayLink); + navigate(replayLink); } } }; diff --git a/frontend/app/components/shared/SessionItem/SessionItem.tsx b/frontend/app/components/shared/SessionItem/SessionItem.tsx index 52e772cad..374a1cd10 100644 --- a/frontend/app/components/shared/SessionItem/SessionItem.tsx +++ b/frontend/app/components/shared/SessionItem/SessionItem.tsx @@ -2,7 +2,6 @@ import cn from 'classnames'; import { Duration } from 'luxon'; import { observer } from 'mobx-react-lite'; import React, { useState, useCallback, useMemo } from 'react'; -import { RouteComponentProps, useHistory, withRouter } from 'react-router-dom'; import { durationFormatted, formatTimeOrDate } from 'App/date'; import { useStore } from 'App/mstore'; @@ -14,7 +13,7 @@ import { } from 'App/routes'; import { capitalize } from 'App/utils'; import { Avatar, CountryFlag, Icon, Label, TextEllipsis } from 'UI'; - +import { useLocation } from "react-router"; import Counter from './Counter'; import ErrorBars from './ErrorBars'; import PlayLink from './PlayLink'; @@ -78,8 +77,8 @@ const PREFETCH_STATE = { fetched: 2 }; -function SessionItem(props: RouteComponentProps & Props) { - const { location } = useHistory(); +function SessionItem(props: Props) { + const location = useLocation(); const { settingsStore, sessionStore } = useStore(); const { timezone, shownTimezone } = settingsStore.sessionSettings; const [prefetchState, setPrefetched] = useState(PREFETCH_STATE.none); @@ -449,4 +448,4 @@ function SessionItem(props: RouteComponentProps & Props) { ); } -export default withRouter(observer(SessionItem)); +export default observer(SessionItem); diff --git a/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx b/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx index 2d05133b2..1d45cd28b 100644 --- a/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx +++ b/frontend/app/components/shared/SessionsTabOverview/components/SessionList/SessionList.tsx @@ -3,7 +3,7 @@ import { FilterKey } from 'Types/filter/filterType'; import SessionItem from 'Shared/SessionItem'; import { NoContent, Loader, Pagination, Icon } from 'UI'; import { Button } from 'antd' -import { useLocation, withRouter } from 'react-router-dom'; +import { useLocation } from 'react-router'; import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; import { numberWithCommas } from 'App/utils'; import SessionDateRange from './SessionDateRange'; @@ -237,4 +237,4 @@ function SessionList() { ); } -export default withRouter(observer(SessionList)); +export default observer(SessionList); diff --git a/frontend/app/components/ui/NavPrompt/index.tsx b/frontend/app/components/ui/NavPrompt/index.tsx new file mode 100644 index 000000000..90bbbc1dc --- /dev/null +++ b/frontend/app/components/ui/NavPrompt/index.tsx @@ -0,0 +1,46 @@ +import { useCallback, useEffect } from 'react'; +import { useBlocker } from 'react-router'; + +function NavigationPrompt({ when, message }: any) { + const blocker = useCallback( + (tx) => { + if (!when) return false; + + if (typeof message === 'function') { + const result = message(tx.nextLocation); + if (result === true) return false; + } + + return true; + }, + [when, message] + ); + + const { state, proceed, reset, location: nextLocation } = useBlocker(blocker); + + useEffect(() => { + if (state === 'blocked') { + let promptMessage = 'Are you sure you want to leave this page?'; + if (typeof message === 'function') { + const result = message(nextLocation); + if (typeof result === 'string') { + promptMessage = result; + } + } else if (typeof message === 'string') { + promptMessage = message; + } + + const confirmed = window.confirm(promptMessage); + + if (confirmed) { + proceed(); + } else { + reset(); + } + } + }, [state, proceed, reset, nextLocation, message]); + + return null; +} + +export default NavigationPrompt; diff --git a/frontend/app/components/ui/NoSessionPermission/NoSessionPermission.tsx b/frontend/app/components/ui/NoSessionPermission/NoSessionPermission.tsx index 0447a51ef..5ff5d0cd9 100644 --- a/frontend/app/components/ui/NoSessionPermission/NoSessionPermission.tsx +++ b/frontend/app/components/ui/NoSessionPermission/NoSessionPermission.tsx @@ -1,6 +1,6 @@ import { observer } from 'mobx-react-lite'; import React from 'react'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; +import { useNavigate, useLocation } from "react-router"; import { useStore } from 'App/mstore'; import { @@ -16,8 +16,7 @@ import stl from './NoSessionPermission.module.css'; const SESSIONS_ROUTE = sessionsRoute(); const ASSIST_ROUTE = assistRoute(); -interface Props extends RouteComponentProps { - history: any; +interface Props { isLive?: boolean; } function NoSessionPermission(props: Props) { @@ -26,19 +25,20 @@ function NoSessionPermission(props: Props) { const sessionPath = sessionStore.sessionPath; const isAssist = window.location.pathname.includes('/assist/'); const siteId = projectsStore.siteId!; - const { history } = props; + const navigate = useNavigate(); + const location = useLocation(); const backHandler = () => { if ( - sessionPath.pathname === history.location.pathname || + sessionPath.pathname === location.pathname || sessionPath.pathname.includes('/session/') || isAssist ) { - history.push( + navigate( withSiteId(isAssist ? ASSIST_ROUTE : SESSIONS_ROUTE, siteId) ); } else { - history.push( + navigate( sessionPath ? sessionPath.pathname + sessionPath.search : withSiteId(SESSIONS_ROUTE, siteId) @@ -50,7 +50,7 @@ function NoSessionPermission(props: Props) {
Not allowed
- {session.isLive ? ( + {session.live ? ( This session is still live, and you don’t have the necessary permissions to access this feature. Please check with your admin. @@ -68,6 +68,4 @@ function NoSessionPermission(props: Props) { ); } -export default withRouter( - observer(NoSessionPermission) -); +export default observer(NoSessionPermission) diff --git a/frontend/app/components/ui/index.js b/frontend/app/components/ui/index.js index 90b7226a5..58bd6c9dd 100644 --- a/frontend/app/components/ui/index.js +++ b/frontend/app/components/ui/index.js @@ -47,4 +47,5 @@ export { default as Message } from './Message'; export { default as Popover } from './Popover'; export { default as Switch } from './Switch'; export { default as Divider } from './Divider'; -export { default as CodeBlock } from './CodeBlock' \ No newline at end of file +export { default as CodeBlock } from './CodeBlock' +export { default as NavPrompt } from './NavPrompt'; \ No newline at end of file diff --git a/frontend/app/hooks/useSessionSearchQueryHandler.ts b/frontend/app/hooks/useSessionSearchQueryHandler.ts index ac31e4913..4b60d3023 100644 --- a/frontend/app/hooks/useSessionSearchQueryHandler.ts +++ b/frontend/app/hooks/useSessionSearchQueryHandler.ts @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { useHistory } from 'react-router'; +import { useNavigate } from 'react-router'; import { JsonUrlConverter } from 'App/utils/search'; import { useStore } from '@/mstore'; import Search from '@/mstore/types/search'; @@ -15,7 +15,7 @@ interface Props { const useSessionSearchQueryHandler = ({ onBeforeLoad, appliedFilter, loading, onLoaded = () => null }: Props) => { const { searchStore } = useStore(); const [beforeHookLoaded, setBeforeHookLoaded] = useState(!onBeforeLoad); - const history = useHistory(); + const navigate = useNavigate() // Apply filter from the query string when the component mounts useEffect(() => { @@ -27,7 +27,7 @@ const useSessionSearchQueryHandler = ({ onBeforeLoad, appliedFilter, loading, on setBeforeHookLoaded(true); } - const converter = JsonUrlConverter.urlParamsToJson(history.location.search); + const converter = JsonUrlConverter.urlParamsToJson(location.search); const json = getFilterFromJson(converter.toJSON()); const filter = new Search(json); @@ -57,27 +57,27 @@ const useSessionSearchQueryHandler = ({ onBeforeLoad, appliedFilter, loading, on }; void applyFilterFromQuery(); - }, [loading, searchStore, history.location.search, onBeforeLoad]); + }, [loading, searchStore, location.search, onBeforeLoad]); // Update the URL whenever the appliedFilter changes useEffect(() => { const updateUrlWithFilter = () => { if (!loading && beforeHookLoaded) { const query = JsonUrlConverter.jsonToUrlParams(appliedFilter); - history.replace({ search: query }); + navigate({ search: query }, { replace: true }); } }; updateUrlWithFilter(); - }, [appliedFilter, loading, beforeHookLoaded, history]); + }, [appliedFilter, loading, beforeHookLoaded]); // Ensure the URL syncs on remount if already parsed useEffect(() => { if (searchStore.urlParsed) { const query = JsonUrlConverter.jsonToUrlParams(appliedFilter); - history.replace({ search: query }); + navigate({ search: query }, { replace: true }); } - }, [appliedFilter, searchStore.urlParsed, history]); + }, [appliedFilter, searchStore.urlParsed]); return null; }; diff --git a/frontend/app/layout/SideMenu.tsx b/frontend/app/layout/SideMenu.tsx index 85e8f0aa2..6482b0d2d 100644 --- a/frontend/app/layout/SideMenu.tsx +++ b/frontend/app/layout/SideMenu.tsx @@ -1,9 +1,8 @@ import { Divider, Menu, Tag, Typography, Popover, Button } from 'antd'; import cn from 'classnames'; import React from 'react'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; import { observer } from 'mobx-react-lite'; - +import { useLocation, useNavigate } from 'react-router'; import SupportModal from 'App/layout/SupportModal'; import * as routes from 'App/routes'; import { @@ -30,15 +29,15 @@ import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; const { Text } = Typography; -interface Props extends RouteComponentProps { - siteId?: string; +interface Props { isCollapsed?: boolean; } function SideMenu(props: Props) { + const location = useLocation(); + const navigate = useNavigate(); const { - location, - isCollapsed + isCollapsed, } = props; const isPreferencesActive = location.pathname.includes('/client/'); @@ -118,7 +117,7 @@ function SideMenu(props: Props) { const menuRoutes: any = { [MENU.EXIT]: () => - props.history.push(withSiteId(routes.sessions(), siteId)), + navigate(withSiteId(routes.sessions(), siteId)), [MENU.SESSIONS]: () => withSiteId(routes.sessions(), siteId), [MENU.BOOKMARKS]: () => withSiteId(routes.bookmarks(), siteId), [MENU.VAULT]: () => withSiteId(routes.bookmarks(), siteId), @@ -169,7 +168,7 @@ function SideMenu(props: Props) { }; const pushTo = (path: string) => { - props.history.push(path); + navigate(path); }; const RenderDivider = (props: { index: number }) => { @@ -324,8 +323,7 @@ function SideMenu(props: Props) { ); } -export default withRouter(observer(SideMenu)); - +export default observer(SideMenu); const SpotMenuItem = ({ isCollapsed }: any) => { const [isModalVisible, setIsModalVisible] = React.useState(false); diff --git a/frontend/app/layout/SpotToOpenReplayPrompt.tsx b/frontend/app/layout/SpotToOpenReplayPrompt.tsx index db49e49a1..f123f0034 100644 --- a/frontend/app/layout/SpotToOpenReplayPrompt.tsx +++ b/frontend/app/layout/SpotToOpenReplayPrompt.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Modal, Button, List, Divider } from 'antd'; import { CircleDot, Play, TrendingUp, Radio, Sparkles, Plug, ArrowRight } from 'lucide-react'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { onboarding } from 'App/routes'; import { useStore } from 'App/mstore'; @@ -15,7 +15,7 @@ const SpotToOpenReplayPrompt = ({ isVisible, onCancel }: { onCancel: () => void; }) => { const { userStore } = useStore(); - const history = useHistory(); + const navigate = useNavigate() const features = [ { icon: , text: 'Spot', noBorder: true }, { isDivider: true }, @@ -28,7 +28,7 @@ const SpotToOpenReplayPrompt = ({ isVisible, onCancel }: { const onUpgrade = () => { userStore.upgradeScope().then(() => { - history.push(onboarding()); + navigate(onboarding()); onCancel(); }) } diff --git a/frontend/package.json b/frontend/package.json index e4bff1d30..de984c9f7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -62,8 +62,8 @@ "react-draggable": "^4.4.5", "react-google-recaptcha": "^2.1.0", "react-intersection-observer": "^9.13.1", - "react-router": "^5.3.3", - "react-router-dom": "^5.3.3", + "react-router": "^6.30.0", + "react-router-dom": "^6.30.0", "react-select": "^5.3.2", "react-svg-map": "^2.2.0", "react-toastify": "^9.1.1", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 3cd24d927..784ccdd6e 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1581,7 +1581,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.4, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.6, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.24.8, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.4, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.6, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.24.8, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": version: 7.26.9 resolution: "@babel/runtime@npm:7.26.9" dependencies: @@ -2930,6 +2930,13 @@ __metadata: languageName: node linkType: hard +"@remix-run/router@npm:1.23.0": + version: 1.23.0 + resolution: "@remix-run/router@npm:1.23.0" + checksum: 10c1/c87d2227dc07ec5575cb3349dc388c0abd77a7f04bbdde531b183880c19c9550c3adbeb67a1a2ad677fbf13dd6192e2c42f479d5d99e4f01c7b53f250cec6c00 + languageName: node + linkType: hard + "@sentry/browser@npm:^5.21.1": version: 5.30.0 resolution: "@sentry/browser@npm:5.30.0" @@ -8528,20 +8535,6 @@ __metadata: languageName: node linkType: hard -"history@npm:^4.9.0": - version: 4.10.1 - resolution: "history@npm:4.10.1" - dependencies: - "@babel/runtime": "npm:^7.1.2" - loose-envify: "npm:^1.2.0" - resolve-pathname: "npm:^3.0.0" - tiny-invariant: "npm:^1.0.2" - tiny-warning: "npm:^1.0.0" - value-equal: "npm:^1.0.1" - checksum: 10c1/52b1a0cc62f6bff70d29325ce3e16078f070e8d51405866ce617c266dbf66a8b3898b5dbd9272514844760e8c3d1e657240d64175ab6b133b8bb100161218b5b - languageName: node - linkType: hard - "hls.js@npm:^1.5.13": version: 1.5.20 resolution: "hls.js@npm:1.5.20" @@ -8549,7 +8542,7 @@ __metadata: languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.1.0, hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": +"hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" dependencies: @@ -9387,13 +9380,6 @@ __metadata: languageName: node linkType: hard -"isarray@npm:0.0.1": - version: 0.0.1 - resolution: "isarray@npm:0.0.1" - checksum: 10c1/abd2b120df9b6e06385e1d2d109dc4eabac0b950e1a02448cde7416691462d809c32d2acdd28d6c2e2ede3527e6251debc9a56f7c7c8515da4ead1993f6ba514 - languageName: node - linkType: hard - "isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -10563,7 +10549,7 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.2.0, loose-envify@npm:^1.3.1, loose-envify@npm:^1.4.0": +"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.3.1, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" dependencies: @@ -11663,8 +11649,8 @@ __metadata: react-draggable: "npm:^4.4.5" react-google-recaptcha: "npm:^2.1.0" react-intersection-observer: "npm:^9.13.1" - react-router: "npm:^5.3.3" - react-router-dom: "npm:^5.3.3" + react-router: "npm:^6.30.0" + react-router-dom: "npm:^6.30.0" react-select: "npm:^5.3.2" react-svg-map: "npm:^2.2.0" react-toastify: "npm:^9.1.1" @@ -11969,15 +11955,6 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:^1.7.0": - version: 1.9.0 - resolution: "path-to-regexp@npm:1.9.0" - dependencies: - isarray: "npm:0.0.1" - checksum: 10c1/79d0f8f5221b7d1a2c3de08775f7ea158ef7bd978e9ed3c6b0813a89ffcc59edb9eee435913ce73e2aad99384b7101d40d81dfe312cf2f54da53990305108695 - languageName: node - linkType: hard - "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -13676,7 +13653,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^16.12.0, react-is@npm:^16.13.1, react-is@npm:^16.6.0, react-is@npm:^16.7.0": +"react-is@npm:^16.12.0, react-is@npm:^16.13.1, react-is@npm:^16.7.0": version: 16.13.1 resolution: "react-is@npm:16.13.1" checksum: 10c1/b1d8be84f510ec19f3dc7cebd9790076f708943e333e03a26dfadc225bd36b3ac6bbdc94a19e1cd72f43d366de47e21fa04396aba0e86ae0e50610fb4c7a47c8 @@ -13697,39 +13674,27 @@ __metadata: languageName: node linkType: hard -"react-router-dom@npm:^5.3.3": - version: 5.3.4 - resolution: "react-router-dom@npm:5.3.4" +"react-router-dom@npm:^6.30.0": + version: 6.30.0 + resolution: "react-router-dom@npm:6.30.0" dependencies: - "@babel/runtime": "npm:^7.12.13" - history: "npm:^4.9.0" - loose-envify: "npm:^1.3.1" - prop-types: "npm:^15.6.2" - react-router: "npm:5.3.4" - tiny-invariant: "npm:^1.0.2" - tiny-warning: "npm:^1.0.0" + "@remix-run/router": "npm:1.23.0" + react-router: "npm:6.30.0" peerDependencies: - react: ">=15" - checksum: 10c1/1a00acf223dc8ffb7eb6c189dcc858858c4df37b51dd92b5d38aca456b9813596094e19fdcdcc8044daad054c5392de051643988c217e3a296d1df803e5106f4 + react: ">=16.8" + react-dom: ">=16.8" + checksum: 10c1/00e36b130dcd6fdf7c2867b35ad547b28dfcdbc5b342699baaea785dbfbabd6d3e1d9213c3eb1ffb64e4b418b3a1053882b7ab568a68624f301c2ed88f6d38e5 languageName: node linkType: hard -"react-router@npm:5.3.4, react-router@npm:^5.3.3": - version: 5.3.4 - resolution: "react-router@npm:5.3.4" +"react-router@npm:6.30.0, react-router@npm:^6.30.0": + version: 6.30.0 + resolution: "react-router@npm:6.30.0" dependencies: - "@babel/runtime": "npm:^7.12.13" - history: "npm:^4.9.0" - hoist-non-react-statics: "npm:^3.1.0" - loose-envify: "npm:^1.3.1" - path-to-regexp: "npm:^1.7.0" - prop-types: "npm:^15.6.2" - react-is: "npm:^16.6.0" - tiny-invariant: "npm:^1.0.2" - tiny-warning: "npm:^1.0.0" + "@remix-run/router": "npm:1.23.0" peerDependencies: - react: ">=15" - checksum: 10c1/b920516e84fc10980a108b1ad368c4ebd3b5541c4de1bb071053c91ccc6410d1a7326aed6da6101d6728ca825ec661e73b87518078502e3a4879f5be924af2d6 + react: ">=16.8" + checksum: 10c1/6d8b9dfcfa38b930f49a511d635f02dd66dfed6c120df08c41ebfc661c6c4aa388b59ac63e01ffba55a3f334e199ce2f9f76f1fd50d14fca52e213373c18368f languageName: node linkType: hard @@ -14111,13 +14076,6 @@ __metadata: languageName: node linkType: hard -"resolve-pathname@npm:^3.0.0": - version: 3.0.0 - resolution: "resolve-pathname@npm:3.0.0" - checksum: 10c1/7951bb47cbfc98a2cc0e4b62a099ce4191f01e42557b048cf8bf857092e0719a4d021f9765d770ec8967630656f0a78c4b656908183d09dd680649d20d56851b - languageName: node - linkType: hard - "resolve-pkg-maps@npm:^1.0.0": version: 1.0.0 resolution: "resolve-pkg-maps@npm:1.0.0" @@ -15607,20 +15565,13 @@ __metadata: languageName: node linkType: hard -"tiny-invariant@npm:^1.0.2, tiny-invariant@npm:^1.3.1": +"tiny-invariant@npm:^1.3.1": version: 1.3.3 resolution: "tiny-invariant@npm:1.3.3" checksum: 10c1/e9baa182794cfa0499134b3e7a886ab8be2e2cd15e070eedb81e2ae8ff379b8ddba146cb25093242c6768d127a93e7f2ce774c7be9a8035c1b34d02c9a5b0a8c languageName: node linkType: hard -"tiny-warning@npm:^1.0.0": - version: 1.0.3 - resolution: "tiny-warning@npm:1.0.3" - checksum: 10c1/b559861279695929d2ed06390b7a455186cb796a5fd3fb8efa45de786b45976dcb46d038f1bd9aaa744abe3c3cbc08e6650ce34fabf419bc50e3150106e78751 - languageName: node - linkType: hard - "tinyglobby@npm:^0.2.7": version: 0.2.12 resolution: "tinyglobby@npm:0.2.12" @@ -16259,13 +16210,6 @@ __metadata: languageName: node linkType: hard -"value-equal@npm:^1.0.1": - version: 1.0.1 - resolution: "value-equal@npm:1.0.1" - checksum: 10c1/90eacdf332d054e39b44e190d72c4975cf3daee09084e16146eb5ae8590683b11a097d552efe9a6bbb3ea56489a06e5ea02c20acba83c1a84e20912a864d7981 - languageName: node - linkType: hard - "vary@npm:~1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2"