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