feat: Admin/Workbench 双入口架构

- 抽取 ConsoleShell 共享外壳(Layout/Header/Sider/主题切换/侧边栏折叠)
- Sidebar 纯化为接受 menuItems prop 的展示组件
- Admin 管理台:/ 总览 + /projects 项目管理
- Workbench 工作台:/workbench/:projectId 项目作用域
- WorkbenchProjectGate 入口守卫(loading/error/archived/不存在拦截)
- ProjectContext 提供当前项目上下文
- 项目管理表格 active 行增加'进入工作台'按钮
- 项目名称 trim 后最多 10 字符(前后端一致)
- Workbench 总览页展示项目 Descriptions
- Header 区分:管理台显示副标题,工作台显示项目名 + 返回管理台按钮
- 28/28 前端测试通过
- 文档更新:frontend.md ConsoleShell 规范、usage.md 双入口说明
This commit is contained in:
2026-05-28 22:33:03 +08:00
parent d33eb00377
commit 6cb378d7cb
26 changed files with 618 additions and 120 deletions

View File

@@ -300,4 +300,63 @@ describe("项目数据访问层", () => {
rmSync(dir, { force: true, recursive: true });
}
});
test("创建项目名称超过 10 个字符失败", () => {
const dir = makeTempDir();
try {
const db = setupDb(dir);
const result = createProject(db, { name: "这是一个很长的名字" });
expect("error" in result).toBe(true);
expect((result as unknown as { error: string }).error).toContain("不能超过 10 个字符");
db.close();
} finally {
rmSync(dir, { force: true, recursive: true });
}
});
test("创建项目名称刚好 10 个字符成功", () => {
const dir = makeTempDir();
try {
const db = setupDb(dir);
const result = createProject(db, { name: "一二三四五六七八九十" });
expect("error" in result).toBe(false);
const project = (result as { project: { name: string } }).project;
expect(project.name).toBe("一二三四五六七八九十");
db.close();
} finally {
rmSync(dir, { force: true, recursive: true });
}
});
test("更新项目名称超过 10 个字符失败", () => {
const dir = makeTempDir();
try {
const db = setupDb(dir);
const created = createProject(db, { name: "短名" });
const id = (created as { project: { id: string } }).project.id;
const result = updateProject(db, id, { name: "这是一个很长的名字" });
expect("error" in result).toBe(true);
expect((result as unknown as { error: string }).error).toContain("不能超过 10 个字符");
db.close();
} finally {
rmSync(dir, { force: true, recursive: true });
}
});
test("更新项目名称 trim 后为空失败", () => {
const dir = makeTempDir();
try {
const db = setupDb(dir);
const created = createProject(db, { name: "原名" });
const id = (created as { project: { id: string } }).project.id;
const result = updateProject(db, id, { name: " " });
expect("error" in result).toBe(true);
expect((result as unknown as { error: string }).error).toContain("不能为空");
db.close();
} finally {
rmSync(dir, { force: true, recursive: true });
}
});
});