Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions web-ui/src/components/blockers/BlockerCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { useState, useEffect, useRef } from 'react';
import { useState } from 'react';
import Link from 'next/link';
import { Alert02Icon, Loading03Icon, CheckmarkCircle01Icon } from '@hugeicons/react';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
Expand All @@ -21,25 +22,16 @@ export function BlockerCard({ blocker, workspacePath, onAnswered }: BlockerCardP
const [submitted, setSubmitted] = useState(false);
const [error, setError] = useState<string | null>(null);
const [collapsed, setCollapsed] = useState(false);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

const isOpen = blocker.status === 'OPEN';

// Clean up timeout on unmount
useEffect(() => {
return () => {
if (timerRef.current) clearTimeout(timerRef.current);
};
}, []);

const handleSubmit = async () => {
if (!answer.trim() || isSubmitting) return;
setIsSubmitting(true);
setError(null);
try {
await blockersApi.answer(workspacePath, blocker.id, answer.trim());
setSubmitted(true);
timerRef.current = setTimeout(() => onAnswered(), 1500);
} catch (err) {
const apiErr = err as ApiError;
setError(apiErr.detail || 'Failed to submit answer');
Expand All @@ -54,11 +46,16 @@ export function BlockerCard({ blocker, workspacePath, onAnswered }: BlockerCardP
data-testid="blocker-card"
className="border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950/30"
>
<CardContent className="flex items-center gap-2 p-4">
<CheckmarkCircle01Icon className="h-5 w-5 text-green-600 dark:text-green-400" />
<p className="text-sm font-medium text-green-800 dark:text-green-300">
Blocker answered. Task will resume execution.
</p>
<CardContent className="flex items-center justify-between gap-4 p-4">
<div className="flex items-center gap-2">
<CheckmarkCircle01Icon className="h-5 w-5 shrink-0 text-green-600 dark:text-green-400" />
<p className="text-sm font-medium text-green-800 dark:text-green-300">
Answer recorded. Go to Tasks and restart execution to resume the agent.
</p>
</div>
<Button size="sm" variant="outline" asChild>
<Link href="/tasks" onClick={onAnswered}>Go to Tasks</Link>
</Button>
</CardContent>
</Card>
);
Expand All @@ -70,7 +67,10 @@ export function BlockerCard({ blocker, workspacePath, onAnswered }: BlockerCardP
{/* Task context */}
{blocker.task_id && (
<p className="text-xs font-medium text-muted-foreground">
Task {blocker.task_id}
Raised by{' '}
<Link href="/tasks" className="text-primary hover:underline">
Task {blocker.task_id}
</Link>
</p>
)}

Expand Down
10 changes: 10 additions & 0 deletions web-ui/src/components/blockers/BlockerResolutionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ export function BlockerResolutionView({ workspacePath }: BlockerResolutionViewPr
)}
</div>

{/* Info banner — shown only when there are open blockers */}
{openBlockers.length > 0 && (
<div className="rounded-lg border border-border bg-muted/50 px-4 py-3 text-sm">
<p className="font-medium text-foreground">AI execution is paused.</p>
<p className="mt-0.5 text-muted-foreground">
Blockers pause agent execution until you provide the requested information. Answer the question below, then go to Tasks to restart execution.
</p>
</div>
)}

{/* Open blockers */}
{openBlockers.length === 0 ? (
<div className="rounded-lg border bg-muted/50 p-8 text-center">
Expand Down
Loading