Remove buttons: confirm + block program removal with active deployments

The Stage 2 editor split dropped the old confirm() — Remove program/service/job
deleted immediately on click. Restored confirmation on all three (FormFooter
window.confirm with a per-type message).

Removing a program that still has services/jobs referencing it would orphan
their program: ref. Now blocked:
- API: DELETE /config/programs/{name} → 409 'Programs with active jobs or
  services cannot be removed. Delete these first: …' (authoritative).
- App: ProgramFields shows the blocked reason instead of a Remove button when
  the program has deployments.
- CLI: castle delete refuses a program with referencing deployments not named
  the same (which would survive the delete dangling).

Verified live: DELETE program lakehouse → 409. api 52 / cli 24 green; build clean.
This commit is contained in:
2026-06-14 15:52:57 -07:00
parent 2a073fc0b4
commit 82f12c9d61
6 changed files with 47 additions and 3 deletions

View File

@@ -122,18 +122,27 @@ export function FormFooter({
onSave,
onDelete,
deleteLabel,
confirmMessage,
deleteBlocked,
}: {
saving: boolean
saved: boolean
onSave: () => void
onDelete?: () => void
deleteLabel: string
confirmMessage?: string
/** When set, removal is disallowed and this reason is shown instead of the button. */
deleteBlocked?: string
}) {
return (
<div className="flex items-center justify-between pt-3 border-t border-[var(--border)]">
{onDelete ? (
{deleteBlocked ? (
<span className="text-xs text-amber-400">{deleteBlocked}</span>
) : onDelete ? (
<button
onClick={onDelete}
onClick={() => {
if (window.confirm(confirmMessage ?? `${deleteLabel}?`)) onDelete()
}}
className="flex items-center gap-1.5 text-xs text-red-400 hover:text-red-300"
>
<Trash2 size={12} /> {deleteLabel}