This is an automated email from the ASF dual-hosted git repository.

dimuthuupe pushed a commit to branch cybershuttle-staging
in repository https://gitbox.apache.org/repos/asf/airavata.git

commit 646f5f9e0b19b83c3a3c678166f5628213c3e778
Author: ganning127 <[email protected]>
AuthorDate: Sat Apr 5 03:33:04 2025 -0400

    Homepage + redirecting logins
---
 modules/research-framework/portal/src/App.tsx      | 106 ++++++++++++---------
 .../portal/src/assets/Hero.original.png            | Bin 0 -> 3208051 bytes
 .../portal/src/components/auth/Login.tsx           |   4 +
 .../src/components/auth/ProtectedComponent.tsx     |  18 +++-
 .../portal/src/components/auth/UserMenu.tsx        |  23 ++++-
 .../src/components/home/CybershuttleLanding.tsx    |  60 ++++++++++++
 .../src/components/typography/SectionHeading.tsx   |  21 ++++
 .../portal/src/layouts/NavBar.tsx                  |   3 -
 modules/research-framework/portal/src/main.tsx     |  62 +-----------
 9 files changed, 185 insertions(+), 112 deletions(-)

diff --git a/modules/research-framework/portal/src/App.tsx 
b/modules/research-framework/portal/src/App.tsx
index 23694017ce..32b403aacc 100644
--- a/modules/research-framework/portal/src/App.tsx
+++ b/modules/research-framework/portal/src/App.tsx
@@ -1,5 +1,5 @@
 import { useColorMode } from "./components/ui/color-mode";
-import { BrowserRouter, Route, Routes } from "react-router";
+import { Route, Routes, useLocation, useNavigate } from "react-router";
 import Home from "./components/home";
 import { Models } from "./components/models";
 import { Datasets } from "./components/datasets";
@@ -8,62 +8,82 @@ import Notebooks from "./components/notebooks";
 import Repositories from "./components/repositories";
 import { Login } from "./components/auth/Login";
 import ProtectedComponent from "./components/auth/ProtectedComponent";
-import { useAuth } from "react-oidc-context";
-import { useEffect } from "react";
-import { setUserProvider } from "./lib/api";
+import { AuthProvider, AuthProviderProps } from "react-oidc-context";
+import { useEffect, useState } from "react";
 import NavBarFooterLayout from "./layouts/NavBarFooterLayout";
+import { CybershuttleLanding } from "./components/home/CybershuttleLanding";
+import {
+  APP_REDIRECT_URI,
+  BACKEND_URL,
+  CLIENT_ID,
+  OPENID_CONFIG_URL,
+} from "./lib/constants";
+import { WebStorageStateStore } from "oidc-client-ts";
 function App() {
   const colorMode = useColorMode();
+  const navigate = useNavigate();
+  const location = useLocation();
+  const [oidcConfig, setOidcConfig] = useState<AuthProviderProps | null>(null);
+
   if (colorMode.colorMode === "dark") {
     colorMode.toggleColorMode();
   }
 
-  const user = useAuth();
-
   useEffect(() => {
-    if (user.isAuthenticated) {
-      setUserProvider(() => Promise.resolve(user.user ?? null));
-    }
-  }, [user]);
+    const fetchOidcConfig = async () => {
+      try {
+        const response = await fetch(OPENID_CONFIG_URL);
+        const data = await response.json();
 
-  return (
-    <>
-      <BrowserRouter>
-        {/* <Routes>
-          <Route path="/" element={<Login />} />
+        const redirectUri = APP_REDIRECT_URI;
 
-          <Route
-            path="/projects"
-            element={<ProtectedComponent Component={Home} />}
-          />
-          <Route path="/resources">
-            <Route
-              path="notebooks"
-              element={<ProtectedComponent Component={Notebooks} />}
-            />
-            <Route
-              path="datasets"
-              element={<ProtectedComponent Component={Datasets} />}
-            />
-            <Route
-              path="repositories"
-              element={<ProtectedComponent Component={Repositories} />}
-            />
-            <Route
-              path="models"
-              element={<ProtectedComponent Component={Models} />}
-            />
+        const theConfig: AuthProviderProps = {
+          authority: `${BACKEND_URL}/api/v1/identity-management/`,
+          client_id: CLIENT_ID,
+          redirect_uri: redirectUri,
+          response_type: "code",
+          scope: "openid email",
+          metadata: {
+            authorization_endpoint: data.authorization_endpoint,
+            token_endpoint: data.token_endpoint,
+            revocation_endpoint: data.revocation_endpoint,
+            introspection_endpoint: data.introspection_endpoint,
+            userinfo_endpoint: data.userinfo_endpoint,
+            jwks_uri: data.jwks_uri,
+          },
+          userStore: new WebStorageStateStore({ store: window.localStorage }),
+          automaticSilentRenew: true,
+        };
 
-            <Route
-              path=":type/:id"
-              element={<ProtectedComponent Component={ResourceDetails} />}
-            />
-          </Route>
-        </Routes> */}
+        setOidcConfig(theConfig);
+      } catch (error) {
+        console.error("Error fetching OIDC config:", error);
+      }
+    };
 
+    fetchOidcConfig();
+  }, []);
+
+  if (!oidcConfig) {
+    return <div>Loading OIDC configuration...</div>; // Loading state while 
config is fetched
+  }
+
+  return (
+    <>
+      <AuthProvider
+        {...oidcConfig}
+        onSigninCallback={() => {
+          // const from = location.state?.from || "/"; // fallback to homepage
+          // console.log("Redirecting to:", from);
+          // navigate(from, { replace: true });
+          // clear state from url
+          navigate(location.pathname, { replace: true });
+        }}
+      >
         <Routes>
           {/* Public Route */}
           <Route element={<NavBarFooterLayout />}>
+            <Route path="/" element={<CybershuttleLanding />} />
             <Route path="/login" element={<Login />} />
           </Route>
 
@@ -79,7 +99,7 @@ function App() {
             <Route path="/resources/:type/:id" element={<ResourceDetails />} />
           </Route>
         </Routes>
-      </BrowserRouter>
+      </AuthProvider>
     </>
   );
 }
diff --git a/modules/research-framework/portal/src/assets/Hero.original.png 
b/modules/research-framework/portal/src/assets/Hero.original.png
new file mode 100644
index 0000000000..545042c02e
Binary files /dev/null and 
b/modules/research-framework/portal/src/assets/Hero.original.png differ
diff --git a/modules/research-framework/portal/src/components/auth/Login.tsx 
b/modules/research-framework/portal/src/components/auth/Login.tsx
index b338bacb01..7ae1adfb53 100644
--- a/modules/research-framework/portal/src/components/auth/Login.tsx
+++ b/modules/research-framework/portal/src/components/auth/Login.tsx
@@ -13,6 +13,9 @@ export const Login = () => {
     }
   }, [auth]);
 
+  const redirect =
+    new URLSearchParams(window.location.search).get("redirect") || "/";
+
   return (
     <Center height="100vh">
       <Stack
@@ -38,6 +41,7 @@ export const Login = () => {
             onClick={() => {
               console.log("Sign in clicked");
               auth.signinRedirect({
+                redirect_uri: `${window.location.origin}${redirect}`,
                 extraQueryParams: {
                   // This is the prompt that will be shown to the user
                   prompt: "login",
diff --git 
a/modules/research-framework/portal/src/components/auth/ProtectedComponent.tsx 
b/modules/research-framework/portal/src/components/auth/ProtectedComponent.tsx
index ecbf1feb66..d9a27a7c44 100644
--- 
a/modules/research-framework/portal/src/components/auth/ProtectedComponent.tsx
+++ 
b/modules/research-framework/portal/src/components/auth/ProtectedComponent.tsx
@@ -1,16 +1,24 @@
+import { setUserProvider } from "@/lib/api";
+import { useEffect } from "react";
 import { useAuth } from "react-oidc-context";
 import { useNavigate } from "react-router";
 
 function ProtectedComponent({ Component }: { Component: React.FC }) {
   const auth = useAuth();
   const navigate = useNavigate();
+  const path = window.location.pathname;
 
-  if (auth.isLoading) {
-    return;
-  }
+  useEffect(() => {
+    if (!auth.isLoading && !auth.isAuthenticated) {
+      navigate(`/login?redirect=${path}`, { replace: true });
+    }
+
+    if (auth.isAuthenticated) {
+      setUserProvider(() => Promise.resolve(auth.user ?? null));
+    }
+  }, [auth]);
 
-  if (!auth.isAuthenticated) {
-    navigate("/login");
+  if (auth.isLoading || !auth.isAuthenticated) {
     return;
   }
 
diff --git a/modules/research-framework/portal/src/components/auth/UserMenu.tsx 
b/modules/research-framework/portal/src/components/auth/UserMenu.tsx
index b929659908..cba709720f 100644
--- a/modules/research-framework/portal/src/components/auth/UserMenu.tsx
+++ b/modules/research-framework/portal/src/components/auth/UserMenu.tsx
@@ -1,11 +1,28 @@
-import { Avatar, Box, HStack, Text, Menu, Portal } from "@chakra-ui/react";
+import {
+  Avatar,
+  Box,
+  HStack,
+  Text,
+  Menu,
+  Portal,
+  Button,
+} from "@chakra-ui/react";
+import { FaArrowRight } from "react-icons/fa6";
 import { useAuth } from "react-oidc-context";
+import { Link } from "react-router";
 
 export const UserMenu = () => {
   const auth = useAuth();
 
-  if (auth.isLoading || !auth.user) return null;
-
+  if (auth.isLoading || !auth.user)
+    return (
+      <Link to="/login">
+        <Button colorPalette="blue">
+          Login
+          <FaArrowRight />
+        </Button>
+      </Link>
+    );
   const handleLogout = async () => {
     await auth.signoutRedirect();
   };
diff --git 
a/modules/research-framework/portal/src/components/home/CybershuttleLanding.tsx 
b/modules/research-framework/portal/src/components/home/CybershuttleLanding.tsx
new file mode 100644
index 0000000000..0195545767
--- /dev/null
+++ 
b/modules/research-framework/portal/src/components/home/CybershuttleLanding.tsx
@@ -0,0 +1,60 @@
+import { Container, Heading, Image, Text, VStack, Box } from 
"@chakra-ui/react";
+import HeroOriginal from "../../assets/Hero.original.png";
+import { SectionHeading } from "../typography/SectionHeading";
+
+export const CybershuttleLanding = () => {
+  return (
+    <>
+      <Container maxW="breakpoint-lg" mt={8}>
+        <Heading
+          textAlign="center"
+          fontSize={{ base: "3xl", md: "4xl", lg: "5xl" }}
+          fontWeight="black"
+          lineHeight={1.2}
+        >
+          Balance Local and Remote for
+          <br /> Streamlined Research with{" "}
+          <Text as="span" color="blue.600">
+            Cybershuttle
+          </Text>
+        </Heading>
+
+        <Text
+          textAlign="center"
+          fontSize={{ base: "lg", lg: "xl" }}
+          color="gray.600"
+          mt={4}
+        >
+          Cybershuttle expertly balances local and remote computing, seamlessly
+          orchestrating tasks and data between machines. Schedule
+          time-sensitive, small tasks locally, and reserve compute-intensive
+          tasks for powerful remote HPC resources, with data transparently
+          accessible everywhere. This approach significantly enhances the
+          efficiency of scientific workflows compared to fully remote or fully
+          local operations.
+        </Text>
+
+        <Image src={HeroOriginal} alt="Cybershuttle" maxW="100%" mt={8} />
+        <VStack mt={18} alignItems="flex-start" gap={16}>
+          <Box>
+            <SectionHeading>Main Features</SectionHeading>
+          </Box>
+
+          <Box>
+            <SectionHeading>Science-Centrist Extensions</SectionHeading>
+          </Box>
+
+          <Box>
+            <SectionHeading>Collaboration</SectionHeading>
+          </Box>
+
+          <Box>
+            <SectionHeading>Thanks to our Sponsors</SectionHeading>
+          </Box>
+        </VStack>
+
+        <Box my={8} />
+      </Container>
+    </>
+  );
+};
diff --git 
a/modules/research-framework/portal/src/components/typography/SectionHeading.tsx
 
b/modules/research-framework/portal/src/components/typography/SectionHeading.tsx
new file mode 100644
index 0000000000..e86aea2075
--- /dev/null
+++ 
b/modules/research-framework/portal/src/components/typography/SectionHeading.tsx
@@ -0,0 +1,21 @@
+import { Heading, HeadingProps } from "@chakra-ui/react";
+
+interface SectionHeadingProps extends HeadingProps {
+  children: React.ReactNode;
+}
+export const SectionHeading = ({ children, ...props }: SectionHeadingProps) => 
{
+  return (
+    <Heading
+      fontSize={{
+        base: "2xl",
+        md: "3xl",
+        lg: "4xl",
+      }}
+      fontWeight="bold"
+      lineHeight={1.2}
+      {...props}
+    >
+      {children}
+    </Heading>
+  );
+};
diff --git a/modules/research-framework/portal/src/layouts/NavBar.tsx 
b/modules/research-framework/portal/src/layouts/NavBar.tsx
index 43090c1841..10b5537f49 100644
--- a/modules/research-framework/portal/src/layouts/NavBar.tsx
+++ b/modules/research-framework/portal/src/layouts/NavBar.tsx
@@ -4,7 +4,6 @@ import {
   Spacer,
   Image,
   HStack,
-  Avatar,
   Box,
   IconButton,
   useDisclosure,
@@ -15,7 +14,6 @@ import {
 } from "@chakra-ui/react";
 import ApacheAiravataLogo from "../assets/airavata-logo.png";
 import { Link, useNavigate } from "react-router";
-import { useAuth } from "react-oidc-context";
 import { RxHamburgerMenu } from "react-icons/rx";
 import { IoClose } from "react-icons/io5";
 import { UserMenu } from "@/components/auth/UserMenu";
@@ -49,7 +47,6 @@ interface NavLinkProps extends ButtonProps {
 }
 
 const NavBar = () => {
-  const auth = useAuth();
   const { open, onToggle } = useDisclosure();
   const navigate = useNavigate();
 
diff --git a/modules/research-framework/portal/src/main.tsx 
b/modules/research-framework/portal/src/main.tsx
index f0fc512ec3..8ec6752f04 100644
--- a/modules/research-framework/portal/src/main.tsx
+++ b/modules/research-framework/portal/src/main.tsx
@@ -1,70 +1,16 @@
 import { Provider } from "@/components/ui/provider";
-import React, { useEffect, useState } from "react";
+import React from "react";
 import ReactDOM from "react-dom/client";
 import App from "./App";
-import { AuthProvider, AuthProviderProps } from "react-oidc-context";
-import { WebStorageStateStore } from "oidc-client-ts";
-import {
-  APP_REDIRECT_URI,
-  BACKEND_URL,
-  CLIENT_ID,
-  OPENID_CONFIG_URL,
-} from "./lib/constants";
+import { BrowserRouter } from "react-router";
 
 const Index = () => {
-  const [oidcConfig, setOidcConfig] = useState<AuthProviderProps | null>(null);
-
-  useEffect(() => {
-    const fetchOidcConfig = async () => {
-      try {
-        const response = await fetch(OPENID_CONFIG_URL);
-        const data = await response.json();
-
-        const redirectUri = APP_REDIRECT_URI;
-
-        const theConfig: AuthProviderProps = {
-          authority: `${BACKEND_URL}/api/v1/identity-management/`,
-          client_id: CLIENT_ID,
-          redirect_uri: redirectUri,
-          response_type: "code",
-          scope: "openid email",
-          metadata: {
-            authorization_endpoint: data.authorization_endpoint,
-            token_endpoint: data.token_endpoint,
-            revocation_endpoint: data.revocation_endpoint,
-            introspection_endpoint: data.introspection_endpoint,
-            userinfo_endpoint: data.userinfo_endpoint,
-            jwks_uri: data.jwks_uri,
-          },
-          userStore: new WebStorageStateStore({ store: window.localStorage }),
-          automaticSilentRenew: true,
-        };
-
-        setOidcConfig(theConfig);
-      } catch (error) {
-        console.error("Error fetching OIDC config:", error);
-      }
-    };
-
-    fetchOidcConfig();
-  }, []);
-
-  if (!oidcConfig) {
-    return <div>Loading OIDC configuration...</div>; // Loading state while 
config is fetched
-  }
-
   return (
     <React.StrictMode>
       <Provider>
-        <AuthProvider
-          {...oidcConfig}
-          onSigninCallback={async (user) => {
-            console.log("User signed in", user);
-            window.location.href = "/projects";
-          }}
-        >
+        <BrowserRouter>
           <App />
-        </AuthProvider>
+        </BrowserRouter>
       </Provider>
     </React.StrictMode>
   );

Reply via email to