import { useState } from "react" import { useParams } from "react-router-dom" import { useProgram, useProgramGit, useProgramSync } from "@/services/api/hooks" import { subdomainUrl } from "@/lib/labels" import { DetailHeader } from "@/components/detail/DetailHeader" import { ConfigPanel } from "@/components/detail/ConfigPanel" import { DeploymentsSection } from "@/components/detail/DeploymentsSection" import { ProgramActions, ActionOutputPanel, type ActionOutput } from "@/components/ProgramActions" import { GitSyncRow } from "@/components/detail/GitSyncRow" export function ProgramDetailPage() { const { name } = useParams<{ name: string }>() const { data: deployment, isLoading, error, refetch } = useProgram(name ?? "") const git = useProgramGit(name ?? "", !!deployment?.repo) const sync = useProgramSync() const [actionOutput, setActionOutput] = useState(null) const handleSync = () => { if (!deployment) return setActionOutput(null) sync.mutate(deployment.id, { onSuccess: (data) => { const lines = [data.output || (data.pulled ? "Pulled." : "Already up to date.")] if (data.pulled && data.deployments.length) { lines.push("", `May need a restart/apply: ${data.deployments.join(", ")}`) } setActionOutput({ action: "sync", text: lines.join("\n"), ok: true }) }, onError: (err) => { let text = String(err) try { text = JSON.parse((err as Error).message).detail ?? text } catch { text = (err as Error).message ?? text } setActionOutput({ action: "sync", text, ok: false }) }, }) } if (isLoading) { return (
Loading...
) } if (error || !deployment) { return (

Program not found

) } // 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 = isStatic && buildOutputs?.length ? (subdomainUrl(deployment.id) ?? `${deployment.id}.`) : null return (
{actionOutput && actionOutput.action && (
setActionOutput(null)} />
)} {deployment.description && (

{deployment.description}

)}

Program Info

Where the source lives and how wildpc works with it.

{deployment.source && ( <> Source {deployment.source} )} {deployment.repo && ( <> Repo {deployment.repo} {deployment.ref ? ` @ ${deployment.ref}` : ""} )} {deployment.repo && git.data?.is_repo && ( <> Git )} {deployment.version && ( <> Version {deployment.version} )} {deployment.active !== null && ( <> Active {deployment.active ? "โ— active" : "โ—‹ inactive"} )} {servedAt && ( <> Reachable at {servedAt} ยท served (static) )}
{deployment.commands && Object.keys(deployment.commands).length > 0 && (
Commands
{Object.entries(deployment.commands).map(([verb, cmds]) => (
{verb} {cmds.map((argv) => argv.join(" ")).join(" && ")}
))}
)} {deployment.system_dependencies.length > 0 && (
System Dependencies
{deployment.system_dependencies.map((dep) => ( {dep} ))}
)}
) }