feat(space): 桌面端空间成员管理界面 —— 补齐"临时组队协作"前端闭环
此前能建项目空间/切换/启用全员空间,但邀请队友进空间只有后端 API、无界面, 协作故事前端断了一半。本次补上成员管理 UI,让 Space 协作端到端可用。 - api.ts: spaceMembers/addSpaceMember/setSpaceMemberRole/removeSpaceMember + SpaceMemberInfo - SpaceMembers 弹窗: 列成员(名/邮箱/角色 badge); 空间 admin/owner 可邀请(邮箱+角色)/ 下拉改角色/移除, owner 受保护(无下拉/移除); 非管理员只读提示。复用 Dialog/Button/Select - 顶栏加"管理成员"入口(UserCog): 活跃空间为 project/tenant 时显示; App 管弹窗开关, 传 spaceId/spaceName/canManage(空间角色 admin/owner)/selfUserId 实机验证(Wails 桌面 + 全栈后端): demospace(owner)在项目空间邀请 teammate → 成员列表 即时出现(切换器 1人→2人) → 下拉改 teammate 成员→管理员(服务端确认 owner/admin) → owner 自身受保护无改删入口。tsc + 48 前端测试通过。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { LayoutDashboard, Workflow, Database, FileText, Activity, Bookmark, Boxe
|
||||
import { TopBar } from "./shell/TopBar";
|
||||
import { LeftNav, type ViewKey } from "./shell/LeftNav";
|
||||
import { ApprovalBar } from "./shell/ApprovalBar";
|
||||
import { SpaceMembers } from "./shell/SpaceMembers";
|
||||
import { StudioView } from "./studio/StudioView";
|
||||
import { MemoryView } from "./views/MemoryView";
|
||||
import { KbView } from "./views/KbView";
|
||||
@@ -50,6 +51,7 @@ export default function App() {
|
||||
const identity = useMemo<Identity>(() => ({ userId: user?.id ?? "", sessionId: getSessionId() }), [user]);
|
||||
const [run, setRun] = useState<RunState>(emptyRun);
|
||||
const [cmdOpen, setCmdOpen] = useState(false);
|
||||
const [membersOpen, setMembersOpen] = useState(false);
|
||||
const closeRef = useRef<(() => void) | null>(null);
|
||||
|
||||
const execCloseRef = useRef<(() => void) | null>(null);
|
||||
@@ -298,7 +300,7 @@ export default function App() {
|
||||
style={{ background: "radial-gradient(60% 100% at 50% 0%, rgba(124,92,246,0.10), transparent 70%)" }}
|
||||
/>
|
||||
<UpdateBanner />
|
||||
<TopBar user={user} tenant={tenant} tenants={tenants} onSwitchTenant={onSwitchTenant} space={space} spaces={spaces} onSwitchSpace={onSwitchSpace} onCreateSpace={onCreateSpace} onEnableTenantSpace={onEnableTenantSpace} tenantRole={tenant?.role ?? ""} onLogout={onLogout} onCommand={() => setCmdOpen(true)} onOpenUsage={() => setView("usage")} />
|
||||
<TopBar user={user} tenant={tenant} tenants={tenants} onSwitchTenant={onSwitchTenant} space={space} spaces={spaces} onSwitchSpace={onSwitchSpace} onCreateSpace={onCreateSpace} onEnableTenantSpace={onEnableTenantSpace} onManageMembers={() => setMembersOpen(true)} tenantRole={tenant?.role ?? ""} onLogout={onLogout} onCommand={() => setCmdOpen(true)} onOpenUsage={() => setView("usage")} />
|
||||
<ApprovalBar run={run} />
|
||||
<div className="relative flex min-h-0 flex-1">
|
||||
<LeftNav active={view} onSelect={setView} />
|
||||
@@ -323,6 +325,14 @@ export default function App() {
|
||||
</main>
|
||||
</div>
|
||||
<CommandPalette open={cmdOpen} onClose={() => setCmdOpen(false)} commands={commands} />
|
||||
<SpaceMembers
|
||||
open={membersOpen}
|
||||
onClose={() => setMembersOpen(false)}
|
||||
spaceId={space?.space?.id ?? ""}
|
||||
spaceName={space?.space?.kind === "personal" ? "个人空间" : space?.space?.name ?? ""}
|
||||
canManage={space?.role === "owner" || space?.role === "admin"}
|
||||
selfUserId={user?.id ?? ""}
|
||||
/>
|
||||
</div>
|
||||
</ToastProvider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user