diff --git a/studio/src/components/dashboard/graph-command-group.tsx b/studio/src/components/dashboard/graph-command-group.tsx index f9c80215ef..6e12f4f203 100644 --- a/studio/src/components/dashboard/graph-command-group.tsx +++ b/studio/src/components/dashboard/graph-command-group.tsx @@ -120,7 +120,6 @@ function GraphCommandItem({ )} value={value} onSelect={() => { - setNamespace(namespace.name); router.push({ pathname, query: { @@ -128,7 +127,7 @@ function GraphCommandItem({ namespace: namespace.name, ...(isSubgraph ? { subgraphSlug: name } : { slug: name }), } - }); + }).finally(() => setNamespace(namespace.name)); }} > diff --git a/studio/src/components/dashboard/namespace-selector.tsx b/studio/src/components/dashboard/namespace-selector.tsx index b75149d1f1..a7abf058e2 100644 --- a/studio/src/components/dashboard/namespace-selector.tsx +++ b/studio/src/components/dashboard/namespace-selector.tsx @@ -49,19 +49,22 @@ export function NamespaceSelector({ isViewingGraphOrSubgraph, truncateNamespace return (
{isViewingGraphOrSubgraph && ( - setNamespace(namespace.name, false)} - > - {namespace.name} - + <> + setNamespace(namespace.name, false)} + > + {namespace.name} + +
+ )} {!isViewingGraphOrSubgraph && ( diff --git a/studio/src/components/dashboard/workspace-command-wrapper.tsx b/studio/src/components/dashboard/workspace-command-wrapper.tsx index 2ca88c954f..11e73767cc 100644 --- a/studio/src/components/dashboard/workspace-command-wrapper.tsx +++ b/studio/src/components/dashboard/workspace-command-wrapper.tsx @@ -122,20 +122,18 @@ export function WorkspaceCommandWrapper({ No namespace, graph or subgraph matches your criteria.
) : filteredGraphs.map((wns, index) => ( - <> - { - setNamespace(ns, false); - close(); - }} - /> - + { + setNamespace(ns, false); + close(); + }} + /> ))} ) : children} diff --git a/studio/src/components/dashboard/workspace-provider.tsx b/studio/src/components/dashboard/workspace-provider.tsx index 67123d95d2..5e2310864c 100644 --- a/studio/src/components/dashboard/workspace-provider.tsx +++ b/studio/src/components/dashboard/workspace-provider.tsx @@ -37,8 +37,9 @@ export function WorkspaceProvider({ children }: React.PropsWithChildren) { return; } + const actualNamespace = (router.query.namespace as string) || namespace; const currentNamespaces = data.namespaces.map((wns) => wns.name); - if (!currentNamespaces.some((ns) => ns.toLowerCase() === namespace.toLowerCase())) { + if (!currentNamespaces.some((ns) => ns.toLowerCase() === actualNamespace.toLowerCase())) { // The authenticated user doesn't have access to the namespace, pick between the `default` or the // first available namespace if the user doesn't have access to the `default` namespace const ns = currentNamespaces.find((n) => n === DEFAULT_NAMESPACE_NAME) || currentNamespaces[0]; @@ -50,12 +51,21 @@ export function WorkspaceProvider({ children }: React.PropsWithChildren) { namespace: ns, }); } - } else if (!namespaceParam) { - applyParams({ namespace }); + } else if (actualNamespace.toLowerCase() !== namespace.toLowerCase()) { + setNamespace(actualNamespace); + setStoredNamespace(actualNamespace); } setNamespaces(currentNamespaces); - }, [applyParams, data?.response?.code, data?.namespaces, namespace, namespaceParam, setStoredNamespace]); + }, [ + applyParams, + data?.response?.code, + data?.namespaces, + router.query.namespace, + namespace, + namespaceParam, + setStoredNamespace, + ]); // Memoize context components const currentNamespace= useMemo( diff --git a/studio/src/components/dashboard/workspace-selector.tsx b/studio/src/components/dashboard/workspace-selector.tsx index a381231a58..b5feac0db5 100644 --- a/studio/src/components/dashboard/workspace-selector.tsx +++ b/studio/src/components/dashboard/workspace-selector.tsx @@ -20,8 +20,8 @@ export function WorkspaceSelector({ children, truncateNamespace = true }: Worksp const [activeGraph, activeSubgraph] = useMemo( () => { - const routeSegment = router.pathname.split("/")[3]?.toLowerCase(); - const currentSlug = router.query.slug as string; + const routeSegment = router.asPath.split("/")[3]?.toLowerCase(); + const currentSlug = (router.query.slug as string)?.toLowerCase(); return [ routeSegment === "graph" ? namespace.graphs.find((graph) => graph.name.toLowerCase() === currentSlug) @@ -33,13 +33,13 @@ export function WorkspaceSelector({ children, truncateNamespace = true }: Worksp : undefined, ]; }, - [namespace, router.pathname, router.query.slug, subgraphContext?.subgraph?.id], + [namespace, router.asPath, router.query.slug, subgraphContext?.subgraph?.id], ); const isViewingGraphOrSubgraph = !!activeGraph || !!activeSubgraph; return (
{ const organizationSlug = router.query.organizationSlug as string; const checkUserAccess = useCheckUserAccess(); const [isStarBannerDisabled, setDisableStarBanner] = useStarBannerDisabled(); + const { namespace } = useWorkspace(); const isAdmin = checkUserAccess({ rolesToBe: ["organization-admin" ]}); const isAdminOrDeveloper = checkUserAccess({ rolesToBe: ["organization-admin", "organization-developer"] }); @@ -134,21 +136,22 @@ export const DashboardLayout = ({ children }: LayoutProps) => { const links = useMemo(() => { const basePath = `/${user?.currentOrganization.slug || organizationSlug}`; + const nsQueryString = `?namespace=${encodeURIComponent(namespace.name)}`; const navigation: Partial[] = [ { title: "Graphs", - href: basePath + "/graphs", + href: basePath + `/graphs${nsQueryString}`, icon: , }, { title: "Subgraphs", - href: basePath + "/subgraphs", + href: basePath + `/subgraphs${nsQueryString}`, icon: , }, { title: "Feature Flags", - href: basePath + "/feature-flags", + href: basePath + `/feature-flags${nsQueryString}`, icon: , matchExact: false, separator: !isAdminOrDeveloper, @@ -159,17 +162,17 @@ export const DashboardLayout = ({ children }: LayoutProps) => { navigation.push( { title: "Policies", - href: basePath + "/policies", + href: basePath + `/policies${nsQueryString}`, icon: , }, { title: "Check Extensions", - href: basePath + "/check-extensions", + href: basePath + `/check-extensions${nsQueryString}`, icon: , }, { title: "Cache Warmer", - href: basePath + "/cache-warmer", + href: basePath + `/cache-warmer${nsQueryString}`, icon: , separator: true, }, @@ -279,8 +282,10 @@ export const DashboardLayout = ({ children }: LayoutProps) => { organizationSlug, plans.data?.plans?.length, user?.currentOrganization.slug, + namespace, isAdmin, isAdminOrDeveloper, + isApiKeyManager, user?.invitations, ]); diff --git a/studio/src/components/layout/sidenav.tsx b/studio/src/components/layout/sidenav.tsx index ee5945b374..abea732085 100644 --- a/studio/src/components/layout/sidenav.tsx +++ b/studio/src/components/layout/sidenav.tsx @@ -88,8 +88,8 @@ const MobileNav = () => { const Organizations = () => { const user = useContext(UserContext); const router = useRouter(); - const currentPage = router.asPath.split("/")[2]; - const isOrganizationRoot = router.asPath.split("/").length === 3; + const pathSegments = router.pathname.substring(1).split('/'); + const isOrganizationRoot = pathSegments.length === 2; if (!user?.currentOrganization) return null; @@ -112,18 +112,23 @@ const Organizations = () => { (org) => org.slug === orgSlug, ); if (currentOrg) { - router.replace( - isOrganizationRoot && currentPage !== "invitations" - ? `/${currentOrg.slug}/${currentPage}` - : `/${currentOrg.slug}`, - ); + router.replace({ + pathname: isOrganizationRoot && + pathSegments[0]?.toLowerCase() !== "account" && + pathSegments[1]?.toLowerCase() !== "invitations" + ? `/[organizationSlug]/${pathSegments[1]}` + : `/[organizationSlug]`, + query: { + organizationSlug: currentOrg.slug, + }, + }); } }} > {user?.organizations?.map(({ name, slug }) => { return ( - - {name} + + {name} ); })} @@ -199,7 +204,7 @@ export const SideNav = (props: SideNavLayoutProps) => { const isCurrent = item.href && isActive( - encodeURI(item.href), + encodeURI(item.href.split("?")[0]), router.asPath.split("?")[0], item.matchExact, );