Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
nick-delirium
038d8decd5
ui: drop deprecated router api, start moving routing 2025-03-07 14:35:03 +01:00
94 changed files with 893 additions and 1056 deletions

View file

@ -1,5 +1,5 @@
import React, { lazy, Suspense } from 'react'; 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 { Loader } from 'UI';
import withSiteIdUpdater from 'HOCs/withSiteIdUpdater'; import withSiteIdUpdater from 'HOCs/withSiteIdUpdater';
@ -46,13 +46,13 @@ function IFrameRoutes(props: Props) {
<Layout hideHeader={true}> <Layout hideHeader={true}>
<Loader loading={!!loading} className='flex-1'> <Loader loading={!!loading} className='flex-1'>
<Suspense fallback={<Loader loading={true} className='flex-1' />}> <Suspense fallback={<Loader loading={true} className='flex-1' />}>
<Switch key='content'> <Routes key='content'>
<Route exact strict path={withSiteId(SESSION_PATH, siteIdList)} <Route exact strict path={withSiteId(SESSION_PATH, siteIdList)}
component={enhancedComponents.Session} /> component={enhancedComponents.Session} />
<Route exact strict path={withSiteId(LIVE_SESSION_PATH, siteIdList)} <Route exact strict path={withSiteId(LIVE_SESSION_PATH, siteIdList)}
component={enhancedComponents.LiveSession} /> component={enhancedComponents.LiveSession} />
<Route path='*' render={NotFoundPage} /> <Route path='*' render={NotFoundPage} />
</Switch> </Routes>
</Suspense> </Suspense>
</Loader> </Loader>
</Layout> </Layout>

View file

@ -1,6 +1,7 @@
import withSiteIdUpdater from 'HOCs/withSiteIdUpdater'; import withSiteIdUpdater from 'HOCs/withSiteIdUpdater';
import React, { Suspense, lazy } from 'react'; 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 { observer } from 'mobx-react-lite';
import { useStore } from './mstore'; import { useStore } from './mstore';
import { GLOBAL_HAS_NO_RECORDINGS } from 'App/constants/storageKeys'; import { GLOBAL_HAS_NO_RECORDINGS } from 'App/constants/storageKeys';
@ -101,6 +102,7 @@ const HIGHLIGHTS_PATH = routes.highlights();
let debounceSearch: any = () => {}; let debounceSearch: any = () => {};
function PrivateRoutes() { function PrivateRoutes() {
const navigate = useNavigate();
const { projectsStore, userStore, integrationsStore, searchStore } = useStore(); const { projectsStore, userStore, integrationsStore, searchStore } = useStore();
const onboarding = userStore.onboarding; const onboarding = userStore.onboarding;
const scope = userStore.scopeState; const scope = userStore.scopeState;
@ -129,34 +131,52 @@ function PrivateRoutes() {
debounceSearch(); debounceSearch();
}, [searchStore.instance.filters, searchStore.instance.eventsOrder]); }, [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 ( return (
<Suspense fallback={<Loader loading={true} className="flex-1" />}> <Suspense fallback={<Loader loading={true} className="flex-1" />}>
<Switch key="content"> <Routes key="content">
<Route <Route
exact exact
strict strict
path={SCOPE_SETUP} path={SCOPE_SETUP}
component={enhancedComponents.ScopeSetup} element={enhancedComponents.ScopeSetup}
/> />
{redirectToSetup ? <Redirect to={SCOPE_SETUP} /> : null} <Route path={CLIENT_PATH} element={enhancedComponents.Client} />
<Route path={CLIENT_PATH} component={enhancedComponents.Client} />
<Route <Route
path={withSiteId(ONBOARDING_PATH, siteIdList)} path={withSiteId(ONBOARDING_PATH, siteIdList)}
component={enhancedComponents.Onboarding} element={enhancedComponents.Onboarding}
/> />
<Route <Route
exact exact
strict strict
path={SPOTS_LIST_PATH} path={SPOTS_LIST_PATH}
component={enhancedComponents.SpotsList} element={enhancedComponents.SpotsList}
/> />
<Route <Route
exact exact
strict strict
path={SPOT_PATH} path={SPOT_PATH}
component={enhancedComponents.Spot} element={enhancedComponents.Spot}
/> />
{scope === 1 ? <Redirect to={SPOTS_LIST_PATH} /> : null} {scope === 1
? <Route
path="*"
>
<Redirect to={SPOTS_LIST_PATH} />
</Route>
: null
}
<Route <Route
path="/integrations/" path="/integrations/"
render={({ location }) => { render={({ location }) => {
@ -175,111 +195,106 @@ function PrivateRoutes() {
}); });
break; break;
} }
return <Redirect to={CLIENT_PATH} />; return <Route
path="*"
element={<Navigate to={CLIENT_PATH} />}
/>
}} }}
/> />
{redirectToOnboarding && (
<Redirect to={withSiteId(ONBOARDING_REDIRECT_PATH, siteId)} />
)}
{/* DASHBOARD and Metrics */} {/* DASHBOARD and Metrics */}
<Route {[
exact withSiteId(ALERTS_PATH, siteIdList),
strict withSiteId(ALERT_EDIT_PATH, siteIdList),
path={[ withSiteId(ALERT_CREATE_PATH, siteIdList),
withSiteId(ALERTS_PATH, siteIdList), withSiteId(METRICS_PATH, siteIdList),
withSiteId(ALERT_EDIT_PATH, siteIdList), withSiteId(METRICS_DETAILS, siteIdList),
withSiteId(ALERT_CREATE_PATH, siteIdList), withSiteId(METRICS_DETAILS_SUB, siteIdList),
withSiteId(METRICS_PATH, siteIdList), withSiteId(DASHBOARD_PATH, siteIdList),
withSiteId(METRICS_DETAILS, siteIdList), withSiteId(DASHBOARD_SELECT_PATH, siteIdList),
withSiteId(METRICS_DETAILS_SUB, siteIdList), withSiteId(DASHBOARD_METRIC_CREATE_PATH, siteIdList),
withSiteId(DASHBOARD_PATH, siteIdList), withSiteId(DASHBOARD_METRIC_DETAILS_PATH, siteIdList)
withSiteId(DASHBOARD_SELECT_PATH, siteIdList), ].map((path) => (
withSiteId(DASHBOARD_METRIC_CREATE_PATH, siteIdList), <Route
withSiteId(DASHBOARD_METRIC_DETAILS_PATH, siteIdList) path={path}
]} element={enhancedComponents.Dashboard}
component={enhancedComponents.Dashboard} />
/> ))}
<Route <Route
exact exact
strict strict
path={withSiteId(USABILITY_TESTING_PATH, siteIdList)} path={withSiteId(USABILITY_TESTING_PATH, siteIdList)}
component={enhancedComponents.UsabilityTesting} element={enhancedComponents.UsabilityTesting}
/> />
<Route <Route
exact exact
strict strict
path={withSiteId(USABILITY_TESTING_EDIT_PATH, siteIdList)} path={withSiteId(USABILITY_TESTING_EDIT_PATH, siteIdList)}
component={enhancedComponents.UsabilityTestEdit} element={enhancedComponents.UsabilityTestEdit}
/> />
<Route <Route
exact exact
strict strict
path={withSiteId(USABILITY_TESTING_VIEW_PATH, siteIdList)} path={withSiteId(USABILITY_TESTING_VIEW_PATH, siteIdList)}
component={enhancedComponents.UsabilityTestOverview} element={enhancedComponents.UsabilityTestOverview}
/> />
<Route <Route
exact exact
path={withSiteId(MULTIVIEW_INDEX_PATH, siteIdList)} path={withSiteId(MULTIVIEW_INDEX_PATH, siteIdList)}
component={enhancedComponents.Multiview} element={enhancedComponents.Multiview}
/> />
<Route <Route
path={withSiteId(MULTIVIEW_PATH, siteIdList)} path={withSiteId(MULTIVIEW_PATH, siteIdList)}
component={enhancedComponents.Multiview} element={enhancedComponents.Multiview}
/> />
<Route <Route
exact exact
strict strict
path={withSiteId(ASSIST_PATH, siteIdList)} path={withSiteId(ASSIST_PATH, siteIdList)}
component={enhancedComponents.Assist} element={enhancedComponents.Assist}
/> />
<Route <Route
exact exact
strict strict
path={withSiteId(RECORDINGS_PATH, siteIdList)} path={withSiteId(RECORDINGS_PATH, siteIdList)}
component={enhancedComponents.Assist} element={enhancedComponents.Assist}
/> />
<Route <Route
exact exact
strict strict
path={withSiteId(HIGHLIGHTS_PATH, siteIdList)} path={withSiteId(HIGHLIGHTS_PATH, siteIdList)}
component={enhancedComponents.Highlights} element={enhancedComponents.Highlights}
/>
<Route
exact
strict
path={[
withSiteId(SESSIONS_PATH, siteIdList),
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)
]}
component={enhancedComponents.SessionsOverview}
/> />
{[
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) => (
<Route
path={path}
element={<enhancedComponents.SessionsOverview />}
/>
))}
<Route <Route
exact exact
strict strict
path={withSiteId(SESSION_PATH, siteIdList)} path={withSiteId(SESSION_PATH, siteIdList)}
component={enhancedComponents.Session} element={enhancedComponents.Session}
/> />
<Route <Route
exact exact
strict strict
path={withSiteId(LIVE_SESSION_PATH, siteIdList)} path={withSiteId(LIVE_SESSION_PATH, siteIdList)}
component={enhancedComponents.LiveSession} element={enhancedComponents.LiveSession}
/> />
{Object.entries(routes.redirects).map(([fr, to]) => ( {/*<Route path={'*'} element={<Navigate to={withSiteId(routes.sessions(), siteId)} />} />*/}
<Redirect key={fr} exact strict from={fr} to={to} /> </Routes>
))}
<Route path={'*'}>
<Redirect to={withSiteId(routes.sessions(), siteId)} />
</Route>
</Switch>
</Suspense> </Suspense>
); );
} }

View file

@ -1,6 +1,6 @@
import React, { lazy, Suspense, useEffect } from 'react'; import React, { lazy, Suspense, useEffect } from 'react';
import { Loader } from 'UI'; 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 Signup from 'Components/Signup/Signup';
import SupportCallout from 'Shared/SupportCallout'; import SupportCallout from 'Shared/SupportCallout';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
@ -35,13 +35,16 @@ function PublicRoutes() {
return ( return (
<Loader loading={loading} className="flex-1"> <Loader loading={loading} className="flex-1">
<Suspense fallback={<Loader loading={true} className="flex-1" />}> <Suspense fallback={<Loader loading={true} className="flex-1" />}>
<Switch> <Routes>
<Route exact strict path={SPOT_PATH} component={Spot} /> <Route exact strict path={SPOT_PATH} element={<Spot />} />
<Route exact strict path={FORGOT_PASSWORD} component={ForgotPassword} /> <Route exact strict path={FORGOT_PASSWORD} element={<ForgotPassword />} />
<Route exact strict path={LOGIN_PATH} component={Login} /> <Route exact strict path={LOGIN_PATH} element={<Login />} />
<Route exact strict path={SIGNUP_PATH} component={Signup} /> <Route exact strict path={SIGNUP_PATH} element={Signup} />
<Redirect to={LOGIN_PATH} /> <Route
</Switch> path="*"
element={<Navigate to={LOGIN_PATH} replace />}
/>
</Routes>
{!hideSupport && <SupportCallout />} {!hideSupport && <SupportCallout />}
</Suspense> </Suspense>
</Loader> </Loader>

View file

@ -1,6 +1,5 @@
import React, { useEffect, useRef } from 'react'; 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 IFrameRoutes from 'App/IFrameRoutes';
import PrivateRoutes from 'App/PrivateRoutes'; import PrivateRoutes from 'App/PrivateRoutes';
import PublicRoutes from 'App/PublicRoutes'; import PublicRoutes from 'App/PublicRoutes';
@ -19,19 +18,9 @@ import { Loader } from 'UI';
import * as routes from './routes'; import * as routes from './routes';
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
interface RouterProps extends RouteComponentProps { const Router = () => {
match: { const location = useLocation()
params: { const navigate = useNavigate()
siteId: string;
};
};
}
const Router: React.FC<RouterProps> = (props) => {
const {
location,
history,
} = props;
const mstore = useStore(); const mstore = useStore();
const { customFieldStore, projectsStore, sessionStore, searchStore, userStore } = mstore; const { customFieldStore, projectsStore, sessionStore, searchStore, userStore } = mstore;
const jwt = userStore.jwt; const jwt = userStore.jwt;
@ -113,7 +102,7 @@ const Router: React.FC<RouterProps> = (props) => {
) { ) {
const url = new URL(destinationPath, window.location.origin); const url = new URL(destinationPath, window.location.origin);
checkParams(url.search); checkParams(url.search);
history.push(destinationPath); navigate(destinationPath);
localStorage.removeItem(GLOBAL_DESTINATION_PATH); localStorage.removeItem(GLOBAL_DESTINATION_PATH);
} }
}; };
@ -219,4 +208,4 @@ const Router: React.FC<RouterProps> = (props) => {
); );
}; };
export default withRouter(observer(Router)); export default observer(Router);

View file

@ -1,13 +1,11 @@
import React from 'react'; import React from 'react';
import { withRouter } from 'react-router-dom'; import { Routes, Route, Navigate } from 'react-router';
import { Switch, Route, Redirect } from 'react-router';
import { CLIENT_TABS, client as clientRoute } from 'App/routes'; import { CLIENT_TABS, client as clientRoute } from 'App/routes';
import ProfileSettings from './ProfileSettings'; import ProfileSettings from './ProfileSettings';
import Integrations from './Integrations'; import Integrations from './Integrations';
import UserView from './Users/UsersView'; import UserView from './Users/UsersView';
import AuditView from './Audit/AuditView'; import AuditView from './Audit/AuditView';
import Sites from './Sites';
import Projects from './Projects'; import Projects from './Projects';
import CustomFields from './CustomFields'; import CustomFields from './CustomFields';
import Webhooks from './Webhooks'; import Webhooks from './Webhooks';
@ -16,43 +14,27 @@ import Roles from './Roles';
import SessionsListingSettings from 'Components/Client/SessionsListingSettings'; import SessionsListingSettings from 'Components/Client/SessionsListingSettings';
import Modules from 'Components/Client/Modules'; import Modules from 'Components/Client/Modules';
@withRouter function Client() {
export default class Client extends React.PureComponent { return (
constructor(props) { <div className='w-full mx-auto mb-8' style={{ maxWidth: '1360px' }}>
super(props); <Routes>
} <Route exact strict path={clientRoute(CLIENT_TABS.PROFILE)} component={ProfileSettings} />
<Route exact strict path={clientRoute(CLIENT_TABS.SESSIONS_LISTING)} component={SessionsListingSettings} />
setTab = (tab) => { <Route exact strict path={clientRoute(CLIENT_TABS.INTEGRATIONS)} component={Integrations} />
this.props.history.push(clientRoute(tab)); <Route exact strict path={clientRoute(CLIENT_TABS.MANAGE_USERS)} component={UserView} />
}; <Route exact strict path={clientRoute(CLIENT_TABS.SITES)} component={Projects} />
<Route exact strict path={clientRoute(CLIENT_TABS.CUSTOM_FIELDS)} component={CustomFields} />
renderActiveTab = () => ( <Route exact strict path={clientRoute(CLIENT_TABS.WEBHOOKS)} component={Webhooks} />
<Switch> <Route exact strict path={clientRoute(CLIENT_TABS.NOTIFICATIONS)} component={Notifications} />
<Route exact strict path={clientRoute(CLIENT_TABS.PROFILE)} component={ProfileSettings} /> <Route exact strict path={clientRoute(CLIENT_TABS.MANAGE_ROLES)} component={Roles} />
<Route exact strict path={clientRoute(CLIENT_TABS.SESSIONS_LISTING)} component={SessionsListingSettings} /> <Route exact strict path={clientRoute(CLIENT_TABS.AUDIT)} component={AuditView} />
<Route exact strict path={clientRoute(CLIENT_TABS.INTEGRATIONS)} component={Integrations} /> <Route exact strict path={clientRoute(CLIENT_TABS.MODULES)} component={Modules} />
<Route exact strict path={clientRoute(CLIENT_TABS.MANAGE_USERS)} component={UserView} /> <Route path={'*'}>
<Route exact strict path={clientRoute(CLIENT_TABS.SITES)} component={Projects} /> <Navigate to={clientRoute(CLIENT_TABS.PROFILE)} />
<Route exact strict path={clientRoute(CLIENT_TABS.CUSTOM_FIELDS)} component={CustomFields} /> </Route>
<Route exact strict path={clientRoute(CLIENT_TABS.WEBHOOKS)} component={Webhooks} /> </Routes>
<Route exact strict path={clientRoute(CLIENT_TABS.NOTIFICATIONS)} component={Notifications} /> </div>
<Route exact strict path={clientRoute(CLIENT_TABS.MANAGE_ROLES)} component={Roles} /> )
<Route exact strict path={clientRoute(CLIENT_TABS.AUDIT)} component={AuditView} />
<Route exact strict path={clientRoute(CLIENT_TABS.MODULES)} component={Modules} />
<Redirect to={clientRoute(CLIENT_TABS.PROFILE)} />
</Switch>
);
render() {
const {
match: {
params: { activeTab }
}
} = this.props;
return (
<div className='w-full mx-auto mb-8' style={{ maxWidth: '1360px' }}>
{activeTab && this.renderActiveTab()}
</div>
);
}
} }
export default Client;

View file

@ -1,8 +1,8 @@
import React from 'react'; 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 ProjectList from 'Components/Client/Projects/ProjectList';
import ProjectTabs from 'Components/Client/Projects/ProjectTabs'; import ProjectTabs from 'Components/Client/Projects/ProjectTabs';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import { useStore } from '@/mstore'; import { useStore } from '@/mstore';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import {PlusOutlined, KeyOutlined} from '@ant-design/icons' import {PlusOutlined, KeyOutlined} from '@ant-design/icons'
@ -13,12 +13,12 @@ import Project from '@/mstore/types/project';
function Projects() { function Projects() {
const { projectsStore, customFieldStore } = useStore(); const { projectsStore, customFieldStore } = useStore();
const history = useHistory(); const navigate = useNavigate();
const { project, pid, tab } = projectsStore.config; const { project, pid, tab } = projectsStore.config;
const { openModal, closeModal } = useModal(); const { openModal, closeModal } = useModal();
React.useEffect(() => { React.useEffect(() => {
const params = new URLSearchParams(history.location.search); const params = new URLSearchParams(location.search);
const pid = params.get('pid'); const pid = params.get('pid');
const tab = params.get('tab'); const tab = params.get('tab');
projectsStore.setConfigProject(pid ? parseInt(pid) : undefined); projectsStore.setConfigProject(pid ? parseInt(pid) : undefined);
@ -30,7 +30,7 @@ function Projects() {
}, []); }, []);
React.useEffect(() => { React.useEffect(() => {
const params = new URLSearchParams(history.location.search); const params = new URLSearchParams(location.search);
if (projectsStore.config.pid) { if (projectsStore.config.pid) {
params.set('pid', projectsStore.config.pid + ''); params.set('pid', projectsStore.config.pid + '');
} }
@ -38,7 +38,8 @@ function Projects() {
if (projectsStore.config.tab) { if (projectsStore.config.tab) {
params.set('tab', projectsStore.config.tab); params.set('tab', projectsStore.config.tab);
} }
history.push({ search: params.toString() }); const search = params.toString();
navigate(location.pathname + '?' + search);
}, [pid, tab]); }, [pid, tab]);
const createProject = () => { const createProject = () => {

View file

@ -1,6 +1,6 @@
import { Segmented } from 'antd'; import { Segmented } from 'antd';
import React, { ChangeEvent, FormEvent, useEffect, useState } from 'react'; 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 { toast } from 'react-toastify';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { confirm, Form, Icon, Input } from 'UI'; import { confirm, Form, Icon, Input } from 'UI';
@ -13,9 +13,10 @@ type OwnProps = {
onClose: (arg: any) => void; 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 mstore = useStore();
const { projectsStore } = mstore; const { projectsStore } = mstore;
const activeSiteId = projectsStore.active?.id; const activeSiteId = projectsStore.active?.id;
@ -170,4 +171,4 @@ const NewSiteForm = ({ location: { pathname }, onClose }: Props) => {
); );
}; };
export default withRouter(observer(NewSiteForm)); export default observer(NewSiteForm);

View file

@ -1,20 +1,15 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { withRouter, RouteComponentProps } from 'react-router-dom'; import { useNavigate, useParams } from "react-router";
import { Loader } from 'UI'; import { Loader } from 'UI';
import { withSiteId, dashboard, metrics } from "App/routes"; import { withSiteId, dashboard, metrics } from "App/routes";
import DashboardRouter from './components/DashboardRouter'; import DashboardRouter from './components/DashboardRouter';
import withPermissions from 'HOCs/withPermissions'; import withPermissions from 'HOCs/withPermissions';
interface RouterProps { function NewDashboard() {
siteId: string; const { siteId, dashboardId } = useParams()
dashboardId: string; const navigate = useNavigate();
metricId: string;
}
function NewDashboard(props: RouteComponentProps<RouterProps>) {
const { history, match: { params: { siteId, dashboardId } } } = props;
const { dashboardStore } = useStore(); const { dashboardStore } = useStore();
const initId = React.useRef(siteId) const initId = React.useRef(siteId)
const loading = dashboardStore.isLoading; const loading = dashboardStore.isLoading;
@ -23,10 +18,10 @@ function NewDashboard(props: RouteComponentProps<RouterProps>) {
useEffect(() => { useEffect(() => {
if (siteId !== initId.current) { if (siteId !== initId.current) {
if (isMetricListMetric) { if (isMetricListMetric) {
history.push(withSiteId(metrics(), siteId)) navigate(withSiteId(metrics(), siteId))
} }
if (isDbMetric) { if (isDbMetric) {
history.push(withSiteId(dashboard(), siteId)) navigate(withSiteId(dashboard(), siteId))
} }
} }
dashboardStore.fetchList().then((resp) => { dashboardStore.fetchList().then((resp) => {
@ -43,4 +38,4 @@ function NewDashboard(props: RouteComponentProps<RouterProps>) {
); );
} }
export default withRouter(withPermissions(['METRICS'])(observer(NewDashboard))); export default withPermissions(['METRICS'])(observer(NewDashboard))

View file

@ -1,7 +1,7 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { Pagination, NoContent, Icon } from "UI"; import { Pagination, NoContent, Icon } from "UI";
import ErrorListItem from "App/components/Dashboard/components/Errors/ErrorListItem"; 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 { useModal } from "App/components/Modal";
import ErrorDetailsModal from "App/components/Dashboard/components/Errors/ErrorDetailsModal"; import ErrorDetailsModal from "App/components/Dashboard/components/Errors/ErrorDetailsModal";
@ -9,19 +9,18 @@ interface Props {
metric: any; metric: any;
data: any; data: any;
isEdit: any; isEdit: any;
history: any;
location: any;
} }
function CustomMetricTableErrors(props: RouteComponentProps & Props) { function CustomMetricTableErrors(props: Props) {
const { metric, data } = 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 { showModal, hideModal } = useModal();
const onErrorClick = (e: any, error: any) => { const onErrorClick = (e: any, error: any) => {
e.stopPropagation(); e.stopPropagation();
props.history.replace({ const search = new URLSearchParams({ errorId: error.errorId }).toString()
search: new URLSearchParams({ errorId: error.errorId }).toString(), navigate(location.pathname + "?" + search, { replace: true });
});
}; };
useEffect(() => { useEffect(() => {
@ -31,8 +30,8 @@ function CustomMetricTableErrors(props: RouteComponentProps & Props) {
right: true, right: true,
width: 1200, width: 1200,
onClose: () => { onClose: () => {
if (props.history.location.pathname.includes("/dashboard") || props.history.location.pathname.includes("/metrics/")) { if (location.pathname.includes("/dashboard") || location.pathname.includes("/metrics/")) {
props.history.replace({ search: "" }); navigate(location.pathname, { replace: true });
} }
}, },
}); });
@ -60,7 +59,6 @@ function CustomMetricTableErrors(props: RouteComponentProps & Props) {
</div> </div>
))} ))}
{/*{isEdit && (*/}
<div className="my-6 flex items-center justify-center"> <div className="my-6 flex items-center justify-center">
<Pagination <Pagination
page={metric.page} page={metric.page}
@ -72,25 +70,9 @@ function CustomMetricTableErrors(props: RouteComponentProps & Props) {
debounceRequest={500} debounceRequest={500}
/> />
</div> </div>
{/*)}*/}
{/*{!isEdit && (*/}
{/* <ViewMore total={data.total} limit={5} />*/}
{/*)}*/}
</div> </div>
</NoContent> </NoContent>
); );
} }
export default withRouter<Props & RouteComponentProps, React.FunctionComponent>(CustomMetricTableErrors); export default CustomMetricTableErrors;
const ViewMore = ({ total, limit }: any) =>
total > limit && (
<div className="mt-4 flex items-center justify-center cursor-pointer w-fit mx-auto">
<div className="text-center">
<div className="color-teal text-lg">
All <span className="font-medium">{total}</span> errors
</div>
</div>
</div>
);

View file

@ -25,7 +25,7 @@ import {
USER_PATH, USER_PATH,
CATEGORIES, CATEGORIES,
} from 'App/constants/card'; } from 'App/constants/card';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import { dashboardMetricCreate, withSiteId, metricCreate } from 'App/routes'; import { dashboardMetricCreate, withSiteId, metricCreate } from 'App/routes';
import { FilterKey } from 'Types/filter/filterType'; import { FilterKey } from 'Types/filter/filterType';
import MetricsLibraryModal from '../MetricsLibraryModal/MetricsLibraryModal'; import MetricsLibraryModal from '../MetricsLibraryModal/MetricsLibraryModal';
@ -183,16 +183,16 @@ function CategoryTab({
}) { }) {
const items = isMobile ? mobileTabItems[tab] : tabItems[tab]; const items = isMobile ? mobileTabItems[tab] : tabItems[tab];
const { projectsStore, dashboardStore } = useStore(); const { projectsStore, dashboardStore } = useStore();
const history = useHistory(); const navigate = useNavigate();
const handleCardSelection = (card: string) => { const handleCardSelection = (card: string) => {
if (projectsStore.activeSiteId) { if (projectsStore.activeSiteId) {
if (inCards) { if (inCards) {
history.push( navigate(
withSiteId(metricCreate(), projectsStore.activeSiteId) + `?mk=${card}` withSiteId(metricCreate(), projectsStore.activeSiteId) + `?mk=${card}`
); );
} else if (dashboardStore.selectedDashboard) { } else if (dashboardStore.selectedDashboard) {
history.push( navigate(
withSiteId( withSiteId(
dashboardMetricCreate(dashboardStore.selectedDashboard.dashboardId), dashboardMetricCreate(dashboardStore.selectedDashboard.dashboardId),
projectsStore.activeSiteId projectsStore.activeSiteId

View file

@ -6,10 +6,10 @@ import { withSiteId, alertEdit } from 'App/routes';
import { numberWithCommas } from 'App/utils'; import { numberWithCommas } from 'App/utils';
// @ts-ignore // @ts-ignore
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import cn from 'classnames'; import cn from 'classnames';
import Alert from 'Types/alert'; import Alert from 'Types/alert';
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import { useNavigate } from 'react-router';
const getThreshold = (threshold: number) => { const getThreshold = (threshold: number) => {
if (threshold === 15) return '15 Minutes'; if (threshold === 15) return '15 Minutes';
@ -84,7 +84,8 @@ interface Props extends RouteComponentProps {
} }
function AlertListItem(props: Props) { 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) { if (!alert) {
return null; return null;
@ -94,7 +95,7 @@ function AlertListItem(props: Props) {
if (demo) return; if (demo) return;
const path = withSiteId(alertEdit(alert.alertId), siteId); const path = withSiteId(alertEdit(alert.alertId), siteId);
init(alert || {}); init(alert || {});
history.push(path); navigate(path);
}; };
const formTriggerName = () => const formTriggerName = () =>
@ -169,4 +170,4 @@ function AlertListItem(props: Props) {
); );
} }
export default withRouter(observer(AlertListItem)); export default observer(AlertListItem);

View file

@ -1,5 +1,5 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { PageTitle, Icon, Link } from 'UI'; import { PageTitle, Link } from 'UI';
import { Button } from 'antd'; import { Button } from 'antd';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import withPageTitle from 'HOCs/withPageTitle'; import withPageTitle from 'HOCs/withPageTitle';
@ -7,7 +7,7 @@ import { withSiteId, alertCreate } from 'App/routes';
import AlertsList from './AlertsList'; import AlertsList from './AlertsList';
import AlertsSearch from './AlertsSearch'; import AlertsSearch from './AlertsSearch';
import { useHistory } from 'react-router'; import { useLocation } from 'react-router';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
interface IAlertsView { interface IAlertsView {
@ -15,18 +15,16 @@ interface IAlertsView {
} }
function AlertsView({ siteId }: IAlertsView) { function AlertsView({ siteId }: IAlertsView) {
const history = useHistory(); const location = useLocation();
const { alertsStore } = useStore(); const { alertsStore } = useStore();
useEffect(() => { useEffect(() => {
const unmount = history.listen((location) => { return () => {
if (!location.pathname.includes('/alert')) { if (!location.pathname.includes('/alert')) {
alertsStore.updateKey('page', 1); alertsStore.updateKey('page', 1);
} }
}); }
return unmount; }, [location.pathname]);
}, [history]);
return ( return (
<div style={{ maxWidth: '1360px', margin: 'auto'}} className="bg-white rounded-lg shadow-sm py-4 border"> <div style={{ maxWidth: '1360px', margin: 'auto'}} className="bg-white rounded-lg shadow-sm py-4 border">
<div className="flex items-center mb-4 justify-between px-6"> <div className="flex items-center mb-4 justify-between px-6">

View file

@ -6,7 +6,6 @@ import { toast } from 'react-toastify';
import { SLACK, WEBHOOK, TEAMS } from 'App/constants/schedule'; import { SLACK, WEBHOOK, TEAMS } from 'App/constants/schedule';
import Breadcrumb from 'Shared/Breadcrumb'; import Breadcrumb from 'Shared/Breadcrumb';
import { withSiteId, alerts } from 'App/routes'; import { withSiteId, alerts } from 'App/routes';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { useStore } from 'App/mstore' import { useStore } from 'App/mstore'
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import Alert from 'Types/alert' import Alert from 'Types/alert'
@ -16,6 +15,7 @@ import BottomButtons from './AlertForm/BottomButtons';
import NotifyHooks from './AlertForm/NotifyHooks'; import NotifyHooks from './AlertForm/NotifyHooks';
import AlertListItem from './AlertListItem'; import AlertListItem from './AlertListItem';
import Condition from './AlertForm/Condition'; import Condition from './AlertForm/Condition';
import { useNavigate } from "react-router";
const Circle = ({ text }: { text: string }) => ( const Circle = ({ text }: { text: string }) => (
<div <div
@ -52,7 +52,7 @@ interface Select {
value: string | number value: string | number
} }
interface IProps extends RouteComponentProps { interface IProps {
siteId: string; siteId: string;
slackChannels: any[]; slackChannels: any[];
loading: boolean; loading: boolean;
@ -63,7 +63,9 @@ interface IProps extends RouteComponentProps {
} }
const NewAlert = (props: IProps) => { const NewAlert = (props: IProps) => {
const { alertsStore, settingsStore } = useStore(); const navigate = useNavigate();
const { alertsStore, settingsStore, projectsStore } = useStore();
const siteId = projectsStore.activeSiteId
const { const {
fetchTriggerOptions, fetchTriggerOptions,
init, init,
@ -80,9 +82,6 @@ const NewAlert = (props: IProps) => {
const deleting = loading const deleting = loading
const webhooks = settingsStore.webhooks const webhooks = settingsStore.webhooks
const fetchWebhooks = settingsStore.fetchWebhooks const fetchWebhooks = settingsStore.fetchWebhooks
const {
siteId,
} = props;
useEffect(() => { useEffect(() => {
init({}); init({});
@ -120,7 +119,7 @@ const NewAlert = (props: IProps) => {
}) })
) { ) {
remove(instance.alertId).then(() => { remove(instance.alertId).then(() => {
props.history.push(withSiteId(alerts(), siteId)); navigate(withSiteId(alerts(), siteId));
toast.success('Alert deleted'); toast.success('Alert deleted');
}).catch(() => { }).catch(() => {
toast.error('Failed to delete an alert'); toast.error('Failed to delete an alert');
@ -133,7 +132,7 @@ const NewAlert = (props: IProps) => {
save(instance).then(() => { save(instance).then(() => {
if (!wasUpdating) { if (!wasUpdating) {
toast.success('New alert saved'); toast.success('New alert saved');
props.history.push(withSiteId(alerts(), siteId)); navigate(withSiteId(alerts(), siteId));
} else { } else {
toast.success('Alert updated'); toast.success('Alert updated');
} }
@ -296,4 +295,4 @@ const NewAlert = (props: IProps) => {
); );
}; };
export default withRouter(observer(NewAlert)) export default observer(NewAlert)

View file

@ -1,21 +1,19 @@
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { RouteComponentProps, withRouter } from 'react-router';
import { Loader, Pagination } from 'UI'; import { Loader, Pagination } from 'UI';
import { Button } from 'antd' import { Button } from 'antd'
import SessionsModal from './SessionsModal'; import SessionsModal from './SessionsModal';
import CardUserItem from './CardUserItem'; import CardUserItem from './CardUserItem';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { useNavigate, useLocation } from 'react-router';
interface Props { function CardUserList() {
history: any; const navigate = useNavigate();
location: any; const location = useLocation();
}
function CardUserList(props: RouteComponentProps<Props>) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { showModal } = useModal(); 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 { metricStore, dashboardStore } = useStore();
const [data, setData] = useState<any>([ const [data, setData] = useState<any>([
@ -27,7 +25,8 @@ function CardUserList(props: RouteComponentProps<Props>) {
const pageSize = data.length; const pageSize = data.length;
const handleClick = (issue: any) => { 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(<SessionsModal list={[]} />, { right: true, width: 450 }) // showModal(<SessionsModal list={[]} />, { right: true, width: 450 })
} }
@ -35,8 +34,8 @@ function CardUserList(props: RouteComponentProps<Props>) {
if (!userId) return; if (!userId) return;
showModal(<SessionsModal userId={userId} name="test" hash="test" />, { right: true, width: 600, onClose: () => { showModal(<SessionsModal userId={userId} name="test" hash="test" />, { right: true, width: 600, onClose: () => {
if (props.history.location.pathname.includes("/metric")) { if (location.pathname.includes("/metric")) {
props.history.replace({search: ""}); navigate(location.pathname, { replace: true });
} }
}}); }});
}, [userId]); }, [userId]);
@ -75,4 +74,4 @@ function CardUserList(props: RouteComponentProps<Props>) {
); );
} }
export default withRouter(observer(CardUserList)); export default observer(CardUserList);

View file

@ -2,7 +2,7 @@ import React from 'react';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import { Button } from 'antd'; import { Button } from 'antd';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
interface Props { interface Props {
disabled?: boolean; disabled?: boolean;
@ -12,7 +12,7 @@ function CreateDashboardButton({ disabled }: Props) {
const [dashboardCreating, setDashboardCreating] = React.useState(false); const [dashboardCreating, setDashboardCreating] = React.useState(false);
const { projectsStore, dashboardStore } = useStore(); const { projectsStore, dashboardStore } = useStore();
const siteId = projectsStore.siteId; const siteId = projectsStore.siteId;
const history = useHistory(); const navigate = useNavigate();
const createNewDashboard = async () => { const createNewDashboard = async () => {
setDashboardCreating(true); setDashboardCreating(true);
@ -21,7 +21,7 @@ function CreateDashboardButton({ disabled }: Props) {
.save(dashboardStore.dashboardInstance) .save(dashboardStore.dashboardInstance)
.then(async (syncedDashboard) => { .then(async (syncedDashboard) => {
dashboardStore.selectDashboardById(syncedDashboard.dashboardId); dashboardStore.selectDashboardById(syncedDashboard.dashboardId);
history.push(`/${siteId}/dashboard/${syncedDashboard.dashboardId}`); navigate(`/${siteId}/dashboard/${syncedDashboard.dashboardId}`);
}) })
.finally(() => { .finally(() => {
setDashboardCreating(false); setDashboardCreating(false);

View file

@ -2,23 +2,21 @@ import React from 'react';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { SideMenuitem, Icon } from 'UI'; import { SideMenuitem, Icon } from 'UI';
import { withSiteId, dashboardSelected } from 'App/routes'; import { withSiteId, dashboardSelected } from 'App/routes';
import { withRouter } from 'react-router-dom'; import { useNavigate } from 'react-router';
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
interface Props { function DashbaordListModal() {
siteId: string const navigate = useNavigate();
history: any const { dashboardStore, projectsStore } = useStore();
} const siteId = projectsStore.activeSiteId;
function DashbaordListModal(props: Props) {
const { dashboardStore } = useStore();
const { hideModal } = useModal(); const { hideModal } = useModal();
const dashboards = dashboardStore.dashboards; const dashboards = dashboardStore.dashboards;
const activeDashboardId = dashboardStore.selectedDashboard?.dashboardId; const activeDashboardId = dashboardStore.selectedDashboard?.dashboardId;
const onItemClick = (dashboard) => { const onItemClick = (dashboard) => {
dashboardStore.selectDashboardById(dashboard.dashboardId); dashboardStore.selectDashboardById(dashboard.dashboardId);
const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(props.siteId)); const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId));
props.history.push(path); navigate(path);
hideModal(); hideModal();
}; };
return ( return (
@ -46,4 +44,4 @@ function DashbaordListModal(props: Props) {
); );
} }
export default withRouter(DashbaordListModal); export default DashbaordListModal;

View file

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import BackButton from 'Shared/Breadcrumb/BackButton'; import BackButton from 'Shared/Breadcrumb/BackButton';
import { withSiteId } from 'App/routes'; import { withSiteId } from 'App/routes';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { PageTitle, confirm } from 'UI'; import { PageTitle, confirm } from 'UI';
import { Tooltip, Popover, Button } from 'antd'; import { Tooltip, Popover, Button } from 'antd';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
@ -12,21 +11,23 @@ import withModal from 'App/components/Modal/withModal';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import DashboardEditModal from '../DashboardEditModal'; import DashboardEditModal from '../DashboardEditModal';
import AddCardSection from '../AddCardSection/AddCardSection'; import AddCardSection from '../AddCardSection/AddCardSection';
import { useNavigate } from "react-router";
interface IProps { interface IProps {
siteId: string; siteId: string;
renderReport?: any; renderReport?: any;
} }
type Props = IProps & RouteComponentProps; type Props = IProps;
function DashboardHeader(props: Props) { function DashboardHeader(props: Props) {
const { siteId } = props; const navigate = useNavigate();
const [popoverOpen, setPopoverOpen] = React.useState(false); const [popoverOpen, setPopoverOpen] = React.useState(false);
const handleOpenChange = (open: boolean) => { const handleOpenChange = (open: boolean) => {
setPopoverOpen(open); setPopoverOpen(open);
}; };
const { dashboardStore } = useStore(); const { dashboardStore, projectsStore } = useStore();
const siteId = projectsStore.activeSiteId
const [focusTitle, setFocusedInput] = React.useState(true); const [focusTitle, setFocusedInput] = React.useState(true);
const [showEditModal, setShowEditModal] = React.useState(false); const [showEditModal, setShowEditModal] = React.useState(false);
const period = dashboardStore.period; const period = dashboardStore.period;
@ -48,7 +49,7 @@ function DashboardHeader(props: Props) {
}) })
) { ) {
dashboardStore.deleteDashboard(dashboard).then(() => { 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));

View file

@ -1,6 +1,6 @@
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { import {
Empty, Empty,
Switch, Switch,
@ -32,7 +32,7 @@ function DashboardList() {
const list = dashboardStore.filteredList; const list = dashboardStore.filteredList;
const dashboardsSearch = dashboardStore.filter.query; const dashboardsSearch = dashboardStore.filter.query;
const history = useHistory(); const navigate = useNavigate();
// Define custom width and height for each scenario // Define custom width and height for each scenario
const searchImageDimensions = { width: 60, height: 'auto' }; const searchImageDimensions = { width: 60, height: 'auto' };
@ -250,7 +250,7 @@ function DashboardList() {
dashboardSelected(record.dashboardId), dashboardSelected(record.dashboardId),
siteId siteId
); );
history.push(path); navigate(path);
}, },
})} })}
/> />

View file

@ -3,7 +3,7 @@ import withPageTitle from 'HOCs/withPageTitle';
import DashboardList from './DashboardList'; import DashboardList from './DashboardList';
import Header from './Header'; import Header from './Header';
function DashboardsView({history, siteId}: { history: any; siteId: string }) { function DashboardsView() {
return ( return (
<div style={{maxWidth: '1360px', margin: 'auto'}} className="bg-white rounded-lg py-4 border shadow-sm"> <div style={{maxWidth: '1360px', margin: 'auto'}} className="bg-white rounded-lg py-4 border shadow-sm">
<Header /> <Header />

View file

@ -1,108 +1,96 @@
import React from 'react'; import React from 'react';
import {Button, Space} from "antd"; import { Button, Space } from 'antd';
import {ArrowLeft, ArrowRight} from "lucide-react"; import { ArrowLeft, ArrowRight } from 'lucide-react';
import CardBuilder from "Components/Dashboard/components/WidgetForm/CardBuilder"; import { useNavigate, useLocation } from 'react-router';
import {useHistory} from "react-router"; import { useStore } from 'App/mstore';
import {useStore} from "App/mstore"; import { HEATMAP } from 'App/constants/card';
import { HEATMAP } from "App/constants/card"; import { renderClickmapThumbnail } from 'Components/Dashboard/components/WidgetForm/renderMap';
import {renderClickmapThumbnail} from "Components/Dashboard/components/WidgetForm/renderMap"; import WidgetPreview from 'Components/Dashboard/components/WidgetPreview/WidgetPreview';
import WidgetPreview from "Components/Dashboard/components/WidgetPreview/WidgetPreview";
import WidgetFormNew from 'Components/Dashboard/components/WidgetForm/WidgetFormNew'; import WidgetFormNew from 'Components/Dashboard/components/WidgetForm/WidgetFormNew';
const getTitleByType = (type: string) => {
switch (type) {
case HEATMAP:
return 'Heatmap';
default:
return 'Trend Single';
}
}
interface Props { interface Props {
// cardType: string, onBack?: () => void;
onBack?: () => void onAdded?: () => void;
onAdded?: () => void extra?: React.ReactNode;
extra?: React.ReactNode
} }
function CreateCard(props: Props) { function CreateCard(props: Props) {
const history = useHistory(); const location = useLocation();
const {metricStore, dashboardStore, aiFiltersStore} = useStore(); const navigate = useNavigate();
const metric = metricStore.instance; const { metricStore, dashboardStore } = useStore();
const siteId: string = history.location.pathname.split('/')[1]; const metric = metricStore.instance;
const dashboardId: string = history.location.pathname.split('/')[3]; const dashboardId: string = location.pathname.split('/')[3];
const isItDashboard = history.location.pathname.includes('dashboard') const isItDashboard = location.pathname.includes('dashboard');
// const title = getTitleByType(metric.metricType)
const createNewDashboard = async () => { const createNewDashboard = async () => {
dashboardStore.initDashboard(); dashboardStore.initDashboard();
return await dashboardStore return await dashboardStore
.save(dashboardStore.dashboardInstance) .save(dashboardStore.dashboardInstance)
.then(async (syncedDashboard) => { .then(async (syncedDashboard) => {
dashboardStore.selectDashboardById(syncedDashboard.dashboardId); dashboardStore.selectDashboardById(syncedDashboard.dashboardId);
return syncedDashboard.dashboardId; return syncedDashboard.dashboardId;
}); });
} };
const addCardToDashboard = async (dashboardId: string, metricId: string) => { const addCardToDashboard = async (dashboardId: string, metricId: string) => {
return dashboardStore.addWidgetToDashboard( return dashboardStore.addWidgetToDashboard(
dashboardStore.getDashboard(parseInt(dashboardId, 10))!, [metricId] 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 (
<div className="flex gap-4 flex-col">
<div className="flex items-center justify-between">
<Space>
{props.onBack ? <Button type="text" onClick={props.onBack}>
<ArrowLeft size={16} />
</Button> : null}
<div className="text-xl leading-4 font-medium">
{metric.name}
</div>
</Space>
<Button type="primary" onClick={createDashboardAndAddCard}>
<Space>
Create <ArrowRight size={14}/>
</Space>
</Button>
</div>
{props.extra}
{/*<CardBuilder siteId={siteId}/>*/}
<WidgetFormNew/>
<WidgetPreview className="" name={metric.name} isEditing={true}/>
</div>
); );
};
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 (
<div className="flex gap-4 flex-col">
<div className="flex items-center justify-between">
<Space>
{props.onBack ? (
<Button type="text" onClick={props.onBack}>
<ArrowLeft size={16} />
</Button>
) : null}
<div className="text-xl leading-4 font-medium">{metric.name}</div>
</Space>
<Button type="primary" onClick={createDashboardAndAddCard}>
<Space>
Create <ArrowRight size={14} />
</Space>
</Button>
</div>
{props.extra}
<WidgetFormNew />
<WidgetPreview className="" name={metric.name} isEditing={true} />
</div>
);
} }
export default CreateCard; export default CreateCard;

View file

@ -1,13 +1,13 @@
import React, { useMemo, useState, useEffect } from 'react'; import React, { useMemo, useState, useEffect } from 'react';
import { Button, Input, Segmented, Space } from 'antd'; import { Button, Input, Segmented, Space } from 'antd';
import { RightOutlined } from '@ant-design/icons'; 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 { CARD_LIST, CARD_CATEGORIES, CardType } from './ExampleCards';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import Option from './Option'; import Option from './Option';
import CardsLibrary from 'Components/Dashboard/components/DashboardList/NewDashModal/CardsLibrary'; import CardsLibrary from 'Components/Dashboard/components/DashboardList/NewDashModal/CardsLibrary';
import { FUNNEL } from 'App/constants/card'; import { FUNNEL } from 'App/constants/card';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { FilterKey } from 'Types/filter/filterType'; import { FilterKey } from 'Types/filter/filterType';
import FilterSeries from '@/mstore/types/filterSeries'; import FilterSeries from '@/mstore/types/filterSeries';
@ -32,7 +32,7 @@ const SelectCard: React.FC<SelectCardProps> = (props: SelectCardProps) => {
const isCreatingDashboard = !dashboardId && location.pathname.includes('dashboard'); const isCreatingDashboard = !dashboardId && location.pathname.includes('dashboard');
const [dashboardCreating, setDashboardCreating] = useState<boolean>(false); const [dashboardCreating, setDashboardCreating] = useState<boolean>(false);
const [dashboardUpdating, setDashboardUpdating] = useState<boolean>(false); const [dashboardUpdating, setDashboardUpdating] = useState<boolean>(false);
const history = useHistory(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
if (dashboardId) { if (dashboardId) {
@ -49,8 +49,7 @@ const SelectCard: React.FC<SelectCardProps> = (props: SelectCardProps) => {
.save(dashboardStore.dashboardInstance) .save(dashboardStore.dashboardInstance)
.then(async (syncedDashboard) => { .then(async (syncedDashboard) => {
dashboardStore.selectDashboardById(syncedDashboard.dashboardId); dashboardStore.selectDashboardById(syncedDashboard.dashboardId);
history.push(`/${siteId}/dashboard/${syncedDashboard.dashboardId}`); navigate(`/${siteId}/dashboard/${syncedDashboard.dashboardId}`);
//return syncedDashboard.dashboardId;
}).finally(() => { }).finally(() => {
setDashboardCreating(false); setDashboardCreating(false);
}); });

View file

@ -3,20 +3,20 @@ import { useObserver } from 'mobx-react-lite';
import DashboardMetricSelection from '../DashboardMetricSelection'; import DashboardMetricSelection from '../DashboardMetricSelection';
import DashboardForm from '../DashboardForm'; import DashboardForm from '../DashboardForm';
import { Button } from 'antd'; import { Button } from 'antd';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
import { dashboardMetricCreate, withSiteId } from 'App/routes'; import { dashboardMetricCreate, withSiteId } from 'App/routes';
import { useNavigate } from "react-router";
interface Props extends RouteComponentProps { interface Props {
history: any
siteId?: string
dashboardId?: string dashboardId?: string
onMetricAdd?: () => void; onMetricAdd?: () => void;
} }
function DashboardModal(props: Props) { function DashboardModal(props: Props) {
const { history, siteId, dashboardId } = props; const navigate = useNavigate();
const { dashboardStore } = useStore(); const { dashboardId } = props;
const { dashboardStore, projectsStore } = useStore();
const siteId = projectsStore.activeSiteId
const selectedWidgetsCount = useObserver(() => dashboardStore.selectedWidgets.length); const selectedWidgetsCount = useObserver(() => dashboardStore.selectedWidgets.length);
const { hideModal } = useModal(); const { hideModal } = useModal();
const dashboard = useObserver(() => dashboardStore.dashboardInstance); const dashboard = useObserver(() => dashboardStore.dashboardInstance);
@ -28,7 +28,7 @@ function DashboardModal(props: Props) {
await dashboardStore.fetch(dashboard.dashboardId) await dashboardStore.fetch(dashboard.dashboardId)
} }
dashboardStore.selectDashboardById(syncedDashboard.dashboardId); dashboardStore.selectDashboardById(syncedDashboard.dashboardId);
history.push(withSiteId(`/dashboard/${syncedDashboard.dashboardId}`, siteId)) navigate(withSiteId(`/dashboard/${syncedDashboard.dashboardId}`, siteId))
}) })
.then(hideModal) .then(hideModal)
} }
@ -36,7 +36,7 @@ function DashboardModal(props: Props) {
const handleCreateNew = () => { const handleCreateNew = () => {
const path = withSiteId(dashboardMetricCreate(dashboardId), siteId); const path = withSiteId(dashboardMetricCreate(dashboardId), siteId);
props.onMetricAdd(); props.onMetricAdd();
history.push(path); navigate(path);
hideModal(); hideModal();
} }
const isDashboardExists = dashboard.exists() const isDashboardExists = dashboard.exists()
@ -81,4 +81,4 @@ function DashboardModal(props: Props) {
)); ));
} }
export default withRouter(DashboardModal); export default DashboardModal;

View file

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { Switch, Route } from 'react-router'; import { Routes, Route } from 'react-router';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { import {
metrics, metrics,
@ -22,26 +21,18 @@ import WidgetSubDetailsView from '../WidgetSubDetailsView';
import DashboardsView from '../DashboardList'; import DashboardsView from '../DashboardList';
import Alerts from '../Alerts'; import Alerts from '../Alerts';
import CreateAlert from '../Alerts/NewAlert' import CreateAlert from '../Alerts/NewAlert'
import { useParams, useNavigate } from "react-router";
function DashboardViewSelected({ siteId, dashboardId }: { siteId: string; dashboardId: string }) { function DashboardViewSelected({ siteId, dashboardId }: { siteId: string; dashboardId: string }) {
return <DashboardView siteId={siteId} dashboardId={dashboardId} />; return <DashboardView siteId={siteId} dashboardId={dashboardId} />;
} }
interface Props extends RouteComponentProps { function DashboardRouter(props: any) {
match: any; const { siteId, dashboardId } = useParams();
}
function DashboardRouter(props: Props) {
const {
match: {
params: { siteId, dashboardId },
},
history,
} = props;
return ( return (
<div> <div>
<Switch> <Routes>
<Route exact strict path={withSiteId(metrics(), siteId)}> <Route exact strict path={withSiteId(metrics(), siteId)}>
<MetricsView siteId={siteId} /> <MetricsView siteId={siteId} />
</Route> </Route>
@ -55,7 +46,7 @@ function DashboardRouter(props: Props) {
</Route> </Route>
<Route exact path={withSiteId(dashboard(), siteId)}> <Route exact path={withSiteId(dashboard(), siteId)}>
<DashboardsView siteId={siteId} history={history} /> <DashboardsView siteId={siteId} />
</Route> </Route>
<Route exact strict path={withSiteId(dashboardMetricDetails(dashboardId), siteId)}> <Route exact strict path={withSiteId(dashboardMetricDetails(dashboardId), siteId)}>
@ -83,9 +74,9 @@ function DashboardRouter(props: Props) {
{/* @ts-ignore */} {/* @ts-ignore */}
<CreateAlert siteId={siteId} {...props} /> <CreateAlert siteId={siteId} {...props} />
</Route> </Route>
</Switch> </Routes>
</div> </div>
); );
} }
export default withRouter(DashboardRouter); export default DashboardRouter;

View file

@ -1,20 +1,21 @@
import React from 'react'; import React from 'react';
import { SideMenuitem } from 'UI'; import { SideMenuitem } from 'UI';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { withSiteId, metrics, dashboard, alerts } from 'App/routes'; import { withSiteId, metrics, dashboard, alerts } from 'App/routes';
import { useLocation, useNavigate } from "react-router";
interface Props extends RouteComponentProps { interface Props {
siteId: string; siteId: string;
history: any;
} }
function DashboardSideMenu(props: Props) { function DashboardSideMenu(props: Props) {
const { history, siteId } = props; const { siteId } = props;
const isMetric = history.location.pathname.includes('metrics'); const navigate = useNavigate();
const isDashboards = history.location.pathname.includes('dashboard'); const location = useLocation();
const isAlerts = history.location.pathname.includes('alerts'); const isMetric = location.pathname.includes('metrics');
const isDashboards = location.pathname.includes('dashboard');
const isAlerts = location.pathname.includes('alerts');
const redirect = (path: string) => { const redirect = (path: string) => {
history.push(path); navigate(path);
}; };
return ( return (
@ -53,4 +54,4 @@ function DashboardSideMenu(props: Props) {
); );
} }
export default withRouter(DashboardSideMenu); export default DashboardSideMenu;

View file

@ -5,29 +5,28 @@ import {Loader} from 'UI';
import {withSiteId} from 'App/routes'; import {withSiteId} from 'App/routes';
import withModal from 'App/components/Modal/withModal'; import withModal from 'App/components/Modal/withModal';
import DashboardWidgetGrid from '../DashboardWidgetGrid'; import DashboardWidgetGrid from '../DashboardWidgetGrid';
import {withRouter, RouteComponentProps} from 'react-router-dom';
import {useModal} from 'App/components/Modal'; import {useModal} from 'App/components/Modal';
import DashboardModal from '../DashboardModal'; import DashboardModal from '../DashboardModal';
import AlertFormModal from 'App/components/Alerts/AlertFormModal'; import AlertFormModal from 'App/components/Alerts/AlertFormModal';
import withPageTitle from 'HOCs/withPageTitle'; import withPageTitle from 'HOCs/withPageTitle';
import withReport from 'App/components/hocs/withReport'; import withReport from 'App/components/hocs/withReport';
import DashboardHeader from '../DashboardHeader'; import DashboardHeader from '../DashboardHeader';
import {useHistory} from "react-router"; import { useNavigate } from "react-router";
import AiQuery from "./AiQuery"; import AiQuery from "./AiQuery";
interface IProps { interface IProps {
siteId: string;
dashboardId: any; dashboardId: any;
renderReport?: any; renderReport?: any;
} }
type Props = IProps & RouteComponentProps; type Props = IProps;
function DashboardView(props: Props) { function DashboardView(props: Props) {
const {siteId, dashboardId} = props; const { dashboardId } = props;
const {dashboardStore} = useStore(); const { dashboardStore, projectsStore } = useStore();
const siteId = projectsStore.activeSiteId;
const {showModal, hideModal} = useModal(); const {showModal, hideModal} = useModal();
const history = useHistory(); const navigate = useNavigate();
const showAlertModal = dashboardStore.showAlertModal; const showAlertModal = dashboardStore.showAlertModal;
const loading = dashboardStore.fetchingDashboard; const loading = dashboardStore.fetchingDashboard;
@ -38,9 +37,8 @@ function DashboardView(props: Props) {
const trimQuery = () => { const trimQuery = () => {
if (!queryParams.has('modal')) return; if (!queryParams.has('modal')) return;
queryParams.delete('modal'); queryParams.delete('modal');
history.replace({ const search = queryParams.toString();
search: queryParams.toString(), navigate(location.pathname + "?" + search, { replace: true });
});
}; };
useEffect(() => { useEffect(() => {
@ -60,7 +58,7 @@ function DashboardView(props: Props) {
}, [showAlertModal]) }, [showAlertModal])
const pushQuery = () => { const pushQuery = () => {
if (!queryParams.has('modal')) history.push('?modal=addMetric'); if (!queryParams.has('modal')) navigate('?modal=addMetric');
}; };
useEffect(() => { useEffect(() => {
@ -75,7 +73,7 @@ function DashboardView(props: Props) {
useEffect(() => { useEffect(() => {
const isExists = dashboardStore.getDashboardById(dashboardId); const isExists = dashboardStore.getDashboardById(dashboardId);
if (!isExists) { if (!isExists) {
history.push(withSiteId(`/dashboard`, siteId)); navigate(withSiteId(`/dashboard`, siteId));
} }
}, [dashboardId]); }, [dashboardId]);

View file

@ -6,18 +6,18 @@ import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
import { dashboardMetricCreate, withSiteId } from 'App/routes'; import { dashboardMetricCreate, withSiteId } from 'App/routes';
import { withRouter, RouteComponentProps } from 'react-router-dom'; import { useNavigate } from "react-router";
interface IProps extends RouteComponentProps { interface IProps {
siteId: string;
title: string; title: string;
description: string; description: string;
} }
function AddMetric({ history, siteId, title, description }: IProps) { function AddMetric({ title, description }: IProps) {
const [metrics, setMetrics] = React.useState<Record<string, any>[]>([]); const [metrics, setMetrics] = React.useState<Record<string, any>[]>([]);
const navigate = useNavigate();
const { dashboardStore } = useStore(); const { dashboardStore, projectsStore } = useStore();
const siteId = projectsStore.activeSiteId
const { hideModal } = useModal(); const { hideModal } = useModal();
React.useEffect(() => { React.useEffect(() => {
@ -47,8 +47,8 @@ function AddMetric({ history, siteId, title, description }: IProps) {
const onCreateNew = () => { const onCreateNew = () => {
const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId); const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId);
if (!queryParams.has('modal')) history.push('?modal=addMetric'); if (!queryParams.has('modal')) navigate('?modal=addMetric');
history.push(path); navigate(path);
hideModal(); hideModal();
}; };
@ -110,4 +110,4 @@ function AddMetric({ history, siteId, title, description }: IProps) {
); );
} }
export default withRouter(observer(AddMetric)); export default observer(AddMetric);

View file

@ -6,18 +6,19 @@ import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
import { dashboardMetricCreate, withSiteId } from 'App/routes'; import { dashboardMetricCreate, withSiteId } from 'App/routes';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { WidgetCategoryItem } from 'App/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection'; import { WidgetCategoryItem } from 'App/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection';
import { useNavigate } from "react-router";
interface IProps extends RouteComponentProps { interface IProps {
siteId: string;
title: string; title: string;
description: string; description: string;
} }
function AddPredefinedMetric({ history, siteId, title, description }: IProps) { function AddPredefinedMetric({ title, description }: IProps) {
const navigate = useNavigate();
const [categories, setCategories] = React.useState([]); const [categories, setCategories] = React.useState([]);
const { dashboardStore } = useStore(); const { dashboardStore, projectsStore } = useStore();
const siteId = projectsStore.activeSiteId
const { hideModal } = useModal(); const { hideModal } = useModal();
const [activeCategory, setActiveCategory] = React.useState<Record<string, any>>(); const [activeCategory, setActiveCategory] = React.useState<Record<string, any>>();
@ -62,8 +63,8 @@ function AddPredefinedMetric({ history, siteId, title, description }: IProps) {
const onCreateNew = () => { const onCreateNew = () => {
const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId); const path = withSiteId(dashboardMetricCreate(dashboard.dashboardId), siteId);
if (!queryParams.has('modal')) history.push('?modal=addMetric'); if (!queryParams.has('modal')) navigate('?modal=addMetric');
history.push(path); navigate(path);
hideModal(); hideModal();
}; };
@ -155,4 +156,4 @@ function AddPredefinedMetric({ history, siteId, title, description }: IProps) {
); );
} }
export default withRouter(observer(AddPredefinedMetric)); export default observer(AddPredefinedMetric);

View file

@ -2,7 +2,6 @@ import { Table, Typography } from 'antd';
import type { TableProps } from 'antd'; import type { TableProps } from 'antd';
import { useObserver } from 'mobx-react-lite'; import { useObserver } from 'mobx-react-lite';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
@ -10,8 +9,8 @@ import { NoContent } from 'UI';
import { InfoCircleOutlined } from '@ant-design/icons'; import { InfoCircleOutlined } from '@ant-design/icons';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
import FunnelIssueModal from '../FunnelIssueModal'; import FunnelIssueModal from '../FunnelIssueModal';
import FunnelIssuesListItem from '../FunnelIssuesListItem';
const { Text } = Typography; const { Text } = Typography;
import { useLocation, useNavigate } from "react-router";
interface Issue { interface Issue {
issueId: string; issueId: string;
@ -70,26 +69,25 @@ const columns: TableProps<Issue>['columns'] = [
}, },
]; ];
interface Props extends RouteComponentProps { interface Props {
loading?: boolean; loading?: boolean;
issues: Issue[]; issues: Issue[];
history: any;
location: any;
} }
function FunnelIssuesList(props: Props) { function FunnelIssuesList(props: Props) {
const { issues, loading } = props; const { issues, loading } = props;
const location = useLocation();
const navigate = useNavigate();
const { funnelStore } = useStore(); const { funnelStore } = useStore();
const issuesSort = useObserver(() => funnelStore.issuesSort); const issuesSort = useObserver(() => funnelStore.issuesSort);
const issuesFilter = useObserver(() => const issuesFilter = useObserver(() =>
funnelStore.issuesFilter.map((issue: any) => issue.value) funnelStore.issuesFilter.map((issue: any) => issue.value)
); );
const { showModal } = useModal(); const { showModal } = useModal();
const issueId = new URLSearchParams(props.location.search).get('issueId'); const issueId = new URLSearchParams(location.search).get('issueId');
const onIssueClick = (issue: any) => { const onIssueClick = (issue: any) => {
props.history.replace({ const search = new URLSearchParams({ issueId: issue.issueId }).toString();
search: new URLSearchParams({ issueId: issue.issueId }).toString(), navigate(location.pathname + '?' + search, { replace: true });
});
}; };
useEffect(() => { useEffect(() => {
@ -99,8 +97,8 @@ function FunnelIssuesList(props: Props) {
right: true, right: true,
width: 1000, width: 1000,
onClose: () => { onClose: () => {
if (props.history.location.pathname.includes('/metric')) { if (location.pathname.includes('/metric')) {
props.history.replace({ search: '' }); navigate(location.pathname, { replace: true });
} }
}, },
}); });
@ -147,4 +145,4 @@ function FunnelIssuesList(props: Props) {
)); ));
} }
export default withRouter(FunnelIssuesList); export default FunnelIssuesList;

View file

@ -21,7 +21,7 @@ import { TYPE_ICONS, TYPE_NAMES } from 'App/constants/card';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { EllipsisVertical } from 'lucide-react'; import { EllipsisVertical } from 'lucide-react';
import cn from 'classnames' import cn from 'classnames'
@ -61,7 +61,7 @@ const MetricListItem: React.FC<Props> = ({
renderColumn, renderColumn,
inLibrary, inLibrary,
}) => { }) => {
const history = useHistory(); const navigate = useNavigate();
const { metricStore } = useStore(); const { metricStore } = useStore();
const [isEdit, setIsEdit] = useState(false); const [isEdit, setIsEdit] = useState(false);
const [newName, setNewName] = useState(metric.name); const [newName, setNewName] = useState(metric.name);
@ -75,7 +75,7 @@ const MetricListItem: React.FC<Props> = ({
return toggleSelection(e); return toggleSelection(e);
} }
const path = withSiteId(`/metrics/${metric.metricId}`, siteId); const path = withSiteId(`/metrics/${metric.metricId}`, siteId);
history.push(path); navigate(path);
}; };
const onMenuClick = async ({ key }: { key: string }) => { const onMenuClick = async ({ key }: { key: string }) => {
@ -99,7 +99,7 @@ const MetricListItem: React.FC<Props> = ({
try { try {
metric.update({ name: newName }); metric.update({ name: newName });
await metricStore.save(metric); await metricStore.save(metric);
metricStore.fetchList(); void metricStore.fetchList();
setIsEdit(false); setIsEdit(false);
} catch (e) { } catch (e) {
toast.error('Failed to rename card'); toast.error('Failed to rename card');

View file

@ -3,21 +3,22 @@ import React from 'react';
import MetricsLibraryModal from '../MetricsLibraryModal'; import MetricsLibraryModal from '../MetricsLibraryModal';
import MetricTypeItem, { MetricType } from '../MetricTypeItem/MetricTypeItem'; import MetricTypeItem, { MetricType } from '../MetricTypeItem/MetricTypeItem';
import { TYPES, LIBRARY, INSIGHTS } from 'App/constants/card'; import { TYPES, LIBRARY, INSIGHTS } from 'App/constants/card';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { dashboardMetricCreate, metricCreate, withSiteId } from 'App/routes'; import { dashboardMetricCreate, metricCreate, withSiteId } from 'App/routes';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { ENTERPRISE_REQUEIRED } from 'App/constants'; import { ENTERPRISE_REQUEIRED } from 'App/constants';
import { useNavigate } from "react-router";
interface Props extends RouteComponentProps { interface Props {
dashboardId?: number; dashboardId?: number;
siteId: string;
isList?: boolean; isList?: boolean;
} }
function MetricTypeList(props: Props) { function MetricTypeList(props: Props) {
const { dashboardId, siteId, history, isList = false } = props; const navigate = useNavigate();
const { metricStore, userStore } = useStore(); const { dashboardId, isList = false } = props;
const { metricStore, userStore, projectsStore } = useStore();
const siteId = projectsStore.activeSiteId;
const { showModal, hideModal } = useModal(); const { showModal, hideModal } = useModal();
const isEnterprise = userStore.isEnterprise; const isEnterprise = userStore.isEnterprise;
@ -49,13 +50,11 @@ function MetricTypeList(props: Props) {
}); });
} }
const path = dashboardId ? withSiteId(dashboardMetricCreate(dashboardId + ''), siteId) : const path = dashboardId
withSiteId(metricCreate(), siteId); ? withSiteId(dashboardMetricCreate(dashboardId + ''), siteId)
: withSiteId(metricCreate(), siteId);
const queryString = new URLSearchParams({ type: slug }).toString(); const queryString = new URLSearchParams({ type: slug }).toString();
history.push({ navigate(path + `?${queryString}`);
pathname: path,
search: `?${queryString}`
});
}; };
return ( return (
@ -67,4 +66,4 @@ function MetricTypeList(props: Props) {
); );
} }
export default withRouter(observer(MetricTypeList)); export default observer(MetricTypeList);

View file

@ -1,18 +1,20 @@
import React from 'react'; import React from 'react';
import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper'; import WidgetWrapper from 'App/components/Dashboard/components/WidgetWrapper';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { withSiteId } from 'App/routes'; import { withSiteId } from 'App/routes';
interface Props extends RouteComponentProps { import { useNavigate } from "react-router";
interface Props {
list: any; list: any;
siteId: any; siteId: any;
selectedList: any; selectedList: any;
} }
function GridView(props: Props) { function GridView(props: Props) {
const { siteId, list, selectedList, history } = props; const { siteId, list, selectedList } = props;
const navigate = useNavigate();
const onItemClick = (metricId: number) => { const onItemClick = (metricId: number) => {
const path = withSiteId(`/metrics/${metricId}`, siteId); const path = withSiteId(`/metrics/${metricId}`, siteId);
history.push(path); navigate(path);
}; };
return ( return (
@ -33,4 +35,4 @@ function GridView(props: Props) {
); );
} }
export default withRouter(GridView); export default GridView;

View file

@ -15,7 +15,7 @@ import { EllipsisVertical } from 'lucide-react';
import { TablePaginationConfig, SorterResult } from 'antd/lib/table/interface'; import { TablePaginationConfig, SorterResult } from 'antd/lib/table/interface';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { withSiteId } from 'App/routes'; import { withSiteId } from 'App/routes';
import { Icon } from 'UI'; import { Icon } from 'UI';
import cn from 'classnames'; import cn from 'classnames';
@ -49,7 +49,7 @@ const ListView: React.FC<Props> = ({
const [editingMetricId, setEditingMetricId] = useState<number | null>(null); const [editingMetricId, setEditingMetricId] = useState<number | null>(null);
const [newName, setNewName] = useState(''); const [newName, setNewName] = useState('');
const { metricStore } = useStore(); const { metricStore } = useStore();
const history = useHistory(); const navigate = useNavigate();
const sortedData = useMemo(() => { const sortedData = useMemo(() => {
return [...list].sort((a, b) => { return [...list].sort((a, b) => {
@ -142,7 +142,7 @@ const ListView: React.FC<Props> = ({
const onItemClick = (metric: Widget) => { const onItemClick = (metric: Widget) => {
if (disableSelection) { if (disableSelection) {
const path = withSiteId(`/metrics/${metric.metricId}`, siteId); const path = withSiteId(`/metrics/${metric.metricId}`, siteId);
history.push(path); navigate(path);
} else { } else {
toggleSelection?.(metric.metricId); toggleSelection?.(metric.metricId);
} }

View file

@ -17,7 +17,7 @@ import FilterItem from 'Shared/Filters/FilterItem';
import { import {
TIMESERIES, TABLE, HEATMAP, FUNNEL, ERRORS, INSIGHTS, USER_PATH, RETENTION TIMESERIES, TABLE, HEATMAP, FUNNEL, ERRORS, INSIGHTS, USER_PATH, RETENTION
} from 'App/constants/card'; } from 'App/constants/card';
import {useHistory} from "react-router"; import { useNavigate } from "react-router";
const tableOptions = metricOf.filter((i) => i.type === 'table'); const tableOptions = metricOf.filter((i) => i.type === 'table');
@ -217,8 +217,8 @@ interface CardBuilderProps {
} }
const CardBuilder = observer((props: CardBuilderProps) => { const CardBuilder = observer((props: CardBuilderProps) => {
const history = useHistory(); const navigate = useNavigate();
const {siteId, dashboardId, metricId} = props; const { siteId, dashboardId, metricId } = props;
const {metricStore, dashboardStore, aiFiltersStore} = useStore(); const {metricStore, dashboardStore, aiFiltersStore} = useStore();
const [aiQuery, setAiQuery] = useState(''); const [aiQuery, setAiQuery] = useState('');
const [aiAskChart, setAiAskChart] = useState(''); const [aiAskChart, setAiAskChart] = useState('');
@ -259,7 +259,7 @@ const CardBuilder = observer((props: CardBuilderProps) => {
const route = parseInt(dashboardId, 10) > 0 const route = parseInt(dashboardId, 10) > 0
? withSiteId(dashboardMetricDetails(dashboardId, savedMetric.metricId), siteId) ? withSiteId(dashboardMetricDetails(dashboardId, savedMetric.metricId), siteId)
: withSiteId(metricDetails(savedMetric.metricId), siteId); : withSiteId(metricDetails(savedMetric.metricId), siteId);
history.replace(route); navigate(route, { replace: true });
if (parseInt(dashboardId, 10) > 0) { if (parseInt(dashboardId, 10) > 0) {
dashboardStore.addWidgetToDashboard( dashboardStore.addWidgetToDashboard(
dashboardStore.getDashboard(parseInt(dashboardId, 10)), 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 () => { const onDelete = useCallback(async () => {
if (await confirm({ if (await confirm({

View file

@ -1,4 +1,4 @@
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { Button, Dropdown, MenuProps, Modal } from 'antd'; 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'; import { showAddToDashboardModal } from 'Components/Dashboard/components/AddToDashboardButton';
const CardViewMenu = () => { const CardViewMenu = () => {
const history = useHistory(); const navigate = useNavigate();
const { alertsStore, metricStore, dashboardStore } = useStore(); const { alertsStore, metricStore, dashboardStore } = useStore();
const widget = metricStore.instance; const widget = metricStore.instance;
const { openModal, closeModal } = useModal(); const { openModal, closeModal } = useModal();
@ -60,7 +60,7 @@ const CardViewMenu = () => {
metricStore metricStore
.delete(widget) .delete(widget)
.then(() => { .then(() => {
history.goBack(); navigate(-1)
}) })
.catch(() => { .catch(() => {
toast.error('Failed to remove card'); toast.error('Failed to remove card');

View file

@ -1,13 +1,13 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { Loader } from 'UI'; import { Loader, NavPrompt } from 'UI';
import WidgetPreview from '../WidgetPreview'; import WidgetPreview from '../WidgetPreview';
import WidgetSessions from '../WidgetSessions'; import WidgetSessions from '../WidgetSessions';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { dashboardMetricDetails, metricDetails, withSiteId } from 'App/routes'; import { dashboardMetricDetails, metricDetails, withSiteId } from 'App/routes';
import Breadcrumb from 'Shared/Breadcrumb'; import Breadcrumb from 'Shared/Breadcrumb';
import { FilterKey } from 'Types/filter/filterType'; import { FilterKey } from 'Types/filter/filterType';
import { Prompt, useHistory, useLocation } from 'react-router'; import { useNavigate, useLocation } from 'react-router';
import { import {
TIMESERIES, TIMESERIES,
TABLE, TABLE,
@ -29,7 +29,6 @@ import { CARD_LIST, CardType } from 'Components/Dashboard/components/DashboardLi
import FilterSeries from '@/mstore/types/filterSeries'; import FilterSeries from '@/mstore/types/filterSeries';
interface Props { interface Props {
history: any;
match: any; match: any;
siteId: 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 dashboard = dashboardStore.dashboards.find((d: any) => d.dashboardId == dashboardId);
const dashboardName = dashboard ? dashboard.name : null; const dashboardName = dashboard ? dashboard.name : null;
const [metricNotFound, setMetricNotFound] = useState(false); const [metricNotFound, setMetricNotFound] = useState(false);
const history = useHistory(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const [initialInstance, setInitialInstance] = useState(); const [initialInstance, setInitialInstance] = useState();
const isClickMap = widget.metricType === HEATMAP; const isClickMap = widget.metricType === HEATMAP;
@ -112,9 +111,9 @@ function WidgetView({ match: { params: { siteId, dashboardId, metricId } } }: Pr
useEffect(() => { useEffect(() => {
if (metricNotFound) { if (metricNotFound) {
history.replace(withSiteId('/metrics', siteId)); navigate(withSiteId('/metrics', siteId), { replace: true });
} }
}, [metricNotFound, history, siteId]); }, [metricNotFound, siteId]);
const undoChanges = () => { const undoChanges = () => {
const w = new Widget(); const w = new Widget();
@ -134,15 +133,15 @@ function WidgetView({ match: { params: { siteId, dashboardId, metricId } } }: Pr
setInitialInstance(widget.toJson()); setInitialInstance(widget.toJson());
if (wasCreating) { if (wasCreating) {
if (parseInt(dashboardId, 10) > 0) { if (parseInt(dashboardId, 10) > 0) {
history.replace( navigate(
withSiteId(dashboardMetricDetails(dashboardId, savedMetric.metricId), siteId) withSiteId(dashboardMetricDetails(dashboardId, savedMetric.metricId), siteId)
); , { replace: true });
void dashboardStore.addWidgetToDashboard( void dashboardStore.addWidgetToDashboard(
dashboardStore.getDashboard(parseInt(dashboardId, 10))!, dashboardStore.getDashboard(parseInt(dashboardId, 10))!,
[savedMetric.metricId] [savedMetric.metricId]
); );
} else { } 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 ( return (
<Loader loading={loading}> <Loader loading={loading}>
<Prompt <NavPrompt
when={hasChanged} when={hasChanged}
message={(loc: any) => message={(loc: any) =>
loc.pathname.includes('/metrics/') || loc.pathname.includes('/metric/') loc.pathname.includes('/metrics/') || loc.pathname.includes('/metric/')

View file

@ -1,48 +1,48 @@
import React from 'react'; import React from 'react';
import {useHistory} from "react-router"; import { useNavigate } from 'react-router';
import {useStore} from "App/mstore"; import { useStore } from 'App/mstore';
import {useObserver} from "mobx-react-lite"; import { Button, Dropdown, MenuProps } from 'antd';
import {Button, Dropdown, MenuProps, message, Modal} from "antd"; import { EllipsisVertical, EyeOffIcon, PencilIcon } from 'lucide-react';
import {BellIcon, EllipsisVertical, EyeOffIcon, PencilIcon, TrashIcon} from "lucide-react"; import { dashboardMetricDetails, withSiteId } from 'App/routes';
import {toast} from "react-toastify";
import {dashboardMetricDetails, withSiteId} from "App/routes";
function CardMenu({card}: any) { function CardMenu({ card }: any) {
const siteId = location.pathname.split('/')[1]; const siteId = location.pathname.split('/')[1];
const history = useHistory(); const navigate = useNavigate();
const {dashboardStore, metricStore} = useStore(); const { dashboardStore } = useStore();
const dashboardId = dashboardStore.selectedDashboard?.dashboardId; const dashboardId = dashboardStore.selectedDashboard?.dashboardId;
const items: MenuProps['items'] = [ const items: MenuProps['items'] = [
{ {
key: 'edit', key: 'edit',
label: "Edit", label: 'Edit',
icon: <PencilIcon size={16}/>, icon: <PencilIcon size={16} />,
}, },
{ {
key: 'hide', key: 'hide',
label: 'Remove from Dashboard', label: 'Remove from Dashboard',
icon: <EyeOffIcon size={16}/>, icon: <EyeOffIcon size={16} />,
}, },
]; ];
const onClick: MenuProps['onClick'] = ({key}) => { const onClick: MenuProps['onClick'] = ({ key }) => {
if (key === 'edit') { if (key === 'edit') {
history.push( navigate(
withSiteId(dashboardMetricDetails(dashboardId, card.metricId), siteId) withSiteId(dashboardMetricDetails(dashboardId, card.metricId), siteId)
) );
} else if (key === 'hide') { } else if (key === 'hide') {
dashboardStore.deleteDashboardWidget(dashboardId!, card.widgetId).then(r => null); dashboardStore
} .deleteDashboardWidget(dashboardId!, card.widgetId)
}; .then((r) => null);
}
};
return ( return (
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Dropdown menu={{items, onClick}} overlayStyle={{minWidth: '120px'}}> <Dropdown menu={{ items, onClick }} overlayStyle={{ minWidth: '120px' }}>
<Button type="text" icon={<EllipsisVertical size={16}/>}/> <Button type="text" icon={<EllipsisVertical size={16} />} />
</Dropdown> </Dropdown>
</div> </div>
); );
} }
export default CardMenu; export default CardMenu;

View file

@ -1,14 +1,14 @@
import React, { useRef, lazy } from 'react'; import React, { useRef, lazy } from 'react';
import cn from 'classnames'; import cn from 'classnames';
import { ItemMenu, TextEllipsis } from 'UI'; import { TextEllipsis } from 'UI';
import { useDrag, useDrop } from 'react-dnd'; import { useDrag, useDrop } from 'react-dnd';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { withSiteId, dashboardMetricDetails } from 'App/routes'; import { withSiteId, dashboardMetricDetails } from 'App/routes';
import TemplateOverlay from './TemplateOverlay'; import TemplateOverlay from './TemplateOverlay';
import { FilterKey } from 'App/types/filter/filterType'; import { FilterKey } from 'App/types/filter/filterType';
import { TIMESERIES } from 'App/constants/card'; import { TIMESERIES } from 'App/constants/card';
import { useNavigate } from "react-router";
const WidgetChart = lazy( const WidgetChart = lazy(
() => import('Components/Dashboard/components/WidgetChart') () => import('Components/Dashboard/components/WidgetChart')
@ -22,17 +22,17 @@ interface Props {
isPreview?: boolean; isPreview?: boolean;
isTemplate?: boolean; isTemplate?: boolean;
dashboardId?: string; dashboardId?: string;
siteId?: string;
active?: boolean; active?: boolean;
history?: any;
onClick?: () => void; onClick?: () => void;
isSaved?: boolean; isSaved?: boolean;
hideName?: boolean; hideName?: boolean;
grid?: string; grid?: string;
isGridView?: boolean; isGridView?: boolean;
} }
function WidgetWrapper(props: Props & RouteComponentProps) { function WidgetWrapper(props: Props) {
const { dashboardStore } = useStore(); const { dashboardStore, projectsStore } = useStore();
const siteId = projectsStore.activeSiteId;
const navigate = useNavigate();
const { const {
isSaved = false, isSaved = false,
active = false, active = false,
@ -40,7 +40,6 @@ function WidgetWrapper(props: Props & RouteComponentProps) {
moveListItem = null, moveListItem = null,
isPreview = false, isPreview = false,
isTemplate = false, isTemplate = false,
siteId,
grid = '', grid = '',
isGridView = false, isGridView = false,
} = props; } = props;
@ -83,7 +82,7 @@ function WidgetWrapper(props: Props & RouteComponentProps) {
const onChartClick = () => { const onChartClick = () => {
if (!isSaved || isPredefined) return; if (!isSaved || isPredefined) return;
props.history.push( navigate(
withSiteId( withSiteId(
dashboardMetricDetails(dashboard?.dashboardId, widget.metricId), dashboardMetricDetails(dashboard?.dashboardId, widget.metricId),
siteId siteId
@ -150,4 +149,4 @@ function WidgetWrapper(props: Props & RouteComponentProps) {
); );
} }
export default withRouter(observer(WidgetWrapper)); export default observer(WidgetWrapper);

View file

@ -4,8 +4,8 @@ import { Card, Tooltip } from 'antd';
import { useDrag, useDrop } from 'react-dnd'; import { useDrag, useDrop } from 'react-dnd';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { withSiteId, dashboardMetricDetails } from 'App/routes'; import { withSiteId, dashboardMetricDetails } from 'App/routes';
import { useNavigate } from "react-router";
import TemplateOverlay from './TemplateOverlay'; import TemplateOverlay from './TemplateOverlay';
import stl from './widgetWrapper.module.css'; import stl from './widgetWrapper.module.css';
import { FilterKey } from 'App/types/filter/filterType'; import { FilterKey } from 'App/types/filter/filterType';
@ -25,7 +25,6 @@ interface Props {
dashboardId?: string; dashboardId?: string;
siteId?: string; siteId?: string;
active?: boolean; active?: boolean;
history?: any;
onClick?: () => void; onClick?: () => void;
isWidget?: boolean; isWidget?: boolean;
hideName?: boolean; hideName?: boolean;
@ -35,8 +34,10 @@ interface Props {
isSaved?: boolean; isSaved?: boolean;
} }
function WidgetWrapperNew(props: Props & RouteComponentProps) { function WidgetWrapperNew(props: Props) {
const { dashboardStore, metricStore } = useStore(); const navigate = useNavigate()
const { dashboardStore, metricStore, projectsStore } = useStore();
const siteId = projectsStore.activeSiteId;
const { const {
isWidget = false, isWidget = false,
active = false, active = false,
@ -44,7 +45,6 @@ function WidgetWrapperNew(props: Props & RouteComponentProps) {
moveListItem = null, moveListItem = null,
isPreview = false, isPreview = false,
isTemplate = false, isTemplate = false,
siteId,
grid = '', grid = '',
isGridView = false, isGridView = false,
showMenu = false, showMenu = false,
@ -80,7 +80,7 @@ function WidgetWrapperNew(props: Props & RouteComponentProps) {
const onChartClick = () => { const onChartClick = () => {
// if (!isWidget || isPredefined) return; // if (!isWidget || isPredefined) return;
props.history.push( navigate(
withSiteId(dashboardMetricDetails(dashboard?.dashboardId, widget.metricId), siteId) 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);

View file

@ -3,8 +3,7 @@ import { FilterKey } from 'Types/filter/filterType';
import cn from 'classnames'; import cn from 'classnames';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React from 'react'; import React from 'react';
import { withRouter } from 'react-router-dom'; import { useNavigate } from 'react-router'
import { resentOrDate } from 'App/date'; import { resentOrDate } from 'App/date';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { sessions as sessionsRoute } from 'App/routes'; import { sessions as sessionsRoute } from 'App/routes';
@ -17,6 +16,7 @@ import { Button } from 'antd'
import SessionBar from './SessionBar'; import SessionBar from './SessionBar';
function MainSection(props) { function MainSection(props) {
const navigate = useNavigate();
const { errorStore, searchStore } = useStore(); const { errorStore, searchStore } = useStore();
const error = errorStore.instance; const error = errorStore.instance;
const trace = errorStore.instanceTrace; const trace = errorStore.instanceTrace;
@ -26,7 +26,7 @@ function MainSection(props) {
const findSessions = () => { const findSessions = () => {
searchStore.addFilterByKeyAndValue(FilterKey.ERROR, error.message); searchStore.addFilterByKeyAndValue(FilterKey.ERROR, error.message);
props.history.push(sessionsRoute()); navigate(sessionsRoute());
}; };
return ( return (
<div <div
@ -129,6 +129,4 @@ function MainSection(props) {
); );
} }
export default withRouter( export default observer(MainSection)
(observer(MainSection))
);

View file

@ -2,11 +2,11 @@ import React from 'react'
import { PageTitle } from 'UI' import { PageTitle } from 'UI'
import { Button } from 'antd' import { Button } from 'antd'
import FFlagsSearch from "Components/FFlags/FFlagsSearch"; import FFlagsSearch from "Components/FFlags/FFlagsSearch";
import { useHistory } from "react-router"; import { useNavigate } from "react-router";
import { newFFlag, withSiteId } from 'App/routes'; import { newFFlag, withSiteId } from 'App/routes';
function FFlagsListHeader({ siteId }: { siteId: string }) { function FFlagsListHeader({ siteId }: { siteId: string }) {
const history = useHistory(); const navigate = useNavigate();
return ( return (
<div className="flex items-center justify-between px-6"> <div className="flex items-center justify-between px-6">
@ -14,7 +14,7 @@ function FFlagsListHeader({ siteId }: { siteId: string }) {
<PageTitle title="Feature Flags" /> <PageTitle title="Feature Flags" />
</div> </div>
<div className="ml-auto flex items-center"> <div className="ml-auto flex items-center">
<Button type="primary" onClick={() => history.push(withSiteId(newFFlag(), siteId))}> <Button type="primary" onClick={() => navigate(withSiteId(newFFlag(), siteId))}>
Create Feature Flag Create Feature Flag
</Button> </Button>
<div className="mx-2"></div> <div className="mx-2"></div>

View file

@ -4,7 +4,7 @@ import { useStore } from 'App/mstore';
import { Loader, NoContent, ItemMenu } from 'UI'; import { Loader, NoContent, ItemMenu } from 'UI';
import { Button, Switch } from 'antd' import { Button, Switch } from 'antd'
import Breadcrumb from 'Shared/Breadcrumb'; import Breadcrumb from 'Shared/Breadcrumb';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { withSiteId, fflag, fflags } from 'App/routes'; import { withSiteId, fflag, fflags } from 'App/routes';
import Multivariant from "Components/FFlags/NewFFlag/Multivariant"; import Multivariant from "Components/FFlags/NewFFlag/Multivariant";
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@ -12,7 +12,7 @@ import RolloutCondition from "Shared/ConditionSet";
function FlagView({ siteId, fflagId }: { siteId: string; fflagId: string }) { function FlagView({ siteId, fflagId }: { siteId: string; fflagId: string }) {
const { featureFlagsStore } = useStore(); const { featureFlagsStore } = useStore();
const history = useHistory(); const navigate = useNavigate();
React.useEffect(() => { React.useEffect(() => {
if (fflagId) { if (fflagId) {
@ -27,7 +27,7 @@ function FlagView({ siteId, fflagId }: { siteId: string; fflagId: string }) {
const deleteHandler = () => { const deleteHandler = () => {
featureFlagsStore.deleteFlag(current.featureFlagId).then(() => { featureFlagsStore.deleteFlag(current.featureFlagId).then(() => {
toast.success('Feature flag deleted.'); 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'} className={'ml-auto text-main'}
type={'text'} type={'text'}
onClick={() => onClick={() =>
history.push( navigate(
withSiteId( withSiteId(
fflag( fflag(
featureFlagsStore.currentFflag?.featureFlagId.toString() featureFlagsStore.currentFflag?.featureFlagId.toString()

View file

@ -3,19 +3,19 @@ import { observer } from 'mobx-react-lite';
import cn from 'classnames'; import cn from 'classnames';
import { ItemMenu } from 'UI'; import { ItemMenu } from 'UI';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { fflags, withSiteId } from "App/routes"; import { fflags, withSiteId } from "App/routes";
import { Button } from 'antd'; import { Button } from 'antd';
function Header({ current, onCancel, onSave, isNew, siteId }: any) { function Header({ current, onCancel, onSave, isNew, siteId }: any) {
const { featureFlagsStore } = useStore(); const { featureFlagsStore } = useStore();
const history = useHistory(); const navigate = useNavigate()
const deleteHandler = () => { const deleteHandler = () => {
featureFlagsStore.deleteFlag(current.featureFlagId).then(() => { featureFlagsStore.deleteFlag(current.featureFlagId).then(() => {
toast.success('Feature flag deleted.'); toast.success('Feature flag deleted.');
history.push(withSiteId(fflags(), siteId)); navigate(withSiteId(fflags(), siteId));
}); });
}; };

View file

@ -1,12 +1,12 @@
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore'; 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 Breadcrumb from 'Shared/Breadcrumb';
import { Button, Switch } from 'antd' import { Button, Switch } from 'antd'
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
import HowTo from 'Components/FFlags/NewFFlag/HowTo'; 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 {withSiteId, fflags, fflagRead} from 'App/routes';
import Description from './Description'; import Description from './Description';
import Header from './Header'; import Header from './Header';
@ -32,7 +32,7 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) {
const current = featureFlagsStore.currentFflag; const current = featureFlagsStore.currentFflag;
const { showModal } = useModal(); const { showModal } = useModal();
const history = useHistory(); const navigate = useNavigate()
if (featureFlagsStore.isLoading) return <Loader loading={true} />; if (featureFlagsStore.isLoading) return <Loader loading={true} />;
if (!current) return ( if (!current) return (
@ -52,7 +52,7 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) {
}; };
const onCancel = () => { const onCancel = () => {
history.goBack() navigate(-1)
}; };
const onSave = () => { const onSave = () => {
@ -61,7 +61,7 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) {
if (fflagId) { if (fflagId) {
featureFlagsStore.updateFlag().then(() => { featureFlagsStore.updateFlag().then(() => {
toast.success('Feature flag updated.'); toast.success('Feature flag updated.');
history.push(withSiteId(fflagRead(fflagId), siteId)); navigate(withSiteId(fflagRead(fflagId), siteId));
}) })
.catch(() => { .catch(() => {
toast.error(`Failed to update flag, check your data and try again.`) 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 { } else {
featureFlagsStore.createFlag().then(() => { featureFlagsStore.createFlag().then(() => {
toast.success('Feature flag created.'); toast.success('Feature flag created.');
history.push(withSiteId(fflags(), siteId)); navigate(withSiteId(fflags(), siteId));
}).catch(() => { }).catch(() => {
toast.error('Failed to create flag.'); toast.error('Failed to create flag.');
}) })
@ -79,7 +79,7 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) {
const showDescription = Boolean(current.description?.length); const showDescription = Boolean(current.description?.length);
return ( return (
<div className={'w-full mx-auto mb-4'} style={{ maxWidth: '1360px' }}> <div className={'w-full mx-auto mb-4'} style={{ maxWidth: '1360px' }}>
<Prompt <NavPrompt
when={current.hasChanged} when={current.hasChanged}
message={() => { message={() => {
return 'You have unsaved changes. Are you sure you want to leave?'; return 'You have unsaved changes. Are you sure you want to leave?';

View file

@ -1,13 +1,12 @@
import React from 'react'; import React from 'react';
import { Icon } from 'UI'; import { Icon } from 'UI';
import { withRouter } from 'react-router-dom'; import { useNavigate } from "react-router";
interface Props {
history: any; function PreferencesView() {
} const navigate = useNavigate();
function PreferencesView(props: Props) {
const onExit = () => { const onExit = () => {
props.history.push('/'); navigate('/');
}; };
return ( return (
<> <>
@ -24,4 +23,4 @@ function PreferencesView(props: Props) {
); );
} }
export default withRouter(PreferencesView); export default PreferencesView;

View file

@ -1,39 +1,38 @@
import React from 'react'; import React from 'react';
import cn from 'classnames';
import { Icon } from 'UI'; import { Icon } from 'UI';
import { CLIENT_TABS, client as clientRoute } from 'App/routes'; import { CLIENT_TABS, client as clientRoute } from 'App/routes';
import { withRouter, RouteComponentProps } from 'react-router'; import { useNavigate } from "react-router";
interface Props { interface Props {
history: any;
className: string; className: string;
account: any; account: any;
} }
function SettingsMenu(props: RouteComponentProps<Props>) { function SettingsMenu(props: Props) {
const { history, account, className }: any = props; const navigate = useNavigate();
const { account, className }: any = props;
const isAdmin = account.admin || account.superAdmin; const isAdmin = account.admin || account.superAdmin;
const isEnterprise = account.edition === 'ee'; const isEnterprise = account.edition === 'ee';
const navigateTo = (path: any) => { const navigateTo = (path: any) => {
switch (path) { switch (path) {
case 'sessions-listing': case 'sessions-listing':
return history.push(clientRoute(CLIENT_TABS.SESSIONS_LISTING)); return navigate(clientRoute(CLIENT_TABS.SESSIONS_LISTING));
case 'projects': case 'projects':
return history.push(clientRoute(CLIENT_TABS.SITES)); return navigate(clientRoute(CLIENT_TABS.SITES));
case 'team': case 'team':
return history.push(clientRoute(CLIENT_TABS.MANAGE_USERS)); return navigate(clientRoute(CLIENT_TABS.MANAGE_USERS));
case 'metadata': case 'metadata':
return history.push(clientRoute(CLIENT_TABS.CUSTOM_FIELDS)); return navigate(clientRoute(CLIENT_TABS.CUSTOM_FIELDS));
case 'webhooks': case 'webhooks':
return history.push(clientRoute(CLIENT_TABS.WEBHOOKS)); return navigate(clientRoute(CLIENT_TABS.WEBHOOKS));
case 'integrations': case 'integrations':
return history.push(clientRoute(CLIENT_TABS.INTEGRATIONS)); return navigate(clientRoute(CLIENT_TABS.INTEGRATIONS));
case 'notifications': case 'notifications':
return history.push(clientRoute(CLIENT_TABS.NOTIFICATIONS)); return navigate(clientRoute(CLIENT_TABS.NOTIFICATIONS));
case 'roles': case 'roles':
return history.push(clientRoute(CLIENT_TABS.MANAGE_ROLES)); return navigate(clientRoute(CLIENT_TABS.MANAGE_ROLES));
case 'audit': case 'audit':
return history.push(clientRoute(CLIENT_TABS.AUDIT)); return navigate(clientRoute(CLIENT_TABS.AUDIT));
} }
}; };
return ( return (
@ -64,7 +63,7 @@ function SettingsMenu(props: RouteComponentProps<Props>) {
); );
} }
export default withRouter(SettingsMenu); export default SettingsMenu;
function MenuItem({ onClick, label, icon }: any) { function MenuItem({ onClick, label, icon }: any) {
return ( return (

View file

@ -1,24 +1,21 @@
import React from 'react'; import React from 'react';
import { withRouter } from 'react-router-dom';
import { client, CLIENT_DEFAULT_TAB } from 'App/routes'; import { client, CLIENT_DEFAULT_TAB } from 'App/routes';
import { Icon } from 'UI'; import { Icon } from 'UI';
import { getInitials } from 'App/utils'; import { getInitials } from 'App/utils';
import { useStore } from "App/mstore"; import { useStore } from "App/mstore";
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useNavigate } from "react-router";
const CLIENT_PATH = client(CLIENT_DEFAULT_TAB); const CLIENT_PATH = client(CLIENT_DEFAULT_TAB);
interface Props { function UserMenu() {
history: any; const navigate = useNavigate();
}
function UserMenu(props: Props) {
const { history }: any = props;
const { loginStore, userStore } = useStore(); const { loginStore, userStore } = useStore();
const account = userStore.account; const account = userStore.account;
const onLogoutClick = userStore.logout; const onLogoutClick = userStore.logout;
const onAccountClick = () => { const onAccountClick = () => {
history.push(CLIENT_PATH); navigate(CLIENT_PATH);
}; };
const onLogout = () => { const onLogout = () => {
@ -59,4 +56,4 @@ function UserMenu(props: Props) {
); );
} }
export default withRouter(observer(UserMenu)) export default observer(UserMenu)

View file

@ -12,11 +12,11 @@ import { toast } from 'react-toastify';
import EditHlModal from './EditHlModal'; import EditHlModal from './EditHlModal';
import HighlightsListHeader from './HighlightsListHeader'; import HighlightsListHeader from './HighlightsListHeader';
import withPermissions from 'HOCs/withPermissions'; import withPermissions from 'HOCs/withPermissions';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { highlights, withSiteId } from 'App/routes' import { highlights, withSiteId } from 'App/routes'
function HighlightsList() { function HighlightsList() {
const history = useHistory(); const navigate = useNavigate()
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const hlId = params.get('highlight'); const hlId = params.get('highlight');
const { notesStore, projectsStore, userStore } = useStore(); const { notesStore, projectsStore, userStore } = useStore();
@ -31,7 +31,7 @@ function HighlightsList() {
React.useEffect(() => { React.useEffect(() => {
if (hlId) { if (hlId) {
setActiveId(hlId); setActiveId(hlId);
history.replace(withSiteId(highlights(), projectsStore.siteId)); navigate(withSiteId(highlights(), projectsStore.siteId), { replace: true });
} }
}, [hlId]) }, [hlId])

View file

@ -3,7 +3,7 @@ import cn from 'classnames';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
// Consider using a different approach for titles in functional components // Consider using a different approach for titles in functional components
import ReCAPTCHA from 'react-google-recaptcha'; 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 { observer } from 'mobx-react-lite';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@ -20,13 +20,8 @@ import stl from './login.module.css';
const FORGOT_PASSWORD = forgotPassword(); const FORGOT_PASSWORD = forgotPassword();
const SIGNUP_ROUTE = signup(); const SIGNUP_ROUTE = signup();
interface LoginProps { const Login = () => {
location: Location; const location = useLocation()
}
const Login = ({
location
}: LoginProps) => {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const CAPTCHA_ENABLED = React.useMemo(() => { const CAPTCHA_ENABLED = React.useMemo(() => {
@ -38,12 +33,12 @@ const Login = ({
const loading = loginStore.loading; const loading = loginStore.loading;
const authDetails = userStore.authStore.authDetails; const authDetails = userStore.authStore.authDetails;
const setJwt = userStore.updateJwt; const setJwt = userStore.updateJwt;
const history = useHistory(); const navigate = useNavigate()
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
useEffect(() => { useEffect(() => {
if (authDetails && !authDetails.tenants) { if (authDetails && !authDetails.tenants) {
history.push(SIGNUP_ROUTE); navigate(SIGNUP_ROUTE);
} }
}, [authDetails]); }, [authDetails]);

View file

@ -1,8 +1,7 @@
import React, { useEffect } from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import ModalOverlay from './ModalOverlay'; import ModalOverlay from './ModalOverlay';
import cn from 'classnames'; import cn from 'classnames';
import { useHistory } from 'react-router';
const DEFAULT_WIDTH = 350; const DEFAULT_WIDTH = 350;
interface Props { interface Props {
@ -13,16 +12,6 @@ interface Props {
width?: number; width?: number;
} }
function Modal({ component, className = 'bg-white', props, hideModal }: Props) { 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 ? ( return component ? (
ReactDOM.createPortal( ReactDOM.createPortal(
<ModalOverlay hideModal={hideModal} left={!props.right} right={props.right}> <ModalOverlay hideModal={hideModal} left={!props.right} right={props.right}>

View file

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { Redirect, Route, RouteComponentProps, Switch } from 'react-router'; import { Navigate, Route, Routes } from 'react-router';
import { withRouter } from 'react-router-dom'; import { useParams, useNavigate } from "react-router";
import { OB_TABS, onboarding as onboardingRoute } from 'App/routes'; import { OB_TABS, onboarding as onboardingRoute } from 'App/routes';
import { withSiteId } from 'App/routes'; import { withSiteId } from 'App/routes';
import { Icon } from 'UI'; import { Icon } from 'UI';
@ -12,22 +11,14 @@ import IntegrationsTab from './components/IntegrationsTab';
import ManageUsersTab from './components/ManageUsersTab'; import ManageUsersTab from './components/ManageUsersTab';
import SideMenu from './components/SideMenu'; import SideMenu from './components/SideMenu';
interface Props {
match: {
params: {
activeTab: string;
siteId: string;
};
};
history: RouteComponentProps['history'];
}
const platformMap = { const platformMap = {
ios: 'mobile', ios: 'mobile',
web: 'web', web: 'web',
}; };
const Onboarding = (props: Props) => { const Onboarding = (props) => {
const navigate = useNavigate();
const { activeTab, siteId } = useParams();
const platforms = [ const platforms = [
{ {
label: ( label: (
@ -47,18 +38,13 @@ const Onboarding = (props: Props) => {
} as const, } as const,
] as const; ] as const;
const [platform, setPlatform] = React.useState(platforms[0]); const [platform, setPlatform] = React.useState(platforms[0]);
const {
match: {
params: { activeTab, siteId },
},
} = props;
const route = (path: string) => { const route = (path: string) => {
return withSiteId(onboardingRoute(path)); return withSiteId(onboardingRoute(path));
}; };
const onMenuItemClick = (tab: string) => { const onMenuItemClick = (tab: string) => {
props.history.push(withSiteId(onboardingRoute(tab), siteId)); navigate(withSiteId(onboardingRoute(tab), siteId));
}; };
return ( return (
@ -69,7 +55,7 @@ const Onboarding = (props: Props) => {
className="bg-white w-full rounded-lg mx-auto mb-8 border" className="bg-white w-full rounded-lg mx-auto mb-8 border"
style={{ maxWidth: '1360px' }} style={{ maxWidth: '1360px' }}
> >
<Switch> <Routes>
<Route exact strict path={route(OB_TABS.INSTALLING)}> <Route exact strict path={route(OB_TABS.INSTALLING)}>
<InstallOpenReplayTab <InstallOpenReplayTab
platforms={platforms} platforms={platforms}
@ -98,17 +84,14 @@ const Onboarding = (props: Props) => {
path={route(OB_TABS.INTEGRATIONS)} path={route(OB_TABS.INTEGRATIONS)}
component={IntegrationsTab} component={IntegrationsTab}
/> />
<Redirect to={route(OB_TABS.INSTALLING)} /> <Route path={'*'}>
</Switch> <Navigate to={route(OB_TABS.INSTALLING)} />
</Route>
</Routes>
</div> </div>
</div> </div>
{/* <div className="py-6 px-4 w-full flex items-center fixed bottom-0 bg-white border-t z-10">
<div className="ml-auto">
<OnboardingNavButton />
</div>
</div> */}
</div> </div>
); );
}; };
export default withRouter(Onboarding); export default Onboarding;

View file

@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
import { Icon, SideMenuitem } from 'UI'; import { SideMenuitem } from 'UI';
import cn from 'classnames'; import cn from 'classnames';
import stl from './onboardingMenu.module.css'; import stl from './onboardingMenu.module.css';
import { OB_TABS, onboarding as onboardingRoute } from 'App/routes'; import { OB_TABS, onboarding as onboardingRoute } from 'App/routes';
import { withRouter } from 'react-router-dom'; import * as routes from 'App/routes';
import * as routes from '../../../../routes'; import { useParams, useNavigate } from "react-router";
const withSiteId = routes.withSiteId; const withSiteId = routes.withSiteId;
@ -15,7 +15,7 @@ const MENU_ITEMS = [
OB_TABS.INTEGRATIONS, OB_TABS.INTEGRATIONS,
]; ];
const Item = ({ icon, text, completed, active, onClick }) => ( const Item = ({ text, completed, active, onClick }) => (
<div <div
className={cn('cursor-pointer', stl.stepWrapper, { className={cn('cursor-pointer', stl.stepWrapper, {
[stl.completed]: completed, [stl.completed]: completed,
@ -39,17 +39,14 @@ const Item = ({ icon, text, completed, active, onClick }) => (
</div> </div>
); );
const OnboardingMenu = (props) => { const OnboardingMenu = () => {
const { const { siteId, activeTab } = useParams();
match: { const navigate = useNavigate();
params: { activeTab, siteId },
},
history,
} = props;
const activeIndex = MENU_ITEMS.findIndex((i) => i === activeTab); const activeIndex = MENU_ITEMS.findIndex((i) => i === activeTab);
const setTab = (tab) => { const setTab = (tab) => {
history.push(withSiteId(onboardingRoute(tab), siteId)); navigate(withSiteId(onboardingRoute(tab), siteId));
}; };
return ( return (
@ -61,28 +58,24 @@ const OnboardingMenu = (props) => {
<> <>
<Item <Item
icon="check"
text="Install OpenReplay" text="Install OpenReplay"
completed={activeIndex >= 0} completed={activeIndex >= 0}
active={activeIndex === 0} active={activeIndex === 0}
onClick={() => setTab(MENU_ITEMS[0])} onClick={() => setTab(MENU_ITEMS[0])}
/> />
<Item <Item
icon="check"
text="Identify Users" text="Identify Users"
completed={activeIndex >= 1} completed={activeIndex >= 1}
active={activeIndex === 1} active={activeIndex === 1}
onClick={() => setTab(MENU_ITEMS[1])} onClick={() => setTab(MENU_ITEMS[1])}
/> />
<Item <Item
icon="check"
text="Invite Collaborators" text="Invite Collaborators"
completed={activeIndex >= 2} completed={activeIndex >= 2}
active={activeIndex === 2} active={activeIndex === 2}
onClick={() => setTab(MENU_ITEMS[2])} onClick={() => setTab(MENU_ITEMS[2])}
/> />
<Item <Item
icon="check"
text="Integrations" text="Integrations"
completed={activeIndex >= 3} completed={activeIndex >= 3}
active={activeIndex === 3} active={activeIndex === 3}
@ -93,4 +86,4 @@ const OnboardingMenu = (props) => {
); );
}; };
export default withRouter(OnboardingMenu); export default OnboardingMenu;

View file

@ -1,9 +1,9 @@
import React from 'react' import React from 'react'
import { withRouter } from 'react-router'
import { Button } from 'antd' import { Button } from 'antd'
import { OB_TABS, onboarding as onboardingRoute, withSiteId } from 'App/routes' import { OB_TABS, onboarding as onboardingRoute, withSiteId } from 'App/routes'
import { sessions } from 'App/routes'; import { sessions } from 'App/routes';
import { useStore } from 'App/mstore' 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 MENU_ITEMS = [OB_TABS.INSTALLING, OB_TABS.IDENTIFY_USERS, OB_TABS.MANAGE_USERS, OB_TABS.INTEGRATIONS]
const BTN_MSGS = [ const BTN_MSGS = [
@ -13,7 +13,9 @@ const BTN_MSGS = [
'See Recorded Sessions' 'See Recorded Sessions'
] ]
const OnboardingNavButton = ({ match: { params: { activeTab, siteId } }, history }) => { const OnboardingNavButton = () => {
const { activeTab, siteId } = useParams();
const navigate = useNavigate();
const { userStore } = useStore(); const { userStore } = useStore();
const activeIndex = MENU_ITEMS.findIndex(i => i === activeTab); const activeIndex = MENU_ITEMS.findIndex(i => i === activeTab);
const completed = activeIndex == MENU_ITEMS.length - 1; const completed = activeIndex == MENU_ITEMS.length - 1;
@ -21,7 +23,7 @@ const OnboardingNavButton = ({ match: { params: { activeTab, siteId } }, history
const setTab = () => { const setTab = () => {
if (!completed) { if (!completed) {
const tab = MENU_ITEMS[activeIndex+1] const tab = MENU_ITEMS[activeIndex+1]
history.push(withSiteId(onboardingRoute(tab), siteId)); navigate(withSiteId(onboardingRoute(tab), siteId));
} else { } else {
onDone() onDone()
} }
@ -29,7 +31,7 @@ const OnboardingNavButton = ({ match: { params: { activeTab, siteId } }, history
const onDone = () => { const onDone = () => {
userStore.setOnboarding(true); userStore.setOnboarding(true);
history.push(sessions()); navigate(sessions());
} }
return ( return (
@ -53,4 +55,4 @@ const OnboardingNavButton = ({ match: { params: { activeTab, siteId } }, history
) )
} }
export default withRouter(OnboardingNavButton) export default OnboardingNavButton

View file

@ -1,53 +1,38 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { sessions, withSiteId, onboarding as onboardingRoute } from 'App/routes'; import { sessions, withSiteId, onboarding as onboardingRoute } from 'App/routes';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { useParams, useNavigate } from "react-router";
export interface WithOnboardingProps { export interface WithOnboardingProps {
history: RouteComponentProps['history'];
skip?: () => void; skip?: () => void;
navTo?: (tab: string) => void; navTo?: (tab: string) => void;
site?: any; site?: any;
match: {
params: {
activeTab: string;
siteId: string;
};
};
} }
const withOnboarding = <P extends Record<string, any>>(
const withOnboarding = <P extends RouteComponentProps>( Component: React.ComponentType<P>
Component: React.ComponentType<P & WithOnboardingProps>
) => { ) => {
const WithOnboarding: React.FC<P & WithOnboardingProps> = (props) => { const WithOnboarding: React.FC<P & WithOnboardingProps> = (props) => {
const { siteId } = useParams();
const navigate = useNavigate();
const { projectsStore, userStore } = useStore(); const { projectsStore, userStore } = useStore();
const sites = projectsStore.list; const sites = projectsStore.list;
const {
match: {
params: { siteId },
},
} = props;
const site = useMemo(() => sites.find((s: any) => s.id === siteId), [sites, siteId]); const site = useMemo(() => sites.find((s: any) => s.id === siteId), [sites, siteId]);
const skip = () => { const skip = () => {
userStore.setOnboarding(true); userStore.setOnboarding(true);
props.history.push(withSiteId(sessions(), siteId)); navigate(withSiteId(sessions(), siteId));
}; };
const navTo = (tab: string) => { const navTo = (tab: string) => {
props.history.push(withSiteId(onboardingRoute(tab), siteId)); navigate(withSiteId(onboardingRoute(tab), siteId));
}; };
return <Component skip={skip} navTo={navTo} {...props} site={site} />; return <Component skip={skip} navTo={navTo} {...props} site={site} />;
}; };
return withRouter( return observer(WithOnboarding)
observer(
WithOnboarding
)
);
}; };
export default withOnboarding; export default withOnboarding;

View file

@ -3,27 +3,25 @@ import withPageTitle from 'HOCs/withPageTitle';
import SessionsTabOverview from 'Shared/SessionsTabOverview/SessionsTabOverview'; import SessionsTabOverview from 'Shared/SessionsTabOverview/SessionsTabOverview';
import FFlagsList from 'Components/FFlags'; import FFlagsList from 'Components/FFlags';
import NewFFlag from 'Components/FFlags/NewFFlag'; import NewFFlag from 'Components/FFlags/NewFFlag';
import { Switch, Route } from 'react-router'; import { Routes, Route } from 'react-router';
import { sessions, fflags, withSiteId, newFFlag, fflag, fflagRead, bookmarks } from 'App/routes'; import {
import { withRouter, RouteComponentProps, useLocation } from 'react-router-dom'; 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 FlagView from 'Components/FFlags/FlagView/FlagView';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useStore } from '@/mstore'; import { useStore } from '@/mstore';
import Bookmarks from 'Shared/SessionsTabOverview/components/Bookmarks/Bookmarks'; import Bookmarks from 'Shared/SessionsTabOverview/components/Bookmarks/Bookmarks';
// @ts-ignore function Overview() {
interface IProps extends RouteComponentProps {
match: {
params: {
siteId: string;
fflagId?: string;
};
};
}
// TODO should move these routes to the Routes file
function Overview({ match: { params } }: IProps) {
const { searchStore } = useStore(); const { searchStore } = useStore();
const { siteId, fflagId } = params; const { siteId, fflagId } = useParams();
const location = useLocation(); const location = useLocation();
const tab = location.pathname.split('/')[2]; const tab = location.pathname.split('/')[2];
@ -31,34 +29,50 @@ function Overview({ match: { params } }: IProps) {
searchStore.setActiveTab(tab); searchStore.setActiveTab(tab);
}, [tab]); }, [tab]);
return ( console.log(location.pathname)
<Switch> const renderContent = () => {
<Route exact strict const path = location.pathname;
path={withSiteId(sessions(), siteId)}>
if (path.startsWith(withSiteId(sessions()), siteId)) {
return (
<div className="mb-5 w-full mx-auto" style={{ maxWidth: '1360px' }}> <div className="mb-5 w-full mx-auto" style={{ maxWidth: '1360px' }}>
<SessionsTabOverview /> <SessionsTabOverview />
</div> </div>
</Route> );
<Route exact strict }
path={withSiteId(bookmarks(), siteId)}>
if (path.startsWith(withSiteId(bookmarks()), siteId)) {
return (
<div className="mb-5 w-full mx-auto" style={{ maxWidth: '1360px' }}> <div className="mb-5 w-full mx-auto" style={{ maxWidth: '1360px' }}>
<Bookmarks /> <Bookmarks />
</div> </div>
</Route> );
<Route exact strict path={withSiteId(fflags(), siteId)}> }
<FFlagsList siteId={siteId} />
</Route> if (path.startsWith(withSiteId(fflags()), siteId)) {
<Route exact strict path={withSiteId(newFFlag(), siteId)}> return <FFlagsList siteId={siteId} />;
<NewFFlag siteId={siteId} /> }
</Route>
<Route exact strict path={withSiteId(fflag(), siteId)}> if (path.startsWith(withSiteId(newFFlag()), siteId)) {
<NewFFlag siteId={siteId} fflagId={fflagId} /> return <NewFFlag siteId={siteId} />;
</Route> }
<Route exact strict path={withSiteId(fflagRead(), siteId)}>
<FlagView siteId={siteId} fflagId={fflagId!} /> if (path.match(new RegExp(`^${withSiteId(fflag(), siteId).replace(':fflagId', '\\d+')}$`))) {
</Route> return <NewFFlag siteId={siteId} fflagId={fflagId} />;
</Switch> }
);
if (path.match(new RegExp(`^${withSiteId(fflagRead(), siteId).replace(':fflagId', '\\d+')}$`))) {
return <FlagView siteId={siteId} fflagId={fflagId!} />;
}
return (
<div className="mb-5 w-full mx-auto" style={{ maxWidth: '1360px' }}>
<SessionsTabOverview />
</div>
);
};
return renderContent();
} }
export default withPageTitle('Sessions - OpenReplay')(withRouter(observer(Overview))); export default withPageTitle('Sessions - OpenReplay')(observer(Overview));

View file

@ -1,7 +1,7 @@
import { ArrowRightOutlined } from '@ant-design/icons'; import { ArrowRightOutlined } from '@ant-design/icons';
import { Button, Card, Radio } from 'antd'; import { Button, Card, Radio } from 'antd';
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import * as routes from 'App/routes' import * as routes from 'App/routes'
import { SPOT_ONBOARDING } from "App/constants/storageKeys"; import { SPOT_ONBOARDING } from "App/constants/storageKeys";
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
@ -27,24 +27,24 @@ function ScopeForm() {
const downgradeScope = userStore.downgradeScope const downgradeScope = userStore.downgradeScope
const scopeState = userStore.scopeState const scopeState = userStore.scopeState
const [scope, setScope] = React.useState(getDefaultSetup); const [scope, setScope] = React.useState(getDefaultSetup);
const navigate = useNavigate()
React.useEffect(() => { React.useEffect(() => {
if (scopeState !== 0) { if (scopeState !== 0) {
if (scopeState === 2) { if (scopeState === 2) {
history.replace(routes.onboarding()) navigate(routes.onboarding(), { replace: true })
} else { } else {
history.replace(routes.spotsList()) navigate(routes.spotsList(), { replace: true })
} }
} }
}, [scopeState]) }, [scopeState])
const history = useHistory();
const onContinue = () => { const onContinue = () => {
if (scope === Scope.FULL) { if (scope === Scope.FULL) {
void upgradeScope(); void upgradeScope();
history.replace(routes.onboarding()) navigate(routes.onboarding(), { replace: true })
} else { } else {
void downgradeScope(); void downgradeScope();
history.replace(routes.spotsList()) navigate(routes.spotsList(), { replace: true })
} }
}; };
return ( return (

View file

@ -4,7 +4,7 @@ import { PlayButton, PlayingState } from '@/player-ui';
import { PlayerContext } from 'Components/Session/playerContext'; import { PlayerContext } from 'Components/Session/playerContext';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { Button } from 'antd'; import { Button } from 'antd';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import { CirclePlay } from 'lucide-react'; import { CirclePlay } from 'lucide-react';
import { withSiteId } from '@/routes'; import { withSiteId } from '@/routes';
import * as routes from '@/routes'; import * as routes from '@/routes';
@ -20,7 +20,7 @@ function ClipPlayerControls({
}) { }) {
const { projectsStore } = useStore(); const { projectsStore } = useStore();
const { player, store } = React.useContext(PlayerContext); const { player, store } = React.useContext(PlayerContext);
const history = useHistory(); const navigate = useNavigate();
const siteId = projectsStore.siteId; const siteId = projectsStore.siteId;
const { playing, completed } = store.get(); const { playing, completed } = store.get();
@ -37,7 +37,7 @@ function ClipPlayerControls({
const showFullSession = () => { const showFullSession = () => {
const path = withSiteId(routes.session(session.sessionId), siteId); const path = withSiteId(routes.session(session.sessionId), siteId);
history.push(path + '?jumpto=' + Math.round(range[0])); navigate(path + '?jumpto=' + Math.round(range[0]));
}; };
return ( return (

View file

@ -3,7 +3,7 @@ import cn from 'classnames';
import { Icon } from 'UI'; import { Icon } from 'UI';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite'; 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 { multiview, liveSession, withSiteId } from 'App/routes';
import { PlayerContext, ILivePlayerContext } from 'App/components/Session/playerContext'; import { PlayerContext, ILivePlayerContext } from 'App/components/Session/playerContext';
@ -44,7 +44,7 @@ const CurrentTab = React.memo(() => (
)); ));
function AssistTabs({ session }: { session: Record<string, any> }) { function AssistTabs({ session }: { session: Record<string, any> }) {
const history = useHistory(); const navigate = useNavigate();
const { store } = React.useContext(PlayerContext) as unknown as ILivePlayerContext const { store } = React.useContext(PlayerContext) as unknown as ILivePlayerContext
const { recordingState, calling, remoteControl } = store.get() const { recordingState, calling, remoteControl } = store.get()
const isDisabled = recordingState !== 0 || calling !== 0 || remoteControl !== 0 const isDisabled = recordingState !== 0 || calling !== 0 || remoteControl !== 0
@ -63,12 +63,12 @@ function AssistTabs({ session }: { session: Record<string, any> }) {
const openGrid = () => { const openGrid = () => {
if (isDisabled) return; if (isDisabled) return;
const sessionIdQuery = encodeURIComponent(assistMultiviewStore.sessions.map((s) => s.sessionId).join(',')); 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) => { const openLiveSession = (sessionId: string) => {
if (isDisabled) return; if (isDisabled) return;
assistMultiviewStore.setActiveSession(sessionId); assistMultiviewStore.setActiveSession(sessionId);
history.push(withSiteId(liveSession(sessionId), siteId)); navigate(withSiteId(liveSession(sessionId), siteId));
}; };
return ( return (

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import { import {
withSiteId, withSiteId,
multiview, multiview,
@ -24,7 +24,7 @@ function LivePlayerBlockHeader({
const session = sessionStore.current; const session = sessionStore.current;
const closedLive = sessionStore.fetchFailed || (isAssist && !session.live); const closedLive = sessionStore.fetchFailed || (isAssist && !session.live);
const siteId = projectsStore.siteId; const siteId = projectsStore.siteId;
const history = useHistory(); const navigate = useNavigate()
const { width, height } = store.get(); const { width, height } = store.get();
const metaList = customFieldStore.list.map((i: any) => i.key) const metaList = customFieldStore.list.map((i: any) => i.key)
@ -35,7 +35,7 @@ function LivePlayerBlockHeader({
}, []); }, []);
const backHandler = () => { const backHandler = () => {
history.goBack(); navigate(-1)
}; };
const { userId, metadata, isCallActive, agentIds } = session; const { userId, metadata, isCallActive, agentIds } = session;
@ -48,7 +48,7 @@ function LivePlayerBlockHeader({
const openGrid = () => { const openGrid = () => {
const sessionIdQuery = encodeURIComponent(assistMultiviewStore.sessions.map((s) => s?.sessionId).join(',')); const sessionIdQuery = encodeURIComponent(assistMultiviewStore.sessions.map((s) => s?.sessionId).join(','));
return history.push(withSiteId(multiview(sessionIdQuery), siteId)); return navigate(withSiteId(multiview(sessionIdQuery), siteId));
}; };
return ( return (

View file

@ -1,7 +1,7 @@
import cn from 'classnames'; import cn from 'classnames';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { MobilePlayerContext } from 'App/components/Session/playerContext'; import { MobilePlayerContext } from 'App/components/Session/playerContext';
import { FullScreenButton, PlayButton, PlayingState } from 'App/player-ui'; import { FullScreenButton, PlayButton, PlayingState } from 'App/player-ui';
@ -28,7 +28,6 @@ import {
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { session as sessionRoute, withSiteId } from 'App/routes'; import { session as sessionRoute, withSiteId } from 'App/routes';
import { SummaryButton } from 'Components/Session_/Player/Controls/Controls'; import { SummaryButton } from 'Components/Session_/Player/Controls/Controls';
import { MobEventsList, WebEventsList } from "../../../Session_/Player/Controls/EventsList";
import useShortcuts from '../ReplayPlayer/useShortcuts'; import useShortcuts from '../ReplayPlayer/useShortcuts';
export const SKIP_INTERVALS = { export const SKIP_INTERVALS = {
@ -46,7 +45,7 @@ function Controls(props: any) {
const permissions = userStore.account.permissions || []; const permissions = userStore.account.permissions || [];
const disableDevtools = userStore.isEnterprise && !(permissions.includes('DEV_TOOLS') || permissions.includes('SERVICE_DEV_TOOLS')); const disableDevtools = userStore.isEnterprise && !(permissions.includes('DEV_TOOLS') || permissions.includes('SERVICE_DEV_TOOLS'));
const { player, store } = React.useContext(MobilePlayerContext); const { player, store } = React.useContext(MobilePlayerContext);
const history = useHistory(); const navigate = useNavigate()
const { playing, completed, skip, speed, messagesLoading } = store.get(); const { playing, completed, skip, speed, messagesLoading } = store.get();
const { uiPlayerStore, projectsStore } = useStore(); const { uiPlayerStore, projectsStore } = useStore();
const fullscreen = uiPlayerStore.fullscreen; const fullscreen = uiPlayerStore.fullscreen;
@ -68,11 +67,11 @@ function Controls(props: any) {
const sessionTz = session?.timezone; const sessionTz = session?.timezone;
const nextHandler = () => { const nextHandler = () => {
history.push(withSiteId(sessionRoute(nextSessionId), siteId)); navigate(withSiteId(sessionRoute(nextSessionId), siteId));
}; };
const prevHandler = () => { const prevHandler = () => {
history.push(withSiteId(sessionRoute(previousSessionId), siteId)); navigate(withSiteId(sessionRoute(previousSessionId), siteId));
}; };
useShortcuts({ useShortcuts({

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import { sessions as sessionsRoute, withSiteId } from 'App/routes'; import { sessions as sessionsRoute, withSiteId } from 'App/routes';
import { BackLink } from 'UI'; import { BackLink } from 'UI';
import cn from 'classnames'; import cn from 'classnames';
@ -30,7 +30,7 @@ function PlayerBlockHeader(props: Props) {
const { customFieldStore, projectsStore, sessionStore } = useStore(); const { customFieldStore, projectsStore, sessionStore } = useStore();
const session = sessionStore.current; const session = sessionStore.current;
const siteId = projectsStore.siteId!; const siteId = projectsStore.siteId!;
const history = useHistory(); const navigate = useNavigate();
const { const {
fullscreen, fullscreen,
setActiveTab, setActiveTab,
@ -46,7 +46,7 @@ function PlayerBlockHeader(props: Props) {
}, []); }, []);
const backHandler = () => { const backHandler = () => {
history.push(withSiteId(SESSIONS_ROUTE, siteId)); navigate(withSiteId(SESSIONS_ROUTE, siteId));
}; };
const { metadata } = session; const { metadata } = session;

View file

@ -1,6 +1,5 @@
import { useStore } from "App/mstore"; import { useStore } from "App/mstore";
import React from 'react'; import React from 'react';
import { withRouter } from 'react-router-dom';
import { import {
sessions as sessionsRoute, sessions as sessionsRoute,
liveSession as liveSessionRoute, liveSession as liveSessionRoute,
@ -15,6 +14,7 @@ import { PlayerContext } from 'App/components/Session/playerContext';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import stl from './playerBlockHeader.module.css'; import stl from './playerBlockHeader.module.css';
import { IFRAME } from 'App/constants/storageKeys'; import { IFRAME } from 'App/constants/storageKeys';
import { useNavigate } from "react-router";
const SESSIONS_ROUTE = sessionsRoute(); const SESSIONS_ROUTE = sessionsRoute();
@ -28,13 +28,12 @@ function PlayerBlockHeader(props: any) {
const playerState = store?.get?.() || { width: 0, height: 0, showEvents: false } const playerState = store?.get?.() || { width: 0, height: 0, showEvents: false }
const { width = 0, height = 0, showEvents = false } = playerState const { width = 0, height = 0, showEvents = false } = playerState
const metaList = customFieldStore.list.map((i: any) => i.key) const metaList = customFieldStore.list.map((i: any) => i.key)
const navigate = useNavigate();
const { const {
fullscreen, fullscreen,
closedLive = false, closedLive = false,
setActiveTab, setActiveTab,
activeTab, activeTab,
history,
} = props; } = props;
React.useEffect(() => { React.useEffect(() => {
@ -46,12 +45,12 @@ function PlayerBlockHeader(props: any) {
const backHandler = () => { const backHandler = () => {
if ( if (
sessionPath.pathname === history.location.pathname || sessionPath.pathname === location.pathname ||
sessionPath.pathname.includes('/session/') || sessionPath.pathname.includes('/assist/') sessionPath.pathname.includes('/session/') || sessionPath.pathname.includes('/assist/')
) { ) {
history.push(withSiteId(SESSIONS_ROUTE, siteId)); navigate(withSiteId(SESSIONS_ROUTE, siteId));
} else { } else {
history.push( navigate(
sessionPath ? sessionPath.pathname + sessionPath.search : withSiteId(SESSIONS_ROUTE, siteId) sessionPath ? sessionPath.pathname + sessionPath.search : withSiteId(SESSIONS_ROUTE, siteId)
); );
} }
@ -127,4 +126,4 @@ function PlayerBlockHeader(props: any) {
const PlayerHeaderCont = observer(PlayerBlockHeader); const PlayerHeaderCont = observer(PlayerBlockHeader);
export default withRouter(PlayerHeaderCont); export default PlayerHeaderCont;

View file

@ -2,7 +2,7 @@ import React from 'react';
import { client as settingsPath, CLIENT_TABS } from 'App/routes'; import { client as settingsPath, CLIENT_TABS } from 'App/routes';
import { Icon } from 'UI'; import { Icon } from 'UI';
import { LoadingOutlined } from '@ant-design/icons'; import { LoadingOutlined } from '@ant-design/icons';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import { Button } from 'antd'; import { Button } from 'antd';
export function LoadingFetch({ provider }: { provider: string }) { export function LoadingFetch({ provider }: { provider: string }) {
@ -25,7 +25,7 @@ export function FailedFetch({
provider: string; provider: string;
onRetry: () => void; onRetry: () => void;
}) { }) {
const history = useHistory(); const navigate = useNavigate();
const intPath = settingsPath(CLIENT_TABS.INTEGRATIONS); const intPath = settingsPath(CLIENT_TABS.INTEGRATIONS);
return ( return (
<div <div
@ -45,7 +45,7 @@ export function FailedFetch({
Retry Retry
</Button> </Button>
<Button type='text' size='small' onClick={() => history.push(intPath)}> <Button type='text' size='small' onClick={() => navigate(intPath)}>
Check Configuration Check Configuration
</Button> </Button>
</div> </div>

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { Button, Checkbox, Input } from 'antd'; import { Button, Checkbox, Input } from 'antd';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import { withSiteId, sessions } from 'App/routes'; import { withSiteId, sessions } from 'App/routes';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
@ -14,7 +14,7 @@ interface Props {
} }
function SaveModal({ onSave, hideModal }: Props) { function SaveModal({ onSave, hideModal }: Props) {
const history = useHistory(); const navigate = useNavigate();
const { projectsStore, searchStore } = useStore(); const { projectsStore, searchStore } = useStore();
const [name, setName] = React.useState(''); const [name, setName] = React.useState('');
const [ignoreClRage, setIgnoreClRage] = React.useState(false); const [ignoreClRage, setIgnoreClRage] = React.useState(false);
@ -32,7 +32,7 @@ function SaveModal({ onSave, hideModal }: Props) {
'tag', 'tag',
tagId.toString(), tagId.toString(),
) )
history.push( navigate(
withSiteId( withSiteId(
sessions(), sessions(),
siteId siteId

View file

@ -2,7 +2,7 @@ import React from 'react';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { BackLink } from 'UI'; import { BackLink } from 'UI';
import { observer } from 'mobx-react-lite'; 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 { liveSession, assist, withSiteId, multiview } from 'App/routes';
import AssistSessionsModal from 'App/components/Session_/Player/Controls/AssistSessionsModal'; import AssistSessionsModal from 'App/components/Session_/Player/Controls/AssistSessionsModal';
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
@ -19,14 +19,14 @@ function Multiview({
const { showModal, hideModal } = useModal(); const { showModal, hideModal } = useModal();
const { assistMultiviewStore, projectsStore, searchStoreLive, sessionStore } = useStore(); const { assistMultiviewStore, projectsStore, searchStoreLive, sessionStore } = useStore();
const siteId = projectsStore.siteId!; const siteId = projectsStore.siteId!;
const history = useHistory(); const navigate = useNavigate()
// @ts-ignore // @ts-ignore
const { sessionsquery } = useParams(); const { sessionsquery } = useParams();
const total = sessionStore.totalLiveSessions; const total = sessionStore.totalLiveSessions;
const onSessionsChange = (sessions: Array<Record<string, any> | undefined>) => { const onSessionsChange = (sessions: Array<Record<string, any> | undefined>) => {
const sessionIdQuery = encodeURIComponent(sessions.map((s) => s && s.sessionId).join(',')); 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(() => { React.useEffect(() => {
@ -46,12 +46,12 @@ function Multiview({
const openLiveSession = (e: React.MouseEvent, sessionId: string) => { const openLiveSession = (e: React.MouseEvent, sessionId: string) => {
e.stopPropagation(); e.stopPropagation();
assistMultiviewStore.setActiveSession(sessionId); assistMultiviewStore.setActiveSession(sessionId);
history.push(withSiteId(liveSession(sessionId) + '?multi=true', siteId)); navigate(withSiteId(liveSession(sessionId) + '?multi=true', siteId));
}; };
const returnToList = () => { const returnToList = () => {
assistMultiviewStore.reset(); assistMultiviewStore.reset();
history.push(withSiteId(assist(), siteId)); navigate(withSiteId(assist(), siteId));
}; };
const openListModal = () => { const openListModal = () => {

View file

@ -3,7 +3,7 @@ import cn from 'classnames';
import { Icon } from 'UI'; import { Icon } from 'UI';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite'; 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 { multiview, liveSession, withSiteId } from 'App/routes';
interface ITab { interface ITab {
@ -42,7 +42,7 @@ const CurrentTab = React.memo(() => (
)); ));
function AssistTabs({ session }: { session: Record<string, any> }) { function AssistTabs({ session }: { session: Record<string, any> }) {
const history = useHistory(); const navigate = useNavigate()
const { assistMultiviewStore, projectsStore } = useStore(); const { assistMultiviewStore, projectsStore } = useStore();
const siteId = projectsStore.siteId!; const siteId = projectsStore.siteId!;
@ -56,11 +56,11 @@ function AssistTabs({ session }: { session: Record<string, any> }) {
const openGrid = () => { const openGrid = () => {
const sessionIdQuery = encodeURIComponent(assistMultiviewStore.sessions.map((s) => s.sessionId).join(',')); 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) => { const openLiveSession = (sessionId: string) => {
assistMultiviewStore.setActiveSession(sessionId); assistMultiviewStore.setActiveSession(sessionId);
history.push(withSiteId(liveSession(sessionId), siteId)); navigate(withSiteId(liveSession(sessionId), siteId));
}; };
return ( return (

View file

@ -3,7 +3,7 @@ import { Switch } from 'antd';
import cn from 'classnames'; import cn from 'classnames';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import { PlayerContext } from 'App/components/Session/playerContext'; import { PlayerContext } from 'App/components/Session/playerContext';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { FullScreenButton, PlayButton, PlayingState } from 'App/player-ui'; 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 LogsButton from 'App/components/Session/Player/SharedComponents/BackendLogs/LogsButton';
import ControlButton from './ControlButton'; import ControlButton from './ControlButton';
import { WebEventsList } from "./EventsList";
import Timeline from './Timeline'; import Timeline from './Timeline';
import PlayerControls from './components/PlayerControls'; import PlayerControls from './components/PlayerControls';
import styles from './controls.module.css'; import styles from './controls.module.css';
@ -92,7 +91,7 @@ function Controls({ setActiveTab }: any) {
const changeSkipInterval = uiPlayerStore.changeSkipInterval; const changeSkipInterval = uiPlayerStore.changeSkipInterval;
const skipInterval = uiPlayerStore.skipInterval; const skipInterval = uiPlayerStore.skipInterval;
const showStorageRedux = !uiPlayerStore.hiddenHints.storage; const showStorageRedux = !uiPlayerStore.hiddenHints.storage;
const history = useHistory(); const navigate = useNavigate()
const siteId = projectsStore.siteId; const siteId = projectsStore.siteId;
const { const {
playing, playing,
@ -113,11 +112,11 @@ function Controls({ setActiveTab }: any) {
const sessionTz = session?.timezone; const sessionTz = session?.timezone;
const nextHandler = () => { const nextHandler = () => {
history.push(withSiteId(sessionRoute(nextSessionId), siteId)); navigate(withSiteId(sessionRoute(nextSessionId), siteId));
}; };
const prevHandler = () => { const prevHandler = () => {
history.push(withSiteId(sessionRoute(previousSessionId), siteId)); navigate(withSiteId(sessionRoute(previousSessionId), siteId));
}; };
useShortcuts({ useShortcuts({

View file

@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import cn from 'classnames'; import cn from 'classnames';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { withRouter } from 'react-router-dom'; import { useNavigate } from "react-router";
import { Link } from 'UI'; import { Link } from 'UI';
import { Button } from 'antd' import { Button } from 'antd'
import { session as sessionRoute, withSiteId } from 'App/routes'; import { session as sessionRoute, withSiteId } from 'App/routes';
@ -10,7 +10,8 @@ import clsOv from './overlay.module.css';
import AutoplayToggle from 'Shared/AutoplayToggle'; import AutoplayToggle from 'Shared/AutoplayToggle';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
function AutoplayTimer({ history }: any) { function AutoplayTimer() {
const navigate = useNavigate();
let timer: NodeJS.Timer; let timer: NodeJS.Timer;
const [cancelled, setCancelled] = useState(false); const [cancelled, setCancelled] = useState(false);
const [counter, setCounter] = useState(5); const [counter, setCounter] = useState(5);
@ -25,8 +26,8 @@ function AutoplayTimer({ history }: any) {
} }
if (counter === 0) { if (counter === 0) {
const siteId = projectsStore.getSiteId().siteId; const siteId = projectsStore.activeSiteId;
history.push(withSiteId(sessionRoute(nextId), siteId)); navigate(withSiteId(sessionRoute(nextId), siteId));
} }
return () => clearTimeout(timer); return () => clearTimeout(timer);
@ -60,14 +61,10 @@ function AutoplayTimer({ history }: any) {
</Link> </Link>
</div> </div>
</div> </div>
{/* <div className="mt-2 flex items-center color-gray-dark">
Turn on/off auto-replay in <Icon name="ellipsis-v" className="mx-1" /> More options
</div> */}
</div> </div>
</div> </div>
); );
} }
export default withRouter( export default observer(AutoplayTimer)
observer(AutoplayTimer)
);

View file

@ -1,16 +1,16 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { withSiteId, session as sessionRoute } from 'App/routes'; import { withSiteId, session as sessionRoute } from 'App/routes';
import AutoplayToggle from 'Shared/AutoplayToggle/AutoplayToggle'; import AutoplayToggle from 'Shared/AutoplayToggle/AutoplayToggle';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import cn from 'classnames'; import cn from 'classnames';
import { LeftOutlined, RightOutlined } from '@ant-design/icons'; import { LeftOutlined, RightOutlined } from '@ant-design/icons';
import { Button, Popover } from 'antd'; import { Button, Popover } from 'antd';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useParams, useNavigate } from 'react-router';
const PER_PAGE = 10; const PER_PAGE = 10;
interface Props extends RouteComponentProps { interface Props {
defaultList: any; defaultList: any;
currentPage: number; currentPage: number;
latestRequestTime: any; latestRequestTime: any;
@ -24,12 +24,8 @@ function QueueControls(props: Props) {
const total = sessionStore.total; const total = sessionStore.total;
const sessionIds = sessionStore.sessionIds ?? []; const sessionIds = sessionStore.sessionIds ?? [];
const setAutoplayValues = sessionStore.setAutoplayValues; const setAutoplayValues = sessionStore.setAutoplayValues;
const { const { sessionId } = useParams();
match: { const navigate = useNavigate();
// @ts-ignore
params: { sessionId },
},
} = props;
const currentPage = searchStore.currentPage; const currentPage = searchStore.currentPage;
@ -46,13 +42,13 @@ function QueueControls(props: Props) {
}, []); }, []);
const nextHandler = () => { const nextHandler = () => {
const siteId = projectsStore.getSiteId().siteId!; const siteId = projectsStore.activeSiteId!;
props.history.push(withSiteId(sessionRoute(nextId), siteId)); navigate(withSiteId(sessionRoute(nextId), siteId));
}; };
const prevHandler = () => { const prevHandler = () => {
const siteId = projectsStore.getSiteId().siteId!; const siteId = projectsStore.activeSiteId!;
props.history.push(withSiteId(sessionRoute(previousId), siteId)); navigate(withSiteId(sessionRoute(previousId), siteId));
}; };
return ( return (
@ -108,4 +104,4 @@ function QueueControls(props: Props) {
); );
} }
export default withRouter(observer(QueueControls)); export default observer(QueueControls);

View file

@ -1,30 +1,19 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { Icon } from 'UI';
import SignupForm from './SignupForm'; import SignupForm from './SignupForm';
import HealthModal from 'Components/Header/HealthStatus/HealthModal/HealthModal'; import HealthModal from 'Components/Header/HealthStatus/HealthModal/HealthModal';
import { getHealthRequest } from 'Components/Header/HealthStatus/getHealth'; import { getHealthRequest } from 'Components/Header/HealthStatus/getHealth';
import withPageTitle from 'HOCs/withPageTitle'; import withPageTitle from 'HOCs/withPageTitle';
import { login } from 'App/routes'; import { login } from 'App/routes';
import Copyright from 'Shared/Copyright'; import Copyright from 'Shared/Copyright';
import { useNavigate } from "react-router";
const LOGIN_ROUTE = login(); const LOGIN_ROUTE = login();
const BulletItem: React.FC<{ text: string }> = ({ text }) => (
<div className='flex items-center mb-4'>
<div className='mr-3 h-8 w-8 rounded-full bg-white shadow flex items-center justify-center'>
<Icon name='check' size='26' />
</div>
<div>{text}</div>
</div>
);
const healthStatusCheck_key = '__or__healthStatusCheck_key'; const healthStatusCheck_key = '__or__healthStatusCheck_key';
type SignupProps = RouteComponentProps; const Signup = () => {
const navigate = useNavigate();
const Signup: React.FC<SignupProps> = ({ history }) => {
const { userStore } = useStore(); const { userStore } = useStore();
const authDetails = userStore.authStore.authDetails; const authDetails = userStore.authStore.authDetails;
const [healthModalPassed, setHealthModalPassed] = useState<boolean>(localStorage.getItem(healthStatusCheck_key) === 'true'); const [healthModalPassed, setHealthModalPassed] = useState<boolean>(localStorage.getItem(healthStatusCheck_key) === 'true');
@ -47,7 +36,7 @@ const Signup: React.FC<SignupProps> = ({ history }) => {
if (!authDetails) return if (!authDetails) return
if (authDetails) { if (authDetails) {
if (authDetails.tenants) { if (authDetails.tenants) {
history.push(LOGIN_ROUTE); navigate(LOGIN_ROUTE);
} else { } else {
void getHealth(); void getHealth();
} }
@ -79,4 +68,4 @@ const Signup: React.FC<SignupProps> = ({ history }) => {
); );
}; };
export default withRouter(withPageTitle('Signup - OpenReplay')(observer(Signup))); export default withPageTitle('Signup - OpenReplay')(observer(Signup));

View file

@ -1,7 +1,7 @@
import cn from 'classnames'; import cn from 'classnames';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React from 'react'; import React from 'react';
import { useHistory, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { import {
@ -27,7 +27,7 @@ import spotPlayerStore, { PANELS } from './spotPlayerStore';
function SpotPlayer() { function SpotPlayer() {
const defaultHeight = getDefaultPanelHeight(); const defaultHeight = getDefaultPanelHeight();
const history = useHistory(); const navigate = useNavigate()
const [panelHeight, setPanelHeight] = React.useState(defaultHeight); const [panelHeight, setPanelHeight] = React.useState(defaultHeight);
const { spotStore, userStore } = useStore(); const { spotStore, userStore } = useStore();
const userEmail = userStore.account.name; const userEmail = userStore.account.name;
@ -47,7 +47,7 @@ function SpotPlayer() {
if (pubKey) { if (pubKey) {
spotStore.setAccessKey(pubKey); spotStore.setAccessKey(pubKey);
} else { } else {
history.push('/'); navigate('/');
} }
} }
}, [loggedIn]); }, [loggedIn]);

View file

@ -20,7 +20,7 @@ import {
import copy from 'copy-to-clipboard'; import copy from 'copy-to-clipboard';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import Tabs from 'App/components/Session/Tabs'; import Tabs from 'App/components/Session/Tabs';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
@ -58,7 +58,7 @@ function SpotPlayerHeader({
const comments = spotStore.currentSpot?.comments ?? []; const comments = spotStore.currentSpot?.comments ?? [];
const [dropdownOpen, setDropdownOpen] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false);
const history = useHistory(); const navigate = useNavigate()
const onCopy = () => { const onCopy = () => {
copy(window.location.href); copy(window.location.href);
@ -66,7 +66,7 @@ function SpotPlayerHeader({
}; };
const navigateToSpotsList = () => { const navigateToSpotsList = () => {
history.push(spotLink); navigate(spotLink);
}; };
const items: MenuProps['items'] = [ const items: MenuProps['items'] = [
@ -88,7 +88,7 @@ function SpotPlayerHeader({
await downloadFile(url, `${spotStore.currentSpot!.title}.webm`); await downloadFile(url, `${spotStore.currentSpot!.title}.webm`);
} else if (key === '2') { } else if (key === '2') {
spotStore.deleteSpot([spotStore.currentSpot!.spotId]).then(() => { spotStore.deleteSpot([spotStore.currentSpot!.spotId]).then(() => {
history.push(spotsList()); navigate(spotsList());
message.success('Spot successfully deleted'); message.success('Spot successfully deleted');
}); });
} }

View file

@ -12,7 +12,7 @@ import { Button, Checkbox, Dropdown, Tooltip } from 'antd';
import copy from 'copy-to-clipboard'; import copy from 'copy-to-clipboard';
import { Link2 } from 'lucide-react'; import { Link2 } from 'lucide-react';
import React, { useState } from '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 { toast } from 'react-toastify';
import { TextEllipsis } from "UI"; import { TextEllipsis } from "UI";
@ -45,7 +45,7 @@ function SpotListItem({
const [isEdit, setIsEdit] = useState(false); const [isEdit, setIsEdit] = useState(false);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [tooltipText, setTooltipText] = useState('Copy link to clipboard'); const [tooltipText, setTooltipText] = useState('Copy link to clipboard');
const history = useHistory(); const navigate = useNavigate();
const { siteId } = useParams<{ siteId: string }>(); const { siteId } = useParams<{ siteId: string }>();
const menuItems = [ const menuItems = [
@ -104,7 +104,7 @@ function SpotListItem({
const fullLink = `${window.location.origin}${spotLink}`; const fullLink = `${window.location.origin}${spotLink}`;
window.open(fullLink, '_blank'); window.open(fullLink, '_blank');
} else { } else {
history.push(withSiteId(spotUrl(spot.spotId.toString()), siteId)); navigate(withSiteId(spotUrl(spot.spotId.toString()), siteId));
} }
}; };

View file

@ -7,14 +7,14 @@ import {
usabilityTestingView, usabilityTestingView,
usabilityTestingEdit, usabilityTestingEdit,
} from 'App/routes'; } from 'App/routes';
import { useParams, useHistory, Prompt } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router';
import Breadcrumb from 'Shared/Breadcrumb'; import Breadcrumb from 'Shared/Breadcrumb';
import { EditOutlined, DeleteOutlined, MoreOutlined } from '@ant-design/icons'; import { EditOutlined, DeleteOutlined, MoreOutlined } from '@ant-design/icons';
import {Power, Info, ListTodo} from 'lucide-react'; import {Power, Info, ListTodo} from 'lucide-react';
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { confirm } from 'UI'; import { confirm, NavPrompt } from 'UI';
import StepsModal from './StepsModal'; import StepsModal from './StepsModal';
import SidePanel from './SidePanel'; import SidePanel from './SidePanel';
import usePageTitle from 'App/hooks/usePageTitle'; import usePageTitle from 'App/hooks/usePageTitle';
@ -48,12 +48,12 @@ function TestEdit() {
const [isOverviewEditing, setIsOverviewEditing] = React.useState(false); const [isOverviewEditing, setIsOverviewEditing] = React.useState(false);
const { showModal, hideModal } = useModal(); const { showModal, hideModal } = useModal();
const history = useHistory(); const navigate = useNavigate()
usePageTitle(`Usability Tests | ${uxtestingStore.instance ? 'Edit' : 'Create'}`); usePageTitle(`Usability Tests | ${uxtestingStore.instance ? 'Edit' : 'Create'}`);
React.useEffect(() => { React.useEffect(() => {
if (uxtestingStore.instanceCreationSiteId && siteId !== uxtestingStore.instanceCreationSiteId) { if (uxtestingStore.instanceCreationSiteId && siteId !== uxtestingStore.instanceCreationSiteId) {
history.push(withSiteId(usabilityTesting(), siteId)); navigate(withSiteId(usabilityTesting(), siteId));
} }
}, [siteId]); }, [siteId]);
React.useEffect(() => { React.useEffect(() => {
@ -66,7 +66,7 @@ function TestEdit() {
}); });
} else { } else {
if (!uxtestingStore.instance) { if (!uxtestingStore.instance) {
history.push(withSiteId(usabilityTesting(), siteId)); navigate(withSiteId(usabilityTesting(), siteId));
} else { } else {
setConclusion(uxtestingStore.instance!.conclusionMessage); setConclusion(uxtestingStore.instance!.conclusionMessage);
setGuidelines(uxtestingStore.instance!.guidelines); setGuidelines(uxtestingStore.instance!.guidelines);
@ -89,16 +89,16 @@ function TestEdit() {
); );
} else { } else {
toast.success('The usability test is now live and accessible to participants.'); 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 { } else {
uxtestingStore.createNewTest(isPreview).then((test) => { uxtestingStore.createNewTest(isPreview).then((test) => {
if (isPreview) { if (isPreview) {
window.open(`${test.startingPath}?oruxt=${test.testId}`, '_blank', 'noopener,noreferrer'); 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 { } else {
history.push(withSiteId(usabilityTestingView(test.testId), siteId)); navigate(withSiteId(usabilityTestingView(test.testId), siteId));
} }
}); });
} }
@ -128,7 +128,7 @@ function TestEdit() {
}) })
) { ) {
uxtestingStore.deleteTest(testId).then(() => { uxtestingStore.deleteTest(testId).then(() => {
history.push(withSiteId(usabilityTesting(), siteId)); navigate(withSiteId(usabilityTesting(), siteId));
}); });
} }
} }
@ -155,7 +155,7 @@ function TestEdit() {
}, },
]} ]}
/> />
<Prompt <NavPrompt
when={hasChanged} when={hasChanged}
message={() => { message={() => {
return 'You have unsaved changes. Are you sure you want to leave?'; return 'You have unsaved changes. Are you sure you want to leave?';

View file

@ -4,10 +4,10 @@ import { getPdf2 } from 'Components/AssistStats/pdfGenerator';
import { useModal } from 'Components/Modal'; import { useModal } from 'Components/Modal';
import LiveTestsModal from 'Components/UsabilityTesting/LiveTestsModal'; import LiveTestsModal from 'Components/UsabilityTesting/LiveTestsModal';
import React from 'react'; 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 { InfoCircleOutlined } from '@ant-design/icons';
import { withSiteId, usabilityTesting, usabilityTestingEdit } from 'App/routes'; 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 Breadcrumb from 'Shared/Breadcrumb';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
@ -91,7 +91,7 @@ function TestOverview() {
// @ts-ignore // @ts-ignore
const { siteId, testId } = useParams(); const { siteId, testId } = useParams();
const { showModal, hideModal } = useModal(); const { showModal, hideModal } = useModal();
const history = useHistory(); const navigate = useNavigate()
const { uxtestingStore } = useStore(); const { uxtestingStore } = useStore();
React.useEffect(() => { React.useEffect(() => {
@ -99,7 +99,7 @@ function TestOverview() {
try { try {
await uxtestingStore.getTest(testId); await uxtestingStore.getTest(testId);
} catch { } catch {
history.push(withSiteId(usabilityTesting(), siteId)); navigate(withSiteId(usabilityTesting(), siteId));
} }
}; };
@ -375,7 +375,7 @@ const TaskSummary = observer(() => {
const Title = observer(({ testId, siteId }: any) => { const Title = observer(({ testId, siteId }: any) => {
const [truncate, setTruncate] = React.useState(true); const [truncate, setTruncate] = React.useState(true);
const { uxtestingStore } = useStore(); const { uxtestingStore } = useStore();
const history = useHistory(); const navigate = useNavigate()
const handleChange = (value: string) => { const handleChange = (value: string) => {
uxtestingStore.updateTestStatus(value); uxtestingStore.updateTestStatus(value);
@ -411,7 +411,7 @@ const Title = observer(({ testId, siteId }: any) => {
}) })
) { ) {
uxtestingStore.deleteTest(testId).then(() => { 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' confirmButton: 'Edit'
}) })
) { ) {
history.push(withSiteId(usabilityTestingEdit(testId), siteId)); navigate(withSiteId(usabilityTestingEdit(testId), siteId));
} }
}; };

View file

@ -9,7 +9,7 @@ import { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
import { Loader, NoContent, Pagination, Link, Icon } from 'UI'; import { Loader, NoContent, Pagination, Link, Icon } from 'UI';
import { checkForRecent, getDateFromMill } from 'App/date'; import { checkForRecent, getDateFromMill } from 'App/date';
import { ArrowRightOutlined, PlusOutlined } from '@ant-design/icons'; 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 { withSiteId, usabilityTestingEdit, usabilityTestingView } from 'App/routes';
import { debounce } from 'App/utils'; import { debounce } from 'App/utils';
import withPageTitle from 'HOCs/withPageTitle'; import withPageTitle from 'HOCs/withPageTitle';
@ -45,7 +45,7 @@ function TestsTable() {
// @ts-ignore // @ts-ignore
const { siteId } = useParams(); const { siteId } = useParams();
const history = useHistory(); const navigate = useNavigate()
const onClose = (confirmed: boolean) => { const onClose = (confirmed: boolean) => {
if (confirmed) { if (confirmed) {
@ -65,7 +65,7 @@ function TestsTable() {
}; };
const redirect = (path: string) => { const redirect = (path: string) => {
history.push(withSiteId(usabilityTestingEdit(path), siteId)); navigate(withSiteId(usabilityTestingEdit(path), siteId));
}; };
return ( return (
@ -197,10 +197,10 @@ const statusMap = {
function Row({ test, siteId }: { test: UxTListEntry; siteId: string }) { function Row({ test, siteId }: { test: UxTListEntry; siteId: string }) {
const link = usabilityTestingView(test.testId.toString()); const link = usabilityTestingView(test.testId.toString());
const editLink = usabilityTestingEdit(test.testId.toString()); const editLink = usabilityTestingEdit(test.testId.toString());
const history = useHistory(); const navigate = useNavigate()
const redirect = () => { const redirect = () => {
history.push(withSiteId(test.status === 'preview' ? editLink : link, siteId)); navigate(withSiteId(test.status === 'preview' ? editLink : link, siteId));
}; };
return ( return (
<div <div

View file

@ -1,75 +1,77 @@
import React from 'react'; import React from 'react';
import { withRouter } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router';
import { removeQueryParams, addQueryParams, setQueryParams, parseQuery } from 'App/routes'; import { removeQueryParams, addQueryParams, setQueryParams, parseQuery } from 'App/routes';
/* eslint-disable react/sort-comp */
const withLocationHandlers = (propNames) => (BaseComponent) => { const withLocationHandlers = (propNames) => (BaseComponent) => {
@withRouter const WrapperComponent = (props) => {
class WrapperClass extends React.Component { const location = useLocation();
getQuery = (names) => parseQuery(this.props.location, names); const navigate = useNavigate();
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 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]; const namesArray = Array.isArray(names) ? names : [names];
/* to avoid update stack overflow */ /* to avoid update stack overflow */
const actualNames = Object.keys(this.getQuery(namesArray)); const actualNames = Object.keys(getQuery(namesArray));
if (actualNames.length > 0) { 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; const setQuery = (params, replace = false) => {
history[replace ? 'replace' : 'push'](setQueryParams(location, params)); navigate(setQueryParams(location, params), { replace });
};
query = {
all: this.getQuery,
get: this.getParam,
add: this.addQuery,
remove: this.removeQuery,
set: this.setQuery, // TODO: use namespaces
}; };
getHash = () => this.props.location.hash.substring(1); const query = {
setHash = (hash) => { all: getQuery,
const { location, history } = this.props; get: getParam,
history.push({ ...location, hash: `#${hash}` }); add: addQuery,
}; remove: removeQuery,
removeHash = () => { set: setQuery, // TODO: use namespaces
const { location, history } = this.props;
history.push({ ...location, hash: '' });
};
hash = {
get: this.getHash,
set: this.setHash,
remove: this.removeHash,
}; };
getQueryProps() { const getHash = () => location.hash.substring(1);
if (Array.isArray(propNames)) return this.getQuery(propNames);
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') { if (propNames !== null && typeof propNames === 'object') {
const values = Object.values(propNames); const values = Object.values(propNames);
const query = this.getQuery(values); const query = getQuery(values);
const queryProps = {}; const queryProps = {};
Object.keys(propNames).map((key) => { Object.keys(propNames).forEach((key) => {
queryProps[key] = query[propNames[key]]; queryProps[key] = query[propNames[key]];
}); });
return queryProps; return queryProps;
} }
return {}; return {};
} };
render() { const queryProps = getQueryProps();
const queryProps = this.getQueryProps(); return <BaseComponent query={query} hash={hash} {...queryProps} {...props} />;
return <BaseComponent query={this.query} hash={this.hash} {...queryProps} {...this.props} />; };
}
} return WrapperComponent;
return WrapperClass;
}; };
export default withLocationHandlers;
export default withLocationHandlers;

View file

@ -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 <BaseComponent {...other} history={{ ...history, push: push }} />
}))

View file

@ -1,13 +1,15 @@
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import { useStore } from "App/mstore"; import { useStore } from "App/mstore";
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import { useParams, useNavigate } from 'react-router';
const withSiteIdUpdater = (BaseComponent) => { const withSiteIdUpdater = (BaseComponent) => {
const WrapperComponent = (props) => { const WrapperComponent = (props) => {
const { siteId: urlSiteId } = useParams();
const navigate = useNavigate();
const { projectsStore } = useStore(); const { projectsStore } = useStore();
const siteId = projectsStore.siteId; const siteId = projectsStore.siteId;
const setSiteId = projectsStore.setSiteId; const setSiteId = projectsStore.setSiteId;
const urlSiteId = props.match.params.siteId
const prevSiteIdRef = useRef(siteId); const prevSiteIdRef = useRef(siteId);
useEffect(() => { useEffect(() => {
@ -17,15 +19,15 @@ const withSiteIdUpdater = (BaseComponent) => {
}, []); }, []);
useEffect(() => { useEffect(() => {
const { location: { pathname }, history } = props; const pathname = location.pathname;
const shouldUrlUpdate = urlSiteId && parseInt(urlSiteId, 10) !== parseInt(siteId, 10); const shouldUrlUpdate = urlSiteId && parseInt(urlSiteId, 10) !== parseInt(siteId, 10);
if (shouldUrlUpdate) { if (shouldUrlUpdate) {
const path = ['', siteId].concat(pathname.split('/').slice(2)).join('/'); const path = ['', siteId].concat(pathname.split('/').slice(2)).join('/');
history.push(path); navigate(path);
} }
prevSiteIdRef.current = siteId; prevSiteIdRef.current = siteId;
}, [urlSiteId, siteId, props.location.pathname, props.history]); }, [urlSiteId, siteId, location.pathname]);
const key = siteId; const key = siteId;

View file

@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import { Button } from 'antd'; import { Button } from 'antd';
import { useHistory } from 'react-router-dom'; import { useNavigate } from 'react-router';
import { LeftOutlined, ArrowLeftOutlined } from '@ant-design/icons'; import { LeftOutlined, ArrowLeftOutlined } from '@ant-design/icons';
function BackButton({ compact }: { compact?: boolean }) { function BackButton({ compact }: { compact?: boolean }) {
const history = useHistory(); const navigate = useNavigate()
const siteId = location.pathname.split('/')[1]; const siteId = location.pathname.split('/')[1];
const handleBackClick = () => { const handleBackClick = () => {
history.push(`/${siteId}/dashboard`); navigate(`/${siteId}/dashboard`);
}; };
if (compact) { if (compact) {
return ( return (

View file

@ -1,13 +1,13 @@
import React from 'react'; import React from 'react';
import { useNavigate } from "react-router";
import { Icon } from 'UI'; import { Icon } from 'UI';
import cn from 'classnames'; import cn from 'classnames';
import { Step } from 'App/mstore/types/gettingStarted'; import { Step } from 'App/mstore/types/gettingStarted';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { onboarding as onboardingRoute, withSiteId } from 'App/routes'; import { onboarding as onboardingRoute, withSiteId } from 'App/routes';
import { RouteComponentProps, withRouter } from 'react-router';
import { useModal } from 'App/components/Modal'; import { useModal } from 'App/components/Modal';
interface StepListProps extends RouteComponentProps { interface StepListProps {
title: string; title: string;
steps: Step[]; steps: Step[];
status: 'pending' | 'completed'; status: 'pending' | 'completed';
@ -61,6 +61,7 @@ const StepItem = React.memo(
); );
const StepList = React.memo((props: StepListProps) => { const StepList = React.memo((props: StepListProps) => {
const navigate = useNavigate()
const { title, steps } = props; const { title, steps } = props;
const { hideModal } = useModal(); const { hideModal } = useModal();
@ -79,10 +80,9 @@ const StepList = React.memo((props: StepListProps) => {
} }
const onClick = (step: any) => { const onClick = (step: any) => {
const { history } = props;
const siteId = projectsStore.getSiteId().siteId!; const siteId = projectsStore.getSiteId().siteId!;
hideModal(); hideModal();
history.push(withSiteId(onboardingRoute(step.url), siteId)); navigate(withSiteId(onboardingRoute(step.url), siteId));
}; };
return ( return (
@ -97,4 +97,4 @@ const StepList = React.memo((props: StepListProps) => {
); );
}); });
export default withRouter(StepList); export default StepList;

View file

@ -2,12 +2,12 @@ import React from 'react'
import { Icon } from 'UI' import { Icon } from 'UI'
import { Button } from 'antd' import { Button } from 'antd'
import { CLIENT_TABS, client as clientRoute } from 'App/routes'; import { CLIENT_TABS, client as clientRoute } from 'App/routes';
import { withRouter } from 'react-router-dom'; import { useNavigate } from "react-router";
function IntegrateSlackTeamsButton({ history }) {
function IntegrateSlackTeamsButton() {
const navigate = useNavigate();
const gotoPreferencesIntegrations = () => { const gotoPreferencesIntegrations = () => {
history.push(clientRoute(CLIENT_TABS.INTEGRATIONS)); navigate(clientRoute(CLIENT_TABS.INTEGRATIONS));
} }
return ( return (
@ -26,4 +26,4 @@ function IntegrateSlackTeamsButton({ history }) {
) )
} }
export default withRouter(IntegrateSlackTeamsButton) export default IntegrateSlackTeamsButton

View file

@ -3,17 +3,16 @@ import { Alert, Space, Button } from 'antd';
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import { useStore } from "App/mstore"; import { useStore } from "App/mstore";
import { onboarding as onboardingRoute } from 'App/routes'; import { onboarding as onboardingRoute } from 'App/routes';
import { withRouter } from 'react-router-dom';
import * as routes from '../../../routes'; import * as routes from '../../../routes';
import { SquareArrowOutUpRight } from 'lucide-react'; import { SquareArrowOutUpRight } from 'lucide-react';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
const withSiteId = routes.withSiteId; const withSiteId = routes.withSiteId;
const NoSessionsMessage = () => { const NoSessionsMessage = () => {
const { projectsStore } = useStore(); const { projectsStore } = useStore();
const siteId = projectsStore.siteId; const siteId = projectsStore.siteId;
const history = useHistory(); const navigate = useNavigate();
const activeSite = projectsStore.active; const activeSite = projectsStore.active;
const showNoSessions = !!activeSite && !activeSite.recorded; const showNoSessions = !!activeSite && !activeSite.recorded;
const onboardingPath = withSiteId(onboardingRoute('installing'), siteId); const onboardingPath = withSiteId(onboardingRoute('installing'), siteId);
@ -41,7 +40,7 @@ const NoSessionsMessage = () => {
<Button <Button
type="default" type="default"
size="small" size="small"
onClick={() => history.push(onboardingPath)} onClick={() => navigate(onboardingPath)}
> >
Complete Project Setup Complete Project Setup
</Button> </Button>
@ -55,4 +54,4 @@ const NoSessionsMessage = () => {
); );
}; };
export default withRouter(observer(NoSessionsMessage)); export default observer(NoSessionsMessage)

View file

@ -5,11 +5,10 @@ import {
import { Button, Dropdown, MenuProps, Space, Typography } from 'antd'; import { Button, Dropdown, MenuProps, Space, Typography } from 'antd';
import cn from 'classnames'; import cn from 'classnames';
import React from 'react'; import React from 'react';
import { withRouter } from 'react-router-dom'; import { useLocation } from 'react-router'
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { hasSiteId, siteChangeAvailable } from 'App/routes'; import { hasSiteId, siteChangeAvailable } from 'App/routes';
import NewSiteForm from 'Components/Client/Sites/NewSiteForm';
import { Icon } from 'UI'; import { Icon } from 'UI';
import { useModal } from 'Components/ModalContext'; import { useModal } from 'Components/ModalContext';
import ProjectForm from 'Components/Client/Projects/ProjectForm'; import ProjectForm from 'Components/Client/Projects/ProjectForm';
@ -17,15 +16,15 @@ import Project from '@/mstore/types/project';
const { Text } = Typography; const { Text } = Typography;
function ProjectDropdown(props: { location: any }) { function ProjectDropdown() {
const mstore = useStore(); const mstore = useStore();
const location = useLocation();
const { projectsStore, searchStore, searchStoreLive, userStore } = mstore; const { projectsStore, searchStore, searchStoreLive, userStore } = mstore;
const account = userStore.account; const account = userStore.account;
const sites = projectsStore.list; const sites = projectsStore.list;
const siteId = projectsStore.siteId; const siteId = projectsStore.siteId;
const setSiteId = projectsStore.setSiteId; const setSiteId = projectsStore.setSiteId;
const initProject = projectsStore.initProject; const initProject = projectsStore.initProject;
const { location } = props;
const isAdmin = account.admin || account.superAdmin; const isAdmin = account.admin || account.superAdmin;
const activeSite = sites.find((s) => s.id === siteId); const activeSite = sites.find((s) => s.id === siteId);
const showCurrent = const showCurrent =
@ -139,6 +138,4 @@ function ProjectDropdown(props: { location: any }) {
); );
} }
export default withRouter( export default observer(ProjectDropdown)
observer(ProjectDropdown)
);

View file

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { import {
liveSession as liveSessionRoute, liveSession as liveSessionRoute,
@ -31,7 +31,7 @@ interface Props {
function PlayLink(props: Props) { function PlayLink(props: Props) {
const { projectsStore } = useStore(); const { projectsStore } = useStore();
const { isAssist, viewed, sessionId, onClick = null, queryParams } = props; const { isAssist, viewed, sessionId, onClick = null, queryParams } = props;
const history = useHistory(); const navigate = useNavigate()
const defaultIconName = getIconName(viewed); const defaultIconName = getIconName(viewed);
const [iconName, setIconName] = useState<typeof PLAY_ICON_NAMES[keyof typeof PLAY_ICON_NAMES]>(defaultIconName); const [iconName, setIconName] = useState<typeof PLAY_ICON_NAMES[keyof typeof PLAY_ICON_NAMES]>(defaultIconName);
@ -58,7 +58,7 @@ function PlayLink(props: Props) {
} else { } else {
e.preventDefault(); e.preventDefault();
props.beforeOpen(); props.beforeOpen();
history.push(replayLink); navigate(replayLink);
} }
} }
}; };

View file

@ -2,7 +2,6 @@ import cn from 'classnames';
import { Duration } from 'luxon'; import { Duration } from 'luxon';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React, { useState, useCallback, useMemo } from 'react'; import React, { useState, useCallback, useMemo } from 'react';
import { RouteComponentProps, useHistory, withRouter } from 'react-router-dom';
import { durationFormatted, formatTimeOrDate } from 'App/date'; import { durationFormatted, formatTimeOrDate } from 'App/date';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
@ -14,7 +13,7 @@ import {
} from 'App/routes'; } from 'App/routes';
import { capitalize } from 'App/utils'; import { capitalize } from 'App/utils';
import { Avatar, CountryFlag, Icon, Label, TextEllipsis } from 'UI'; import { Avatar, CountryFlag, Icon, Label, TextEllipsis } from 'UI';
import { useLocation } from "react-router";
import Counter from './Counter'; import Counter from './Counter';
import ErrorBars from './ErrorBars'; import ErrorBars from './ErrorBars';
import PlayLink from './PlayLink'; import PlayLink from './PlayLink';
@ -78,8 +77,8 @@ const PREFETCH_STATE = {
fetched: 2 fetched: 2
}; };
function SessionItem(props: RouteComponentProps & Props) { function SessionItem(props: Props) {
const { location } = useHistory(); const location = useLocation();
const { settingsStore, sessionStore } = useStore(); const { settingsStore, sessionStore } = useStore();
const { timezone, shownTimezone } = settingsStore.sessionSettings; const { timezone, shownTimezone } = settingsStore.sessionSettings;
const [prefetchState, setPrefetched] = useState(PREFETCH_STATE.none); 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);

View file

@ -3,7 +3,7 @@ import { FilterKey } from 'Types/filter/filterType';
import SessionItem from 'Shared/SessionItem'; import SessionItem from 'Shared/SessionItem';
import { NoContent, Loader, Pagination, Icon } from 'UI'; import { NoContent, Loader, Pagination, Icon } from 'UI';
import { Button } from 'antd' import { Button } from 'antd'
import { useLocation, withRouter } from 'react-router-dom'; import { useLocation } from 'react-router';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
import { numberWithCommas } from 'App/utils'; import { numberWithCommas } from 'App/utils';
import SessionDateRange from './SessionDateRange'; import SessionDateRange from './SessionDateRange';
@ -237,4 +237,4 @@ function SessionList() {
); );
} }
export default withRouter(observer(SessionList)); export default observer(SessionList);

View file

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

View file

@ -1,6 +1,6 @@
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React from 'react'; import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom'; import { useNavigate, useLocation } from "react-router";
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { import {
@ -16,8 +16,7 @@ import stl from './NoSessionPermission.module.css';
const SESSIONS_ROUTE = sessionsRoute(); const SESSIONS_ROUTE = sessionsRoute();
const ASSIST_ROUTE = assistRoute(); const ASSIST_ROUTE = assistRoute();
interface Props extends RouteComponentProps { interface Props {
history: any;
isLive?: boolean; isLive?: boolean;
} }
function NoSessionPermission(props: Props) { function NoSessionPermission(props: Props) {
@ -26,19 +25,20 @@ function NoSessionPermission(props: Props) {
const sessionPath = sessionStore.sessionPath; const sessionPath = sessionStore.sessionPath;
const isAssist = window.location.pathname.includes('/assist/'); const isAssist = window.location.pathname.includes('/assist/');
const siteId = projectsStore.siteId!; const siteId = projectsStore.siteId!;
const { history } = props; const navigate = useNavigate();
const location = useLocation();
const backHandler = () => { const backHandler = () => {
if ( if (
sessionPath.pathname === history.location.pathname || sessionPath.pathname === location.pathname ||
sessionPath.pathname.includes('/session/') || sessionPath.pathname.includes('/session/') ||
isAssist isAssist
) { ) {
history.push( navigate(
withSiteId(isAssist ? ASSIST_ROUTE : SESSIONS_ROUTE, siteId) withSiteId(isAssist ? ASSIST_ROUTE : SESSIONS_ROUTE, siteId)
); );
} else { } else {
history.push( navigate(
sessionPath sessionPath
? sessionPath.pathname + sessionPath.search ? sessionPath.pathname + sessionPath.search
: withSiteId(SESSIONS_ROUTE, siteId) : withSiteId(SESSIONS_ROUTE, siteId)
@ -50,7 +50,7 @@ function NoSessionPermission(props: Props) {
<div className={stl.wrapper}> <div className={stl.wrapper}>
<Icon name="shield-lock" size="50" className="py-16" /> <Icon name="shield-lock" size="50" className="py-16" />
<div className={stl.title}>Not allowed</div> <div className={stl.title}>Not allowed</div>
{session.isLive ? ( {session.live ? (
<span> <span>
This session is still live, and you dont have the necessary This session is still live, and you dont have the necessary
permissions to access this feature. Please check with your admin. permissions to access this feature. Please check with your admin.
@ -68,6 +68,4 @@ function NoSessionPermission(props: Props) {
); );
} }
export default withRouter( export default observer(NoSessionPermission)
observer(NoSessionPermission)
);

View file

@ -47,4 +47,5 @@ export { default as Message } from './Message';
export { default as Popover } from './Popover'; export { default as Popover } from './Popover';
export { default as Switch } from './Switch'; export { default as Switch } from './Switch';
export { default as Divider } from './Divider'; export { default as Divider } from './Divider';
export { default as CodeBlock } from './CodeBlock' export { default as CodeBlock } from './CodeBlock'
export { default as NavPrompt } from './NavPrompt';

View file

@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useHistory } from 'react-router'; import { useNavigate } from 'react-router';
import { JsonUrlConverter } from 'App/utils/search'; import { JsonUrlConverter } from 'App/utils/search';
import { useStore } from '@/mstore'; import { useStore } from '@/mstore';
import Search from '@/mstore/types/search'; import Search from '@/mstore/types/search';
@ -15,7 +15,7 @@ interface Props {
const useSessionSearchQueryHandler = ({ onBeforeLoad, appliedFilter, loading, onLoaded = () => null }: Props) => { const useSessionSearchQueryHandler = ({ onBeforeLoad, appliedFilter, loading, onLoaded = () => null }: Props) => {
const { searchStore } = useStore(); const { searchStore } = useStore();
const [beforeHookLoaded, setBeforeHookLoaded] = useState(!onBeforeLoad); const [beforeHookLoaded, setBeforeHookLoaded] = useState(!onBeforeLoad);
const history = useHistory(); const navigate = useNavigate()
// Apply filter from the query string when the component mounts // Apply filter from the query string when the component mounts
useEffect(() => { useEffect(() => {
@ -27,7 +27,7 @@ const useSessionSearchQueryHandler = ({ onBeforeLoad, appliedFilter, loading, on
setBeforeHookLoaded(true); setBeforeHookLoaded(true);
} }
const converter = JsonUrlConverter.urlParamsToJson(history.location.search); const converter = JsonUrlConverter.urlParamsToJson(location.search);
const json = getFilterFromJson(converter.toJSON()); const json = getFilterFromJson(converter.toJSON());
const filter = new Search(json); const filter = new Search(json);
@ -57,27 +57,27 @@ const useSessionSearchQueryHandler = ({ onBeforeLoad, appliedFilter, loading, on
}; };
void applyFilterFromQuery(); void applyFilterFromQuery();
}, [loading, searchStore, history.location.search, onBeforeLoad]); }, [loading, searchStore, location.search, onBeforeLoad]);
// Update the URL whenever the appliedFilter changes // Update the URL whenever the appliedFilter changes
useEffect(() => { useEffect(() => {
const updateUrlWithFilter = () => { const updateUrlWithFilter = () => {
if (!loading && beforeHookLoaded) { if (!loading && beforeHookLoaded) {
const query = JsonUrlConverter.jsonToUrlParams(appliedFilter); const query = JsonUrlConverter.jsonToUrlParams(appliedFilter);
history.replace({ search: query }); navigate({ search: query }, { replace: true });
} }
}; };
updateUrlWithFilter(); updateUrlWithFilter();
}, [appliedFilter, loading, beforeHookLoaded, history]); }, [appliedFilter, loading, beforeHookLoaded]);
// Ensure the URL syncs on remount if already parsed // Ensure the URL syncs on remount if already parsed
useEffect(() => { useEffect(() => {
if (searchStore.urlParsed) { if (searchStore.urlParsed) {
const query = JsonUrlConverter.jsonToUrlParams(appliedFilter); const query = JsonUrlConverter.jsonToUrlParams(appliedFilter);
history.replace({ search: query }); navigate({ search: query }, { replace: true });
} }
}, [appliedFilter, searchStore.urlParsed, history]); }, [appliedFilter, searchStore.urlParsed]);
return null; return null;
}; };

View file

@ -1,9 +1,8 @@
import { Divider, Menu, Tag, Typography, Popover, Button } from 'antd'; import { Divider, Menu, Tag, Typography, Popover, Button } from 'antd';
import cn from 'classnames'; import cn from 'classnames';
import React from 'react'; import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useLocation, useNavigate } from 'react-router';
import SupportModal from 'App/layout/SupportModal'; import SupportModal from 'App/layout/SupportModal';
import * as routes from 'App/routes'; import * as routes from 'App/routes';
import { import {
@ -30,15 +29,15 @@ import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
const { Text } = Typography; const { Text } = Typography;
interface Props extends RouteComponentProps { interface Props {
siteId?: string;
isCollapsed?: boolean; isCollapsed?: boolean;
} }
function SideMenu(props: Props) { function SideMenu(props: Props) {
const location = useLocation();
const navigate = useNavigate();
const { const {
location, isCollapsed,
isCollapsed
} = props; } = props;
const isPreferencesActive = location.pathname.includes('/client/'); const isPreferencesActive = location.pathname.includes('/client/');
@ -118,7 +117,7 @@ function SideMenu(props: Props) {
const menuRoutes: any = { const menuRoutes: any = {
[MENU.EXIT]: () => [MENU.EXIT]: () =>
props.history.push(withSiteId(routes.sessions(), siteId)), navigate(withSiteId(routes.sessions(), siteId)),
[MENU.SESSIONS]: () => withSiteId(routes.sessions(), siteId), [MENU.SESSIONS]: () => withSiteId(routes.sessions(), siteId),
[MENU.BOOKMARKS]: () => withSiteId(routes.bookmarks(), siteId), [MENU.BOOKMARKS]: () => withSiteId(routes.bookmarks(), siteId),
[MENU.VAULT]: () => withSiteId(routes.bookmarks(), siteId), [MENU.VAULT]: () => withSiteId(routes.bookmarks(), siteId),
@ -169,7 +168,7 @@ function SideMenu(props: Props) {
}; };
const pushTo = (path: string) => { const pushTo = (path: string) => {
props.history.push(path); navigate(path);
}; };
const RenderDivider = (props: { index: number }) => { 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 SpotMenuItem = ({ isCollapsed }: any) => {
const [isModalVisible, setIsModalVisible] = React.useState(false); const [isModalVisible, setIsModalVisible] = React.useState(false);

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { Modal, Button, List, Divider } from 'antd'; import { Modal, Button, List, Divider } from 'antd';
import { CircleDot, Play, TrendingUp, Radio, Sparkles, Plug, ArrowRight } from 'lucide-react'; 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 { onboarding } from 'App/routes';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
@ -15,7 +15,7 @@ const SpotToOpenReplayPrompt = ({ isVisible, onCancel }: {
onCancel: () => void; onCancel: () => void;
}) => { }) => {
const { userStore } = useStore(); const { userStore } = useStore();
const history = useHistory(); const navigate = useNavigate()
const features = [ const features = [
{ icon: <CircleDot />, text: 'Spot', noBorder: true }, { icon: <CircleDot />, text: 'Spot', noBorder: true },
{ isDivider: true }, { isDivider: true },
@ -28,7 +28,7 @@ const SpotToOpenReplayPrompt = ({ isVisible, onCancel }: {
const onUpgrade = () => { const onUpgrade = () => {
userStore.upgradeScope().then(() => { userStore.upgradeScope().then(() => {
history.push(onboarding()); navigate(onboarding());
onCancel(); onCancel();
}) })
} }

View file

@ -62,8 +62,8 @@
"react-draggable": "^4.4.5", "react-draggable": "^4.4.5",
"react-google-recaptcha": "^2.1.0", "react-google-recaptcha": "^2.1.0",
"react-intersection-observer": "^9.13.1", "react-intersection-observer": "^9.13.1",
"react-router": "^5.3.3", "react-router": "^6.30.0",
"react-router-dom": "^5.3.3", "react-router-dom": "^6.30.0",
"react-select": "^5.3.2", "react-select": "^5.3.2",
"react-svg-map": "^2.2.0", "react-svg-map": "^2.2.0",
"react-toastify": "^9.1.1", "react-toastify": "^9.1.1",

View file

@ -1581,7 +1581,7 @@ __metadata:
languageName: node languageName: node
linkType: hard 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 version: 7.26.9
resolution: "@babel/runtime@npm:7.26.9" resolution: "@babel/runtime@npm:7.26.9"
dependencies: dependencies:
@ -2930,6 +2930,13 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "@sentry/browser@npm:^5.21.1":
version: 5.30.0 version: 5.30.0
resolution: "@sentry/browser@npm:5.30.0" resolution: "@sentry/browser@npm:5.30.0"
@ -8528,20 +8535,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "hls.js@npm:^1.5.13":
version: 1.5.20 version: 1.5.20
resolution: "hls.js@npm:1.5.20" resolution: "hls.js@npm:1.5.20"
@ -8549,7 +8542,7 @@ __metadata:
languageName: node languageName: node
linkType: hard 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 version: 3.3.2
resolution: "hoist-non-react-statics@npm:3.3.2" resolution: "hoist-non-react-statics@npm:3.3.2"
dependencies: dependencies:
@ -9387,13 +9380,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "isarray@npm:^2.0.5":
version: 2.0.5 version: 2.0.5
resolution: "isarray@npm:2.0.5" resolution: "isarray@npm:2.0.5"
@ -10563,7 +10549,7 @@ __metadata:
languageName: node languageName: node
linkType: hard 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 version: 1.4.0
resolution: "loose-envify@npm:1.4.0" resolution: "loose-envify@npm:1.4.0"
dependencies: dependencies:
@ -11663,8 +11649,8 @@ __metadata:
react-draggable: "npm:^4.4.5" react-draggable: "npm:^4.4.5"
react-google-recaptcha: "npm:^2.1.0" react-google-recaptcha: "npm:^2.1.0"
react-intersection-observer: "npm:^9.13.1" react-intersection-observer: "npm:^9.13.1"
react-router: "npm:^5.3.3" react-router: "npm:^6.30.0"
react-router-dom: "npm:^5.3.3" react-router-dom: "npm:^6.30.0"
react-select: "npm:^5.3.2" react-select: "npm:^5.3.2"
react-svg-map: "npm:^2.2.0" react-svg-map: "npm:^2.2.0"
react-toastify: "npm:^9.1.1" react-toastify: "npm:^9.1.1"
@ -11969,15 +11955,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "path-type@npm:^4.0.0":
version: 4.0.0 version: 4.0.0
resolution: "path-type@npm:4.0.0" resolution: "path-type@npm:4.0.0"
@ -13676,7 +13653,7 @@ __metadata:
languageName: node languageName: node
linkType: hard 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 version: 16.13.1
resolution: "react-is@npm:16.13.1" resolution: "react-is@npm:16.13.1"
checksum: 10c1/b1d8be84f510ec19f3dc7cebd9790076f708943e333e03a26dfadc225bd36b3ac6bbdc94a19e1cd72f43d366de47e21fa04396aba0e86ae0e50610fb4c7a47c8 checksum: 10c1/b1d8be84f510ec19f3dc7cebd9790076f708943e333e03a26dfadc225bd36b3ac6bbdc94a19e1cd72f43d366de47e21fa04396aba0e86ae0e50610fb4c7a47c8
@ -13697,39 +13674,27 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-router-dom@npm:^5.3.3": "react-router-dom@npm:^6.30.0":
version: 5.3.4 version: 6.30.0
resolution: "react-router-dom@npm:5.3.4" resolution: "react-router-dom@npm:6.30.0"
dependencies: dependencies:
"@babel/runtime": "npm:^7.12.13" "@remix-run/router": "npm:1.23.0"
history: "npm:^4.9.0" react-router: "npm:6.30.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"
peerDependencies: peerDependencies:
react: ">=15" react: ">=16.8"
checksum: 10c1/1a00acf223dc8ffb7eb6c189dcc858858c4df37b51dd92b5d38aca456b9813596094e19fdcdcc8044daad054c5392de051643988c217e3a296d1df803e5106f4 react-dom: ">=16.8"
checksum: 10c1/00e36b130dcd6fdf7c2867b35ad547b28dfcdbc5b342699baaea785dbfbabd6d3e1d9213c3eb1ffb64e4b418b3a1053882b7ab568a68624f301c2ed88f6d38e5
languageName: node languageName: node
linkType: hard linkType: hard
"react-router@npm:5.3.4, react-router@npm:^5.3.3": "react-router@npm:6.30.0, react-router@npm:^6.30.0":
version: 5.3.4 version: 6.30.0
resolution: "react-router@npm:5.3.4" resolution: "react-router@npm:6.30.0"
dependencies: dependencies:
"@babel/runtime": "npm:^7.12.13" "@remix-run/router": "npm:1.23.0"
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"
peerDependencies: peerDependencies:
react: ">=15" react: ">=16.8"
checksum: 10c1/b920516e84fc10980a108b1ad368c4ebd3b5541c4de1bb071053c91ccc6410d1a7326aed6da6101d6728ca825ec661e73b87518078502e3a4879f5be924af2d6 checksum: 10c1/6d8b9dfcfa38b930f49a511d635f02dd66dfed6c120df08c41ebfc661c6c4aa388b59ac63e01ffba55a3f334e199ce2f9f76f1fd50d14fca52e213373c18368f
languageName: node languageName: node
linkType: hard linkType: hard
@ -14111,13 +14076,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "resolve-pkg-maps@npm:^1.0.0":
version: 1.0.0 version: 1.0.0
resolution: "resolve-pkg-maps@npm:1.0.0" resolution: "resolve-pkg-maps@npm:1.0.0"
@ -15607,20 +15565,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tiny-invariant@npm:^1.0.2, tiny-invariant@npm:^1.3.1": "tiny-invariant@npm:^1.3.1":
version: 1.3.3 version: 1.3.3
resolution: "tiny-invariant@npm:1.3.3" resolution: "tiny-invariant@npm:1.3.3"
checksum: 10c1/e9baa182794cfa0499134b3e7a886ab8be2e2cd15e070eedb81e2ae8ff379b8ddba146cb25093242c6768d127a93e7f2ce774c7be9a8035c1b34d02c9a5b0a8c checksum: 10c1/e9baa182794cfa0499134b3e7a886ab8be2e2cd15e070eedb81e2ae8ff379b8ddba146cb25093242c6768d127a93e7f2ce774c7be9a8035c1b34d02c9a5b0a8c
languageName: node languageName: node
linkType: hard 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": "tinyglobby@npm:^0.2.7":
version: 0.2.12 version: 0.2.12
resolution: "tinyglobby@npm:0.2.12" resolution: "tinyglobby@npm:0.2.12"
@ -16259,13 +16210,6 @@ __metadata:
languageName: node languageName: node
linkType: hard 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": "vary@npm:~1.1.2":
version: 1.1.2 version: 1.1.2
resolution: "vary@npm:1.1.2" resolution: "vary@npm:1.1.2"