From d5dd04b2bcad4275b95a54fcfd0c12a5f761c972 Mon Sep 17 00:00:00 2001 From: Chandrasekhar Ramakrishnan Date: Wed, 5 Feb 2025 11:18:13 +0100 Subject: [PATCH] refactor: adopt typed v2-centric URL scheme (#3504) --- client/scripts/generate_sitemap.sh | 6 - client/src/App.jsx | 9 +- .../EarlyAccessBanner.module.scss | 9 - .../earlyAccessBanner/EarlyAccessBanner.tsx | 154 ------------------ .../src/components/navbar/AnonymousNavBar.tsx | 2 +- .../src/components/navbar/LoggedInNavBar.tsx | 2 +- client/src/components/navbar/NavBarItems.tsx | 2 +- ...navbar.constans.ts => navbar.constants.ts} | 0 client/src/error-boundary/ErrorBoundary.tsx | 13 +- .../CodeRepositoryDisplay.tsx | 4 +- .../src/features/dashboardV2/DashboardV2.tsx | 11 +- client/src/features/groupsV2/new/GroupNew.tsx | 7 +- .../settings/GroupSettingsMetadata.tsx | 9 +- .../platform/components/StatusBanner.tsx | 4 +- .../fields/SlugPreviewFormField.tsx | 16 +- .../projectsV2/fields/formField.types.ts | 2 +- .../projectsV2/list/ProjectV2ListDisplay.tsx | 12 +- .../features/projectsV2/new/ProjectV2New.tsx | 11 +- client/src/features/rootV2/RootV2.tsx | 53 +++--- client/src/landing/NavBar.jsx | 26 ++- client/src/not-found/NotFound.tsx | 3 +- client/src/routing/routes.constants.ts | 82 +++++----- tests/cypress/e2e/connectedServicesV2.spec.ts | 8 +- tests/cypress/e2e/dashboardV2.spec.ts | 10 +- tests/cypress/e2e/groupV2.spec.ts | 16 +- .../groupV2DataConnectorCredentials.spec.ts | 30 ++-- tests/cypress/e2e/maintenance.spec.ts | 2 +- tests/cypress/e2e/navV2.spec.ts | 4 +- tests/cypress/e2e/projectV2.spec.ts | 73 ++++----- .../projectV2DataConnectorCredentials.spec.ts | 4 +- tests/cypress/e2e/projectV2Session.spec.ts | 62 +++---- .../e2e/projectV2SessionSecrets.spec.ts | 16 +- tests/cypress/e2e/projectV2setup.spec.ts | 19 ++- tests/cypress/e2e/searchV2.spec.ts | 18 +- 34 files changed, 281 insertions(+), 418 deletions(-) delete mode 100644 client/src/components/earlyAccessBanner/EarlyAccessBanner.module.scss delete mode 100644 client/src/components/earlyAccessBanner/EarlyAccessBanner.tsx rename client/src/components/navbar/{navbar.constans.ts => navbar.constants.ts} (100%) diff --git a/client/scripts/generate_sitemap.sh b/client/scripts/generate_sitemap.sh index 530935ec08..77d554a562 100755 --- a/client/scripts/generate_sitemap.sh +++ b/client/scripts/generate_sitemap.sh @@ -39,12 +39,6 @@ tee > "${OUTPUT_FILE}" << EOF ${BASE_URL}/help - - ${BASE_URL}/help/docs - - - ${BASE_URL}/help/features - ${BASE_URL}/help/status diff --git a/client/src/App.jsx b/client/src/App.jsx index 0419b1ac4d..a98467a6d2 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -37,7 +37,6 @@ import LazyDatasetAddToProject from "./dataset/addtoproject/LazyDatasetAddToProj import { DatasetCoordinator } from "./dataset/Dataset.state"; import LazyShowDataset from "./dataset/LazyShowDataset"; import LazyAdminPage from "./features/admin/LazyAdminPage"; -import LazyDashboardV2 from "./features/dashboardV2/LazyDashboardV2"; import { Favicon } from "./features/favicon/Favicon"; import { Unavailable } from "./features/maintenance/Maintenance"; import LazyRootV1 from "./features/rootV1/LazyRootV1"; @@ -45,7 +44,6 @@ import LazyRootV2 from "./features/rootV2/LazyRootV2"; import { useGetUserQuery } from "./features/usersV2/api/users.api"; import LazyAnonymousHome from "./landing/LazyAnonymousHome"; import { FooterNavbar, RenkuNavBar } from "./landing/NavBar"; -import LazyNotFound from "./not-found/LazyNotFound"; import NotificationsManager from "./notifications/NotificationsManager"; import Cookie from "./privacy/Cookie"; import LazyProjectView from "./project/LazyProjectView"; @@ -83,7 +81,7 @@ function CentralContentContainer({ user, socket }) { {user.logged ? ( - + ) : (
@@ -123,9 +121,6 @@ function CentralContentContainer({ user, socket }) { - - - {userInfo?.isLoggedIn && userInfo.is_admin && ( @@ -134,7 +129,7 @@ function CentralContentContainer({ user, socket }) { )} - +
diff --git a/client/src/components/earlyAccessBanner/EarlyAccessBanner.module.scss b/client/src/components/earlyAccessBanner/EarlyAccessBanner.module.scss deleted file mode 100644 index 500cb8142e..0000000000 --- a/client/src/components/earlyAccessBanner/EarlyAccessBanner.module.scss +++ /dev/null @@ -1,9 +0,0 @@ -@import "../../styles/bootstrap/_custom_bootstrap_variables.scss"; -.EarlyAccessBannerBtn { - &:hover, - &:active { - background-color: #{$rk-green-hover} !important; - border-color: #{$rk-green-hover} !important; - color: var(--bs-white) !important; - } -} diff --git a/client/src/components/earlyAccessBanner/EarlyAccessBanner.tsx b/client/src/components/earlyAccessBanner/EarlyAccessBanner.tsx deleted file mode 100644 index 0e68d8693e..0000000000 --- a/client/src/components/earlyAccessBanner/EarlyAccessBanner.tsx +++ /dev/null @@ -1,154 +0,0 @@ -/*! - * 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 cx from "classnames"; -import { useLocation } from "react-router-dom"; -import { Link } from "react-router-dom-v5-compat"; -import { Alert, Container } from "reactstrap"; -import { Links } from "../../utils/constants/Docs.js"; -import { Url } from "../../utils/helpers/url"; -import style from "./EarlyAccessBanner.module.scss"; - -const BANNER_DARK_IMG = "/static/public/img/earlyAccessDark.png"; -const BANNER_LIGHT_IMG = "/static/public/img/earlyAccessLight.svg"; -const LOGO_V2 = "/static/public/img/renku2.0Logo.svg"; -const LOGO_V2_DARK = "/static/public/img/renku2.0LogoDark.svg"; - -interface EarlyAccessBannerProps { - theme: "dark" | "light"; -} - -export function DashboardBanner({ user }: { user: { logged: boolean } }) { - const location = useLocation(); - - if (location.pathname !== Url.get(Url.pages.landing)) return null; - - if (user.logged) return ; - - return ; -} - -export function EarlyAccessBanner({ theme }: EarlyAccessBannerProps) { - const themeAssets = { - light: { - btnStyles: ["bg-rk-blue", "text-rk-white"], - bannerStyles: ["bg-white", "text-rk-blue"], - dotsImgUrl: BANNER_LIGHT_IMG, - logoImgUrl: LOGO_V2_DARK, - linkStyle: "text-rk-blue", - }, - dark: { - btnStyles: ["bg-white", "text-rk-blue"], - bannerStyles: ["bg-rk-blue", "text-rk-white"], - dotsImgUrl: BANNER_DARK_IMG, - logoImgUrl: LOGO_V2, - linkStyle: "text-white", - }, - }; - - const callToActionBtn = ( - - Try it out - - ); - - return ( - - -
- Small image -
-
- Full image -
-
- Renku 2.0 -
EARLY ACCESS!
-
-
- {callToActionBtn} - - Learn more - -
-
-
- ); -} diff --git a/client/src/components/navbar/AnonymousNavBar.tsx b/client/src/components/navbar/AnonymousNavBar.tsx index b6f413796f..b7dac6a6f6 100644 --- a/client/src/components/navbar/AnonymousNavBar.tsx +++ b/client/src/components/navbar/AnonymousNavBar.tsx @@ -33,7 +33,7 @@ import { RenkuToolbarItemUser, RenkuToolbarNotifications, } from "./NavBarItems"; -import { RENKU_LOGO } from "./navbar.constans"; +import { RENKU_LOGO } from "./navbar.constants"; export default function AnonymousNavBar() { const { params, model, notifications } = useContext(AppContext); diff --git a/client/src/components/navbar/LoggedInNavBar.tsx b/client/src/components/navbar/LoggedInNavBar.tsx index 100d86aa47..e76b450043 100644 --- a/client/src/components/navbar/LoggedInNavBar.tsx +++ b/client/src/components/navbar/LoggedInNavBar.tsx @@ -33,7 +33,7 @@ import { RenkuToolbarItemUser, RenkuToolbarNotifications, } from "./NavBarItems"; -import { RENKU_LOGO } from "./navbar.constans"; +import { RENKU_LOGO } from "./navbar.constants"; export default function LoggedInNavBar() { const { params, model, notifications } = useContext(AppContext); diff --git a/client/src/components/navbar/NavBarItems.tsx b/client/src/components/navbar/NavBarItems.tsx index 3538edf615..5529bb5fb9 100644 --- a/client/src/components/navbar/NavBarItems.tsx +++ b/client/src/components/navbar/NavBarItems.tsx @@ -345,7 +345,7 @@ export function RenkuToolbarItemUser({ {isV2 && ( <> Integrations diff --git a/client/src/components/navbar/navbar.constans.ts b/client/src/components/navbar/navbar.constants.ts similarity index 100% rename from client/src/components/navbar/navbar.constans.ts rename to client/src/components/navbar/navbar.constants.ts diff --git a/client/src/error-boundary/ErrorBoundary.tsx b/client/src/error-boundary/ErrorBoundary.tsx index 8e64f0b97b..acf83d2880 100644 --- a/client/src/error-boundary/ErrorBoundary.tsx +++ b/client/src/error-boundary/ErrorBoundary.tsx @@ -19,11 +19,13 @@ import * as Sentry from "@sentry/react"; import cx from "classnames"; import { ReactNode, useCallback } from "react"; +import { useLocation } from "react-router-dom-v5-compat"; import { ArrowLeft } from "react-bootstrap-icons"; import { StyleHandler } from "../index"; import rkOopsImg from "../styles/assets/oops.svg"; import rkOopsV2Img from "../styles/assets/oopsV2.svg"; import useLegacySelector from "../utils/customHooks/useLegacySelector.hook"; +import { isRenkuLegacy } from "../utils/helpers/HelperFunctionsV2"; interface AppErrorBoundaryProps { children?: ReactNode; @@ -52,7 +54,8 @@ export function AppErrorBoundary({ children }: AppErrorBoundaryProps) { } function ErrorPage() { - const isV2 = location.pathname.startsWith("/v2"); + const location = useLocation(); + const isLegacy = isRenkuLegacy(location.pathname); const logged = useLegacySelector((state) => state.stateModel.user.logged); return ( <> @@ -61,10 +64,10 @@ function ErrorPage() { className={cx("d-flex", "flex-column", "align-items-center", "mt-5")} >
- +

Your user account is not currently connected to{" "} {provider.display_name}. See{" "} - + connected services . @@ -819,7 +819,7 @@ function RepositoryPermissionsAlert({

Your user account is not currently connected to{" "} {provider.display_name}. See{" "} - + connected services . diff --git a/client/src/features/dashboardV2/DashboardV2.tsx b/client/src/features/dashboardV2/DashboardV2.tsx index 6ce7b0a54e..0c6baec610 100644 --- a/client/src/features/dashboardV2/DashboardV2.tsx +++ b/client/src/features/dashboardV2/DashboardV2.tsx @@ -567,16 +567,20 @@ function ViewAllLink({ noItems: boolean; total: number; }) { + const searchUrl = ABSOLUTE_ROUTES.v2.search; return noItems ? ( View other {type === "project" ? "projects" : "groups"} ) : ( View all my {total > 5 ? total : ""}{" "} @@ -586,6 +590,7 @@ function ViewAllLink({ } function EmptyProjectsButtons() { + const searchUrl = ABSOLUTE_ROUTES.v2.search; return (

diff --git a/client/src/features/groupsV2/new/GroupNew.tsx b/client/src/features/groupsV2/new/GroupNew.tsx index 3519bbbc93..b7ffb5bffc 100644 --- a/client/src/features/groupsV2/new/GroupNew.tsx +++ b/client/src/features/groupsV2/new/GroupNew.tsx @@ -159,7 +159,10 @@ function GroupV2CreationDetails() { } }, [result, navigate]); - const url = "renkulab.io/v2/groups/"; + const groupPath = generatePath(ABSOLUTE_ROUTES.v2.groups.show.root, { + slug: "", + }); + const parentPath = `${groupPath}/`; const resetUrl = useCallback(() => { setValue("slug", slugFromTitle(currentName, true, true), { @@ -188,7 +191,7 @@ function GroupV2CreationDetails() { errors={errors} name="slug" resetFunction={resetUrl} - url={url} + parentPath={parentPath} slug={currentSlug} dirtyFields={dirtyFields} label="Group URL" diff --git a/client/src/features/groupsV2/settings/GroupSettingsMetadata.tsx b/client/src/features/groupsV2/settings/GroupSettingsMetadata.tsx index c7ca76f0e3..c998478d09 100644 --- a/client/src/features/groupsV2/settings/GroupSettingsMetadata.tsx +++ b/client/src/features/groupsV2/settings/GroupSettingsMetadata.tsx @@ -177,6 +177,13 @@ export default function GroupMetadataForm({ group }: GroupMetadataFormProps) { }); }, [setValue, group.slug]); + const { params } = useContext(AppContext); + const baseUrl = params?.BASE_URL ?? window.location.origin; + const groupPath = generatePath(ABSOLUTE_ROUTES.v2.groups.show.root, { + slug: "", + }); + const url = `${baseUrl}${groupPath}/`; + return (
{updateGroupResult.error && ( @@ -216,7 +223,7 @@ export default function GroupMetadataForm({ group }: GroupMetadataFormProps) { errors={errors} name={"slug"} resetFunction={resetUrl} - url="renkulab.io/v2/groups/" + url={url} /> {errors.slug && dirtyFields.slug && (
diff --git a/client/src/features/platform/components/StatusBanner.tsx b/client/src/features/platform/components/StatusBanner.tsx index c0f12b8997..1142d2bf54 100644 --- a/client/src/features/platform/components/StatusBanner.tsx +++ b/client/src/features/platform/components/StatusBanner.tsx @@ -301,8 +301,8 @@ function StatusPageMaintenance({ const location = useLocation(); const isDashboard = (userLogged && location.pathname === "/") || - location.pathname === "/v2" || - location.pathname === "/v2/"; + location.pathname === "/v1" || + location.pathname === "/v1/"; // 1. There is a scheduled maintenance in < 48 hours: show it on all pages except the landing page // 2. There is a scheduled maintenance in < 7 days: show it on the v1 Dashboard and the v2 Dashboard diff --git a/client/src/features/projectsV2/fields/SlugPreviewFormField.tsx b/client/src/features/projectsV2/fields/SlugPreviewFormField.tsx index 1d21fc3993..9bed08aa59 100644 --- a/client/src/features/projectsV2/fields/SlugPreviewFormField.tsx +++ b/client/src/features/projectsV2/fields/SlugPreviewFormField.tsx @@ -17,12 +17,15 @@ */ import cx from "classnames"; -import { useState } from "react"; +import { useState, useContext } from "react"; import type { FieldValues } from "react-hook-form"; import { FormText } from "reactstrap"; + +import ChevronFlippedIcon from "../../../components/icons/ChevronFlippedIcon.tsx"; +import AppContext from "../../../utils/context/appContext.ts"; + import { SlugPreviewFormFieldProps } from "./formField.types.ts"; import SlugFormField from "./SlugFormField"; -import ChevronFlippedIcon from "../../../components/icons/ChevronFlippedIcon.tsx"; export default function SlugPreviewFormField({ compact = false, @@ -30,25 +33,28 @@ export default function SlugPreviewFormField({ errors, name, resetFunction, - url, + parentPath, slug, dirtyFields, entityName, }: SlugPreviewFormFieldProps) { const [isCollapseOpen, setIsCollapseOpen] = useState(false); const toggleCollapse = () => setIsCollapseOpen(!isCollapseOpen); + const { params } = useContext(AppContext); + const baseUrl = params?.BASE_URL ?? window.location.origin; + const url = `${baseUrl}${parentPath}`; const slugPreview = (
- The URL for this {entityName} will be{" "} + The url for this {entityName} will be{" "} {url} {slug || ""}