mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-23 19:05:16 +02:00
feat(icon-rail): integrate user profile component and enhance layout with user settings options
This commit is contained in:
parent
282510f93c
commit
e60c5399af
4 changed files with 62 additions and 20 deletions
|
|
@ -6,6 +6,8 @@ import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import type { SearchSpace } from "../../types/layout.types";
|
import type { SearchSpace } from "../../types/layout.types";
|
||||||
|
import type { User } from "../../types/layout.types";
|
||||||
|
import { SidebarUserProfile } from "../sidebar/SidebarUserProfile";
|
||||||
import { SearchSpaceAvatar } from "./SearchSpaceAvatar";
|
import { SearchSpaceAvatar } from "./SearchSpaceAvatar";
|
||||||
|
|
||||||
interface IconRailProps {
|
interface IconRailProps {
|
||||||
|
|
@ -15,6 +17,11 @@ interface IconRailProps {
|
||||||
onSearchSpaceDelete?: (searchSpace: SearchSpace) => void;
|
onSearchSpaceDelete?: (searchSpace: SearchSpace) => void;
|
||||||
onSearchSpaceSettings?: (searchSpace: SearchSpace) => void;
|
onSearchSpaceSettings?: (searchSpace: SearchSpace) => void;
|
||||||
onAddSearchSpace: () => void;
|
onAddSearchSpace: () => void;
|
||||||
|
user: User;
|
||||||
|
onUserSettings?: () => void;
|
||||||
|
onLogout?: () => void;
|
||||||
|
theme?: string;
|
||||||
|
setTheme?: (theme: "light" | "dark" | "system") => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,11 +32,16 @@ export function IconRail({
|
||||||
onSearchSpaceDelete,
|
onSearchSpaceDelete,
|
||||||
onSearchSpaceSettings,
|
onSearchSpaceSettings,
|
||||||
onAddSearchSpace,
|
onAddSearchSpace,
|
||||||
|
user,
|
||||||
|
onUserSettings,
|
||||||
|
onLogout,
|
||||||
|
theme,
|
||||||
|
setTheme,
|
||||||
className,
|
className,
|
||||||
}: IconRailProps) {
|
}: IconRailProps) {
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex h-full w-14 flex-col items-center", className)}>
|
<div className={cn("flex h-full w-14 min-h-0 flex-col items-center", className)}>
|
||||||
<ScrollArea className="w-full">
|
<ScrollArea className="w-full min-h-0 flex-1">
|
||||||
<div className="flex flex-col items-center gap-2 px-1.5 py-3">
|
<div className="flex flex-col items-center gap-2 px-1.5 py-3">
|
||||||
{searchSpaces.map((searchSpace) => (
|
{searchSpaces.map((searchSpace) => (
|
||||||
<SearchSpaceAvatar
|
<SearchSpaceAvatar
|
||||||
|
|
@ -65,6 +77,14 @@ export function IconRail({
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
<SidebarUserProfile
|
||||||
|
user={user}
|
||||||
|
onUserSettings={onUserSettings}
|
||||||
|
onLogout={onLogout}
|
||||||
|
isCollapsed
|
||||||
|
theme={theme}
|
||||||
|
setTheme={setTheme}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,11 @@ export function LayoutShell({
|
||||||
onSearchSpaceDelete={onSearchSpaceDelete}
|
onSearchSpaceDelete={onSearchSpaceDelete}
|
||||||
onSearchSpaceSettings={onSearchSpaceSettings}
|
onSearchSpaceSettings={onSearchSpaceSettings}
|
||||||
onAddSearchSpace={onAddSearchSpace}
|
onAddSearchSpace={onAddSearchSpace}
|
||||||
|
user={user}
|
||||||
|
onUserSettings={onUserSettings}
|
||||||
|
onLogout={onLogout}
|
||||||
|
theme={theme}
|
||||||
|
setTheme={setTheme}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -423,6 +428,7 @@ export function LayoutShell({
|
||||||
pageUsage={pageUsage}
|
pageUsage={pageUsage}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
setTheme={setTheme}
|
setTheme={setTheme}
|
||||||
|
renderUserProfile={false}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex shrink-0 transition-[border-radius] duration-200",
|
"flex shrink-0 transition-[border-radius] duration-200",
|
||||||
anySlideOutOpen ? "rounded-l-xl delay-0" : "rounded-xl delay-150"
|
anySlideOutOpen ? "rounded-l-xl delay-0" : "rounded-xl delay-150"
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ interface SidebarProps {
|
||||||
disableTooltips?: boolean;
|
disableTooltips?: boolean;
|
||||||
sidebarWidth?: number;
|
sidebarWidth?: number;
|
||||||
isResizing?: boolean;
|
isResizing?: boolean;
|
||||||
|
renderUserProfile?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Sidebar({
|
export function Sidebar({
|
||||||
|
|
@ -95,6 +96,7 @@ export function Sidebar({
|
||||||
disableTooltips = false,
|
disableTooltips = false,
|
||||||
sidebarWidth = SIDEBAR_MIN_WIDTH,
|
sidebarWidth = SIDEBAR_MIN_WIDTH,
|
||||||
isResizing = false,
|
isResizing = false,
|
||||||
|
renderUserProfile = true,
|
||||||
}: SidebarProps) {
|
}: SidebarProps) {
|
||||||
const t = useTranslations("sidebar");
|
const t = useTranslations("sidebar");
|
||||||
const [openDropdownChatId, setOpenDropdownChatId] = useState<number | null>(null);
|
const [openDropdownChatId, setOpenDropdownChatId] = useState<number | null>(null);
|
||||||
|
|
@ -275,14 +277,16 @@ export function Sidebar({
|
||||||
|
|
||||||
<SidebarUsageFooter pageUsage={pageUsage} isCollapsed={isCollapsed} />
|
<SidebarUsageFooter pageUsage={pageUsage} isCollapsed={isCollapsed} />
|
||||||
|
|
||||||
<SidebarUserProfile
|
{renderUserProfile && (
|
||||||
user={user}
|
<SidebarUserProfile
|
||||||
onUserSettings={onUserSettings}
|
user={user}
|
||||||
onLogout={onLogout}
|
onUserSettings={onUserSettings}
|
||||||
isCollapsed={isCollapsed}
|
onLogout={onLogout}
|
||||||
theme={theme}
|
isCollapsed={isCollapsed}
|
||||||
setTheme={setTheme}
|
theme={theme}
|
||||||
/>
|
setTheme={setTheme}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -113,19 +113,23 @@ function UserAvatar({
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
initials,
|
initials,
|
||||||
bgColor,
|
bgColor,
|
||||||
|
size = "sm",
|
||||||
}: {
|
}: {
|
||||||
avatarUrl?: string;
|
avatarUrl?: string;
|
||||||
initials: string;
|
initials: string;
|
||||||
bgColor: string;
|
bgColor: string;
|
||||||
|
size?: "sm" | "md";
|
||||||
}) {
|
}) {
|
||||||
|
const sizeClass = size === "md" ? "h-9 w-9" : "h-8 w-8";
|
||||||
|
|
||||||
if (avatarUrl) {
|
if (avatarUrl) {
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
src={avatarUrl}
|
src={avatarUrl}
|
||||||
alt="User avatar"
|
alt="User avatar"
|
||||||
width={32}
|
width={size === "md" ? 36 : 32}
|
||||||
height={32}
|
height={size === "md" ? 36 : 32}
|
||||||
className="h-8 w-8 shrink-0 rounded-lg object-cover select-none"
|
className={cn(sizeClass, "shrink-0 rounded-full object-cover select-none")}
|
||||||
referrerPolicy="no-referrer"
|
referrerPolicy="no-referrer"
|
||||||
unoptimized
|
unoptimized
|
||||||
/>
|
/>
|
||||||
|
|
@ -134,7 +138,10 @@ function UserAvatar({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<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 }}
|
style={{ backgroundColor: bgColor }}
|
||||||
>
|
>
|
||||||
{initials}
|
{initials}
|
||||||
|
|
@ -181,24 +188,29 @@ export function SidebarUserProfile({
|
||||||
// Collapsed view - just show avatar with dropdown
|
// Collapsed view - just show avatar with dropdown
|
||||||
if (isCollapsed) {
|
if (isCollapsed) {
|
||||||
return (
|
return (
|
||||||
<div className="border-t p-2">
|
<div className="border-t px-1.5 py-2">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-10 w-full items-center justify-center rounded-md",
|
"mx-auto flex h-9 w-9 items-center justify-center rounded-full",
|
||||||
"hover:bg-accent transition-colors",
|
"transition-opacity hover:opacity-90",
|
||||||
"focus:outline-none focus-visible:outline-none",
|
"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>
|
<span className="sr-only">{displayName}</span>
|
||||||
</button>
|
</button>
|
||||||
</DropdownMenuTrigger>
|
</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">
|
<DropdownMenuLabel className="font-normal">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<UserAvatar avatarUrl={user.avatarUrl} initials={initials} bgColor={bgColor} />
|
<UserAvatar avatarUrl={user.avatarUrl} initials={initials} bgColor={bgColor} />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue