feat(web/ui): unify config sections, restore home padding, improve project repos
- Add shared .config-section component class and migrate Tool Workshop and Config Profiles editors to use it. - Restore --space-4 padding on home hero, summary cards, and sections with --space-3 on mobile. - Restructure expandable project card: add 'Repositories' header with repo count and move 'Add Repository' button into the section header. - Left-align repository blocks so they fill from the project detail edge.
This commit is contained in:
@@ -119,89 +119,99 @@ export const ProjectCard = ({
|
||||
|
||||
{expanded && (
|
||||
<div className="project-detail">
|
||||
{(project.repositories || []).length === 0 ? (
|
||||
<p className="muted">No repositories yet.</p>
|
||||
) : (
|
||||
<div className="repo-list">
|
||||
{(project.repositories || []).map((repo) => (
|
||||
<div key={repo.id} className="card card-md repo-block">
|
||||
<div className="repo-header">
|
||||
<h4>{repo.name}</h4>
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={() => onCreateWorkspace(repo.id)}
|
||||
type="button"
|
||||
>
|
||||
<Icon name="add" size="sm" /> New Workspace
|
||||
</button>
|
||||
</div>
|
||||
{showCreateForm === repo.id && (
|
||||
<WorkspaceCreateForm
|
||||
defaultProjectId={project.id}
|
||||
defaultRepoId={repo.id}
|
||||
onSubmit={onCreated}
|
||||
onCancel={onCancelCreate}
|
||||
/>
|
||||
)}
|
||||
{repo.workspaces.length === 0 ? (
|
||||
<p className="muted">No workspaces.</p>
|
||||
) : (
|
||||
<div className="workspace-grid">
|
||||
{repo.workspaces.map((ws) => (
|
||||
<div
|
||||
key={ws.id}
|
||||
className={`workspace-chip ${ws.status}`}
|
||||
>
|
||||
<a href={`/workspaces/${ws.id}`}>{ws.name}</a>
|
||||
<span className="ws-branch">
|
||||
<Icon name="branch" size="sm" /> {ws.branch}
|
||||
</span>
|
||||
{ws.instance_count > 0 && (
|
||||
<span className="ws-instances">
|
||||
{ws.instance_count} tool
|
||||
{ws.instance_count > 1 ? "s" : ""}
|
||||
</span>
|
||||
)}
|
||||
<div className="ws-actions">
|
||||
<button
|
||||
type="button"
|
||||
disabled={workspaceLoading === ws.id}
|
||||
onClick={() =>
|
||||
onWorkspaceAction(repo.id, ws, "sync")
|
||||
}
|
||||
>
|
||||
<Icon name="refresh" size="sm" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="danger-text"
|
||||
disabled={workspaceLoading === ws.id}
|
||||
onClick={() =>
|
||||
onWorkspaceAction(repo.id, ws, "delete")
|
||||
}
|
||||
>
|
||||
<Icon name="delete" size="sm" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<section className="project-repos">
|
||||
<div className="project-repos-header">
|
||||
<div>
|
||||
<h4 className="config-section-title">Repositories</h4>
|
||||
<p className="config-section-subtitle">
|
||||
{project.repositories?.length ?? 0} repo
|
||||
{(project.repositories?.length ?? 0) !== 1 ? "s" : ""}
|
||||
</p>
|
||||
</div>
|
||||
{onAddRepository && (
|
||||
<button
|
||||
className="btn btn-sm btn-secondary"
|
||||
onClick={onAddRepository}
|
||||
type="button"
|
||||
>
|
||||
<Icon name="add" size="sm" /> Add Repository
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{(project.repositories || []).length === 0 ? (
|
||||
<p className="muted">No repositories yet.</p>
|
||||
) : (
|
||||
<div className="repo-list">
|
||||
{(project.repositories || []).map((repo) => (
|
||||
<div key={repo.id} className="card card-md repo-block">
|
||||
<div className="repo-header">
|
||||
<h4>{repo.name}</h4>
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={() => onCreateWorkspace(repo.id)}
|
||||
type="button"
|
||||
>
|
||||
<Icon name="add" size="sm" /> New Workspace
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{onAddRepository && (
|
||||
<div className="project-add-repo">
|
||||
<button
|
||||
className="primary-button"
|
||||
onClick={onAddRepository}
|
||||
type="button"
|
||||
>
|
||||
<Icon name="add" size="sm" /> Add Repository
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{showCreateForm === repo.id && (
|
||||
<WorkspaceCreateForm
|
||||
defaultProjectId={project.id}
|
||||
defaultRepoId={repo.id}
|
||||
onSubmit={onCreated}
|
||||
onCancel={onCancelCreate}
|
||||
/>
|
||||
)}
|
||||
{repo.workspaces.length === 0 ? (
|
||||
<p className="muted">No workspaces.</p>
|
||||
) : (
|
||||
<div className="workspace-grid">
|
||||
{repo.workspaces.map((ws) => (
|
||||
<div
|
||||
key={ws.id}
|
||||
className={`workspace-chip ${ws.status}`}
|
||||
>
|
||||
<a href={`/workspaces/${ws.id}`}>{ws.name}</a>
|
||||
<span className="ws-branch">
|
||||
<Icon name="branch" size="sm" /> {ws.branch}
|
||||
</span>
|
||||
{ws.instance_count > 0 && (
|
||||
<span className="ws-instances">
|
||||
{ws.instance_count} tool
|
||||
{ws.instance_count > 1 ? "s" : ""}
|
||||
</span>
|
||||
)}
|
||||
<div className="ws-actions">
|
||||
<button
|
||||
type="button"
|
||||
disabled={workspaceLoading === ws.id}
|
||||
onClick={() =>
|
||||
onWorkspaceAction(repo.id, ws, "sync")
|
||||
}
|
||||
>
|
||||
<Icon name="refresh" size="sm" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="danger-text"
|
||||
disabled={workspaceLoading === ws.id}
|
||||
onClick={() =>
|
||||
onWorkspaceAction(repo.id, ws, "delete")
|
||||
}
|
||||
>
|
||||
<Icon name="delete" size="sm" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
|
||||
Reference in New Issue
Block a user