remove: session operations center and progress panel
The bottom-right progress panel duplicated feedback already shown by toasts. Remove it and the operation-tracking state to simplify the UI: - Delete state/session-operations.tsx and session-progress-panel.tsx. - Remove SessionOperationsProvider/SessionProgressPanel from AppShell. - Remove startOperation/completeOperation calls from useInstanceActions and ToolStarter. - Remove SessionOperationsProvider wrapper from DashboardPage.test.tsx. - Remove .session-progress-panel CSS rules. - Format use-events.test.ts mock to match project lint rules. Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed).
This commit is contained in:
@@ -30,17 +30,25 @@ describe("useEvents", () => {
|
||||
return EventSource.OPEN;
|
||||
},
|
||||
url: "http://localhost:8000/events/stream",
|
||||
addEventListener: vi.fn((type: string, handler: (e: MessageEvent) => void) => {
|
||||
if (!listeners.has(type)) listeners.set(type, new Set());
|
||||
listeners.get(type)!.add(handler);
|
||||
}),
|
||||
removeEventListener: vi.fn((type: string, handler: (e: MessageEvent) => void) => {
|
||||
listeners.get(type)?.delete(handler);
|
||||
}),
|
||||
addEventListener: vi.fn(
|
||||
(type: string, handler: (e: MessageEvent) => void) => {
|
||||
if (!listeners.has(type)) listeners.set(type, new Set());
|
||||
listeners.get(type)!.add(handler);
|
||||
},
|
||||
),
|
||||
removeEventListener: vi.fn(
|
||||
(type: string, handler: (e: MessageEvent) => void) => {
|
||||
listeners.get(type)?.delete(handler);
|
||||
},
|
||||
),
|
||||
emit: (type: string, data: string) => {
|
||||
listeners.get(type)?.forEach((handler) => handler({ data } as MessageEvent));
|
||||
listeners
|
||||
.get(type)
|
||||
?.forEach((handler) => handler({ data } as MessageEvent));
|
||||
},
|
||||
} as unknown as EventSource & { emit: (type: string, data: string) => void };
|
||||
} as unknown as EventSource & {
|
||||
emit: (type: string, data: string) => void;
|
||||
};
|
||||
mockedCreateEventSource.mockReturnValue(mockEs);
|
||||
mockedProbeEventStreamStatus.mockResolvedValue(null);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
} from "../api/sessions";
|
||||
import type { Session } from "../api/sessions";
|
||||
import { useSessions } from "../state/sessions";
|
||||
import { useSessionOperations } from "../state/session-operations";
|
||||
|
||||
interface UseInstanceActionsOptions {
|
||||
onRefresh: () => Promise<void>;
|
||||
@@ -33,7 +32,6 @@ export function useInstanceActions(
|
||||
): UseInstanceActionsReturn {
|
||||
const { onRefresh } = options;
|
||||
const { removeSession } = useSessions();
|
||||
const { startOperation, completeOperation } = useSessionOperations();
|
||||
const [loadingSessionId, setLoadingSessionId] = useState<string | null>(null);
|
||||
const [dirtyDeleteSession, setDirtyDeleteSession] = useState<Session | null>(
|
||||
null,
|
||||
@@ -66,7 +64,6 @@ export function useInstanceActions(
|
||||
async (session: Session) => {
|
||||
if (loadingSessionId === session.id) return;
|
||||
setLoadingSessionId(session.id);
|
||||
startOperation("start", session.id, session.display_name);
|
||||
try {
|
||||
await startInstance(
|
||||
session.project_id,
|
||||
@@ -74,20 +71,17 @@ export function useInstanceActions(
|
||||
session.id,
|
||||
);
|
||||
await onRefresh();
|
||||
} catch {
|
||||
completeOperation(session.id, "start", "error");
|
||||
} finally {
|
||||
setLoadingSessionId(null);
|
||||
}
|
||||
},
|
||||
[loadingSessionId, onRefresh, startOperation, completeOperation],
|
||||
[loadingSessionId, onRefresh],
|
||||
);
|
||||
|
||||
const handleStop = useCallback(
|
||||
async (session: Session) => {
|
||||
if (loadingSessionId === session.id) return;
|
||||
setLoadingSessionId(session.id);
|
||||
startOperation("stop", session.id, session.display_name);
|
||||
try {
|
||||
await stopInstance(
|
||||
session.project_id,
|
||||
@@ -95,20 +89,17 @@ export function useInstanceActions(
|
||||
session.id,
|
||||
);
|
||||
await onRefresh();
|
||||
} catch {
|
||||
completeOperation(session.id, "stop", "error");
|
||||
} finally {
|
||||
setLoadingSessionId(null);
|
||||
}
|
||||
},
|
||||
[loadingSessionId, onRefresh, startOperation, completeOperation],
|
||||
[loadingSessionId, onRefresh],
|
||||
);
|
||||
|
||||
const handleDelete = useCallback(
|
||||
async (session: Session) => {
|
||||
if (loadingSessionId === session.id) return;
|
||||
setLoadingSessionId(session.id);
|
||||
startOperation("delete", session.id, session.display_name);
|
||||
try {
|
||||
await deleteInstance(
|
||||
session.project_id,
|
||||
@@ -120,7 +111,6 @@ export function useInstanceActions(
|
||||
removeSession(session.id);
|
||||
await onRefresh();
|
||||
} catch (error) {
|
||||
completeOperation(session.id, "delete", "error");
|
||||
const axiosError = error as {
|
||||
response?: {
|
||||
status?: number;
|
||||
@@ -139,20 +129,13 @@ export function useInstanceActions(
|
||||
setLoadingSessionId(null);
|
||||
}
|
||||
},
|
||||
[
|
||||
loadingSessionId,
|
||||
onRefresh,
|
||||
removeSession,
|
||||
startOperation,
|
||||
completeOperation,
|
||||
],
|
||||
[loadingSessionId, onRefresh, removeSession],
|
||||
);
|
||||
|
||||
const handleForceDelete = useCallback(
|
||||
async (session: Session) => {
|
||||
if (loadingSessionId === session.id) return;
|
||||
setLoadingSessionId(session.id);
|
||||
startOperation("delete", session.id, session.display_name);
|
||||
try {
|
||||
await deleteInstance(
|
||||
session.project_id,
|
||||
@@ -164,26 +147,17 @@ export function useInstanceActions(
|
||||
setDirtyDeleteFiles([]);
|
||||
removeSession(session.id);
|
||||
await onRefresh();
|
||||
} catch {
|
||||
completeOperation(session.id, "delete", "error");
|
||||
} finally {
|
||||
setLoadingSessionId(null);
|
||||
}
|
||||
},
|
||||
[
|
||||
loadingSessionId,
|
||||
onRefresh,
|
||||
removeSession,
|
||||
startOperation,
|
||||
completeOperation,
|
||||
],
|
||||
[loadingSessionId, onRefresh, removeSession],
|
||||
);
|
||||
|
||||
const handleRecreateTunnel = useCallback(
|
||||
async (session: Session) => {
|
||||
if (loadingSessionId === session.id) return;
|
||||
setLoadingSessionId(session.id);
|
||||
startOperation("recreate-tunnel", session.id, session.display_name);
|
||||
try {
|
||||
await recreateInstanceTunnel(
|
||||
session.project_id,
|
||||
@@ -191,18 +165,16 @@ export function useInstanceActions(
|
||||
session.id,
|
||||
);
|
||||
await onRefresh();
|
||||
completeOperation(session.id, "recreate-tunnel", "success");
|
||||
} catch (err) {
|
||||
const message =
|
||||
(err as { response?: { data?: { detail?: string } } })?.response?.data
|
||||
?.detail || "Failed to recreate tunnel";
|
||||
completeOperation(session.id, "recreate-tunnel", "error", message);
|
||||
alert(message);
|
||||
} finally {
|
||||
setLoadingSessionId(null);
|
||||
}
|
||||
},
|
||||
[loadingSessionId, onRefresh, startOperation, completeOperation],
|
||||
[loadingSessionId, onRefresh],
|
||||
);
|
||||
|
||||
const handleRename = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user