Files
Agent-System/web/src/lib/projectAdapter.ts
T
WeiCHENandCursor 70493a3ed5 feat(web): wire project list and create to server API
Login syncs projects from GET /api/v1/projects; create project uses POST with server-assigned id while preserving local sessions and templates.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 02:50:22 +00:00

60 lines
1.8 KiB
TypeScript

import { Project } from '../types/projectWorkflow';
import { ProjectInfo, ProjectMember } from '../types/workbench';
type UserCatalogEntry = { id: number; name: string };
export function resolveUserDisplayName(
userId: number,
catalog: UserCatalogEntry[],
fallback = '未知用户'
): string {
return catalog.find((u) => u.id === userId)?.name ?? fallback;
}
export function mapServerProjectToProjectInfo(
project: Project,
catalog: UserCatalogEntry[],
existing?: ProjectInfo
): ProjectInfo {
const creatorName = resolveUserDisplayName(project.creator_user_id, catalog);
const defaultMember: ProjectMember = {
userId: project.creator_user_id,
userName: creatorName,
role: 'creator',
status: 'active',
joinedAt: project.created_at,
};
return {
id: project.id,
title: project.name,
deviceType: project.device_type || existing?.deviceType || '',
description: project.intended_use || existing?.description || '',
creatorId: project.creator_user_id,
creatorName,
createdAt: project.created_at,
members: existing?.members?.length ? existing.members : [defaultMember],
};
}
/** 服务端项目覆盖同 id 本地项,保留本地成员等演示扩展字段。 */
export function mergeProjectLists(local: ProjectInfo[], fromServer: ProjectInfo[]): ProjectInfo[] {
const byId = new Map<number, ProjectInfo>();
for (const project of local) {
byId.set(project.id, project);
}
for (const project of fromServer) {
const previous = byId.get(project.id);
if (!previous) {
byId.set(project.id, project);
continue;
}
byId.set(project.id, {
...previous,
...project,
members: previous.members.length > 0 ? previous.members : project.members,
});
}
return [...byId.values()];
}