diff --git a/src-web/components/AppRouter.tsx b/src-web/components/AppRouter.tsx index c3e3725c..1f01ffd4 100644 --- a/src-web/components/AppRouter.tsx +++ b/src-web/components/AppRouter.tsx @@ -1,19 +1,18 @@ -import { createBrowserRouter, Navigate, Outlet, RouterProvider } from 'react-router-dom'; +import { createBrowserRouter, Navigate, Outlet, RouterProvider, useParams } from 'react-router-dom'; import { routePaths, useAppRoutes } from '../hooks/useAppRoutes'; -import { useRecentRequests } from '../hooks/useRecentRequests'; import { useHttpRequests } from '../hooks/useHttpRequests'; +import { useRecentRequests } from '../hooks/useRecentRequests'; +import { DialogProvider } from './DialogContext'; import { GlobalHooks } from './GlobalHooks'; +import RouteError from './RouteError'; import Workspace from './Workspace'; import Workspaces from './Workspaces'; -import { DialogProvider } from './DialogContext'; -import { useActiveEnvironmentId } from '../hooks/useActiveEnvironmentId'; -import RouteError from './RouteError'; const router = createBrowserRouter([ { path: '/', errorElement: , - element: , + element: , children: [ { path: '/', @@ -26,18 +25,20 @@ const router = createBrowserRouter([ { path: routePaths.workspace({ workspaceId: ':workspaceId', - environmentId: ':environmentId', }), element: , }, { path: routePaths.request({ workspaceId: ':workspaceId', - environmentId: ':environmentId', requestId: ':requestId', }), element: , }, + { + path: '/workspaces/:workspaceId/environments/:environmentId/requests/:requestId', + element: , + }, ], }, ]); @@ -48,7 +49,6 @@ export function AppRouter() { function WorkspaceOrRedirect() { const recentRequests = useRecentRequests(); - const activeEnvironmentId = useActiveEnvironmentId(); const requests = useHttpRequests(); const request = requests.find((r) => r.id === recentRequests[0]); const routes = useAppRoutes(); @@ -58,20 +58,43 @@ function WorkspaceOrRedirect() { } const { id: requestId, workspaceId } = request; - const environmentId = activeEnvironmentId ?? undefined; return ( ); } -function Layout() { +function RedirectLegacyEnvironmentURLs() { + const routes = useAppRoutes(); + const { + requestId, + environmentId: rawEnvironmentId, + workspaceId, + } = useParams<{ + requestId?: string; + workspaceId?: string; + environmentId?: string; + }>(); + const environmentId = rawEnvironmentId === '__default__' ? undefined : rawEnvironmentId; + + let to = '/'; + if (workspaceId != null && requestId != null) { + to = routes.paths.request({ workspaceId, environmentId, requestId }); + } else if (workspaceId != null) { + to = routes.paths.workspace({ workspaceId, environmentId }); + } else { + to = routes.paths.workspaces(); + } + + return ; +} + +function DefaultLayout() { return ( diff --git a/src-web/hooks/useActiveEnvironmentId.ts b/src-web/hooks/useActiveEnvironmentId.ts index a0b333c8..71f14d33 100644 --- a/src-web/hooks/useActiveEnvironmentId.ts +++ b/src-web/hooks/useActiveEnvironmentId.ts @@ -1,9 +1,11 @@ -import { useParams } from 'react-router-dom'; -import type { RouteParamsRequest } from './useAppRoutes'; +import { useSearchParams } from 'react-router-dom'; + +export const QUERY_ENVIRONMENT_ID = 'environment_id'; export function useActiveEnvironmentId(): string | null { - const { environmentId } = useParams(); - if (environmentId == null || environmentId === '__default__') { + const [params] = useSearchParams(); + const environmentId = params.get(QUERY_ENVIRONMENT_ID); + if (environmentId == null) { return null; } diff --git a/src-web/hooks/useAppRoutes.ts b/src-web/hooks/useAppRoutes.ts index 664c01dc..07110e83 100644 --- a/src-web/hooks/useAppRoutes.ts +++ b/src-web/hooks/useAppRoutes.ts @@ -1,4 +1,5 @@ import { useNavigate } from 'react-router-dom'; +import { QUERY_ENVIRONMENT_ID } from './useActiveEnvironmentId'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; import { useActiveRequestId } from './useActiveRequestId'; import type { Environment } from '../lib/models'; @@ -23,7 +24,11 @@ export const routePaths = { environmentId: ':environmentId', } as RouteParamsWorkspace, ) { - return `/workspaces/${workspaceId}/environments/${environmentId ?? '__default__'}`; + let path = `/workspaces/${workspaceId}`; + if (environmentId != null) { + path += `?${QUERY_ENVIRONMENT_ID}=${encodeURIComponent(environmentId)}`; + } + return path; }, request( { workspaceId, environmentId, requestId } = { @@ -32,7 +37,11 @@ export const routePaths = { requestId: ':requestId', } as RouteParamsRequest, ) { - return `${this.workspace({ workspaceId, environmentId })}/requests/${requestId}`; + let path = `/workspaces/${workspaceId}/requests/${requestId}`; + if (environmentId != null) { + path += `?${QUERY_ENVIRONMENT_ID}=${encodeURIComponent(environmentId)}`; + } + return path; }, }; @@ -41,16 +50,16 @@ export function useAppRoutes() { const requestId = useActiveRequestId(); const nav = useNavigate(); - const navigate = useCallback(( - path: T, - ...params: Parameters<(typeof routePaths)[T]> - ) => { - // Not sure how to make TS work here, but it's good from the - // outside caller perspective. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const resolvedPath = routePaths[path](...(params as any)); - nav(resolvedPath); - }, [nav]); + const navigate = useCallback( + (path: T, ...params: Parameters<(typeof routePaths)[T]>) => { + // Not sure how to make TS work here, but it's good from the + // outside caller perspective. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const resolvedPath = routePaths[path](...(params as any)); + nav(resolvedPath); + }, + [nav], + ); const setEnvironment = useCallback( (environment: Environment | null) => {