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

lahirujayathilake pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git


The following commit(s) were added to refs/heads/master by this push:
     new bfdfb05db nav bar to be collapsible and made it responsive to screen 
size (#411)
bfdfb05db is described below

commit bfdfb05dbd985259d20ecb462881491526864fc5
Author: Oluwatimi Omoteso <[email protected]>
AuthorDate: Fri Nov 15 11:43:07 2024 -0500

    nav bar to be collapsible and made it responsive to screen size (#411)
    
    * edited nav bar to be collapsible as well as made it responsive to screen 
size
    * fixed scroll issue with navbar
    * Adjusted navbar drawer behavior for mobile and desktop consistency
---
 custos-portal/package.json                    |   5 +-
 custos-portal/src/components/NavContainer.tsx | 202 ++++++++++++++++++--------
 custos-portal/src/index.tsx                   |  13 +-
 custos-portal/src/lib/constants.ts            |  10 +-
 4 files changed, 150 insertions(+), 80 deletions(-)

diff --git a/custos-portal/package.json b/custos-portal/package.json
index 3e789cb76..a3893e3b5 100644
--- a/custos-portal/package.json
+++ b/custos-portal/package.json
@@ -22,7 +22,8 @@
     "react-icons": "^5.3.0",
     "react-oidc-context": "^3.1.0",
     "react-router-dom": "^6.26.1",
-    "react-tag-input-component": "^2.0.2"
+    "react-tag-input-component": "^2.0.2",
+    "serve": "^14.2.4"
   },
   "devDependencies": {
     "@eslint/js": "^9.9.0",
@@ -38,4 +39,4 @@
     "typescript-eslint": "^8.0.1",
     "vite": "^5.4.1"
   }
-}
\ No newline at end of file
+}
diff --git a/custos-portal/src/components/NavContainer.tsx 
b/custos-portal/src/components/NavContainer.tsx
index ffa051262..3b2678907 100644
--- a/custos-portal/src/components/NavContainer.tsx
+++ b/custos-portal/src/components/NavContainer.tsx
@@ -1,3 +1,4 @@
+import React, { useState, useEffect, memo } from 'react';
 import {
   Grid,
   GridItem,
@@ -7,16 +8,22 @@ import {
   Icon,
   Text,
   Flex,
-  Button
+  Button,
+  Drawer,
+  DrawerOverlay,
+  DrawerContent,
+  DrawerCloseButton,
+  DrawerBody,
+  useDisclosure,
+  Spacer
 } from '@chakra-ui/react';
 import { Link } from 'react-router-dom';
-import { FiUser, FiUsers } from "react-icons/fi";
+import { FiUser, FiUsers, FiChevronLeft, FiChevronRight, FiMenu } from 
"react-icons/fi";
 import { AiOutlineAppstore } from "react-icons/ai";
 import { IconType } from "react-icons";
 import { MdLogout } from "react-icons/md";
 import { useAuth } from 'react-oidc-context';
 
-
 interface NavContainerProps {
   activeTab: string;
   children: React.ReactNode;
@@ -27,86 +34,161 @@ interface NavItemProps {
   icon: IconType;
   text: string;
   activeTab: string;
+  isCollapsed: boolean;
+  onClose: () => void;
 }
 
-const NavItem = ({ to, icon, text, activeTab }: NavItemProps) => {
+const NavItem = memo(({ to, icon, text, activeTab, isCollapsed, onClose }: 
NavItemProps) => {
   const isActive = activeTab.toLowerCase() === text.toLowerCase();
   return (
-    <Link to={to}>
+    <Link to={to} onClick={onClose}>
       <Stack
-        direction='row'
-        align='center'
+        direction="row"
+        align="center"
         color={isActive ? 'black' : 'default.secondary'}
         py={2}
         px={1}
-        _hover={{
-          bg: 'gray.100',
-        }}
-        fontSize='sm'
+        _hover={{ bg: 'gray.100' }}
+        fontSize="sm"
       >
         <Icon as={icon} />
-        <Text fontWeight='semibold'>{text}</Text>
+        {!isCollapsed && <Text fontWeight="semibold">{text}</Text>}
       </Stack>
     </Link>
-  )
-}
+  );
+});
 
-
-export const NavContainer = ({ activeTab, children }: NavContainerProps) => {
+export const NavContainer = memo(({ activeTab, children }: NavContainerProps) 
=> {
   const auth = useAuth();
-  return (
-    <>
-      <Grid templateColumns='repeat(15, 1fr)'>
-        <GridItem colSpan={3} bg='#F7F7F7'>
-          <Flex h='100vh' >
-            <Box position='fixed'>
-              <Flex justifyContent='space-between' direction="column" 
h='100vh' p={4} >
-              <Box>
-                <Heading size='md'>
-                  Custos Auth Portal
-                </Heading>
+  
+  const [isCollapsed, setIsCollapsed] = useState(() => {
+    const saved = localStorage.getItem('navCollapsed');
+    return saved ? JSON.parse(saved) : false;
+  });
+  
+  const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
+  const { isOpen, onOpen, onClose } = useDisclosure();
 
-                <Stack direction='column'  mt={4}>
-                  <NavItem to='/applications' icon={AiOutlineAppstore} 
text="Applications" activeTab={activeTab} />
-                  <NavItem to='/groups' icon={FiUsers} text="Groups" 
activeTab={activeTab} />
-                  <NavItem to='/users' icon={FiUser} text="Users" 
activeTab={activeTab} />
-                </Stack>
-              </Box>
+  useEffect(() => {
+    const handleResize = () => setIsMobile(window.innerWidth < 768);
+    window.addEventListener('resize', handleResize);
+    return () => window.removeEventListener('resize', handleResize);
+  }, []);
+
+  useEffect(() => {
+    localStorage.setItem('navCollapsed', JSON.stringify(isCollapsed));
+  }, [isCollapsed]);
+
+  const toggleCollapse = () => {
+    setIsCollapsed((prev: boolean) => !prev);
+  };
 
+  if (isMobile) {
+    return (
+      <>
+        <Box position="fixed" top={4} left={4} zIndex={10}>
+          <Button onClick={onOpen} variant="ghost">
+            <Icon as={FiMenu} w={6} h={6} />
+          </Button>
+        </Box>
+        <Drawer isOpen={isOpen} placement="left" onClose={onClose}>
+          <DrawerOverlay />
+          <DrawerContent bg="#F7F7F7">
+            <DrawerCloseButton />
+            <DrawerBody p={4} display="flex" flexDirection="column">
+              <Heading size="md">Custos Auth Portal</Heading>
+              <Stack direction="column" mt={4}>
+                <NavItem to="/applications" icon={AiOutlineAppstore} 
text="Applications" activeTab={activeTab} isCollapsed={false} onClose={onClose} 
/>
+                <NavItem to="/groups" icon={FiUsers} text="Groups" 
activeTab={activeTab} isCollapsed={false} onClose={onClose} />
+                <NavItem to="/users" icon={FiUser} text="Users" 
activeTab={activeTab} isCollapsed={false} onClose={onClose} />
+              </Stack>
+              <Spacer />
               <Box>
-                <Text fontWeight='bold'>
-                  {auth.user?.profile?.name}
-                </Text>
-                <Text fontSize='sm' color='gray.500'>
-                  {auth.user?.profile?.email}
-                </Text>
-                <Button 
-                  variant='unstyled' 
-                  w='fit-content'
-                  size='sm'
-                  _hover={{
-                    color: 'gray.500'
-                  }}
+                <Text fontWeight="bold">{auth.user?.profile?.name}</Text>
+                <Text fontSize="sm" 
color="gray.500">{auth.user?.profile?.email}</Text>
+                <Button
+                  variant="unstyled"
+                  w="fit-content"
+                  size="sm"
+                  _hover={{ color: "gray.500" }}
                   onClick={async () => {
                     await auth.removeUser();
+                    onClose();
                   }}
                 >
-                  <Flex alignItems='center' gap={2} w='fit-content'>
-                      <Icon as={MdLogout} />
-                      <Text as='span' >Logout</Text>
+                  <Flex alignItems="center" gap={2} w="fit-content">
+                    <Icon as={MdLogout} />
+                    <Text as="span">Logout</Text>
                   </Flex>
                 </Button>
               </Box>
-            </Flex>
-            </Box>
-          </Flex>
-
-        </GridItem>
-        <GridItem colSpan={12} p={16}>
+            </DrawerBody>
+          </DrawerContent>
+        </Drawer>
+        <Box p={5} pt={16}>
           {children}
-        </GridItem>
-      </Grid>
-    </>
-  )
-}
+        </Box>
+      </>
+    );
+  }
 
+  return (
+    <Grid templateColumns="repeat(15, 1fr)" minHeight="100vh">
+      <GridItem
+        colSpan={isCollapsed ? 1 : 3}
+        minWidth={isCollapsed ? "60px" : "240px"}
+        maxWidth={isCollapsed ? "60px" : "240px"}
+        bg="#F7F7F7"
+        position="fixed"
+        h="100vh"
+      >
+        <Flex h="100vh" p={4} direction="column" 
justifyContent="space-between">
+          <Box>
+            <Flex justifyContent="space-between" align="center">
+              {!isCollapsed && <Heading size="md">Custos Auth Portal</Heading>}
+              <Button variant="ghost" onClick={toggleCollapse} size="sm">
+                <Icon as={isCollapsed ? FiChevronRight : FiChevronLeft} />
+              </Button>
+            </Flex>
+            <Stack direction="column" mt={4}>
+              <NavItem to="/applications" icon={AiOutlineAppstore} 
text="Applications" activeTab={activeTab} isCollapsed={isCollapsed} 
onClose={onClose} />
+              <NavItem to="/groups" icon={FiUsers} text="Groups" 
activeTab={activeTab} isCollapsed={isCollapsed} onClose={onClose} />
+              <NavItem to="/users" icon={FiUser} text="Users" 
activeTab={activeTab} isCollapsed={isCollapsed} onClose={onClose} />
+            </Stack>
+          </Box>
+          <Box mt="auto">
+            {!isCollapsed && (
+              <>
+                <Text fontWeight="bold">{auth.user?.profile?.name}</Text>
+                <Text fontSize="sm" 
color="gray.500">{auth.user?.profile?.email}</Text>
+              </>
+            )}
+            <Button
+              variant="unstyled"
+              w="fit-content"
+              size="sm"
+              _hover={{ color: "gray.500" }}
+              onClick={async () => {
+                await auth.removeUser();
+              }}
+            >
+              <Flex alignItems="center" gap={2} w="fit-content">
+                <Icon as={MdLogout} />
+                {!isCollapsed && <Text as="span">Logout</Text>}
+              </Flex>
+            </Button>
+          </Box>
+        </Flex>
+      </GridItem>
+      <GridItem
+        colSpan={isCollapsed ? 14 : 12}
+        p={10} 
+        ml={isCollapsed ? "60px" : "240px"}
+        minWidth="0"
+        overflowY="auto"
+      >
+        {children}
+      </GridItem>
+    </Grid>
+  );
+});
diff --git a/custos-portal/src/index.tsx b/custos-portal/src/index.tsx
index c141db60e..9bf129409 100644
--- a/custos-portal/src/index.tsx
+++ b/custos-portal/src/index.tsx
@@ -4,7 +4,7 @@ import { extendTheme, ChakraProvider } from '@chakra-ui/react';
 import { AuthProvider, AuthProviderProps } from 'react-oidc-context';
 import { APP_REDIRECT_URI, BACKEND_URL, CLIENT_ID, TENANT_ID } from 
'./lib/constants';
 import { WebStorageStateStore } from 'oidc-client-ts';
-import React, { useEffect, useState } from 'react';
+import { useEffect, useState } from 'react';
 import localOidcConfig from './lib/localOidcConfig.json';
 
 const theme = extendTheme({
@@ -31,17 +31,10 @@ const Index = () => {
     const fetchOidcConfig = async () => {
       try {
         let data;
-        if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
-          data = localOidcConfig;
-        } else {
-          const response = await 
fetch(`${BACKEND_URL}/api/v1/identity-management/tenant/${TENANT_ID}/.well-known/openid-configuration`);
 // Replace with actual API endpoint
-          data = await response.json();
-        }
-
-        // Determine redirect_uri based on environment
+        const response = await 
fetch(`${BACKEND_URL}/api/v1/identity-management/tenant/${TENANT_ID}/.well-known/openid-configuration`);
 // Replace with actual API endpoint
+        data = await response.json();
         const redirectUri = APP_REDIRECT_URI;
 
-        // Create the OIDC config based on the fetched data
         const theConfig: AuthProviderProps = {
           authority: `${BACKEND_URL}/api/v1/identity-management/`,
           client_id: CLIENT_ID,
diff --git a/custos-portal/src/lib/constants.ts 
b/custos-portal/src/lib/constants.ts
index 9966e7e56..d164a3d7b 100644
--- a/custos-portal/src/lib/constants.ts
+++ b/custos-portal/src/lib/constants.ts
@@ -5,15 +5,9 @@ export let CLIENT_ID:string;
 export let BACKEND_URL:string;
 export let APP_URL:string;
 
-if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
-    CLIENT_ID = 'veda-dafsxhsztbsczrmmbftw-10000000';
-    BACKEND_URL = 'http://localhost:8081';
+    CLIENT_ID = 'custos-kgap8hu6ih4hddvlzzlb-10000000';
+    BACKEND_URL = 'https://api.playground.usecustos.org';
     APP_URL = 'http://localhost:5173'
-} else {
-    CLIENT_ID = 'veda-iui65nmkgaf7bihdyndc-10000000';
-    BACKEND_URL = 'https://api.veda.usecustos.org';
-    APP_URL = 'https://veda.usecustos.org'
-}
 
 export const APP_REDIRECT_URI = `${APP_URL}/oauth-callback`;
 export const TENANT_ID = '10000000';
\ No newline at end of file

Reply via email to