Tools page cards link to the tool detail page, not the program

ProgramCard/ProgramList take an optional linkBase (default /programs); the Tools
page passes /tools so a tool tile opens /tools/<name> (its deployment detail),
not /programs/<name>.
This commit is contained in:
2026-07-01 12:59:31 -07:00
parent 86d4552fdc
commit 183c589b63
3 changed files with 10 additions and 5 deletions

View File

@@ -5,9 +5,13 @@ import { StackBadge } from "./StackBadge"
interface ProgramCardProps { interface ProgramCardProps {
program: ProgramSummary program: ProgramSummary
// Where the card links. Defaults to the program page; the Tools page passes
// "/tools" so a tool card opens its tool detail page (a tool is 1:1 with its
// program, same name).
linkBase?: string
} }
export function ProgramCard({ program }: ProgramCardProps) { export function ProgramCard({ program, linkBase = "/programs" }: ProgramCardProps) {
// The dot reflects the uniform lifecycle state (a tool on PATH, a service // 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 // 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. // Deployment section, not here — a card just shows state and links through.
@@ -26,7 +30,7 @@ export function ProgramCard({ program }: ProgramCardProps) {
title={program.active === true ? "active" : program.active === false ? "inactive" : "no deployment"} title={program.active === true ? "active" : program.active === false ? "inactive" : "no deployment"}
/> />
<Link <Link
to={`/programs/${program.id}`} to={`${linkBase}/${program.id}`}
className="text-base font-semibold hover:text-[var(--primary)] transition-colors after:absolute after:inset-0" className="text-base font-semibold hover:text-[var(--primary)] transition-colors after:absolute after:inset-0"
> >
{program.id} {program.id}

View File

@@ -4,9 +4,10 @@ import { ProgramCard } from "./ProgramCard"
interface ProgramListProps { interface ProgramListProps {
programs: ProgramSummary[] programs: ProgramSummary[]
linkBase?: string // where each card links (default "/programs")
} }
export function ProgramList({ programs }: ProgramListProps) { export function ProgramList({ programs, linkBase }: ProgramListProps) {
const [search, setSearch] = useState("") const [search, setSearch] = useState("")
const filtered = useMemo(() => { const filtered = useMemo(() => {
@@ -38,7 +39,7 @@ export function ProgramList({ programs }: ProgramListProps) {
) : ( ) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{filtered.map((program) => ( {filtered.map((program) => (
<ProgramCard key={program.id} program={program} /> <ProgramCard key={program.id} program={program} linkBase={linkBase} />
))} ))}
</div> </div>
)} )}

View File

@@ -14,7 +14,7 @@ export function Tools() {
{isLoading ? ( {isLoading ? (
<p className="text-[var(--muted)]">Loading...</p> <p className="text-[var(--muted)]">Loading...</p>
) : programs && programs.length > 0 ? ( ) : programs && programs.length > 0 ? (
<ProgramList programs={programs} /> <ProgramList programs={programs} linkBase="/tools" />
) : ( ) : (
<p className="text-[var(--muted)]">No tools yet.</p> <p className="text-[var(--muted)]">No tools yet.</p>
)} )}