From 23a87625dc5986bdd0a20b56171495c4eb61d543 Mon Sep 17 00:00:00 2001 From: Renz Date: Wed, 12 Nov 2025 09:17:39 +0000 Subject: [PATCH] Fix: Interactive restore now shows dynamic status updates during operation Issue: Interactive cluster restore showed 'Status: Initializing...' throughout the entire restore process, making it appear stuck even though restore was working. Root cause: - Status and phase were set once in NewRestoreExecution() - Never updated during the restore process - Only changed to 'Completed' or 'Failed' at the end - No visual feedback about what stage of restore was running Solution: Time-based status progression Added logic in Update() tick handler to change status based on elapsed time: - 0-2 sec: 'Initializing restore...' / Phase: Starting - 2-5 sec: Context-aware status: - If cleanup: 'Cleaning N existing database(s)...' / Phase: Cleanup - If cluster: 'Extracting cluster archive...' / Phase: Extraction - If single: 'Preparing restore...' / Phase: Preparation - 5-10 sec: - If cluster: 'Restoring global objects...' / Phase: Globals - If single: 'Restoring database...' / Phase: Restore - 10+ sec: 'Restoring [cluster] databases...' / Phase: Restore Benefits: - User sees the restore is progressing through stages - Different status messages for cluster vs single database restore - Shows cleanup phase when enabled - Spinner + changing status = clear visual feedback - Better user experience during long-running restores Note: These are estimated phases since the restore engine runs in silent mode (no stdout interference with TUI). Actual operation may be faster or slower than time estimates, but provides much better UX than static 'Initializing'. --- internal/tui/restore_exec.go | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/internal/tui/restore_exec.go b/internal/tui/restore_exec.go index 492ebe5..506f5f7 100644 --- a/internal/tui/restore_exec.go +++ b/internal/tui/restore_exec.go @@ -175,6 +175,43 @@ func (m RestoreExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if !m.done { m.spinnerFrame = (m.spinnerFrame + 1) % len(m.spinnerFrames) m.elapsed = time.Since(m.startTime) + + // Update status based on elapsed time to show progress + // This provides visual feedback even though we don't have real-time progress + elapsedSec := int(m.elapsed.Seconds()) + + if elapsedSec < 2 { + m.status = "Initializing restore..." + m.phase = "Starting" + } else if elapsedSec < 5 { + if m.cleanClusterFirst && len(m.existingDBs) > 0 { + m.status = fmt.Sprintf("Cleaning %d existing database(s)...", len(m.existingDBs)) + m.phase = "Cleanup" + } else if m.restoreType == "restore-cluster" { + m.status = "Extracting cluster archive..." + m.phase = "Extraction" + } else { + m.status = "Preparing restore..." + m.phase = "Preparation" + } + } else if elapsedSec < 10 { + if m.restoreType == "restore-cluster" { + m.status = "Restoring global objects..." + m.phase = "Globals" + } else { + m.status = fmt.Sprintf("Restoring database '%s'...", m.targetDB) + m.phase = "Restore" + } + } else { + if m.restoreType == "restore-cluster" { + m.status = "Restoring cluster databases..." + m.phase = "Restore" + } else { + m.status = fmt.Sprintf("Restoring database '%s'...", m.targetDB) + m.phase = "Restore" + } + } + return m, restoreTickCmd() } return m, nil @@ -199,7 +236,7 @@ func (m RestoreExecutionModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.elapsed = msg.elapsed if m.err == nil { - m.status = "Completed" + m.status = "Restore completed successfully" m.phase = "Done" m.progress = 100 } else {