diff --git a/app/src/components/detail/JobFields.tsx b/app/src/components/detail/JobFields.tsx
index df17aae..07f2176 100644
--- a/app/src/components/detail/JobFields.tsx
+++ b/app/src/components/detail/JobFields.tsx
@@ -74,6 +74,7 @@ export function JobFields({ job, onSave, onDelete }: Props) {
onSave={handleSave}
onDelete={onDelete ? () => onDelete(job.id) : undefined}
deleteLabel="Remove job"
+ confirmMessage={`Remove job "${job.id}" from castle.yaml? Run a deploy afterward to tear down its timer.`}
/>
)
diff --git a/app/src/components/detail/ProgramFields.tsx b/app/src/components/detail/ProgramFields.tsx
index 8e2d3bb..f4eb8ff 100644
--- a/app/src/components/detail/ProgramFields.tsx
+++ b/app/src/components/detail/ProgramFields.tsx
@@ -95,6 +95,12 @@ export function ProgramFields({ program, onSave, onDelete }: Props) {
onSave={handleSave}
onDelete={onDelete ? () => onDelete(program.id) : undefined}
deleteLabel="Remove program"
+ confirmMessage={`Remove program "${program.id}" from castle.yaml? (Source on disk is untouched.)`}
+ deleteBlocked={
+ program.services.length + program.jobs.length > 0
+ ? "Programs with active jobs or services cannot be removed — delete those first."
+ : undefined
+ }
/>
)
diff --git a/app/src/components/detail/ServiceFields.tsx b/app/src/components/detail/ServiceFields.tsx
index aa54b51..9185286 100644
--- a/app/src/components/detail/ServiceFields.tsx
+++ b/app/src/components/detail/ServiceFields.tsx
@@ -119,6 +119,7 @@ export function ServiceFields({ service, onSave, onDelete }: Props) {
onSave={handleSave}
onDelete={onDelete ? () => onDelete(service.id) : undefined}
deleteLabel="Remove service"
+ confirmMessage={`Remove service "${service.id}" from castle.yaml? Run a deploy afterward to tear down its unit.`}
/>
)
diff --git a/app/src/components/detail/fields.tsx b/app/src/components/detail/fields.tsx
index ee0ceb6..672a334 100644
--- a/app/src/components/detail/fields.tsx
+++ b/app/src/components/detail/fields.tsx
@@ -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 (
- {onDelete ? (
+ {deleteBlocked ? (
+ {deleteBlocked}
+ ) : onDelete ? (