console: React 生产级重写(左列表 + 中对话 + 右详情)
旧单页 console.html 换成 Vite + React + TypeScript 单文件构建产物 (309525 字节,sha 90345ec8…,重建字节一致),源码在 cloud/console-react/,改页面只改 src 再 npm run build:console, 产物不手改。布局:左侧列表(模块导航 + 任务列表)、中间标准对话 (实时链路时间线 + 继续会话聊天框,共用 Chat 组件)、右侧详情 (元数据、操作、评审、审批、协调子任务、子代理、线程)。 API 与后端语义未改,key 只存浏览器 localStorage。 6 个 console 标记单测随实现改名更新(意图不变:壳确实调了 这些接口、turn 信息仍在详情里、旧 goal 输入仍不存在); P1 标记单测未动且通过。
This commit is contained in:
1 parent
3f4e849a93
commit
ebc5624893
57 files changed
+4789
-1746
No files matched your search
@@ -0,0 +1,48 @@
|
||||
import { useState } from "react";
|
||||
import { useConsole } from "../context";
|
||||
import { Card, Bar, DataListRaw } from "../components/ui";
|
||||
import { ROLES } from "../api/types";
|
||||
|
||||
// Right-panel context for the Projects view. Previously embedded in the
|
||||
// projects card; moved here so the centre stays the project list/create
|
||||
// and the right stays the people for the selected project.
|
||||
export default function MembersPanel() {
|
||||
const ctx = useConsole();
|
||||
const [inviteUser, setInviteUser] = useState("");
|
||||
const [inviteRole, setInviteRole] = useState("contributor");
|
||||
const proj = ctx.projects.find((p) => p.id === ctx.projectId);
|
||||
return (
|
||||
<Card title="项目成员">
|
||||
{proj ? <div className="muted">{proj.name} | {proj.id} | 仓库 {proj.repo_url || "未绑定"} | 我的角色 {proj.my_role || "未知"}</div> : <div className="muted">先选项目</div>}
|
||||
<Bar>
|
||||
<input type="text" placeholder="成员 user_id 或用户名" value={inviteUser} onChange={(e) => setInviteUser(e.target.value)} />
|
||||
<select aria-label="邀请角色" value={inviteRole} onChange={(e) => setInviteRole(e.target.value)}>
|
||||
<option value="viewer">viewer(只读)</option>
|
||||
<option value="contributor">contributor(可建任务)</option>
|
||||
<option value="maintainer">maintainer(可批合并)</option>
|
||||
<option value="owner">owner(可管项目)</option>
|
||||
</select>
|
||||
<button onClick={async () => { await ctx.inviteMember(inviteUser, inviteRole); setInviteUser(""); }}>邀请成员</button>
|
||||
<button onClick={ctx.refreshMembers}>刷新成员</button>
|
||||
</Bar>
|
||||
{ctx.memberError ? <div className="muted">{ctx.memberError}</div>
|
||||
: ctx.members.length === 0 ? <div className="muted">先选项目</div>
|
||||
: (
|
||||
<DataListRaw>
|
||||
{ctx.members.map((m) => {
|
||||
const label = (m.username || "") + (m.username ? "(" + String(m.user_id).slice(0, 8) + ")" : String(m.user_id).slice(0, 8));
|
||||
return (
|
||||
<li key={m.user_id}>
|
||||
<span className="row-main">{label}</span>
|
||||
<select aria-label="成员角色" value={m.role || "viewer"} onChange={(e) => void ctx.changeMemberRole(m.user_id, e.target.value)}>
|
||||
{ROLES.map((r) => <option key={r} value={r}>{r}</option>)}
|
||||
</select>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</DataListRaw>
|
||||
)}
|
||||
<span className="muted">{ctx.memberMsg}</span>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user