feat: add project-aware agent workspace
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
Activity,
|
||||
ArrowRightLeft,
|
||||
Bell,
|
||||
Bot,
|
||||
CheckCircle2,
|
||||
@@ -17,7 +18,9 @@ import {
|
||||
UserRound,
|
||||
type LucideIcon
|
||||
} from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
import { useRef, useState, type ReactNode } from "react";
|
||||
import { AnimatePresence } from "motion/react";
|
||||
import { ProjectSwitchDialog } from "./project-switch-dialog";
|
||||
import {
|
||||
MAP_COMPACT_RADIUS_CLASS_NAME,
|
||||
MAP_ICON_CELL_RADIUS_CLASS_NAME,
|
||||
@@ -39,6 +42,7 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from "@/shared/ui/dropdown-menu";
|
||||
import type { WorkbenchProject } from "../api/project-client";
|
||||
import type { WorkbenchAlert, WorkbenchUser } from "../types";
|
||||
import {
|
||||
compactHeaderControlButtonClassName,
|
||||
@@ -259,10 +263,22 @@ export function AlertMenu({
|
||||
);
|
||||
}
|
||||
|
||||
export type WorkbenchProjectSelection = {
|
||||
projects: WorkbenchProject[];
|
||||
selectedProjectId: string;
|
||||
pendingProjectId: string | null;
|
||||
error: string | null;
|
||||
onSelectProject: (projectId: string) => void;
|
||||
onOpen?: () => void;
|
||||
onRefresh?: () => void;
|
||||
refreshing?: boolean;
|
||||
};
|
||||
|
||||
export function UserMenu({
|
||||
open,
|
||||
onOpenChange,
|
||||
user,
|
||||
projectSelection,
|
||||
onRefreshTiles,
|
||||
onShowDataStatus,
|
||||
onShowShortcuts,
|
||||
@@ -272,16 +288,23 @@ export function UserMenu({
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
user: WorkbenchUser;
|
||||
projectSelection?: WorkbenchProjectSelection;
|
||||
onRefreshTiles: () => void;
|
||||
onShowDataStatus: () => void;
|
||||
onShowShortcuts: () => void;
|
||||
onExportConfig: () => void;
|
||||
onLogout?: () => Promise<void>;
|
||||
}) {
|
||||
const [projectDialogOpen, setProjectDialogOpen] = useState(false);
|
||||
const [projectDialogKeyboard, setProjectDialogKeyboard] = useState(false);
|
||||
const triggerRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu modal={false} open={open} onOpenChange={onOpenChange}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
ref={triggerRef}
|
||||
type="button"
|
||||
className={cn(
|
||||
"group lg:h-8 lg:w-auto lg:justify-start lg:px-2",
|
||||
@@ -297,8 +320,18 @@ export function UserMenu({
|
||||
<ChevronDown size={14} className="hidden shrink-0 text-[var(--ink-muted)] transition group-hover:text-[var(--action-blue-hover)] group-data-[state=open]:rotate-180 group-data-[state=open]:text-[var(--action-blue-hover)] 2xl:block" aria-hidden="true" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className={cn("w-72", menuSurfaceClassName)}>
|
||||
<DropdownMenuContent align="end" className={cn("z-[90] w-72", menuSurfaceClassName)}
|
||||
onKeyDownCapture={() => setProjectDialogKeyboard(true)}
|
||||
onPointerDownCapture={() => setProjectDialogKeyboard(false)}
|
||||
onCloseAutoFocus={(event) => { if (projectDialogOpen) event.preventDefault(); }}>
|
||||
<MenuHeader title={user.name} meta={user.role} icon={ShieldCheck} />
|
||||
{projectSelection ? (
|
||||
<>
|
||||
<MenuAction icon={ArrowRightLeft} label="切换项目" description="选择要进入的供水项目"
|
||||
onSelect={() => { onOpenChange(false); setProjectDialogOpen(true); projectSelection.onOpen?.(); }} />
|
||||
<MenuSeparator />
|
||||
</>
|
||||
) : null}
|
||||
<MenuSectionLabel>会话控制</MenuSectionLabel>
|
||||
<MenuAction icon={Activity} label="查看运行状态" description="检查数据源与运行健康度" onSelect={onShowDataStatus} />
|
||||
<MenuAction icon={RefreshCw} label="刷新地图瓦片" description="请求地图重新渲染业务图层" onSelect={onRefreshTiles} />
|
||||
@@ -319,6 +352,12 @@ export function UserMenu({
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<AnimatePresence>
|
||||
{projectDialogOpen && projectSelection ? <ProjectSwitchDialog selection={projectSelection} keyboardOpened={projectDialogKeyboard}
|
||||
onClose={() => setProjectDialogOpen(false)}
|
||||
onRestoreFocus={() => triggerRef.current?.focus()} /> : null}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user