fix(ticket #7): ajoute le bouton Annuler aux notifications de fin de background task
This commit is contained in:
@ -268,7 +268,7 @@ describe("ProjectsView — LS7 conversation viewer integration", () => {
|
||||
const toast = await screen.findByRole("button", {
|
||||
name: /Appel Main -> DevBackend terminé/i,
|
||||
});
|
||||
expect(within(toast).getByText("Task task-ren")).toBeTruthy();
|
||||
expect(within(toast).getByText("alpha · Task task-ren")).toBeTruthy();
|
||||
|
||||
fireEvent.click(toast);
|
||||
|
||||
@ -346,7 +346,7 @@ describe("ProjectsView — LS7 conversation viewer integration", () => {
|
||||
const toast = await screen.findByRole("button", {
|
||||
name: /Background task failed/i,
|
||||
});
|
||||
expect(within(toast).getByText("agent-ge · task-gen")).toBeTruthy();
|
||||
expect(within(toast).getByText("alpha · agent-ge · task-gen")).toBeTruthy();
|
||||
|
||||
fireEvent.click(toast);
|
||||
|
||||
@ -355,4 +355,38 @@ describe("ProjectsView — LS7 conversation viewer integration", () => {
|
||||
).toBeNull();
|
||||
expect(await screen.findByText("Work")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("prefixes task toast context with a truncatable project-name line and falls back when unresolved", async () => {
|
||||
const project = new MockProjectGateway();
|
||||
const created = await project.createProject(
|
||||
"alpha with a very long project name that must stay on one line",
|
||||
"/p/a",
|
||||
);
|
||||
const system = new MockSystemGateway();
|
||||
renderView(project, { system });
|
||||
|
||||
await openProjectAndWorkTab("/p/a");
|
||||
system.emit({
|
||||
type: "backgroundTaskChanged",
|
||||
projectId: created.id,
|
||||
agentId: "agent-long-name-context",
|
||||
taskId: "task-long-name-context",
|
||||
state: "completed",
|
||||
});
|
||||
system.emit({
|
||||
type: "backgroundTaskChanged",
|
||||
projectId: "project-not-in-view",
|
||||
agentId: "agent-fallback-context",
|
||||
taskId: "task-fallback-context",
|
||||
state: "failed",
|
||||
});
|
||||
|
||||
const longNameLine = await screen.findByText(
|
||||
"alpha with a very long project name that must stay on one line · agent-lo · task-lon",
|
||||
);
|
||||
expect(longNameLine.className).toContain("truncate");
|
||||
expect(
|
||||
await screen.findByText("Projet inconnu · agent-fa · task-fal"),
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
* open; the project tab bar (`role="tablist"`) is always present.
|
||||
*/
|
||||
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { useEffect, useMemo, useState, type ReactNode } from "react";
|
||||
|
||||
import type { Agent, DomainEvent, LayoutInfo } from "@/domain";
|
||||
import { LayoutGrid, LayoutTabs } from "@/features/layout";
|
||||
@ -99,6 +99,7 @@ interface BackgroundTaskToast {
|
||||
taskId: string;
|
||||
state: string;
|
||||
title: string;
|
||||
projectName: string;
|
||||
subtitle: string;
|
||||
conversationId?: string;
|
||||
}
|
||||
@ -135,6 +136,13 @@ function agentLabel(agents: Agent[], agentId: string): string {
|
||||
);
|
||||
}
|
||||
|
||||
function projectToastName(
|
||||
projectNameById: ReadonlyMap<string, string>,
|
||||
projectId: string,
|
||||
): string {
|
||||
return projectNameById.get(projectId) ?? "Projet inconnu";
|
||||
}
|
||||
|
||||
function rendezvousToastStateLabel(state: string): string {
|
||||
switch (state) {
|
||||
case "completed":
|
||||
@ -190,6 +198,12 @@ export function ProjectsView() {
|
||||
const [taskToasts, setTaskToasts] = useState<BackgroundTaskToast[]>([]);
|
||||
|
||||
const active = vm.openTabs.find((t) => t.id === vm.activeTabId) ?? null;
|
||||
const projectNameById = useMemo(() => {
|
||||
const names = new Map<string, string>();
|
||||
for (const project of vm.projects) names.set(project.id, project.name);
|
||||
for (const tab of vm.openTabs) names.set(tab.id, tab.name);
|
||||
return names;
|
||||
}, [vm.projects, vm.openTabs]);
|
||||
|
||||
// `gitRepository` signal for the plugin `when` mini-language (#43, F3 —
|
||||
// Architect-mandated, blocking for #43 closure): whether the *current*
|
||||
@ -290,6 +304,7 @@ export function ProjectsView() {
|
||||
title: labels
|
||||
? `Appel ${labels.requester} -> ${labels.target} ${rendezvousToastStateLabel(event.state)}`
|
||||
: `Background task ${event.state}`,
|
||||
projectName: projectToastName(projectNameById, event.projectId),
|
||||
subtitle: labels
|
||||
? `Task ${shortTaskId(event.taskId)}`
|
||||
: `${shortTaskId(event.agentId)} · ${shortTaskId(event.taskId)}`,
|
||||
@ -310,7 +325,7 @@ export function ProjectsView() {
|
||||
cancelled = true;
|
||||
unsubscribe?.();
|
||||
};
|
||||
}, [agent, system]);
|
||||
}, [agent, projectNameById, system]);
|
||||
|
||||
const activeLayoutKind = activeLayout?.kind ?? "terminal";
|
||||
|
||||
@ -913,7 +928,7 @@ export function ProjectsView() {
|
||||
{toast.title}
|
||||
</span>
|
||||
<span className="mt-0.5 block truncate text-xs text-muted">
|
||||
{toast.subtitle}
|
||||
{toast.projectName} · {toast.subtitle}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user