fix(frontend): remove stale spawn modal references from shell and sidebar (#224)

This commit is contained in:
yyovil 2026-06-13 13:20:53 +05:30 committed by GitHub
parent f7df36bb8b
commit 680a85fceb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 7 additions and 88 deletions

View File

@ -1,6 +1,5 @@
import { SidebarProvider } from "@/components/ui/sidebar";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { render, screen } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { Sidebar } from "./Sidebar";
import type { WorkspaceSummary } from "../types/workspace";
@ -25,24 +24,20 @@ const workspace: WorkspaceSummary = {
sessions: [],
};
function renderSidebar(onRemoveProject = vi.fn().mockResolvedValue(undefined)) {
function renderSidebar() {
render(
<SidebarProvider>
<Sidebar
daemonStatus={{ state: "running" }}
onCreateProject={vi.fn()}
onRemoveProject={onRemoveProject}
workspaces={[workspace]}
/>
</SidebarProvider>,
);
return onRemoveProject;
}
beforeEach(() => {
navigateMock.mockReset();
vi.spyOn(window, "confirm").mockReturnValue(true);
vi.spyOn(window, "alert").mockImplementation(() => undefined);
});
afterEach(() => {
@ -50,30 +45,6 @@ afterEach(() => {
});
describe("Sidebar", () => {
it("confirms project removal before calling the remove handler", async () => {
const user = userEvent.setup();
const onRemoveProject = renderSidebar();
await user.click(screen.getByLabelText("Project actions for Project One"));
await user.click(await screen.findByRole("menuitem", { name: "Remove project" }));
expect(window.confirm).toHaveBeenCalledWith(
"Remove project Project One? This stops its live sessions and removes it from the sidebar, but keeps the repository folder and stored history on disk.",
);
await waitFor(() => expect(onRemoveProject).toHaveBeenCalledTimes(1));
});
it("does not remove the project when confirmation is cancelled", async () => {
vi.mocked(window.confirm).mockReturnValue(false);
const user = userEvent.setup();
const onRemoveProject = renderSidebar();
await user.click(screen.getByLabelText("Project actions for Project One"));
await user.click(await screen.findByRole("menuitem", { name: "Remove project" }));
expect(onRemoveProject).not.toHaveBeenCalled();
});
it("hides the worker count in every state that reveals project actions", () => {
renderSidebar();

View File

@ -1,16 +1,5 @@
import { useNavigate, useParams, useRouterState } from "@tanstack/react-router";
import {
ChevronRight,
GitPullRequest,
Moon,
MoreHorizontal,
Plus,
Search,
Settings,
Sun,
Trash2,
Waypoints,
} from "lucide-react";
import { ChevronRight, GitPullRequest, Moon, Plus, Search, Settings, Sun, Waypoints } from "lucide-react";
import { useState } from "react";
import {
attentionZone,
@ -377,25 +366,6 @@ function ProjectItem({
}
};
const removeProject = async () => {
setRemoveError(null);
const confirmed = window.confirm(
`Remove project ${workspace.name}? This stops its live sessions and removes it from the sidebar, but keeps the repository folder and stored history on disk.`,
);
if (!confirmed) return;
setIsRemoving(true);
try {
await onRemoveProject();
} catch (err) {
const message = err instanceof Error ? err.message : "Could not remove project";
setRemoveError(message);
window.alert(message);
} finally {
setIsRemoving(false);
}
};
return (
<SidebarMenuItem className="mb-px group-data-[collapsible=icon]:mb-0">
{/* project-sidebar__proj-row */}

View File

@ -1,6 +1,6 @@
import { createFileRoute, Outlet, useNavigate, useParams } from "@tanstack/react-router";
import { createFileRoute, Outlet, useNavigate } from "@tanstack/react-router";
import { useQueryClient } from "@tanstack/react-query";
import { useCallback, useEffect, useState } from "react";
import { type CSSProperties, useCallback, useEffect } from "react";
import { ShellTopbar } from "../components/ShellTopbar";
import { Sidebar } from "../components/Sidebar";
import { SidebarProvider } from "../components/ui/sidebar";
@ -34,7 +34,6 @@ function errorMessage(error: unknown) {
// instead of Zustand. The daemon-status effect runs here exactly once.
function ShellLayout() {
const navigate = useNavigate();
const params = useParams({ strict: false }) as { projectId?: string };
const queryClient = useQueryClient();
const workspaceQuery = useWorkspaceQuery();
const workspaces = workspaceQuery.data ?? [];
@ -67,20 +66,6 @@ function ShellLayout() {
[navigate, updateWorkspaces],
);
const removeProject = useCallback(
async (projectId: string) => {
const { error } = await apiClient.DELETE("/api/v1/projects/{id}", { params: { path: { id: projectId } } });
if (error) throw new Error(apiErrorMessage(error));
updateWorkspaces((current) => current.filter((item) => item.id !== projectId));
await queryClient.invalidateQueries({ queryKey: workspaceQueryKey });
if (params.projectId === projectId) {
void navigate({ to: "/" });
}
},
[navigate, params.projectId, queryClient, updateWorkspaces],
);
useEffect(() => {
document.documentElement.dataset.theme = theme;
document.documentElement.style.colorScheme = theme;
@ -113,7 +98,7 @@ function ShellLayout() {
}, [navigate, workspaces]);
return (
<ShellProvider value={{ daemonStatus, openSpawn, createProject, createTask }}>
<ShellProvider value={{ daemonStatus, createProject }}>
{/* The topbar spans the full window width above the sidebar row (the
macOS traffic lights + TitlebarNav cluster sit in its left inset),
and the sidebar hangs below it so the sidebar border stops at the
@ -130,7 +115,7 @@ function ShellLayout() {
onOpenChange={(open) => open !== isSidebarOpen && toggleSidebar()}
open={isSidebarOpen}
style={
{ "--sidebar-width": "var(--ao-sidebar-w, 240px)", "--sidebar-width-icon": "48px" } as React.CSSProperties
{ "--sidebar-width": "var(--ao-sidebar-w, 240px)", "--sidebar-width-icon": "48px" } as CSSProperties
}
>
<Sidebar
@ -155,13 +140,6 @@ function ShellLayout() {
<TitlebarNav />
</SidebarProvider>
</div>
<SpawnWorkerModal
defaultProjectId={spawnProjectId}
onCreateTask={createTask}
onOpenChange={setSpawnOpen}
open={spawnOpen}
workspaces={workspaces}
/>
</ShellProvider>
);
}