Skip to content

Commit fb5c26c

Browse files
STRRLclaude
andcommitted
feat: add task retry functionality with UI feedback
Implement comprehensive task retry feature across backend and frontend: - Add input validation and event emission for RetryTask method - Add retry button with loading states for failed tasks - Improve error display and user feedback during retry operations - Set default window dimensions for consistent initial layout 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 248503b commit fb5c26c

File tree

2 files changed

+62
-5
lines changed

2 files changed

+62
-5
lines changed

app.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ const (
2727
ProgressTaskComplete = 100
2828
)
2929

30+
const (
31+
defaultWindowWidth = 3464
32+
defaultWindowHeight = 2200
33+
)
34+
3035
// App struct
3136
type App struct {
3237
ctx context.Context
@@ -81,6 +86,10 @@ func (a *App) startup(ctx context.Context) {
8186
a.ctx = ctx
8287
a.logger.Info("TransCube starting up")
8388

89+
if err := runtime.WindowSetSize(a.ctx, defaultWindowWidth, defaultWindowHeight); err != nil {
90+
a.logger.Warn("Failed to set window size", "error", err)
91+
}
92+
8493
// Log environment info for debugging
8594
pathFinder := utils.NewPathFinder()
8695
debugInfo := pathFinder.DebugInfo()
@@ -289,6 +298,14 @@ func (a *App) ensureTaskLoaded(taskID string) (*types.Task, error) {
289298

290299
// RetryTask retries a failed task
291300
func (a *App) RetryTask(taskID string) (*types.Task, error) {
301+
if taskID == "" {
302+
return nil, fmt.Errorf("taskID is required")
303+
}
304+
305+
if _, err := a.ensureTaskLoaded(taskID); err != nil {
306+
return nil, err
307+
}
308+
292309
task, err := a.taskManager.RetryTask(taskID)
293310
if err != nil {
294311
return nil, err
@@ -297,6 +314,7 @@ func (a *App) RetryTask(taskID string) (*types.Task, error) {
297314
a.logger.Info("Retrying task", "taskId", taskID)
298315

299316
go a.processTask(taskID)
317+
a.emitReloadEvent()
300318

301319
return task, nil
302320
}

frontend/src/pages/HomePage.tsx

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
AlertDialogTitle,
1616
} from '@/components/ui/alert-dialog'
1717
import { Clock, Plus, Trash2, RefreshCcw, Loader2 } from 'lucide-react'
18-
import { GetAllTasks, DeleteTask, ListActiveTasks } from '../../wailsjs/go/main/App'
18+
import { GetAllTasks, DeleteTask, ListActiveTasks, RetryTask } from '../../wailsjs/go/main/App'
1919
import { EventsOn } from '../../wailsjs/runtime/runtime'
2020

2121
export default function HomePage() {
@@ -32,6 +32,7 @@ export default function HomePage() {
3232
const [refreshing, setRefreshing] = useState(false)
3333
const [pollMs, setPollMs] = useState(1500)
3434
const idleRoundsRef = useRef(0)
35+
const [retryingTasks, setRetryingTasks] = useState<Record<string, boolean>>({})
3536

3637
const isActiveStatus = useCallback(
3738
(status: string | undefined) =>
@@ -158,6 +159,23 @@ export default function HomePage() {
158159
}
159160
}
160161

162+
const handleRetry = useCallback(async (taskId: string) => {
163+
setRetryingTasks((prev) => ({ ...prev, [taskId]: true }))
164+
try {
165+
await RetryTask(taskId)
166+
await Promise.all([loadTasks(), loadActiveTasks()])
167+
} catch (err) {
168+
console.error('Retry failed:', err)
169+
const message = err instanceof Error ? err.message : 'Failed to retry task'
170+
alert(message)
171+
} finally {
172+
setRetryingTasks((prev) => {
173+
const { [taskId]: _removed, ...rest } = prev
174+
return rest
175+
})
176+
}
177+
}, [loadActiveTasks, loadTasks])
178+
161179
const combinedTasks = useMemo(() => {
162180
const tasks = [...activeTasks, ...processedVideos]
163181
return tasks.sort((a, b) => {
@@ -348,10 +366,31 @@ export default function HomePage() {
348366
</p>
349367
</div>
350368

351-
{task.status === 'failed' && task.error && (
352-
<p className="text-xs text-destructive leading-relaxed">
353-
Error: {task.error}
354-
</p>
369+
{task.status === 'failed' && (
370+
<div className="space-y-2">
371+
{task.error && (
372+
<p className="text-xs text-destructive leading-relaxed">
373+
Error: {task.error}
374+
</p>
375+
)}
376+
<Button
377+
variant="outline"
378+
size="sm"
379+
className="w-fit"
380+
onClick={() => handleRetry(task.id)}
381+
disabled={!!retryingTasks[task.id]}
382+
type="button"
383+
>
384+
{retryingTasks[task.id] ? (
385+
<>
386+
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
387+
Retrying…
388+
</>
389+
) : (
390+
'Retry'
391+
)}
392+
</Button>
393+
</div>
355394
)}
356395
</CardContent>
357396
</Card>

0 commit comments

Comments
 (0)