refactor(app): rename Component* UI widgets to accurate domain names
The dashboard widgets were named after the old 'component' concept. Renamed each to what it actually renders: - ComponentCard → ServiceCard (ServiceSummary, /services/, service actions) - ComponentTable → ProgramTable (ProgramSummary[], the program catalog) - ComponentEditor→ DeploymentEditor (DeploymentDetail, the unified type) - ComponentFields→ DeploymentFields (AnyDetail) - AddComponent → AddDeployment Also purged the domain term 'component' from the rest of the app: the useComponent hook → useDeployment, ConfigPanel + the three detail pages' `component` prop/var → `deployment`, and the gateway/node table headers (Component → Program / Deployment). Props renamed to match (component → service / program / deployment). The React UI directory app/src/components/ keeps its conventional name. App type-checks and builds clean.
This commit is contained in:
115
app/src/components/ServiceCard.tsx
Normal file
115
app/src/components/ServiceCard.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import { ExternalLink, Play, RefreshCw, Server, Square, Terminal } from "lucide-react"
|
||||
import { Link } from "react-router-dom"
|
||||
import type { ServiceSummary, HealthStatus } from "@/types"
|
||||
import { useServiceAction } from "@/services/api/hooks"
|
||||
import { runnerLabel } from "@/lib/labels"
|
||||
import { HealthBadge } from "./HealthBadge"
|
||||
import { StackBadge } from "./StackBadge"
|
||||
|
||||
interface ServiceCardProps {
|
||||
service: ServiceSummary
|
||||
health?: HealthStatus
|
||||
}
|
||||
|
||||
export function ServiceCard({ service, health }: ServiceCardProps) {
|
||||
const hasHttp = service.port != null
|
||||
const { mutate, isPending } = useServiceAction()
|
||||
|
||||
const doAction = (action: string) => {
|
||||
mutate({ name: service.id, action })
|
||||
}
|
||||
|
||||
const isDown = health?.status === "down"
|
||||
|
||||
return (
|
||||
<div className="bg-[var(--card)] border border-[var(--border)] rounded-lg p-5">
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<Link
|
||||
to={`/services/${service.id}`}
|
||||
className="text-base font-semibold hover:text-[var(--primary)] transition-colors"
|
||||
>
|
||||
{service.id}
|
||||
</Link>
|
||||
{health ? (
|
||||
<HealthBadge status={health.status} latency={health.latency_ms} />
|
||||
) : hasHttp ? (
|
||||
<HealthBadge status="unknown" />
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-1.5 mb-2">
|
||||
<StackBadge stack={service.stack} />
|
||||
</div>
|
||||
|
||||
{service.description && (
|
||||
<p className="text-sm text-[var(--muted)] mb-3">{service.description}</p>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3 text-xs text-[var(--muted)]">
|
||||
{service.port && (
|
||||
<span className="flex items-center gap-1 font-mono">
|
||||
<Server size={12} />:{service.port}
|
||||
</span>
|
||||
)}
|
||||
{service.runner && (
|
||||
<span className="flex items-center gap-1">
|
||||
<Terminal size={12} />
|
||||
{runnerLabel(service.runner)}
|
||||
</span>
|
||||
)}
|
||||
{service.proxy_path && (
|
||||
<a
|
||||
href={service.proxy_path + "/"}
|
||||
className="flex items-center gap-1 text-[var(--primary)] hover:underline"
|
||||
>
|
||||
<ExternalLink size={12} />
|
||||
{service.proxy_path}
|
||||
</a>
|
||||
)}
|
||||
{service.port && (
|
||||
<a
|
||||
href={`http://localhost:${service.port}/docs`}
|
||||
className="text-[var(--primary)] hover:underline"
|
||||
>
|
||||
Docs
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{service.managed && (
|
||||
<div className="flex items-center gap-1">
|
||||
{isDown && (
|
||||
<button
|
||||
onClick={() => doAction("start")}
|
||||
disabled={isPending}
|
||||
className="p-1 rounded hover:bg-green-800/30 text-green-400 transition-colors disabled:opacity-40"
|
||||
title="Start"
|
||||
>
|
||||
<Play size={14} />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => doAction("restart")}
|
||||
disabled={isPending}
|
||||
className="p-1 rounded hover:bg-blue-800/30 text-blue-400 transition-colors disabled:opacity-40"
|
||||
title="Restart"
|
||||
>
|
||||
<RefreshCw size={14} />
|
||||
</button>
|
||||
{!isDown && (
|
||||
<button
|
||||
onClick={() => doAction("stop")}
|
||||
disabled={isPending}
|
||||
className="p-1 rounded hover:bg-red-800/30 text-red-400 transition-colors disabled:opacity-40"
|
||||
title="Stop"
|
||||
>
|
||||
<Square size={14} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user