From 78c3fc1593ca29dedacd937f467f6af24e9acb4b Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 10 Jun 2026 15:17:42 +0100 Subject: [PATCH] feat(frontend): top-right profile dropdown for OIDC sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the OIDC user chip in the sidebar with an account avatar in the top-right action bar (the existing UserButton placeholder). The avatar — a brand-purple circle with the user's initial, or the IdP picture — opens a popover showing name, email, and a sign-out action, reusing the existing Avatar/Popover primitives with a hover ring. Renders only under AUTH_TYPE=oidc. --- frontend/src/Navigation.tsx | 53 +-------------- frontend/src/components/ActionButtons.tsx | 3 +- frontend/src/components/ProfileButton.tsx | 83 +++++++++++++++++++++++ frontend/src/locale/en.json | 3 +- 4 files changed, 88 insertions(+), 54 deletions(-) create mode 100644 frontend/src/components/ProfileButton.tsx 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",