|
9 | 9 | ChevronLeft, |
10 | 10 | ChevronRight, |
11 | 11 | User, |
| 12 | + Database, |
| 13 | + Box, |
| 14 | + Bot, |
12 | 15 | } from "lucide-react"; |
13 | 16 |
|
14 | 17 | import { Button } from "@/components/ui/button"; |
@@ -37,7 +40,8 @@ import { StaticScrollArea } from "@/components/ui/scrollArea"; |
37 | 40 | import { USER_ROLES } from "@/const/modelConfig"; |
38 | 41 | import { useConfig } from "@/hooks/useConfig"; |
39 | 42 | import { useResponsiveTextSize } from "@/hooks/useResponsiveTextSize"; |
40 | | -import { Spin, Tag, ConfigProvider } from "antd"; |
| 43 | +import { Spin, Tag, ConfigProvider, Dropdown, Menu } from "antd"; |
| 44 | +import type { MenuProps } from "antd"; |
41 | 45 | import { getRoleColor } from "@/lib/auth"; |
42 | 46 | import { useAuth } from "@/hooks/useAuth"; |
43 | 47 | import { extractColorsFromUri } from "@/lib/avatar"; |
@@ -120,6 +124,7 @@ export function ChatSidebar({ |
120 | 124 | onRename, |
121 | 125 | onDelete, |
122 | 126 | onSettingsClick, |
| 127 | + settingsMenuItems, |
123 | 128 | onDropdownOpenChange, |
124 | 129 | onToggleSidebar, |
125 | 130 | expanded, |
@@ -237,6 +242,100 @@ export function ChatSidebar({ |
237 | 242 | ); |
238 | 243 | }; |
239 | 244 |
|
| 245 | + // Get icon for menu item based on key |
| 246 | + const getMenuIcon = (key: string) => { |
| 247 | + switch (key) { |
| 248 | + case "models": |
| 249 | + return <Box className="h-4 w-4" />; |
| 250 | + case "knowledges": |
| 251 | + return <Database className="h-4 w-4" />; |
| 252 | + case "agents": |
| 253 | + return <Bot className="h-4 w-4" />; |
| 254 | + } |
| 255 | + }; |
| 256 | + |
| 257 | + // Render settings button (with dropdown for admin, plain button for regular users) |
| 258 | + const renderSettingsButton = (isCollapsed: boolean = false) => { |
| 259 | + const buttonContent = ( |
| 260 | + <Button |
| 261 | + variant="ghost" |
| 262 | + size="icon" |
| 263 | + className={isCollapsed ? "h-10 w-10 rounded-full hover:bg-slate-100" : "size-10 rounded-full hover:bg-slate-100"} |
| 264 | + onClick={settingsMenuItems ? undefined : onSettingsClick} |
| 265 | + > |
| 266 | + <Settings className={isCollapsed ? "h-6 w-6" : "size-5"} /> |
| 267 | + </Button> |
| 268 | + ); |
| 269 | + |
| 270 | + // If settingsMenuItems is provided, show dropdown with icons |
| 271 | + if (settingsMenuItems && settingsMenuItems.length > 0) { |
| 272 | + const menuProps: MenuProps = { |
| 273 | + items: settingsMenuItems.map((item, index) => ({ |
| 274 | + key: item.key, |
| 275 | + label: ( |
| 276 | + <div className="flex items-center gap-3 py-1"> |
| 277 | + <span className="text-gray-600">{getMenuIcon(item.key)}</span> |
| 278 | + <span className="text-sm font-medium">{item.label}</span> |
| 279 | + </div> |
| 280 | + ), |
| 281 | + onClick: item.onClick, |
| 282 | + style: { |
| 283 | + padding: "8px 16px", |
| 284 | + }, |
| 285 | + })), |
| 286 | + style: { |
| 287 | + minWidth: "240px", |
| 288 | + borderRadius: "8px", |
| 289 | + boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)", |
| 290 | + }, |
| 291 | + }; |
| 292 | + |
| 293 | + return ( |
| 294 | + <TooltipProvider> |
| 295 | + <Tooltip> |
| 296 | + <TooltipTrigger asChild> |
| 297 | + <ConfigProvider |
| 298 | + getPopupContainer={() => document.body} |
| 299 | + theme={{ |
| 300 | + components: { |
| 301 | + Dropdown: { |
| 302 | + paddingBlock: 4, |
| 303 | + }, |
| 304 | + }, |
| 305 | + }} |
| 306 | + > |
| 307 | + <Dropdown menu={menuProps} placement="topRight" trigger={["click"]}> |
| 308 | + <div className={isCollapsed ? "flex justify-center" : ""}> |
| 309 | + {buttonContent} |
| 310 | + </div> |
| 311 | + </Dropdown> |
| 312 | + </ConfigProvider> |
| 313 | + </TooltipTrigger> |
| 314 | + <TooltipContent side={isCollapsed ? "right" : "top"}> |
| 315 | + {t("chatLeftSidebar.settings")} |
| 316 | + </TooltipContent> |
| 317 | + </Tooltip> |
| 318 | + </TooltipProvider> |
| 319 | + ); |
| 320 | + } |
| 321 | + |
| 322 | + // Otherwise, show plain button for regular users (shouldn't reach here anymore) |
| 323 | + return ( |
| 324 | + <TooltipProvider> |
| 325 | + <Tooltip> |
| 326 | + <TooltipTrigger asChild> |
| 327 | + <div className={isCollapsed ? "flex justify-center" : ""}> |
| 328 | + {buttonContent} |
| 329 | + </div> |
| 330 | + </TooltipTrigger> |
| 331 | + <TooltipContent side={isCollapsed ? "right" : "top"}> |
| 332 | + {t("chatLeftSidebar.settings")} |
| 333 | + </TooltipContent> |
| 334 | + </Tooltip> |
| 335 | + </TooltipProvider> |
| 336 | + ); |
| 337 | + }; |
| 338 | + |
240 | 339 | // Render dialog list items |
241 | 340 | const renderDialogList = (dialogs: ConversationListItem[], title: string) => { |
242 | 341 | if (dialogs.length === 0) return null; |
@@ -461,25 +560,7 @@ export function ChatSidebar({ |
461 | 560 | ) : null} |
462 | 561 |
|
463 | 562 | {/* Settings button */} |
464 | | - <TooltipProvider> |
465 | | - <Tooltip> |
466 | | - <TooltipTrigger asChild> |
467 | | - <div className="flex justify-center"> |
468 | | - <Button |
469 | | - variant="ghost" |
470 | | - size="icon" |
471 | | - className="h-10 w-10 rounded-full hover:bg-slate-100" |
472 | | - onClick={onSettingsClick} |
473 | | - > |
474 | | - <Settings className="h-6 w-6" /> |
475 | | - </Button> |
476 | | - </div> |
477 | | - </TooltipTrigger> |
478 | | - <TooltipContent side="right"> |
479 | | - {t("chatLeftSidebar.settings")} |
480 | | - </TooltipContent> |
481 | | - </Tooltip> |
482 | | - </TooltipProvider> |
| 563 | + {renderSettingsButton(true)} |
483 | 564 | </> |
484 | 565 | ); |
485 | 566 | }; |
@@ -628,14 +709,7 @@ export function ChatSidebar({ |
628 | 709 | </div> |
629 | 710 | </ConfigProvider> |
630 | 711 | ) : null} |
631 | | - <Button |
632 | | - variant="ghost" |
633 | | - size="icon" |
634 | | - className="size-10 rounded-full hover:bg-slate-100" |
635 | | - onClick={onSettingsClick} |
636 | | - > |
637 | | - <Settings className="size-5" /> |
638 | | - </Button> |
| 712 | + {renderSettingsButton(false)} |
639 | 713 | </div> |
640 | 714 | </div> |
641 | 715 | ) : ( |
|
0 commit comments