refactor: unify tool-starting logic with shared useStartTool hook
- New useStartTool hook: calls working createInstance+startInstance API - WorkspacesPage: uses shared hook instead of inline createInstance logic - WorkspaceDetailPage ToolsTab: uses shared hook instead of broken createWorkspaceInstance - Both pages now use identical StartToolModal + identical start logic Quality gates: tsc --noEmit clean
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
/** Shared hook for starting a tool on a workspace. */
|
||||||
|
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { createInstance, startInstance } from "../api/sessions";
|
||||||
|
import type { Workspace } from "../types/workspace";
|
||||||
|
import type { ToolInstance } from "../api/sessions";
|
||||||
|
|
||||||
|
export interface UseStartToolResult {
|
||||||
|
starting: boolean;
|
||||||
|
error: string | null;
|
||||||
|
startTool: (
|
||||||
|
workspace: Workspace,
|
||||||
|
toolTypeId: string,
|
||||||
|
displayName?: string,
|
||||||
|
configProfileId?: string,
|
||||||
|
) => Promise<ToolInstance | null>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useStartTool(): UseStartToolResult {
|
||||||
|
const [starting, setStarting] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const startTool = useCallback(
|
||||||
|
async (
|
||||||
|
workspace: Workspace,
|
||||||
|
toolTypeId: string,
|
||||||
|
displayName?: string,
|
||||||
|
configProfileId?: string,
|
||||||
|
): Promise<ToolInstance | null> => {
|
||||||
|
setStarting(true);
|
||||||
|
setError(null);
|
||||||
|
try {
|
||||||
|
const instance = await createInstance(
|
||||||
|
workspace.project_id,
|
||||||
|
workspace.repo_id,
|
||||||
|
toolTypeId,
|
||||||
|
displayName || workspace.name,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
configProfileId,
|
||||||
|
[],
|
||||||
|
workspace.id,
|
||||||
|
);
|
||||||
|
await startInstance(
|
||||||
|
workspace.project_id,
|
||||||
|
workspace.repo_id,
|
||||||
|
instance.id,
|
||||||
|
configProfileId,
|
||||||
|
);
|
||||||
|
return instance;
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err instanceof Error ? err.message : "Failed to start tool";
|
||||||
|
setError(msg);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
setStarting(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { starting, error, startTool };
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import { useWorkspaces } from "../hooks/use-workspaces";
|
|||||||
import { useWorkspaceFiles } from "../hooks/use-workspace-files";
|
import { useWorkspaceFiles } from "../hooks/use-workspace-files";
|
||||||
import { useWorkspaceGit } from "../hooks/use-workspace-git";
|
import { useWorkspaceGit } from "../hooks/use-workspace-git";
|
||||||
import { useWorkspaceInstances } from "../hooks/use-workspace-instances";
|
import { useWorkspaceInstances } from "../hooks/use-workspace-instances";
|
||||||
|
import { useStartTool } from "../hooks/use-start-tool";
|
||||||
import { useMobileViewport } from "../hooks/use-mobile-viewport";
|
import { useMobileViewport } from "../hooks/use-mobile-viewport";
|
||||||
import { StartToolModal } from "../components/start-tool-modal";
|
import { StartToolModal } from "../components/start-tool-modal";
|
||||||
import type { FileEntry } from "../api/workspace-files";
|
import type { FileEntry } from "../api/workspace-files";
|
||||||
@@ -312,7 +313,8 @@ function GitTab({ workspaceId }: { workspaceId: string }) {
|
|||||||
/* ─── Tools Tab ─── */
|
/* ─── Tools Tab ─── */
|
||||||
|
|
||||||
function ToolsTab({ workspace }: { workspace: Workspace }) {
|
function ToolsTab({ workspace }: { workspace: Workspace }) {
|
||||||
const { instances, loading, create } = useWorkspaceInstances(workspace.id);
|
const { instances, loading, refresh } = useWorkspaceInstances(workspace.id);
|
||||||
|
const { startTool, starting } = useStartTool();
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -326,8 +328,9 @@ function ToolsTab({ workspace }: { workspace: Workspace }) {
|
|||||||
<button
|
<button
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
onClick={() => setShowModal(true)}
|
onClick={() => setShowModal(true)}
|
||||||
|
disabled={starting}
|
||||||
>
|
>
|
||||||
Start Tool
|
{starting ? "Starting..." : "Start Tool"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -355,8 +358,9 @@ function ToolsTab({ workspace }: { workspace: Workspace }) {
|
|||||||
<button
|
<button
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
onClick={() => setShowModal(true)}
|
onClick={() => setShowModal(true)}
|
||||||
|
disabled={starting}
|
||||||
>
|
>
|
||||||
Start Another Tool
|
{starting ? "Starting..." : "Start Another Tool"}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -365,8 +369,16 @@ function ToolsTab({ workspace }: { workspace: Workspace }) {
|
|||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
onClose={() => setShowModal(false)}
|
onClose={() => setShowModal(false)}
|
||||||
onStart={async (toolTypeId, configProfileId) => {
|
onStart={async (toolTypeId, configProfileId) => {
|
||||||
await create(toolTypeId, undefined, configProfileId);
|
const instance = await startTool(
|
||||||
setShowModal(false);
|
workspace,
|
||||||
|
toolTypeId,
|
||||||
|
undefined,
|
||||||
|
configProfileId,
|
||||||
|
);
|
||||||
|
if (instance) {
|
||||||
|
setShowModal(false);
|
||||||
|
await refresh();
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -376,11 +388,7 @@ function ToolsTab({ workspace }: { workspace: Workspace }) {
|
|||||||
|
|
||||||
/* ─── Settings Tab ─── */
|
/* ─── Settings Tab ─── */
|
||||||
|
|
||||||
function SettingsTab({
|
function SettingsTab({ workspace }: { workspace: Workspace }) {
|
||||||
workspace,
|
|
||||||
}: {
|
|
||||||
workspace: Workspace;
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-tab">
|
<div className="settings-tab">
|
||||||
<div className="settings-section">
|
<div className="settings-section">
|
||||||
@@ -411,5 +419,3 @@ function SettingsTab({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { useState } from "react";
|
|||||||
import { Icon } from "../components/icon";
|
import { Icon } from "../components/icon";
|
||||||
import { useWorkspaces } from "../hooks/use-workspaces";
|
import { useWorkspaces } from "../hooks/use-workspaces";
|
||||||
import { useWorkspaceActions } from "../hooks/use-workspace-actions";
|
import { useWorkspaceActions } from "../hooks/use-workspace-actions";
|
||||||
|
import { useStartTool } from "../hooks/use-start-tool";
|
||||||
import { WorkspaceCard } from "../components/workspace-card";
|
import { WorkspaceCard } from "../components/workspace-card";
|
||||||
import { WorkspaceCreateForm } from "../components/workspace-create-form";
|
import { WorkspaceCreateForm } from "../components/workspace-create-form";
|
||||||
import { StartToolModal } from "../components/start-tool-modal";
|
import { StartToolModal } from "../components/start-tool-modal";
|
||||||
import { createInstance, startInstance } from "../api/sessions";
|
|
||||||
import type { Workspace } from "../types/workspace";
|
import type { Workspace } from "../types/workspace";
|
||||||
|
|
||||||
export function WorkspacesPage() {
|
export function WorkspacesPage() {
|
||||||
@@ -16,6 +16,7 @@ export function WorkspacesPage() {
|
|||||||
|
|
||||||
const { workspaces, loading, error, refresh } = useWorkspaces();
|
const { workspaces, loading, error, refresh } = useWorkspaces();
|
||||||
const actions = useWorkspaceActions();
|
const actions = useWorkspaceActions();
|
||||||
|
const { startTool } = useStartTool();
|
||||||
|
|
||||||
const handleDelete = async (workspace: Workspace) => {
|
const handleDelete = async (workspace: Workspace) => {
|
||||||
await actions.delete(workspace, refresh);
|
await actions.delete(workspace, refresh);
|
||||||
@@ -35,29 +36,15 @@ export function WorkspacesPage() {
|
|||||||
configProfileId?: string,
|
configProfileId?: string,
|
||||||
) => {
|
) => {
|
||||||
if (!startWorkspace) return;
|
if (!startWorkspace) return;
|
||||||
try {
|
const instance = await startTool(
|
||||||
const instance = await createInstance(
|
startWorkspace,
|
||||||
startWorkspace.project_id,
|
toolTypeId,
|
||||||
startWorkspace.repo_id,
|
undefined,
|
||||||
toolTypeId,
|
configProfileId,
|
||||||
`${startWorkspace.name} - ${toolTypeId}`,
|
);
|
||||||
undefined,
|
if (instance) {
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
configProfileId,
|
|
||||||
[],
|
|
||||||
startWorkspace.id,
|
|
||||||
);
|
|
||||||
await startInstance(
|
|
||||||
startWorkspace.project_id,
|
|
||||||
startWorkspace.repo_id,
|
|
||||||
instance.id,
|
|
||||||
configProfileId,
|
|
||||||
);
|
|
||||||
setStartWorkspace(null);
|
setStartWorkspace(null);
|
||||||
await refresh();
|
await refresh();
|
||||||
} catch (err) {
|
|
||||||
alert(err instanceof Error ? err.message : "Failed to start tool");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user