diff --git a/packages/dashboard/app/App.tsx b/packages/dashboard/app/App.tsx index ec73184f45..34504f4f09 100644 --- a/packages/dashboard/app/App.tsx +++ b/packages/dashboard/app/App.tsx @@ -1227,9 +1227,9 @@ function AppInner() { pushNav({ type: "modal", close: modalManager.closeScripts }); }, [modalManager, pushNav]); - const openWorkflowStepsWithNav = useCallback(() => { - modalManager.openWorkflowSteps(); - pushNav({ type: "modal", close: modalManager.closeWorkflowSteps }); + const openWorkflowEditorWithNav = useCallback(() => { + modalManager.openWorkflowEditor(); + pushNav({ type: "modal", close: modalManager.closeWorkflowEditor }); }, [modalManager, pushNav]); const openUsageWithNav = useCallback((anchorRect?: DOMRect | null) => { @@ -1803,7 +1803,7 @@ function AppInner() { onOpenGitManager={openGitManagerWithNav} onOpenNodes={handleOpenNodesWithNav} showNodesButton={nodesEnabled} - onOpenWorkflowSteps={openWorkflowStepsWithNav} + onOpenWorkflowEditor={openWorkflowEditorWithNav} onOpenScripts={openScriptsWithNav} onRunScript={runScriptWithNav} onToggleTerminal={toggleTerminalWithNav} @@ -2007,7 +2007,7 @@ function AppInner() { chatHasUnreadResponse={chatHasUnreadResponse} stashOrphanCount={stashOrphanCount} onOpenGitManager={openGitManagerWithNav} - onOpenWorkflowSteps={openWorkflowStepsWithNav} + onOpenWorkflowEditor={openWorkflowEditorWithNav} onOpenSchedules={openSchedulesWithNav} onOpenScripts={openScriptsWithNav} onToggleTerminal={toggleTerminalWithNav} diff --git a/packages/dashboard/app/__tests__/mobile-feature-access-regression.test.tsx b/packages/dashboard/app/__tests__/mobile-feature-access-regression.test.tsx index b6688642a8..6a56028d42 100644 --- a/packages/dashboard/app/__tests__/mobile-feature-access-regression.test.tsx +++ b/packages/dashboard/app/__tests__/mobile-feature-access-regression.test.tsx @@ -49,7 +49,7 @@ const createDefaultMobileNavProps = () => ({ onOpenNodes: vi.fn(), mailboxUnreadCount: 0, onOpenGitManager: vi.fn(), - onOpenWorkflowSteps: vi.fn(), + onOpenWorkflowEditor: vi.fn(), onOpenSchedules: vi.fn(), onOpenScripts: vi.fn(), onToggleTerminal: vi.fn(), diff --git a/packages/dashboard/app/__tests__/tablet-header-controls.test.tsx b/packages/dashboard/app/__tests__/tablet-header-controls.test.tsx index bcb2e9fb32..eca5a3e11f 100644 --- a/packages/dashboard/app/__tests__/tablet-header-controls.test.tsx +++ b/packages/dashboard/app/__tests__/tablet-header-controls.test.tsx @@ -178,9 +178,9 @@ describe("tablet header controls", () => { expect(screen.queryByTitle("Git Manager")).toBeNull(); }); - it("does not render workflow steps button inline on tablet", () => { - renderTabletHeader({ onOpenWorkflowSteps: noop }); - expect(screen.queryByTitle("Workflow Steps")).toBeNull(); + it("does not render workflows button inline on tablet", () => { + renderTabletHeader({ onOpenWorkflowEditor: noop }); + expect(screen.queryByTitle("Workflows")).toBeNull(); }); // ── Overflow menu on tablet ──────────────────────────────────── @@ -254,8 +254,8 @@ describe("tablet header controls", () => { expect(screen.getByTestId("overflow-git-btn")).toBeDefined(); }); - it("overflow menu contains workflow steps on tablet when provided", () => { - renderTabletHeader({ onOpenWorkflowSteps: noop }); + it("overflow menu contains workflows on tablet when provided", () => { + renderTabletHeader({ onOpenWorkflowEditor: noop }); fireEvent.click(screen.getByTitle("More header actions")); expect(screen.getByTestId("overflow-workflow-steps-btn")).toBeDefined(); }); @@ -538,7 +538,7 @@ describe("tablet header controls", () => { const { container } = renderTabletHeader({ onOpenUsage: noop, onOpenActivityLog: noop, - onOpenWorkflowSteps: noop, + onOpenWorkflowEditor: noop, onOpenFiles: noop, onOpenGitManager: noop, }); diff --git a/packages/dashboard/app/components/AppModals.tsx b/packages/dashboard/app/components/AppModals.tsx index 9b543557c1..3b266568f4 100644 --- a/packages/dashboard/app/components/AppModals.tsx +++ b/packages/dashboard/app/components/AppModals.tsx @@ -20,7 +20,6 @@ import { NewTaskModal } from "./NewTaskModal"; import { SystemStatsModal } from "./SystemStatsModal"; import { ActivityLogModal } from "./ActivityLogModal"; import { GitManagerModal } from "./GitManagerModal"; -import { WorkflowStepManager } from "./WorkflowStepManager"; import { AgentListModal } from "./AgentListModal"; import { ModelOnboardingModal } from "./ModelOnboardingModal"; import { ToastContainer } from "./ToastContainer"; @@ -373,19 +372,6 @@ export function AppModals({ /> - - { - modalManager.closeWorkflowSteps(); - modalManager.openWorkflowEditor(); - }} - /> - - {modalManager.workflowEditorOpen && ( diff --git a/packages/dashboard/app/components/Header.tsx b/packages/dashboard/app/components/Header.tsx index 17e3143235..034afa89a4 100644 --- a/packages/dashboard/app/components/Header.tsx +++ b/packages/dashboard/app/components/Header.tsx @@ -197,7 +197,7 @@ export interface HeaderProps { onOpenNodes?: () => void; /** When false, hides the Nodes management button. Defaults to true for backward compat. */ showNodesButton?: boolean; - onOpenWorkflowSteps?: () => void; + onOpenWorkflowEditor?: () => void; onOpenScripts?: () => void; onRunScript?: (name: string, command: string) => void; onToggleTerminal?: () => void; @@ -266,7 +266,7 @@ export function Header({ onOpenGitManager, onOpenNodes, showNodesButton, - onOpenWorkflowSteps, + onOpenWorkflowEditor, onOpenScripts, onRunScript, onToggleTerminal, @@ -1593,12 +1593,12 @@ export function Header({ )} - {/* Workflow Steps - desktop only (moved to overflow on mobile/tablet) */} - {!isCompact && onOpenWorkflowSteps && ( + {/* Workflows - desktop only (moved to overflow on mobile/tablet) */} + {!isCompact && onOpenWorkflowEditor && ( )} - {/* Workflow Steps - in overflow on mobile */} - {onOpenWorkflowSteps && ( + {/* Workflows - in overflow on mobile */} + {onOpenWorkflowEditor && ( )} {/* Settings - always last in overflow menu */} diff --git a/packages/dashboard/app/components/MobileNavBar.tsx b/packages/dashboard/app/components/MobileNavBar.tsx index ac421e502d..6ae76d6df0 100644 --- a/packages/dashboard/app/components/MobileNavBar.tsx +++ b/packages/dashboard/app/components/MobileNavBar.tsx @@ -60,7 +60,7 @@ export interface MobileNavBarProps { chatHasUnreadResponse?: boolean; stashOrphanCount?: number; onOpenGitManager?: () => void; - onOpenWorkflowSteps?: () => void; + onOpenWorkflowEditor?: () => void; onOpenSchedules?: () => void; onOpenScripts?: () => void; onToggleTerminal?: () => void; @@ -127,7 +127,7 @@ export function MobileNavBar({ chatHasUnreadResponse = false, stashOrphanCount = 0, onOpenGitManager, - onOpenWorkflowSteps, + onOpenWorkflowEditor, onOpenSchedules, onOpenScripts, onToggleTerminal, @@ -590,10 +590,10 @@ export function MobileNavBar({ type="button" className="mobile-more-item" data-testid="mobile-more-item-workflow" - onClick={() => handleMoreAction(onOpenWorkflowSteps)} + onClick={() => handleMoreAction(onOpenWorkflowEditor)} > - {t("nav.workflowSteps", "Workflow Steps")} + {t("nav.workflows", "Workflows")} - )} - - - - -
- {loading ? ( -
{t("common.loading", "Loading...")}
- ) : ( - <> - {/* Tab Navigation */} - {!isEditing && ( -
- - -
- )} - - {/* My Workflow Steps Tab */} - {activeTab === "my-steps" && !isEditing && !showCreateChooser && ( - <> - {steps.length === 0 && ( -
- {t("workflow.emptySteps", "No workflow steps defined. Create one to get started, or add one from the Templates tab.")} -
- )} - - {steps.length > 0 && ( -
- {steps.map((step) => ( -
-
-
-
- {step.name} - - {step.enabled ? t("workflow.badgeEnabled", "Enabled") : t("workflow.badgeDisabled", "Disabled")} - - - {(step.mode || "prompt") === "script" ? t("workflow.badgeScript", "Script") : t("workflow.badgeAiPrompt", "AI Prompt")} - - - {(step.phase || "pre-merge") === "post-merge" ? t("workflow.badgePostMerge", "Post-merge") : t("workflow.badgePreMerge", "Pre-merge")} - - - {(step.gateMode || ((step.mode || "prompt") === "script" ? "gate" : "advisory")) === "gate" ? t("workflow.badgeGate", "Gate") : t("workflow.badgeAdvisory", "Advisory")} - - {step.defaultOn && ( - - {t("workflow.badgeDefaultOn", "Default on")} - - )} -
-
- {step.description} -
-
-
- - {deleteConfirmId === step.id ? ( -
- - -
- ) : ( - - )} -
-
-
- ))} -
- )} - - )} - - {/* Templates Tab */} - {activeTab === "templates" && !isEditing && !showCreateChooser && ( - <> - {templatesLoading ? ( -
- - {t("workflow.loadingTemplates", "Loading templates...")} -
- ) : templates.length === 0 ? ( -
- {t("workflow.noTemplates", "No templates available.")} -
- ) : ( -
- {templates.map((template) => { - const IconComponent = getTemplateIcon(template.icon); - const categoryClassName = getCategoryClassName(template.category); - const isAdding = addingTemplateId === template.id; - - return ( -
-
- {/* Icon */} -
- -
- - {/* Content */} -
-
- - {template.name} - - - {template.category} - - {pluginTemplateOwners[template.id] && ( - - {pluginTemplateOwners[template.id]} - - )} -
-
- {template.description} -
- -
-
-
- ); - })} -
- )} - - )} - - {/* Create chooser */} - {showCreateChooser && !isEditing && ( -
-

{t("workflow.createChooserTitle", "How would you like to create this workflow step?")}

-

- {t("workflow.createChooserHint", "Start from a built-in template or create a fully custom workflow step.")} -

- - - - {templatesLoading ? ( -
- - {t("workflow.loadingBuiltInTemplates", "Loading built-in templates...")} -
- ) : templates.length === 0 ? ( -
- {t("workflow.noBuiltInTemplates", "No built-in templates are available right now. You can still create a custom step.")} -
- ) : ( -
- {templates.map((template) => { - const IconComponent = getTemplateIcon(template.icon); - const categoryClassName = getCategoryClassName(template.category); - const isAdding = addingTemplateId === template.id; - - return ( -
-
-
- -
- -
-
- {template.name} - {template.category} - {pluginTemplateOwners[template.id] && ( - - {pluginTemplateOwners[template.id]} - - )} -
-
{template.description}
- -
-
-
- ); - })} -
- )} -
- )} - - {/* Edit / Create form */} - {isEditing && ( -
-

- {isCreating ? t("workflow.newStepTitle", "New Workflow Step") : t("workflow.editStepTitle", "Edit Workflow Step")} -

- -
- {/* Name */} -
- - setForm((prev) => ({ ...prev, name: e.target.value }))} - placeholder={t("workflow.formNamePlaceholder", "e.g. Documentation Review")} - data-testid="workflow-step-name" - /> -
- - {/* Description */} -
- -