diff --git a/apps/web/src/components/features/project/ProjectCard.tsx b/apps/web/src/components/features/project/ProjectCard.tsx new file mode 100644 index 0000000..01ac6fa --- /dev/null +++ b/apps/web/src/components/features/project/ProjectCard.tsx @@ -0,0 +1,120 @@ +import { Icon } from "../../icon"; +import { WorkspaceCreateForm } from "../workspace/workspace-create-form"; +import type { ProjectWithRepos, WorkspaceSummary } from "../../../types"; + +interface Props { + project: ProjectWithRepos; + expanded: boolean; + deleteConfirm: boolean; + workspaceLoading: string | null; + showCreateForm: string | null; + onToggle: () => void; + onEdit: () => void; + onDelete: () => void; + onConfirmDelete: () => void; + onCancelDelete: () => void; + onCreateWorkspace: (repoId: string) => void; + onWorkspaceAction: (repoId: string, workspace: WorkspaceSummary, action: "sync" | "delete") => void; + onCancelCreate: () => void; + onCreated: () => void; +} + +export const ProjectCard = ({ + project, + expanded, + deleteConfirm, + workspaceLoading, + showCreateForm, + onToggle, + onEdit, + onDelete, + onConfirmDelete, + onCancelDelete, + onCreateWorkspace, + onWorkspaceAction, + onCancelCreate, + onCreated, +}: Props) => { + return ( +
+
+ +
+ + {deleteConfirm ? ( +
+ Are you sure? + + +
+ ) : ( + + )} +
+
+ + {expanded && ( +
+ {project.repositories.length === 0 ? ( +

No repositories yet.

+ ) : ( +
+ {project.repositories.map((repo) => ( +
+
+

{repo.name}

+ +
+ {showCreateForm === repo.id && ( + + )} + {repo.workspaces.length === 0 ? ( +

No workspaces.

+ ) : ( +
+ {repo.workspaces.map((ws) => ( +
+ {ws.name} + {ws.branch} + {ws.instance_count > 0 && ( + {ws.instance_count} tool{ws.instance_count > 1 ? "s" : ""} + )} +
+ + +
+
+ ))} +
+ )} +
+ ))} +
+ )} +
+ )} +
+ ); +}; diff --git a/apps/web/src/components/features/project/ProjectDialog.tsx b/apps/web/src/components/features/project/ProjectDialog.tsx new file mode 100644 index 0000000..5468dab --- /dev/null +++ b/apps/web/src/components/features/project/ProjectDialog.tsx @@ -0,0 +1,71 @@ +import { Icon } from "../../icon"; + +interface Props { + mode: "create" | "edit"; + name: string; + description: string; + error: string | null; + onNameChange: (name: string) => void; + onDescriptionChange: (desc: string) => void; + onSubmit: (e: React.FormEvent) => void; + onCancel: () => void; +} + +export const ProjectDialog = ({ + mode, + name, + description, + error, + onNameChange, + onDescriptionChange, + onSubmit, + onCancel, +}: Props) => { + return ( +
+
+

{mode === "create" ? "Create Project" : "Edit Project"}

+
+ +