From 4f5fbada699dd7240d2de7491e520407d9efdf1e Mon Sep 17 00:00:00 2001 From: Vamshi-Microsoft Date: Thu, 30 Apr 2026 16:31:20 +0530 Subject: [PATCH 1/3] feat: add UserProfile component to display authenticated user information in header --- src/frontend/src/components/Header/Header.tsx | 23 ++- .../src/components/Header/UserProfile.tsx | 136 ++++++++++++++++++ 2 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 src/frontend/src/components/Header/UserProfile.tsx diff --git a/src/frontend/src/components/Header/Header.tsx b/src/frontend/src/components/Header/Header.tsx index baccc3bc..3e203100 100644 --- a/src/frontend/src/components/Header/Header.tsx +++ b/src/frontend/src/components/Header/Header.tsx @@ -1,5 +1,7 @@ import React from "react"; import { Subtitle2 } from "@fluentui/react-components"; +import UserProfile from "./UserProfile"; + /** * @component * @name Header @@ -16,6 +18,16 @@ type HeaderProps = { children?: React.ReactNode; }; +// Determine once whether MSAL authentication is enabled, so the hooks inside +// UserProfile (which require MsalProvider in the tree) are only mounted when safe. +const isAuthEnabled = (): boolean => { + // window.appConfig is set in main.jsx after fetching /config + // Falls back to false when the config has not loaded or auth is disabled. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const cfg = (typeof window !== "undefined" ? (window as any).appConfig : null); + return Boolean(cfg && cfg.ENABLE_AUTH); +}; + const Header: React.FC = ({ title = "Contoso", subtitle, children }) => { return (
= ({ title = "Contoso", subtitle, children } {/* HEADER TOOLBAR (rendered only if passed as a child) */} - {children} +
+ {children} + {isAuthEnabled() && } +
); }; diff --git a/src/frontend/src/components/Header/UserProfile.tsx b/src/frontend/src/components/Header/UserProfile.tsx new file mode 100644 index 00000000..daf912be --- /dev/null +++ b/src/frontend/src/components/Header/UserProfile.tsx @@ -0,0 +1,136 @@ +import React from "react"; +import { + Avatar, + Button, + Menu, + MenuTrigger, + MenuPopover, + MenuList, + MenuItem, + MenuDivider, + Tooltip, +} from "@fluentui/react-components"; +import { + Person20Regular, + SignOut20Regular, +} from "@fluentui/react-icons"; +import useAuth from "../../msal-auth/useAuth"; + +/** + * @component UserProfile + * @description Renders an avatar in the header. Clicking opens a menu showing + * the signed-in user's name and email along with a Logout option. + * Designed to be rendered only when MSAL authentication is enabled. + */ +const getInitials = (name?: string, username?: string): string => { + const source = name && name.trim().length > 0 ? name : username || ""; + if (!source) return "U"; + + if (source.includes("@")) { + const prefix = source.split("@")[0]; + const parts = prefix.split(/[._-]/).filter(Boolean); + if (parts.length >= 2) { + return (parts[0][0] + parts[1][0]).toUpperCase(); + } + return prefix.substring(0, 2).toUpperCase(); + } + + return source + .split(" ") + .filter(Boolean) + .map((n) => n[0]) + .join("") + .toUpperCase() + .slice(0, 2); +}; + +const UserProfile: React.FC = () => { + const { isAuthenticated, accounts, logout } = useAuth(); + + if (!isAuthenticated || !accounts || accounts.length === 0) { + return null; + } + + const account = accounts[0]; + const name = account?.name || account?.username || "User"; + const email = account?.username || ""; + const initials = getInitials(account?.name, account?.username); + + const handleLogout = (e: React.MouseEvent) => { + e.stopPropagation(); + logout(); + }; + + return ( + + + + + + + + + } + disabled + style={{ cursor: "default", opacity: 1 }} + > +
+ + {name} + + {email && ( + + {email} + + )} +
+
+ + } onClick={handleLogout}> + Logout + +
+
+
+ ); +}; + +export default UserProfile; From 9921badcc7104bf66c54c7636efa819fea1ad10e Mon Sep 17 00:00:00 2001 From: Vamshi-Microsoft Date: Fri, 8 May 2026 11:56:51 +0530 Subject: [PATCH 2/3] Enhance Header component to conditionally render UserProfile based on auth status and improve code readability --- src/frontend/declarations.d.ts | 14 ++++++- src/frontend/src/components/Header/Header.tsx | 37 +++++++++++-------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/frontend/declarations.d.ts b/src/frontend/declarations.d.ts index 00f9c47c..e0fe54d7 100644 --- a/src/frontend/declarations.d.ts +++ b/src/frontend/declarations.d.ts @@ -2,4 +2,16 @@ declare module "*.png" { const value: string; export default value; } - \ No newline at end of file + +interface AppConfig { + API_URL?: string; + REACT_APP_MSAL_AUTH_CLIENTID?: string; + REACT_APP_MSAL_AUTH_AUTHORITY?: string; + REACT_APP_MSAL_REDIRECT_URL?: string; + REACT_APP_MSAL_POST_REDIRECT_URL?: string; + ENABLE_AUTH?: boolean; +} + +interface Window { + appConfig?: AppConfig; +} diff --git a/src/frontend/src/components/Header/Header.tsx b/src/frontend/src/components/Header/Header.tsx index 3e203100..b78ff168 100644 --- a/src/frontend/src/components/Header/Header.tsx +++ b/src/frontend/src/components/Header/Header.tsx @@ -20,15 +20,17 @@ type HeaderProps = { // Determine once whether MSAL authentication is enabled, so the hooks inside // UserProfile (which require MsalProvider in the tree) are only mounted when safe. +// window.appConfig is set in main.jsx after fetching /config; falls back to +// false when the config has not loaded or auth is disabled. const isAuthEnabled = (): boolean => { - // window.appConfig is set in main.jsx after fetching /config - // Falls back to false when the config has not loaded or auth is disabled. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const cfg = (typeof window !== "undefined" ? (window as any).appConfig : null); - return Boolean(cfg && cfg.ENABLE_AUTH); + if (typeof window === "undefined") return false; + return Boolean(window.appConfig && window.appConfig.ENABLE_AUTH); }; const Header: React.FC = ({ title = "Contoso", subtitle, children }) => { + const authEnabled = isAuthEnabled(); + const hasToolbarContent = React.Children.count(children) > 0 || authEnabled; + return (
= ({ title = "Contoso", subtitle, children } - {/* HEADER TOOLBAR (rendered only if passed as a child) */} -
- {children} - {isAuthEnabled() && } -
+ {/* HEADER TOOLBAR (rendered only when there is toolbar content + or the auth-enabled user profile menu to display) */} + {hasToolbarContent && ( +
+ {children} + {authEnabled && } +
+ )}
); }; From 6cf5d4bcb858caf8f1441dcf979b833571054486 Mon Sep 17 00:00:00 2001 From: Vamshi-Microsoft Date: Fri, 8 May 2026 12:32:44 +0530 Subject: [PATCH 3/3] Update Tooltip content in UserProfile to display a more informative message for signed-in users --- src/frontend/src/components/Header/UserProfile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/components/Header/UserProfile.tsx b/src/frontend/src/components/Header/UserProfile.tsx index daf912be..50be2d49 100644 --- a/src/frontend/src/components/Header/UserProfile.tsx +++ b/src/frontend/src/components/Header/UserProfile.tsx @@ -64,7 +64,7 @@ const UserProfile: React.FC = () => { return ( - +