Metadata indexing is not activated on {totalProjectsText}.{" "}
-
+
Activate indexing on your projects
{" "}
to properly integrate them with Renku.
diff --git a/client/src/features/dashboard/components/ProjectsDashboard.tsx b/client/src/features/dashboard/components/ProjectsDashboard.tsx
index 5f6c93c6ea..09f5fc47e6 100644
--- a/client/src/features/dashboard/components/ProjectsDashboard.tsx
+++ b/client/src/features/dashboard/components/ProjectsDashboard.tsx
@@ -92,21 +92,21 @@ function ProjectAlert({ total }: ProjectAlertProps) {
,{" "}
{" "}
or{" "}
{" "}
for a specific project or dataset.
diff --git a/client/src/features/rootV1/ProjectRootV1.tsx b/client/src/features/rootV1/ProjectRootV1.tsx
new file mode 100644
index 0000000000..a34125a619
--- /dev/null
+++ b/client/src/features/rootV1/ProjectRootV1.tsx
@@ -0,0 +1,71 @@
+/*!
+ * Copyright 2025 - Swiss Data Science Center (SDSC)
+ * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
+ * Eidgenössische Technische Hochschule Zürich (ETHZ).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+import { Route, Routes } from "react-router-dom-v5-compat";
+import ContainerWrap from "../../components/container/ContainerWrap";
+import LazyNotFound from "../../not-found/LazyNotFound";
+import LazyProjectList from "../../project/list/LazyProjectList";
+import LazyNewProject from "../../project/new/LazyNewProject";
+import { RELATIVE_ROUTES } from "../../routing/routes.constants";
+
+export default function RootV1() {
+ return (
+
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+ );
+}
diff --git a/client/src/features/rootV1/RootV1.tsx b/client/src/features/rootV1/RootV1.tsx
index fd6608e371..9ebb6189a0 100644
--- a/client/src/features/rootV1/RootV1.tsx
+++ b/client/src/features/rootV1/RootV1.tsx
@@ -33,6 +33,8 @@ import LazySearchPage from "../kgSearch/LazySearchPage";
import LazySecrets from "../secrets/LazySecrets";
import LazyAnonymousSessionsList from "../session/components/LazyAnonymousSessionsList";
+import ProjectRootV1 from "./ProjectRootV1";
+
export default function RootV1({
user,
}: {
@@ -101,6 +103,14 @@ export default function RootV1({
}
/>
+
+
+
+ }
+ />
@@ -121,12 +124,11 @@ function FooterNavbarLoggedInLinks({ privacyLink }) {
}
function FooterNavbar() {
- const location = useLocation();
-
- return ;
+ return ;
}
-function FooterNavbarInner({ location }) {
+function FooterNavbarInner() {
+ const location = useLocation();
const projectMetadata = useLegacySelector(
(state) => state.stateModel.project?.metadata
);
@@ -190,7 +192,10 @@ function FooterNavbarInner({ location }) {
location.pathname === Url.get(Url.pages.landing) ? (
) : (
-
+
)}
diff --git a/client/src/namespace/Namespace.present.jsx b/client/src/namespace/Namespace.present.jsx
index 602319fda5..1f323a378e 100644
--- a/client/src/namespace/Namespace.present.jsx
+++ b/client/src/namespace/Namespace.present.jsx
@@ -31,20 +31,21 @@ import cx from "classnames";
import { ExternalLink } from "../components/ExternalLinks";
import { Loader } from "../components/Loader";
import NotFound from "../not-found/NotFound";
+import { ABSOLUTE_ROUTES } from "../routing/routes.constants";
const fakeHistory = createMemoryHistory({
initialEntries: ["/"],
initialIndex: 0,
});
fakeHistory.push({
- pathname: "/projects",
+ pathname: ABSOLUTE_ROUTES.v1.projects.root,
search: "?page=1",
});
const NamespaceProjects = (props) => {
const { namespace } = props;
// TODO: I should get the URLs from the redux store: #779
- const searchUrl = "/projects/all";
+ const searchUrl = ABSOLUTE_ROUTES.v1.projects.all;
const searchProjectUrl = (project) => {
return `${searchUrl}?q=${project}`;
};
diff --git a/client/src/not-found/NotFound.tsx b/client/src/not-found/NotFound.tsx
index d28b8c1288..87d9812287 100644
--- a/client/src/not-found/NotFound.tsx
+++ b/client/src/not-found/NotFound.tsx
@@ -24,7 +24,7 @@
*/
import cx from "classnames";
import { ReactNode } from "react";
-import { Link } from "react-router-dom";
+import { Link, useLocation } from "react-router-dom-v5-compat";
import { ArrowLeft } from "react-bootstrap-icons";
import ContainerWrap from "../components/container/ContainerWrap";
@@ -44,6 +44,7 @@ export default function NotFound({
description: description_,
children,
}: NotFoundProps) {
+ const location = useLocation();
const isV2 = !isRenkuLegacy(location.pathname);
const title = title_ ?? "Page not found";
const description =
diff --git a/client/src/routing/routes.constants.ts b/client/src/routing/routes.constants.ts
index 1ede1423d0..aa62ee9d65 100644
--- a/client/src/routing/routes.constants.ts
+++ b/client/src/routing/routes.constants.ts
@@ -18,6 +18,29 @@
export const ABSOLUTE_ROUTES = {
root: "/",
+ v1: {
+ root: "/v1",
+ inactiveKGProjects: "/v1/inactive-kg-projects",
+ search: "/v1/search",
+ projects: {
+ root: "/v1/projects",
+ all: "/v1/projects/all",
+ new: "/v1/projects/new",
+ starred: "/v1/projects/starred",
+ },
+ help: {
+ root: "/v1/help",
+ contact: "/v1/help/contact",
+ status: "/v1/help/status",
+ release: "/v1/help/release",
+ tos: "/v1/help/tos",
+ privacy: "/v1/help/privacy",
+ },
+ notifications: "/v1/notifications",
+ styleGuide: "/v1/style-guide",
+ secrets: "/v1/secrets",
+ sessions: "/v1/sessions",
+ },
v2: {
root: "/v2",
user: "/v2/user",
@@ -56,22 +79,6 @@ export const ABSOLUTE_ROUTES = {
connectedServices: "/v2/connected-services",
secrets: "/v2/secrets",
},
- v1: {
- root: "/v1",
- search: "/v1/search",
- help: {
- root: "/v1/help",
- contact: "/v1/help/contact",
- status: "/v1/help/status",
- release: "/v1/help/release",
- tos: "/v1/help/tos",
- privacy: "/v1/help/privacy",
- },
- notifications: "/v1/notifications",
- styleGuide: "/v1/style-guide",
- secrets: "/v1/secrets",
- sessions: "/v1/sessions",
- },
} as const;
export const RELATIVE_ROUTES = {
@@ -79,14 +86,20 @@ export const RELATIVE_ROUTES = {
datasets: "/datasets",
projects: "/projects",
v1: {
- root: "/v1",
- search: "search",
+ root: "v1/*",
+ projects: {
+ root: "projects/*",
+ all: "all",
+ new: "new",
+ starred: "starred",
+ },
help: "help/*",
- sessions: "sessions",
+ inactiveKGProjects: "inactive-kg-projects",
notifications: "notifications",
+ search: "search",
secrets: "secrets",
+ sessions: "sessions",
styleGuide: "style-guide",
- inactiveKGProjects: "inactive-kg-projects",
},
v2: {
root: "v2/*",
diff --git a/client/src/utils/helpers/url/Url.js b/client/src/utils/helpers/url/Url.js
index 48eee2d36a..8464928689 100644
--- a/client/src/utils/helpers/url/Url.js
+++ b/client/src/utils/helpers/url/Url.js
@@ -147,7 +147,7 @@ function searchValidation(data) {
function projectsSearchUrlBuilder(subSection) {
return (data) => {
// create base url
- let url = subSection ? `/projects/${subSection}` : "/projects";
+ let url = subSection ? `/v1/projects/${subSection}` : "/v1/projects";
// add optional parameters
if (!data || !Object.keys(data).length) return url;
@@ -164,7 +164,7 @@ function projectsSearchUrlBuilder(subSection) {
function projectNewUrlBuilder() {
return (data) => {
// create base url
- let url = "/projects/new";
+ let url = "/v1/projects/new";
if (!data || !data.data) return url;
const search = new URLSearchParams();
search.append("data", data.data);
@@ -271,33 +271,33 @@ const Url = {
// Please assign only strings or UrlRule objects.
pages: {
landing: "/",
- search: "/search",
- inactiveKgProjects: "/inactive-kg-projects",
+ search: "/v1/search",
+ inactiveKgProjects: "/v1/inactive-kg-projects",
help: {
- base: "/help",
- documentation: "/help/docs",
- getting: "/help/getting",
- privacy: "/help/privacy",
- release: "/help/release",
- status: "/help/status",
- tos: "/help/tos",
+ base: "/v1/help",
+ documentation: "/v1/help/docs",
+ getting: "/v1/help/getting",
+ privacy: "/v1/help/privacy",
+ release: "/v1/help/release",
+ status: "/v1/help/status",
+ tos: "/v1/help/tos",
},
projects: {
base: new UrlRule(projectsSearchUrlBuilder(), [], searchValidation, [
- "/projects",
- "/projects?q=test&page=1&orderBy=last_activity_at&orderSearchAsc=false&searchIn=projects",
+ "/v1/projects",
+ "/v1/projects?q=test&page=1&orderBy=last_activity_at&orderSearchAsc=false&searchIn=projects",
]),
all: new UrlRule(projectsSearchUrlBuilder("all"), [], searchValidation, [
- "/projects/all",
- "/projects/all?q=test&page=1&orderBy=last_activity_at&orderSearchAsc=false&searchIn=projects",
+ "/v1/projects/all",
+ "/v1/projects/all?q=test&page=1&orderBy=last_activity_at&orderSearchAsc=false&searchIn=projects",
]),
starred: new UrlRule(
projectsSearchUrlBuilder("starred"),
[],
searchValidation,
[
- "/projects/starred",
- "/projects/starred?q=test&page=1&orderBy=last_activity_at&orderSearchAsc=false&searchIn=projects",
+ "/v1/projects/starred",
+ "/v1/projects/starred?q=test&page=1&orderBy=last_activity_at&orderSearchAsc=false&searchIn=projects",
]
),
},
@@ -309,8 +309,8 @@ const Url = {
["/projects/namespace/path", "/projects/group/subgroup/path"]
),
new: new UrlRule(projectNewUrlBuilder(), [], null, [
- "/projects/new",
- "/projects/new?data=eyJ0aXRsZSI6InRlC3QifQ==",
+ "/v1/projects/new",
+ "/v1/projects/new?data=eyJ0aXRsZSI6InRlC3QifQ==",
]),
datasets: {
base: new UrlRule(projectDatabaseUrlBuilder(), ["path"], null, [
@@ -466,7 +466,7 @@ const Url = {
base: "/datasets",
},
searchEntities: {
- base: "/search",
+ base: "/v1/search",
},
secrets: {
base: "/secrets",
diff --git a/client/src/utils/helpers/url/Url.test.js b/client/src/utils/helpers/url/Url.test.js
index 18ac977af3..6a944a3ccf 100644
--- a/client/src/utils/helpers/url/Url.test.js
+++ b/client/src/utils/helpers/url/Url.test.js
@@ -267,10 +267,10 @@ describe("Url helper class", () => {
const { get, pages } = Url;
expect(get(pages.landing)).toBe("/");
- expect(get(pages.projects)).toBe("/projects");
- expect(get(pages.projects.all)).toBe("/projects/all");
+ expect(get(pages.projects)).toBe("/v1/projects");
+ expect(get(pages.projects.all)).toBe("/v1/projects/all");
expect(get(pages.projects.all, { q: "test", searchIn: "projects" })).toBe(
- "/projects/all?q=test&searchIn=projects"
+ "/v1/projects/all?q=test&searchIn=projects"
);
expect(() => {
diff --git a/client/src/websocket/handlers/kgActivationStatusHandler.ts b/client/src/websocket/handlers/kgActivationStatusHandler.ts
index 3ea9b88f38..79ff55949a 100644
--- a/client/src/websocket/handlers/kgActivationStatusHandler.ts
+++ b/client/src/websocket/handlers/kgActivationStatusHandler.ts
@@ -25,6 +25,7 @@ import {
updateProgress,
} from "../../features/inactiveKgProjects/inactiveKgProjectsSlice";
import type { KgInactiveProjectsState } from "../../features/inactiveKgProjects/inactiveKgProjectsSlice";
+import { ABSOLUTE_ROUTES } from "../../routing/routes.constants";
type ActivationStatus = {
[key: string]: number;
@@ -105,9 +106,9 @@ function processStatusForNotifications(
notifications.addSuccess(
notifications.Topics.KG_ACTIVATION,
"Project indexing has been activated.",
- "/inactive-kg-projects",
+ ABSOLUTE_ROUTES.v1.inactiveKGProjects,
"Go to activation page",
- "/inactive-kg-projects",
+ ABSOLUTE_ROUTES.v1.inactiveKGProjects,
"Check the status of projects that need to be indexed."
);
}
@@ -115,9 +116,9 @@ function processStatusForNotifications(
notifications.addError(
notifications.Topics.KG_ACTIVATION,
"Project indexing has been activated, but with errors.",
- "/inactive-kg-projects",
+ ABSOLUTE_ROUTES.v1.inactiveKGProjects,
"Go to activation page",
- "/inactive-kg-projects",
+ ABSOLUTE_ROUTES.v1.inactiveKGProjects,
"Check the status of projects that need to be indexed"
);
}
diff --git a/tests/cypress/e2e/newProject.spec.ts b/tests/cypress/e2e/newProject.spec.ts
index e143dfe4c7..624d383786 100644
--- a/tests/cypress/e2e/newProject.spec.ts
+++ b/tests/cypress/e2e/newProject.spec.ts
@@ -26,7 +26,7 @@ describe("Add new project", () => {
beforeEach(() => {
fixtures.config().versions().userTest().namespaces();
fixtures.projects().landingUserProjects();
- cy.visit("projects/new");
+ cy.visit("/v1/projects/new");
});
it("create a new project that should change name", () => {
@@ -183,7 +183,7 @@ describe("Add new project shared link", () => {
namespace: "internal-space",
name: "getInternalNamespace",
});
- cy.visit(`projects/new${customValues}`);
+ cy.visit(`/v1/projects/new${customValues}`);
cy.wait("@getTemplates");
// Check feedback messages
@@ -228,7 +228,7 @@ describe("Add new project shared link", () => {
namespace: "internal-space",
name: "getInternalNamespace",
});
- cy.visit(`projects/new${customValues}`);
+ cy.visit(`/v1/projects/new${customValues}`);
cy.wait("@getTemplates");
// Check feedback messages
@@ -249,7 +249,7 @@ describe("Add new project shared link", () => {
namespace: "internal-space",
name: "getInternalNamespace",
});
- cy.visit(`projects/new${customValues}`);
+ cy.visit(`/v1/projects/new${customValues}`);
cy.wait("@getTemplates");
// Check feedback messages
@@ -278,7 +278,7 @@ describe("Add new project shared link", () => {
namespace: "internal-space",
name: "getInternalNamespace",
});
- cy.visit(`projects/new${customValues}`);
+ cy.visit(`/v1/projects/new${customValues}`);
cy.wait("@getTemplates");
// Check feedback messages
@@ -310,7 +310,7 @@ describe("Add new project shared link", () => {
namespace: "internal-space",
name: "getInternalNamespace",
});
- cy.visit(`projects/new${customValues}`);
+ cy.visit(`/v1/projects/new${customValues}`);
cy.wait("@getTemplates");
// Check feedback messages
@@ -337,7 +337,7 @@ describe("Add new project shared link", () => {
namespace: "internal-space",
name: "getInternalNamespace",
});
- cy.visit(`projects/new${customValues}`);
+ cy.visit(`/v1/projects/new${customValues}`);
cy.wait("@getTemplates");
// Check feedback messages