Install/Uninstall no longer live in the program's action cluster (they were a tool's PATH-deployment lifecycle masquerading as a program action). ProgramActions is now dev-verbs-only (build/test/lint/type-check/check). The program detail's Deployment section owns lifecycle, keyed on kind: a tool shows Installed-on-PATH with an Install/Uninstall toggle, a static shows served-by-gateway + its URL, services/jobs link to their own pages. Program list cards drop the quick toggle for a plain active dot. 'Remove program' stays as catalog deletion — no longer a dead-end now that the blocking deployment is visible in the Deployment section.
47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
import { Link } from "react-router-dom"
|
|
import type { ProgramSummary } from "@/types"
|
|
import { KindBadge } from "./KindBadge"
|
|
import { StackBadge } from "./StackBadge"
|
|
|
|
interface ProgramCardProps {
|
|
program: ProgramSummary
|
|
}
|
|
|
|
export function ProgramCard({ program }: ProgramCardProps) {
|
|
// The dot reflects the uniform lifecycle state (a tool on PATH, a service
|
|
// running, a static site served). Lifecycle controls live on the detail page's
|
|
// Deployment section, not here — a card just shows state and links through.
|
|
const dot =
|
|
program.active === true
|
|
? "bg-green-500"
|
|
: program.active === false
|
|
? "bg-[var(--muted)]"
|
|
: "bg-transparent border border-[var(--muted)]"
|
|
|
|
return (
|
|
<div className="relative bg-[var(--card)] border border-[var(--border)] rounded-lg p-5 hover:border-[var(--primary)] transition-colors">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<span
|
|
className={`h-2 w-2 rounded-full shrink-0 ${dot}`}
|
|
title={program.active === true ? "active" : program.active === false ? "inactive" : "no deployment"}
|
|
/>
|
|
<Link
|
|
to={`/programs/${program.id}`}
|
|
className="text-base font-semibold hover:text-[var(--primary)] transition-colors after:absolute after:inset-0"
|
|
>
|
|
{program.id}
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap gap-1.5 mb-2">
|
|
<KindBadge kind={program.kind} />
|
|
<StackBadge stack={program.stack} />
|
|
</div>
|
|
|
|
{program.description && (
|
|
<p className="text-sm text-[var(--muted)]">{program.description}</p>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|