diff --git a/frontend/src/Navigation.tsx b/frontend/src/Navigation.tsx index 58187151..a31f7090 100644 --- a/frontend/src/Navigation.tsx +++ b/frontend/src/Navigation.tsx @@ -4,7 +4,6 @@ import { useDispatch, useSelector } from 'react-redux'; import { NavLink, useNavigate } from 'react-router-dom'; import { LayoutGrid, - LogOut, Menu, PanelLeftClose, PanelLeftOpen, @@ -79,15 +78,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { const selectedAgent = useSelector(selectSelectedAgent); const { isMobile, isTablet } = useMediaQuery(); - const { - showTokenModal, - handleTokenSubmit, - authType, - logout, - userEmail, - userName, - userPicture, - } = useTokenAuth(); + const { showTokenModal, handleTokenSubmit } = useTokenAuth(); const [isDeletingConversation, setIsDeletingConversation] = useState(false); const [uploadModalState, setUploadModalState] = @@ -656,48 +647,6 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { {t('settings.label')}

- {authType === 'oidc' && ( -
- {userPicture ? ( - - ) : ( - - - {(userName || userEmail || '?').charAt(0).toUpperCase()} - - - )} - - {userName && ( -

- {userName} -

- )} - {userEmail && ( -

- {userEmail} -

- )} -
- -
- )}
diff --git a/frontend/src/components/ActionButtons.tsx b/frontend/src/components/ActionButtons.tsx index 4a1e609d..8520315f 100644 --- a/frontend/src/components/ActionButtons.tsx +++ b/frontend/src/components/ActionButtons.tsx @@ -11,6 +11,7 @@ import { updateConversationId, } from '../conversation/conversationSlice'; import { Button } from './ui/button'; +import ProfileButton from './ProfileButton'; interface ActionButtonsProps { className?: string; @@ -86,7 +87,7 @@ export default function ActionButtons({ )} )} -
{/* */}
+
); diff --git a/frontend/src/components/ProfileButton.tsx b/frontend/src/components/ProfileButton.tsx new file mode 100644 index 00000000..790d9a07 --- /dev/null +++ b/frontend/src/components/ProfileButton.tsx @@ -0,0 +1,83 @@ +import { LogOut } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; + +import useTokenAuth from '../hooks/useTokenAuth'; +import { Avatar } from './ui/avatar'; +import { Popover, PopoverContent, PopoverTrigger } from './ui/popover'; + +/** + * Top-right account menu for OIDC sessions: an avatar that opens a popover + * showing the signed-in name/email and a sign-out action. Renders nothing for + * other auth modes, which carry no user identity. + */ +export default function ProfileButton() { + const { t } = useTranslation(); + const { authType, userName, userEmail, userPicture, logout } = useTokenAuth(); + + if (authType !== 'oidc' || (!userName && !userEmail)) return null; + + const initial = (userName || userEmail || '?').charAt(0).toUpperCase(); + const renderAvatar = (size: string, text: string) => + userPicture ? ( + + ) : ( + + + {initial} + + + ); + + return ( + + + + + +
+ {renderAvatar('size-10', 'text-base')} + + {userName && ( +

+ {userName} +

+ )} + {userEmail && ( +

+ {userEmail} +

+ )} +
+
+
+
+ +
+ + + ); +} diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json index 54706a87..8d91f741 100644 --- a/frontend/src/locale/en.json +++ b/frontend/src/locale/en.json @@ -36,7 +36,8 @@ "signInWith": "Sign in with {{provider}}", "notAuthorized": "Your account isn't authorized to access this workspace. Sign in with a different account or contact your administrator.", "accountDisabled": "Your account has been disabled. Contact your administrator.", - "signOut": "Sign out" + "signOut": "Sign out", + "account": "Account" }, "settings": { "label": "Settings",