feat(frontend): boutons cancel/retry des tâches de fond (B8)
Câble l'action utilisateur sur les commandes Tauri B8 : - port WorkStateGateway : ajout de cancelBackgroundTask/retryBackgroundTask (le read-model se rafraîchit via l'événement domaine backgroundTaskChanged ; retry rejoue sous un NOUVEL id de tâche). - adapter Tauri : invoke cancel_background_task/retry_background_task. - adapter mock : no-ops (le refresh réel est piloté par les événements). - ProjectWorkStatePanel (BackgroundTaskRow) : état busy + affichage d'erreur + refresh après action. - test ProjectsView.ls7 : stub de gateway complété. Build vert : npm run build (tsc --noEmit + vite build), vitest 449 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -63,7 +63,11 @@ function fixedWorkState(): WorkStateGateway {
|
||||
},
|
||||
],
|
||||
};
|
||||
return { getProjectWorkState: async () => structuredClone(state) };
|
||||
return {
|
||||
getProjectWorkState: async () => structuredClone(state),
|
||||
cancelBackgroundTask: async () => {},
|
||||
retryBackgroundTask: async () => {},
|
||||
};
|
||||
}
|
||||
|
||||
/** A conversation gateway that returns one identifiable turn for any thread. */
|
||||
|
||||
@ -323,11 +323,36 @@ function InboxRow({ item }: { item: InboxItem }) {
|
||||
);
|
||||
}
|
||||
|
||||
function BackgroundTaskRow({ task }: { task: BackgroundCompletion }) {
|
||||
function BackgroundTaskRow({
|
||||
task,
|
||||
onRefresh,
|
||||
}: {
|
||||
task: BackgroundCompletion;
|
||||
onRefresh: () => Promise<void>;
|
||||
}) {
|
||||
const { workState } = useGateways();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [actionBusy, setActionBusy] = useState(false);
|
||||
const [message, setMessage] = useState<string | null>(null);
|
||||
const hasOutput = Boolean(task.stdoutTail || task.stderrTail);
|
||||
const canCancel = task.status === "running" || task.status === "pending";
|
||||
const canRetry = task.status === "failed" || task.status === "cancelled";
|
||||
|
||||
async function runAction(
|
||||
action: (taskId: string) => Promise<void>,
|
||||
): Promise<void> {
|
||||
setActionBusy(true);
|
||||
setMessage(null);
|
||||
try {
|
||||
await action(task.taskId);
|
||||
await onRefresh();
|
||||
} catch (e) {
|
||||
setMessage(e instanceof Error ? e.message : String(e));
|
||||
} finally {
|
||||
setActionBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<li className="min-w-0 text-xs text-muted">
|
||||
<div className="flex min-w-0 items-start gap-2">
|
||||
@ -348,8 +373,11 @@ function BackgroundTaskRow({ task }: { task: BackgroundCompletion }) {
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
disabled={!canCancel}
|
||||
title="Backend command not exposed yet"
|
||||
disabled={!canCancel || actionBusy}
|
||||
loading={actionBusy}
|
||||
onClick={() =>
|
||||
void runAction((id) => workState.cancelBackgroundTask(id))
|
||||
}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
@ -364,8 +392,11 @@ function BackgroundTaskRow({ task }: { task: BackgroundCompletion }) {
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
disabled={!canRetry}
|
||||
title="Backend command not exposed yet"
|
||||
disabled={!canRetry || actionBusy}
|
||||
loading={actionBusy}
|
||||
onClick={() =>
|
||||
void runAction((id) => workState.retryBackgroundTask(id))
|
||||
}
|
||||
>
|
||||
Retry
|
||||
</Button>
|
||||
@ -376,6 +407,11 @@ function BackgroundTaskRow({ task }: { task: BackgroundCompletion }) {
|
||||
{shortTicket(task.taskId)}
|
||||
</code>
|
||||
</div>
|
||||
{message && (
|
||||
<p role="alert" className="mt-1 text-xs text-danger">
|
||||
{message}
|
||||
</p>
|
||||
)}
|
||||
{open && hasOutput && (
|
||||
<pre
|
||||
aria-label={`task ${shortTicket(task.taskId)} output`}
|
||||
@ -609,7 +645,11 @@ function AgentRow({
|
||||
className="mt-1 flex flex-col gap-1"
|
||||
>
|
||||
{backgroundTasks.map((task) => (
|
||||
<BackgroundTaskRow key={task.taskId} task={task} />
|
||||
<BackgroundTaskRow
|
||||
key={task.taskId}
|
||||
task={task}
|
||||
onRefresh={refreshAfterAction}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user