Polish UI components and rework AgentConfigForm

Major AgentConfigForm rework with improved adapter configuration
fields and layout. Refine sidebar, breadcrumbs, and card/tab
components for visual consistency. Clean up page layouts across
Activity, Agents, Approvals, Costs, Dashboard, Goals, Inbox,
Issues, Org, and Projects pages. Minor heartbeat-run CLI fix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-02-18 10:43:25 -06:00
parent 769d74d896
commit 0d436911cd
29 changed files with 434 additions and 256 deletions

View File

@@ -101,8 +101,7 @@ export function Activity() {
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold">Activity</h2>
<div className="flex items-center justify-end">
<Select value={filter} onValueChange={setFilter}>
<SelectTrigger className="w-[140px] h-8 text-xs">
<SelectValue placeholder="Filter by type" />
@@ -126,7 +125,7 @@ export function Activity() {
)}
{filtered && filtered.length > 0 && (
<div className="border border-border rounded-md divide-y divide-border">
<div className="border border-border divide-y divide-border">
{filtered.map((event) => {
const link = entityLink(event.entityType, event.entityId);
return (

View File

@@ -367,7 +367,7 @@ export function AgentDetail() {
{assignedIssues.length === 0 ? (
<p className="text-sm text-muted-foreground">No assigned issues.</p>
) : (
<div className="border border-border rounded-md">
<div className="border border-border">
{assignedIssues.map((issue) => (
<EntityRow
key={issue.id}
@@ -429,6 +429,7 @@ function ConfigurationTab({ agent }: { agent: Agent }) {
mode="edit"
agent={agent}
onSave={(patch) => updateAgent.mutate(patch)}
isSaving={updateAgent.isPending}
adapterModels={adapterModels}
/>
</div>
@@ -450,7 +451,7 @@ function RunsTab({ runs, companyId }: { runs: HeartbeatRun[]; companyId: string
);
return (
<div className="border border-border rounded-md">
<div className="border border-border">
{sorted.map((run) => {
const statusInfo = runStatusIcons[run.status] ?? { icon: Clock, color: "text-neutral-400" };
const StatusIcon = statusInfo.icon;
@@ -975,7 +976,7 @@ function KeysTab({ agentId }: { agentId: string }) {
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wide mb-2">
Active Keys
</h3>
<div className="border border-border rounded-md divide-y divide-border">
<div className="border border-border divide-y divide-border">
{activeKeys.map((key: AgentKey) => (
<div key={key.id} className="flex items-center justify-between px-4 py-2.5">
<div>
@@ -1005,7 +1006,7 @@ function KeysTab({ agentId }: { agentId: string }) {
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wide mb-2">
Revoked Keys
</h3>
<div className="border border-border rounded-md divide-y divide-border opacity-50">
<div className="border border-border divide-y divide-border opacity-50">
{revokedKeys.map((key: AgentKey) => (
<div key={key.id} className="flex items-center justify-between px-4 py-2.5">
<div>

View File

@@ -93,20 +93,17 @@ export function Agents() {
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<h2 className="text-lg font-semibold">Agents</h2>
<Tabs value={tab} onValueChange={(v) => setTab(v as FilterTab)}>
<TabsList>
<TabsTrigger value="all">All{agents ? ` (${agents.length})` : ""}</TabsTrigger>
<TabsTrigger value="active">Active</TabsTrigger>
<TabsTrigger value="paused">Paused</TabsTrigger>
<TabsTrigger value="error">Error</TabsTrigger>
</TabsList>
</Tabs>
</div>
<Tabs value={tab} onValueChange={(v) => setTab(v as FilterTab)}>
<TabsList variant="line">
<TabsTrigger value="all">All{agents ? ` (${agents.length})` : ""}</TabsTrigger>
<TabsTrigger value="active">Active</TabsTrigger>
<TabsTrigger value="paused">Paused</TabsTrigger>
<TabsTrigger value="error">Error</TabsTrigger>
</TabsList>
</Tabs>
<div className="flex items-center gap-2">
{/* View toggle */}
<div className="flex items-center border border-border rounded-md">
<div className="flex items-center border border-border">
<button
className={cn(
"p-1.5 transition-colors",
@@ -147,7 +144,7 @@ export function Agents() {
{/* List view */}
{view === "list" && filtered.length > 0 && (
<div className="border border-border rounded-md">
<div className="border border-border">
{filtered.map((agent) => {
const budgetPct =
agent.budgetMonthlyCents > 0
@@ -221,7 +218,7 @@ export function Agents() {
{/* Org chart view */}
{view === "org" && filteredOrg.length > 0 && (
<div className="border border-border rounded-md py-1">
<div className="border border-border py-1">
{filteredOrg.map((node) => (
<OrgTreeNode key={node.id} node={node} depth={0} navigate={navigate} agentMap={agentMap} />
))}
@@ -275,7 +272,7 @@ function OrgTreeNode({
return (
<div style={{ paddingLeft: depth * 24 }}>
<button
className="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-accent/30 transition-colors w-full text-left"
className="flex items-center gap-3 px-3 py-2 hover:bg-accent/30 transition-colors w-full text-left"
onClick={() => navigate(`/agents/${node.id}`)}
>
<span className="relative flex h-2.5 w-2.5 shrink-0">

View File

@@ -227,25 +227,22 @@ export function Approvals() {
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<h2 className="text-lg font-semibold">Approvals</h2>
<Tabs value={statusFilter} onValueChange={(v) => setStatusFilter(v as StatusFilter)}>
<TabsList>
<TabsTrigger value="pending">
Pending
{pendingCount > 0 && (
<span className={cn(
"ml-1.5 rounded-full px-1.5 py-0.5 text-[10px] font-medium",
"bg-yellow-500/20 text-yellow-500"
)}>
{pendingCount}
</span>
)}
</TabsTrigger>
<TabsTrigger value="all">All</TabsTrigger>
</TabsList>
</Tabs>
</div>
<Tabs value={statusFilter} onValueChange={(v) => setStatusFilter(v as StatusFilter)}>
<TabsList variant="line">
<TabsTrigger value="pending">
Pending
{pendingCount > 0 && (
<span className={cn(
"ml-1.5 rounded-full px-1.5 py-0.5 text-[10px] font-medium",
"bg-yellow-500/20 text-yellow-500"
)}>
{pendingCount}
</span>
)}
</TabsTrigger>
<TabsTrigger value="all">All</TabsTrigger>
</TabsList>
</Tabs>
</div>
{isLoading && <p className="text-sm text-muted-foreground">Loading...</p>}

View File

@@ -89,11 +89,7 @@ export function Companies() {
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h2 className="text-lg font-semibold">Companies</h2>
<p className="text-sm text-muted-foreground">Manage your companies.</p>
</div>
<div className="flex items-center justify-end">
<Button size="sm" onClick={openOnboarding}>
<Plus className="h-3.5 w-3.5 mr-1.5" />
New Company

View File

@@ -36,8 +36,6 @@ export function Costs() {
return (
<div className="space-y-6">
<h2 className="text-lg font-semibold">Costs</h2>
{isLoading && <p className="text-sm text-muted-foreground">Loading...</p>}
{error && <p className="text-sm text-destructive">{error.message}</p>}

View File

@@ -113,12 +113,9 @@ export function Dashboard() {
return (
<div className="space-y-6">
<div>
<h2 className="text-lg font-semibold">Dashboard</h2>
{selectedCompany && (
<p className="text-sm text-muted-foreground">{selectedCompany.name}</p>
)}
</div>
{selectedCompany && (
<p className="text-sm text-muted-foreground">{selectedCompany.name}</p>
)}
{isLoading && <p className="text-sm text-muted-foreground">Loading...</p>}
{error && <p className="text-sm text-destructive">{error.message}</p>}
@@ -159,7 +156,7 @@ export function Dashboard() {
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-3">
Recent Activity
</h3>
<div className="border border-border rounded-md divide-y divide-border">
<div className="border border-border divide-y divide-border">
{activity.slice(0, 10).map((event) => (
<div key={event.id} className="px-4 py-2 flex items-center justify-between text-sm">
<div className="flex items-center gap-2 min-w-0">
@@ -185,11 +182,11 @@ export function Dashboard() {
Stale Tasks
</h3>
{staleIssues.length === 0 ? (
<div className="border border-border rounded-md p-4">
<div className="border border-border p-4">
<p className="text-sm text-muted-foreground">No stale tasks. All work is up to date.</p>
</div>
) : (
<div className="border border-border rounded-md divide-y divide-border">
<div className="border border-border divide-y divide-border">
{staleIssues.slice(0, 10).map((issue) => (
<div
key={issue.id}

View File

@@ -94,7 +94,7 @@ export function GoalDetail() {
{linkedProjects.length === 0 ? (
<p className="text-sm text-muted-foreground">No linked projects.</p>
) : (
<div className="border border-border rounded-md">
<div className="border border-border">
{linkedProjects.map((project) => (
<EntityRow
key={project.id}

View File

@@ -30,8 +30,6 @@ export function Goals() {
return (
<div className="space-y-4">
<h2 className="text-lg font-semibold">Goals</h2>
{isLoading && <p className="text-sm text-muted-foreground">Loading...</p>}
{error && <p className="text-sm text-destructive">{error.message}</p>}

View File

@@ -116,8 +116,6 @@ export function Inbox() {
return (
<div className="space-y-6">
<h2 className="text-lg font-semibold">Inbox</h2>
{isLoading && <p className="text-sm text-muted-foreground">Loading...</p>}
{error && <p className="text-sm text-destructive">{error.message}</p>}
{actionError && <p className="text-sm text-destructive">{actionError}</p>}
@@ -140,7 +138,7 @@ export function Inbox() {
See all approvals <ExternalLink className="inline h-3 w-3 ml-0.5" />
</button>
</div>
<div className="border border-border rounded-md divide-y divide-border">
<div className="border border-border divide-y divide-border">
{approvals!.map((approval) => (
<div key={approval.id} className="p-4 space-y-2">
<div className="flex items-center gap-2">
@@ -191,7 +189,7 @@ export function Inbox() {
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-3">
Alerts
</h3>
<div className="border border-border rounded-md divide-y divide-border">
<div className="border border-border divide-y divide-border">
{dashboard!.agents.error > 0 && (
<div
className="px-4 py-3 flex items-center gap-3 cursor-pointer hover:bg-accent/50 transition-colors"
@@ -232,7 +230,7 @@ export function Inbox() {
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-3">
Stale Work
</h3>
<div className="border border-border rounded-md divide-y divide-border">
<div className="border border-border divide-y divide-border">
{staleIssues.map((issue) => (
<div
key={issue.id}

View File

@@ -89,17 +89,14 @@ export function Issues() {
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<h2 className="text-lg font-semibold">Issues</h2>
<Tabs value={tab} onValueChange={(v) => setTab(v as TabFilter)}>
<TabsList>
<TabsTrigger value="all">All Issues</TabsTrigger>
<TabsTrigger value="active">Active</TabsTrigger>
<TabsTrigger value="backlog">Backlog</TabsTrigger>
<TabsTrigger value="done">Done</TabsTrigger>
</TabsList>
</Tabs>
</div>
<Tabs value={tab} onValueChange={(v) => setTab(v as TabFilter)}>
<TabsList variant="line">
<TabsTrigger value="all">All Issues</TabsTrigger>
<TabsTrigger value="active">Active</TabsTrigger>
<TabsTrigger value="backlog">Backlog</TabsTrigger>
<TabsTrigger value="done">Done</TabsTrigger>
</TabsList>
</Tabs>
<Button size="sm" onClick={() => openNewIssue()}>
<Plus className="h-4 w-4 mr-1" />
New Issue
@@ -120,7 +117,7 @@ export function Issues() {
{orderedGroups.map(({ status, items }) => (
<div key={status}>
<div className="flex items-center gap-2 px-4 py-2 bg-muted/50 rounded-t-md">
<div className="flex items-center gap-2 px-4 py-2 bg-muted/50">
<StatusIcon status={status} />
<span className="text-xs font-semibold uppercase tracking-wide">
{statusLabel(status)}
@@ -135,7 +132,7 @@ export function Issues() {
<Plus className="h-3 w-3" />
</Button>
</div>
<div className="border border-border rounded-b-md">
<div className="border border-border">
{items.map((issue) => (
<EntityRow
key={issue.id}

View File

@@ -38,8 +38,6 @@ export function MyIssues() {
return (
<div className="space-y-4">
<h2 className="text-lg font-semibold">My Issues</h2>
{isLoading && <p className="text-sm text-muted-foreground">Loading...</p>}
{error && <p className="text-sm text-destructive">{error.message}</p>}
@@ -48,7 +46,7 @@ export function MyIssues() {
)}
{myIssues.length > 0 && (
<div className="border border-border rounded-md">
<div className="border border-border">
{myIssues.map((issue) => (
<EntityRow
key={issue.id}

View File

@@ -106,8 +106,6 @@ export function Org() {
return (
<div className="space-y-4">
<h2 className="text-lg font-semibold">Org Chart</h2>
{isLoading && <p className="text-sm text-muted-foreground">Loading...</p>}
{error && <p className="text-sm text-destructive">{error.message}</p>}
@@ -119,7 +117,7 @@ export function Org() {
)}
{data && data.length > 0 && (
<div className="border border-border rounded-md py-1">
<div className="border border-border py-1">
<OrgTree nodes={data} onSelect={(id) => navigate(`/agents/${id}`)} />
</div>
)}

View File

@@ -87,7 +87,7 @@ export function ProjectDetail() {
{projectIssues.length === 0 ? (
<p className="text-sm text-muted-foreground">No issues in this project.</p>
) : (
<div className="border border-border rounded-md">
<div className="border border-border">
{projectIssues.map((issue) => (
<EntityRow
key={issue.id}

View File

@@ -35,8 +35,7 @@ export function Projects() {
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold">Projects</h2>
<div className="flex items-center justify-end">
<Button size="sm" onClick={openNewProject}>
<Plus className="h-4 w-4 mr-1" />
Add Project
@@ -56,7 +55,7 @@ export function Projects() {
)}
{projects && projects.length > 0 && (
<div className="border border-border rounded-md">
<div className="border border-border">
{projects.map((project) => (
<EntityRow
key={project.id}