Dashboard: programs list deployments; Tools page; statics on Services; editable launcher
- Programs page: each card lists the program's deployments (name · kind) instead of a single kind badge; kind-filter chips removed (nav is the kind lens now). protonmail honestly shows tool + job. - New Tools nav page (Wrench) — programs with a tool deployment (/programs?kind=tool). - Services page now shows statics too: /services returns service + static, and a static renders as a caddy-served card (KindBadge, served URL, no start/stop). - Program detail: DeploymentsSection + deleteConfirm rebuilt over program.deployments; header drops the (now nonexistent) program kind. - ServiceFields / JobFields: launcher is now an editable select (python|command| container|compose|node), persisted into run.launcher via the normal save. clean pnpm build + tsc --noEmit.
This commit is contained in:
@@ -30,8 +30,9 @@ export function ProgramDetailPage() {
|
||||
// A static (caddy) deployment with build outputs is served by the gateway in
|
||||
// place at its own subdomain — show where.
|
||||
const buildOutputs = (deployment.manifest.build as { outputs?: string[] } | undefined)?.outputs
|
||||
const isStatic = deployment.deployments.some((d) => d.kind === "static")
|
||||
const servedAt =
|
||||
deployment.kind === "static" && buildOutputs?.length
|
||||
isStatic && buildOutputs?.length
|
||||
? (subdomainUrl(deployment.id) ?? `${deployment.id}.<gateway.domain>`)
|
||||
: null
|
||||
|
||||
@@ -41,7 +42,6 @@ export function ProgramDetailPage() {
|
||||
backTo="/programs"
|
||||
backLabel="Back to Programs"
|
||||
name={deployment.id}
|
||||
kind={deployment.kind}
|
||||
stack={deployment.stack}
|
||||
source={deployment.source}
|
||||
>
|
||||
|
||||
@@ -17,7 +17,7 @@ export function Services() {
|
||||
<div className="max-w-6xl mx-auto px-6 py-8">
|
||||
<PageHeader
|
||||
title="Services"
|
||||
subtitle="Long-running processes"
|
||||
subtitle="Long-running & served — systemd daemons and caddy statics"
|
||||
actions={
|
||||
<button
|
||||
onClick={() => setCreating((c) => !c)}
|
||||
|
||||
23
app/src/pages/Tools.tsx
Normal file
23
app/src/pages/Tools.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { usePrograms } from "@/services/api/hooks"
|
||||
import { ProgramList } from "@/components/ProgramList"
|
||||
import { PageHeader } from "@/components/PageHeader"
|
||||
|
||||
export function Tools() {
|
||||
// Tools are program-centric — a tool is a program installed on PATH (its path
|
||||
// deployment is 1:1 and trivial), so we list the programs that have one.
|
||||
const { data: programs, isLoading } = usePrograms("tool")
|
||||
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto px-6 py-8">
|
||||
<PageHeader title="Tools" subtitle="CLIs installed on your PATH" />
|
||||
|
||||
{isLoading ? (
|
||||
<p className="text-[var(--muted)]">Loading...</p>
|
||||
) : programs && programs.length > 0 ? (
|
||||
<ProgramList programs={programs} />
|
||||
) : (
|
||||
<p className="text-[var(--muted)]">No tools yet.</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user