feat(icon-rail): integrate user profile component and enhance layout with user settings options

This commit is contained in:
Anish Sarkar 2026-04-28 19:29:14 +05:30
parent 282510f93c
commit e60c5399af
4 changed files with 62 additions and 20 deletions

View file

@ -6,6 +6,8 @@ import { ScrollArea } from "@/components/ui/scroll-area";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import type { SearchSpace } from "../../types/layout.types";
import type { User } from "../../types/layout.types";
import { SidebarUserProfile } from "../sidebar/SidebarUserProfile";
import { SearchSpaceAvatar } from "./SearchSpaceAvatar";
interface IconRailProps {
@ -15,6 +17,11 @@ interface IconRailProps {
onSearchSpaceDelete?: (searchSpace: SearchSpace) => void;
onSearchSpaceSettings?: (searchSpace: SearchSpace) => void;
onAddSearchSpace: () => void;
user: User;
onUserSettings?: () => void;
onLogout?: () => void;
theme?: string;
setTheme?: (theme: "light" | "dark" | "system") => void;
className?: string;
}
@ -25,11 +32,16 @@ export function IconRail({
onSearchSpaceDelete,
onSearchSpaceSettings,
onAddSearchSpace,
user,
onUserSettings,
onLogout,
theme,
setTheme,
className,
}: IconRailProps) {
return (
<div className={cn("flex h-full w-14 flex-col items-center", className)}>
<ScrollArea className="w-full">
<div className={cn("flex h-full w-14 min-h-0 flex-col items-center", className)}>
<ScrollArea className="w-full min-h-0 flex-1">
<div className="flex flex-col items-center gap-2 px-1.5 py-3">
{searchSpaces.map((searchSpace) => (
<SearchSpaceAvatar
@ -65,6 +77,14 @@ export function IconRail({
</Tooltip>
</div>
</ScrollArea>
<SidebarUserProfile
user={user}
onUserSettings={onUserSettings}
onLogout={onLogout}
isCollapsed
theme={theme}
setTheme={setTheme}
/>
</div>
);
}

View file

@ -387,6 +387,11 @@ export function LayoutShell({
onSearchSpaceDelete={onSearchSpaceDelete}
onSearchSpaceSettings={onSearchSpaceSettings}
onAddSearchSpace={onAddSearchSpace}
user={user}
onUserSettings={onUserSettings}
onLogout={onLogout}
theme={theme}
setTheme={setTheme}
/>
</div>
@ -423,6 +428,7 @@ export function LayoutShell({
pageUsage={pageUsage}
theme={theme}
setTheme={setTheme}
renderUserProfile={false}
className={cn(
"flex shrink-0 transition-[border-radius] duration-200",
anySlideOutOpen ? "rounded-l-xl delay-0" : "rounded-xl delay-150"

View file

@ -62,6 +62,7 @@ interface SidebarProps {
disableTooltips?: boolean;
sidebarWidth?: number;
isResizing?: boolean;
renderUserProfile?: boolean;
}
export function Sidebar({
@ -95,6 +96,7 @@ export function Sidebar({
disableTooltips = false,
sidebarWidth = SIDEBAR_MIN_WIDTH,
isResizing = false,
renderUserProfile = true,
}: SidebarProps) {
const t = useTranslations("sidebar");
const [openDropdownChatId, setOpenDropdownChatId] = useState<number | null>(null);
@ -275,14 +277,16 @@ export function Sidebar({
<SidebarUsageFooter pageUsage={pageUsage} isCollapsed={isCollapsed} />
<SidebarUserProfile
user={user}
onUserSettings={onUserSettings}
onLogout={onLogout}
isCollapsed={isCollapsed}
theme={theme}
setTheme={setTheme}
/>
{renderUserProfile && (
<SidebarUserProfile
user={user}
onUserSettings={onUserSettings}
onLogout={onLogout}
isCollapsed={isCollapsed}
theme={theme}
setTheme={setTheme}
/>
)}
</div>
</div>
);

View file

@ -113,19 +113,23 @@ function UserAvatar({
avatarUrl,
initials,
bgColor,
size = "sm",
}: {
avatarUrl?: string;
initials: string;
bgColor: string;
size?: "sm" | "md";
}) {
const sizeClass = size === "md" ? "h-9 w-9" : "h-8 w-8";
if (avatarUrl) {
return (
<Image
src={avatarUrl}
alt="User avatar"
width={32}
height={32}
className="h-8 w-8 shrink-0 rounded-lg object-cover select-none"
width={size === "md" ? 36 : 32}
height={size === "md" ? 36 : 32}
className={cn(sizeClass, "shrink-0 rounded-full object-cover select-none")}
referrerPolicy="no-referrer"
unoptimized
/>
@ -134,7 +138,10 @@ function UserAvatar({
return (
<div
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-xs font-semibold text-white select-none"
className={cn(
sizeClass,
"flex shrink-0 items-center justify-center rounded-full text-xs font-semibold text-white select-none"
)}
style={{ backgroundColor: bgColor }}
>
{initials}
@ -181,24 +188,29 @@ export function SidebarUserProfile({
// Collapsed view - just show avatar with dropdown
if (isCollapsed) {
return (
<div className="border-t p-2">
<div className="border-t px-1.5 py-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
type="button"
className={cn(
"flex h-10 w-full items-center justify-center rounded-md",
"hover:bg-accent transition-colors",
"mx-auto flex h-9 w-9 items-center justify-center rounded-full",
"transition-opacity hover:opacity-90",
"focus:outline-none focus-visible:outline-none",
"data-[state=open]:bg-transparent"
"data-[state=open]:opacity-90"
)}
>
<UserAvatar avatarUrl={user.avatarUrl} initials={initials} bgColor={bgColor} />
<UserAvatar
avatarUrl={user.avatarUrl}
initials={initials}
bgColor={bgColor}
size="md"
/>
<span className="sr-only">{displayName}</span>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48" side="right" align="center" sideOffset={8}>
<DropdownMenuContent className="w-48" side="right" align="end" sideOffset={8}>
<DropdownMenuLabel className="font-normal">
<div className="flex items-center gap-2">
<UserAvatar avatarUrl={user.avatarUrl} initials={initials} bgColor={bgColor} />