fix: open ai workspace console in ops

This commit is contained in:
DCCONSTRUCTIONS
2026-06-09 14:35:34 +03:00
parent 342df60c24
commit b1d19cf5df
4 changed files with 2026 additions and 137 deletions
@@ -4,23 +4,13 @@
* See the LICENSE file for details.
*/
import { useCallback, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { createPortal } from "react-dom";
import useSWR from "swr";
import { MessageCircle } from "lucide-react";
// plane imports
import { Tooltip } from "@plane/propel/tooltip";
// components
import { AIWorkspaceProductSettingsModal } from "@/components/ai-workspace/product-settings-modal";
// services
import { WorkspaceAIWorkspaceService } from "@/services/workspace-ai-workspace.service";
import type {
TAIWorkspaceExecutor,
TAIWorkspaceExecutorInput,
TAIWorkspaceExecutorListResponse,
} from "@/services/workspace-ai-workspace.service";
const workspaceAIWorkspaceService = new WorkspaceAIWorkspaceService();
import { AIWorkspaceProductConsole } from "@/components/ai-workspace/product-console";
type TAIWorkspaceGlobalControlProps = {
workspaceSlug: string;
@@ -29,15 +19,6 @@ type TAIWorkspaceGlobalControlProps = {
export function AIWorkspaceGlobalControl({ workspaceSlug }: TAIWorkspaceGlobalControlProps) {
const [isOpen, setIsOpen] = useState(false);
const [dockSlot, setDockSlot] = useState<Element | null>(null);
const [busyId, setBusyId] = useState("");
const [notice, setNotice] = useState("");
const [actionError, setActionError] = useState("");
const { data, error, isLoading, mutate } = useSWR(
isOpen && workspaceSlug ? `AI_WORKSPACE_EXECUTORS_${workspaceSlug}` : null,
() => workspaceAIWorkspaceService.listExecutors(workspaceSlug),
{ refreshInterval: isOpen ? 10000 : 0 }
);
useEffect(() => {
if (typeof document === "undefined") return;
@@ -58,98 +39,6 @@ export function AIWorkspaceGlobalControl({ workspaceSlug }: TAIWorkspaceGlobalCo
};
}, []);
const handleClose = useCallback(() => {
setIsOpen(false);
setNotice("");
setActionError("");
}, []);
const runMutation = useCallback(
async <T,>(id: string, action: () => Promise<T>, successText: string): Promise<T | null> => {
setBusyId(id);
setActionError("");
setNotice("");
try {
const result = await action();
setNotice(successText);
return result;
} catch (mutationError: any) {
setActionError(
String(mutationError?.message || mutationError?.error || mutationError || "AI Workspace request failed")
);
return null;
} finally {
setBusyId("");
}
},
[]
);
const updateRegistry = useCallback(
async (payload: TAIWorkspaceExecutorListResponse) => {
await mutate(payload, { revalidate: false });
return payload;
},
[mutate]
);
const selectExecutor = useCallback(
async (executor: TAIWorkspaceExecutor) =>
runMutation(
`select:${executor.id}`,
async () => updateRegistry(await workspaceAIWorkspaceService.selectExecutor(workspaceSlug, executor.id)),
"Устройство выбрано"
),
[runMutation, updateRegistry, workspaceSlug]
);
const createExecutor = useCallback(
async (input: TAIWorkspaceExecutorInput) =>
runMutation(
"save",
async () => updateRegistry(await workspaceAIWorkspaceService.createExecutor(workspaceSlug, input)),
"Устройство создано"
),
[runMutation, updateRegistry, workspaceSlug]
);
const updateExecutor = useCallback(
async (executorId: string, input: TAIWorkspaceExecutorInput) =>
runMutation(
"save",
async () => updateRegistry(await workspaceAIWorkspaceService.updateExecutor(workspaceSlug, executorId, input)),
"Устройство сохранено"
),
[runMutation, updateRegistry, workspaceSlug]
);
const deleteExecutor = useCallback(
async (executorId: string) =>
runMutation(
`delete:${executorId}`,
async () => updateRegistry(await workspaceAIWorkspaceService.deleteExecutor(workspaceSlug, executorId)),
"Устройство удалено"
),
[runMutation, updateRegistry, workspaceSlug]
);
const downloadAgent = useCallback(
async (executorId: string, input: TAIWorkspaceExecutorInput, port: string) => {
await runMutation(
"download",
async () => {
const payload = await workspaceAIWorkspaceService.updateExecutor(workspaceSlug, executorId, input);
await updateRegistry(payload);
window.location.href = workspaceAIWorkspaceService.getWindowsAgentInstallerUrl(workspaceSlug, executorId, {
port,
});
},
"Installer подготовлен"
);
},
[runMutation, updateRegistry, workspaceSlug]
);
return (
<>
{dockSlot
@@ -168,26 +57,7 @@ export function AIWorkspaceGlobalControl({ workspaceSlug }: TAIWorkspaceGlobalCo
)
: null}
<AIWorkspaceProductSettingsModal
isOpen={isOpen}
executors={data?.executors ?? []}
selectedExecutorId={data?.selectedExecutorId ?? null}
isLoading={isLoading}
error={error}
notice={notice}
actionError={actionError}
busyId={busyId}
onClose={handleClose}
onReload={() => mutate()}
onSelect={selectExecutor}
onCreate={createExecutor}
onUpdate={updateExecutor}
onDelete={deleteExecutor}
onDownloadAgent={downloadAgent}
getWindowsAgentInstallerUrl={(executorId, options) =>
workspaceAIWorkspaceService.getWindowsAgentInstallerUrl(workspaceSlug, executorId, options)
}
/>
<AIWorkspaceProductConsole open={isOpen} workspaceSlug={workspaceSlug} onClose={() => setIsOpen(false)} />
</>
);
}
File diff suppressed because it is too large Load Diff