From 3781fce1e22a6887d427920cc9dd7a496a372212 Mon Sep 17 00:00:00 2001 From: Scrum4Me Agent <30029041+madhura68@users.noreply.github.com> Date: Wed, 13 May 2026 19:14:49 +0200 Subject: [PATCH] feat(ui): add action buttons to Docker, Git, systemd, and Caddy modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Docker table: Restart and Stop buttons per container row (docker_compose_restart / docker_compose_stop) - Git repos list: Fetch and Pull buttons per repo; Pull disabled when working tree is dirty - systemd units list: Restart button per unit (systemctl_restart) - Caddy: Edit link on /caddy page, new /caddy/edit page with textarea + 3-step Validate → Save+Reload flow - All buttons open ConfirmDialog with exact agent-call preview, then stream output via StreamingTerminal - Add docker_compose_stop to ops-agent/commands.yml.example Co-Authored-By: Claude Sonnet 4.6 --- app/caddy/_components/caddy-editor.tsx | 214 +++++++ app/caddy/edit/page.tsx | 43 ++ app/caddy/page.tsx | 11 +- app/docker/_components/docker-table.tsx | 78 ++- app/git/_components/git-repos-list.tsx | 77 +++ .../_components/systemd-units-list.tsx | 60 ++ ops-agent/commands.yml.example | 12 + package-lock.json | 526 ++++++++++++++++++ 8 files changed, 1019 insertions(+), 2 deletions(-) create mode 100644 app/caddy/_components/caddy-editor.tsx create mode 100644 app/caddy/edit/page.tsx diff --git a/app/caddy/_components/caddy-editor.tsx b/app/caddy/_components/caddy-editor.tsx new file mode 100644 index 0000000..14a4427 --- /dev/null +++ b/app/caddy/_components/caddy-editor.tsx @@ -0,0 +1,214 @@ +'use client' + +import { useCallback, useEffect, useState } from 'react' +import Link from 'next/link' +import { useFlowRun } from '@/hooks/useFlowRun' +import ConfirmDialog from '@/components/ConfirmDialog' +import StreamingTerminal from '@/components/StreamingTerminal' + +type Phase = 'edit' | 'writing' | 'validating' | 'validated' | 'saving' | 'saved' + +type DialogPending = 'validate' | 'save' | null + +type Props = { + initialContent: string + initialError: string | null +} + +const VALIDATE_PREVIEW = + 'cat > /srv/scrum4me/caddy/Caddyfile.new \\\n && mv /srv/scrum4me/caddy/Caddyfile.new /srv/scrum4me/caddy/Caddyfile\ncaddy validate --config /srv/scrum4me/caddy/Caddyfile' + +const SAVE_PREVIEW = 'caddy reload --config /srv/scrum4me/caddy/Caddyfile' + +export default function CaddyEditor({ initialContent, initialError }: Props) { + const [content, setContent] = useState(initialContent) + const [phase, setPhase] = useState('edit') + const [dialogPending, setDialogPending] = useState(null) + + const writeFlow = useFlowRun() + const validateFlow = useFlowRun() + const reloadFlow = useFlowRun() + + // Chain: write done → start validate + useEffect(() => { + if (phase === 'writing' && writeFlow.status === 'done') { + setPhase('validating') + validateFlow.start('caddy_validate') + } else if (phase === 'writing' && (writeFlow.status === 'failed' || writeFlow.status === 'error')) { + setPhase('edit') + } + }, [writeFlow.status, phase, validateFlow.start]) + + // Validate done → validated phase + useEffect(() => { + if (phase === 'validating' && validateFlow.status === 'done') { + setPhase('validated') + } else if (phase === 'validating' && (validateFlow.status === 'failed' || validateFlow.status === 'error')) { + setPhase('edit') + } + }, [validateFlow.status, phase]) + + // Reload done → saved + useEffect(() => { + if (phase === 'saving' && reloadFlow.status === 'done') { + setPhase('saved') + } else if (phase === 'saving' && (reloadFlow.status === 'failed' || reloadFlow.status === 'error')) { + setPhase('validated') + } + }, [reloadFlow.status, phase]) + + const handleValidateConfirm = useCallback(() => { + setDialogPending(null) + setPhase('writing') + writeFlow.reset() + validateFlow.reset() + writeFlow.start('caddy_write_config', [], content) + }, [content, writeFlow.reset, writeFlow.start, validateFlow.reset]) + + const handleSaveConfirm = useCallback(() => { + setDialogPending(null) + setPhase('saving') + reloadFlow.reset() + reloadFlow.start('caddy_reload') + }, [reloadFlow.reset, reloadFlow.start]) + + const handleEditAgain = () => { + writeFlow.reset() + validateFlow.reset() + reloadFlow.reset() + setPhase('edit') + } + + const isActive = phase === 'writing' || phase === 'validating' || phase === 'saving' + + return ( +
+ {initialError && ( +
+ Failed to load current config: {initialError} +
+ )} + + {/* Textarea */} +
+
+

Caddyfile

+ {phase === 'saved' && ( + + + Reloaded successfully + + )} + {phase === 'validated' && ( + + + Config is valid + + )} +
+