Files
codex-app-server/cloud/console-react/src/sections/ProjectNav.tsx
T
host 08484302ac console: 项目导航移到左侧栏,工作台不再承担项目模块
左侧栏 ProjectNav:项目完整列表(名 + 短 id + 我的角色,
选中高亮,点击切换)+ 创建项目(名/GitHub 仓库/repo_url)
+ 选中项目成员(邀请/改角色/新增移除);工作台删除
sec-projects 一节与任务列表里的项目下拉框,侧栏为唯一
项目选择器,自动选第一个项目逻辑不变。
薄模块补齐(均为已核实后端路由):成员移除 DELETE、
自动化详情(GET 单条 + 产出)与编辑 PATCH、分诊条目点进
任务/自动化详情、Workers 搜索与在线筛选、管理区设置配额
POST。主线新功能(Todos、Browser、taskMeta 等)原样保留,
未做任何回退。console 标记单测本地 53 项通过。
2026-10-30 07:43:54 +00:00

52 lines
2.5 KiB
TypeScript

import { useState } from "react";
import { useConsole } from "../context";
import { Card, Bar } from "../components/ui";
import { shortId } from "../api/client";
import MembersPanel from "./MembersPanel";
// Left sidebar: the one project selector for the whole console.
// Lists every project (name + short id + my role), click selects it;
// below the list is the create-project form (same fields as the old
// workbench ProjectsSection: name, GitHub repo select, repo_url) and
// the selected project's members. The workbench no longer carries any
// project-module UI; it only consumes ctx.projectId.
export default function ProjectNav() {
const ctx = useConsole();
const [name, setName] = useState("");
const [repoSelect, setRepoSelect] = useState("");
const [repo, setRepo] = useState("");
return (
<div className="project-nav">
<Card title="项目">
<Bar><button onClick={ctx.refreshProjects}>刷新项目</button></Bar>
{ctx.projects.length === 0 ? <div className="muted">暂无项目</div> : (
<ul className="project-nav-list">
{ctx.projects.map((p) => (
<li key={p.id} className={"project-nav-item" + (p.id === ctx.projectId ? " selected" : "")} onClick={() => ctx.selectProject(p.id)} title={p.id}>
<span className="project-nav-name">{p.name}</span>
<span className="project-nav-meta">{shortId(p.id)}{p.my_role ? " · " + p.my_role : ""}</span>
</li>
))}
</ul>
)}
<h3>创建项目</h3>
<Bar>
<input type="text" placeholder="新项目名" value={name} onChange={(e) => setName(e.target.value)} />
</Bar>
<Bar>
<select aria-label="从 GitHub 仓库选" value={repoSelect} onChange={(e) => { setRepoSelect(e.target.value); if (e.target.value) setRepo(e.target.value); }}>
<option value="">(从 GitHub App 安装仓库里选,或手动输入)</option>
{ctx.githubRepos.map((r) => <option key={r.clone_url} value={r.clone_url}>{r.repo_full_name}</option>)}
</select>
</Bar>
<Bar>
<input type="text" placeholder="repo_url(可选)" value={repo} onChange={(e) => setRepo(e.target.value)} />
<button className="primary" onClick={async () => { await ctx.createProject(name, repo); setName(""); setRepo(""); setRepoSelect(""); }}>创建项目</button>
</Bar>
<span className="muted">{ctx.projectMsg}</span>
</Card>
<MembersPanel />
</div>
);
}