From e522077fd56dd8f9b67ac3b8b431a9abd958feb0 Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Fri, 15 Mar 2024 13:52:08 +0100
Subject: [PATCH] WIP: login/logout buttons

---
 pages/_app.tsx                          | 57 +++++++++++----
 pages/api/auth/[...nextauth].ts         |  4 +-
 pages/info/index.tsx                    |  7 +-
 src/components/header/UserComponent.tsx | 94 +++++++++++++++++++++++++
 src/components/header/index.tsx         | 35 +--------
 5 files changed, 144 insertions(+), 53 deletions(-)
 create mode 100644 src/components/header/UserComponent.tsx

diff --git a/pages/_app.tsx b/pages/_app.tsx
index b52c4ee..86cb2ae 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -4,6 +4,7 @@ import {
   RefineSnackbarProvider,
   ThemedLayoutV2,
   ThemedTitleV2,
+  ThemedSiderV2,
   notificationProvider
 } from '@refinedev/mui'
 import routerProvider from '@refinedev/nextjs-router'
@@ -24,6 +25,9 @@ import dataProvider from '../src/providers/dataProvider'
 import { appWithTranslation, useTranslation } from 'next-i18next'
 import logo from '../src/components/fdo_logo'
 import { API_URL } from '../src/constants'
+import StorageIcon from '@mui/icons-material/Storage'
+import FactCheckIcon from '@mui/icons-material/FactCheck'
+import LightbulbIcon from '@mui/icons-material/Lightbulb'
 
 export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
   noLayout?: boolean
@@ -52,6 +56,7 @@ const App = (props: React.PropsWithChildren) => {
 
   const authProvider: AuthBindings = {
     login: async () => {
+      console.log('login')
       signIn('keycloak', {
         callbackUrl: to ? to.toString() : '/info',
         redirect: true
@@ -62,9 +67,10 @@ const App = (props: React.PropsWithChildren) => {
       }
     },
     logout: async () => {
+      console.log('logout')
       signOut({
         redirect: true,
-        callbackUrl: '/login'
+        callbackUrl: '/info'
       })
 
       return {
@@ -72,12 +78,14 @@ const App = (props: React.PropsWithChildren) => {
       }
     },
     onError: async (error) => {
+      console.log('onError')
       console.error(error)
       return {
         error
       }
     },
     check: async () => {
+      console.log('check')
       if (status === 'unauthenticated') {
         return {
           authenticated: false,
@@ -90,9 +98,11 @@ const App = (props: React.PropsWithChildren) => {
       }
     },
     getPermissions: async () => {
+      console.log('getPermissions')
       return null
     },
     getIdentity: async () => {
+      console.log('getIdentity')
       if (data?.user) {
         const { user } = data
         return {
@@ -128,18 +138,32 @@ const App = (props: React.PropsWithChildren) => {
               name: 'fdo',
               list: '/fdo',
               show: '/fdo/show/:id',
-              create: '/fdo'
+              create: '/fdo',
+              meta: {
+                icon: logo
+              }
             }, {
               name: 'repositories',
               list: '/repositories',
-              show: '/repositories/show/:id'
-            }, {
-              name: 'info',
-              show: '/info'
+              show: '/repositories/show/:id',
+              meta: {
+                icon: <StorageIcon/>
+              }
             }, {
               name: 'profiles',
               list: '/profiles',
-              show: '/profiles/show/:id'
+              show: '/profiles/show/:id',
+              meta: {
+                icon: <FactCheckIcon/>
+              }
+            }, {
+              name: 'info',
+              // show: '/info',
+              list: '/info',
+              meta: {
+                label: 'About',
+                icon: <LightbulbIcon/>
+              }
             }]}
           >
             {props.children}
@@ -164,13 +188,18 @@ function MyApp ({
     return (
       <ThemedLayoutV2
         Header={() => <Header sticky />}
-        Title={({ collapsed }) => (
-          <ThemedTitleV2
-            collapsed={collapsed}
-            text="FDO Manager"
-            icon={logo}
-          />
-        )}
+        Sider={() => <ThemedSiderV2
+          render={({ dashboard, logout, items, collapsed }) => {
+            return <>{dashboard}{items}</>
+          }}
+          Title={({ collapsed }) => (
+            <ThemedTitleV2
+              collapsed={collapsed}
+              text="FDO Manager"
+              icon={logo}
+            />
+          )}
+          />}
         >
         <Component {...pageProps} />
       </ThemedLayoutV2>
diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/auth/[...nextauth].ts
index eba82b2..a3db043 100644
--- a/pages/api/auth/[...nextauth].ts
+++ b/pages/api/auth/[...nextauth].ts
@@ -45,9 +45,7 @@ export const authOptions = {
     },
     async redirect ({ url, baseUrl }: any) {
       console.log('### redirect', url, baseUrl, '### ### ###')
-      return url.startsWith(baseUrl)
-        ? Promise.resolve(url)
-        : Promise.resolve(baseUrl)
+      return Promise.resolve(url)
     },
     async jwt ({ token, user, account, profile, isNewUser }: any) {
       console.log('### jwt', token, user, account, profile, isNewUser, '### ### ###')
diff --git a/pages/info/index.tsx b/pages/info/index.tsx
index 041f95e..7b49f28 100644
--- a/pages/info/index.tsx
+++ b/pages/info/index.tsx
@@ -8,7 +8,7 @@ import { GetServerSideProps } from 'next'
 import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
 
 import { useOne, useShow, useApiUrl } from '@refinedev/core'
-import { Show } from '@refinedev/mui'
+import { List } from '@refinedev/mui'
 import Link from '@mui/material/Link'
 
 const Item = styled(Paper)(({ theme }) => ({
@@ -22,11 +22,10 @@ const Item = styled(Paper)(({ theme }) => ({
 const Info = () => {
   const { queryResult: { data, isLoading } } = useShow({ resource: 'info', id: '' })
 
-  console.log('info', data, isLoading)
   const apiUrl = useApiUrl()
 
   return (
-    <Show>
+    <List title="About">
     <Box>
     <Stack>
       <Item>FDO Manager WebUI Version: {FDO_MANAGER_WEBUI_VERSION}</Item>
@@ -35,7 +34,7 @@ const Info = () => {
       <Item>FDO Manager SDK Version: {data?.data?.fdoSdkVersion}</Item>
     </Stack>
     </Box>
-    </Show>
+    </List>
   )
 }
 
diff --git a/src/components/header/UserComponent.tsx b/src/components/header/UserComponent.tsx
new file mode 100644
index 0000000..d92365c
--- /dev/null
+++ b/src/components/header/UserComponent.tsx
@@ -0,0 +1,94 @@
+import * as React from 'react'
+import Button from '@mui/material/Button'
+import Menu from '@mui/material/Menu'
+import MenuItem from '@mui/material/MenuItem'
+import Stack from '@mui/material/Stack'
+import Typography from '@mui/material/Typography'
+import Avatar from '@mui/material/Avatar'
+import { useGetIdentity, useLogout, useLogin, useTranslate } from '@refinedev/core'
+
+interface IUser {
+  name: string
+  avatar: string
+}
+
+const Login = () => {
+  const t = useTranslate()
+  const { mutate: login } = useLogin()
+  return (
+        <Button
+          style={{ width: '140px' }}
+          // size="large"
+          variant="contained"
+          onClick={() => { login({}) }}
+        >
+          {t('pages.login.signin', 'Sign in')}
+        </Button>
+  )
+}
+
+const UserMenu = ({ user }: any) => {
+  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
+  const open = Boolean(anchorEl)
+  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
+    setAnchorEl(event.currentTarget)
+  }
+  const handleClose = () => {
+    setAnchorEl(null)
+  }
+  const { mutate: logout } = useLogout()
+
+  return (
+    <>
+      <Stack
+                direction="row"
+                gap="16px"
+                alignItems="center"
+                justifyContent="center"
+              >
+        {user?.name && (
+          <Button
+            id="user-button"
+            aria-controls={open ? 'user-menu' : undefined}
+            aria-haspopup="true"
+            aria-expanded={open ? 'true' : undefined}
+            sx={{ padding: '0' }}
+            onClick={handleClick}
+          >
+                  <Typography
+                    sx={{
+                      display: {
+                        xs: 'none',
+                        sm: 'inline-block'
+                      }
+                    }}
+                    variant="subtitle2"
+                  >
+                    {user?.name}
+                  </Typography>
+          </Button>
+        )}
+        <Avatar src={user?.avatar} alt={user?.name} />
+      </Stack>
+      <Menu
+        id="user-menu"
+        anchorEl={anchorEl}
+        open={open}
+        onClose={handleClose}
+        MenuListProps={{
+          'aria-labelledby': 'user-button'
+        }}
+      >
+        {/* <MenuItem onClick={handleClose}>Profile</MenuItem> */}
+        {/* <MenuItem onClick={handleClose}>My account</MenuItem> */}
+        <MenuItem onClick={() => { logout() }}>Logout</MenuItem>
+      </Menu>
+    </>
+  )
+}
+
+export default function UserComponent () {
+  const { data: user } = useGetIdentity<IUser>()
+
+  return (user?.name ? <UserMenu user={user}/> : <Login/>)
+}
diff --git a/src/components/header/index.tsx b/src/components/header/index.tsx
index 2a2865b..13b342a 100644
--- a/src/components/header/index.tsx
+++ b/src/components/header/index.tsx
@@ -10,16 +10,11 @@ import Select from '@mui/material/Select'
 import Stack from '@mui/material/Stack'
 import Toolbar from '@mui/material/Toolbar'
 import Typography from '@mui/material/Typography'
-import { useGetIdentity } from '@refinedev/core'
-import { HamburgerMenu, type RefineThemedLayoutV2HeaderProps } from '@refinedev/mui'
+import { HamburgerMenu, RefineThemedLayoutV2HeaderProps } from '@refinedev/mui'
 import Link from 'next/link'
 import { useRouter } from 'next/router'
 import React, { useContext } from 'react'
-
-interface IUser {
-  name: string
-  avatar: string
-}
+import UserComponent from './UserComponent'
 
 export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
   sticky = true
@@ -27,8 +22,6 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
   const { mode, setMode } = useContext(ColorModeContext)
   const { locale: currentLocale, locales, pathname, query } = useRouter()
 
-  const { data: user } = useGetIdentity<IUser>()
-
   return (
     <AppBar position={sticky ? 'sticky' : 'relative'}>
       <Toolbar>
@@ -102,29 +95,7 @@ export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({
               {mode === 'dark' ? <LightModeOutlined /> : <DarkModeOutlined />}
             </IconButton>
 
-            {(user?.avatar || user?.name) && (
-              <Stack
-                direction="row"
-                gap="16px"
-                alignItems="center"
-                justifyContent="center"
-              >
-                {user?.name && (
-                  <Typography
-                    sx={{
-                      display: {
-                        xs: 'none',
-                        sm: 'inline-block'
-                      }
-                    }}
-                    variant="subtitle2"
-                  >
-                    {user?.name}
-                  </Typography>
-                )}
-                <Avatar src={user?.avatar} alt={user?.name} />
-              </Stack>
-            )}
+            <UserComponent/>
           </Stack>
         </Stack>
       </Toolbar>
-- 
GitLab