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-8', 'text-sm')}
+
+
+
+
+ {renderAvatar('size-10', 'text-base')}
+
+ {userName && (
+
+ {userName}
+
+ )}
+ {userEmail && (
+
+ {userEmail}
+
+ )}
+
+
+
+
+
+
+ {t('auth.signOut')}
+
+
+
+
+ );
+}
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",