mirror of https://github.com/kortix-ai/suna.git
feat: workflow ajustments
This commit is contained in:
parent
6148b199ae
commit
8ee6d79858
|
@ -23,6 +23,10 @@ interface UseWorkflowStepsProps {
|
|||
setSelectedStep: (step: ConditionalStep | null) => void;
|
||||
setInsertIndex: (index: number) => void;
|
||||
setSearchQuery: (query: string) => void;
|
||||
selectedStep: ConditionalStep | null;
|
||||
insertIndex: number;
|
||||
parentStepId: string | null;
|
||||
setParentStepId: (id: string | null) => void;
|
||||
}
|
||||
|
||||
const STEP_CATEGORIES = CATEGORY_DEFINITIONS;
|
||||
|
@ -65,7 +69,11 @@ export function useWorkflowSteps({
|
|||
setPanelMode,
|
||||
setSelectedStep,
|
||||
setInsertIndex,
|
||||
setSearchQuery
|
||||
setSearchQuery,
|
||||
selectedStep,
|
||||
insertIndex,
|
||||
parentStepId,
|
||||
setParentStepId
|
||||
}: UseWorkflowStepsProps) {
|
||||
const generateId = () => Math.random().toString(36).substr(2, 9);
|
||||
|
||||
|
@ -81,18 +89,20 @@ export function useWorkflowSteps({
|
|||
}));
|
||||
}, [agentTools]);
|
||||
|
||||
const handleAddStep = useCallback((index: number) => {
|
||||
const handleAddStep = useCallback((index: number, parentId?: string) => {
|
||||
setInsertIndex(index);
|
||||
setPanelMode('add');
|
||||
setSelectedStep(null);
|
||||
setParentStepId(parentId || null);
|
||||
setIsPanelOpen(true);
|
||||
}, [setInsertIndex, setPanelMode, setSelectedStep, setIsPanelOpen]);
|
||||
}, [setInsertIndex, setPanelMode, setSelectedStep, setParentStepId, setIsPanelOpen]);
|
||||
|
||||
const handleEditStep = useCallback((step: ConditionalStep) => {
|
||||
setSelectedStep(step);
|
||||
setPanelMode('edit');
|
||||
setParentStepId(null);
|
||||
setIsPanelOpen(true);
|
||||
}, [setSelectedStep, setPanelMode, setIsPanelOpen]);
|
||||
}, [setSelectedStep, setPanelMode, setParentStepId, setIsPanelOpen]);
|
||||
|
||||
const handleCreateStep = useCallback((stepType: StepType) => {
|
||||
const newStep: ConditionalStep = {
|
||||
|
@ -113,34 +123,103 @@ export function useWorkflowSteps({
|
|||
};
|
||||
}
|
||||
|
||||
const newSteps = [...steps];
|
||||
newSteps.push(newStep);
|
||||
// Check if we're adding a child step to a condition
|
||||
if (parentStepId) {
|
||||
// Find the parent step and add the new step as its child
|
||||
const findAndUpdateStep = (stepsArray: ConditionalStep[]): ConditionalStep[] => {
|
||||
return stepsArray.map(step => {
|
||||
if (step.id === parentStepId) {
|
||||
return {
|
||||
...step,
|
||||
children: [...(step.children || []), { ...newStep, order: step.children?.length || 0 }]
|
||||
};
|
||||
}
|
||||
// Also check nested children for conditional steps
|
||||
if (step.children && step.children.length > 0) {
|
||||
return {
|
||||
...step,
|
||||
children: findAndUpdateStep(step.children)
|
||||
};
|
||||
}
|
||||
return step;
|
||||
});
|
||||
};
|
||||
|
||||
// Update order values
|
||||
newSteps.forEach((step, idx) => {
|
||||
step.order = idx;
|
||||
});
|
||||
const newSteps = findAndUpdateStep(steps);
|
||||
onStepsChange(newSteps);
|
||||
} else {
|
||||
// Regular step addition to main workflow
|
||||
const newSteps = [...steps];
|
||||
if (insertIndex >= 0 && insertIndex < steps.length) {
|
||||
newSteps.splice(insertIndex, 0, newStep);
|
||||
} else {
|
||||
newSteps.push(newStep);
|
||||
}
|
||||
|
||||
onStepsChange(newSteps);
|
||||
// Update order values
|
||||
newSteps.forEach((step, idx) => {
|
||||
step.order = idx;
|
||||
});
|
||||
|
||||
onStepsChange(newSteps);
|
||||
}
|
||||
|
||||
// Instead of closing panel, switch to edit mode with the new step
|
||||
// Switch to edit mode for the newly created step
|
||||
setSelectedStep(newStep);
|
||||
setPanelMode('edit');
|
||||
setParentStepId(null);
|
||||
setSearchQuery('');
|
||||
}, [steps, onStepsChange, setSelectedStep, setPanelMode, setSearchQuery]);
|
||||
}, [steps, onStepsChange, parentStepId, insertIndex, setSelectedStep, setPanelMode, setParentStepId, setSearchQuery]);
|
||||
|
||||
const handleUpdateStep = useCallback((updates: Partial<ConditionalStep>) => {
|
||||
const newSteps = steps.map(step =>
|
||||
step.id === updates.id ? { ...step, ...updates } : step
|
||||
);
|
||||
// Recursive function to update steps at any level
|
||||
const updateStepRecursive = (stepsArray: ConditionalStep[]): ConditionalStep[] => {
|
||||
return stepsArray.map(step => {
|
||||
if (step.id === updates.id) {
|
||||
return { ...step, ...updates };
|
||||
}
|
||||
// Check nested children for conditional steps
|
||||
if (step.children && step.children.length > 0) {
|
||||
return {
|
||||
...step,
|
||||
children: updateStepRecursive(step.children)
|
||||
};
|
||||
}
|
||||
return step;
|
||||
});
|
||||
};
|
||||
|
||||
const newSteps = updateStepRecursive(steps);
|
||||
onStepsChange(newSteps);
|
||||
}, [steps, onStepsChange]);
|
||||
|
||||
// Update the selected step if it's the one being updated
|
||||
if (updates.id === selectedStep?.id) {
|
||||
setSelectedStep({ ...selectedStep, ...updates });
|
||||
}
|
||||
}, [steps, onStepsChange, selectedStep, setSelectedStep]);
|
||||
|
||||
const handleDeleteStep = useCallback((stepId: string) => {
|
||||
const newSteps = steps.filter(step => step.id !== stepId);
|
||||
// Recursive function to delete steps at any level
|
||||
const deleteStepRecursive = (stepsArray: ConditionalStep[]): ConditionalStep[] => {
|
||||
const filtered = stepsArray.filter(step => step.id !== stepId);
|
||||
return filtered.map(step => {
|
||||
if (step.children && step.children.length > 0) {
|
||||
return {
|
||||
...step,
|
||||
children: deleteStepRecursive(step.children)
|
||||
};
|
||||
}
|
||||
return step;
|
||||
});
|
||||
};
|
||||
|
||||
const newSteps = deleteStepRecursive(steps);
|
||||
|
||||
// Update order values for top-level steps
|
||||
newSteps.forEach((step, idx) => {
|
||||
step.order = idx;
|
||||
});
|
||||
|
||||
onStepsChange(newSteps);
|
||||
setIsPanelOpen(false);
|
||||
}, [steps, onStepsChange, setIsPanelOpen]);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Plus, AlertTriangle, GripVertical } from 'lucide-react';
|
||||
import { Plus, AlertTriangle, GripVertical, Settings } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Input } from '@/components/ui/input';
|
||||
|
@ -15,28 +15,54 @@ import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
|
|||
interface ConditionalGroupProps {
|
||||
conditionSteps: ConditionalStep[];
|
||||
groupKey: string;
|
||||
onUpdate: (updates: Partial<ConditionalStep>) => void;
|
||||
onUpdateStep: (updates: Partial<ConditionalStep>) => void;
|
||||
onAddElse: (afterStepId: string) => void;
|
||||
onAddElseIf: (afterStepId: string) => void;
|
||||
onRemove: (stepId: string) => void;
|
||||
onEdit: (step: ConditionalStep) => void;
|
||||
onAddStep: (index: number, parentStepId?: string) => void;
|
||||
agentTools?: any;
|
||||
isLoadingTools?: boolean;
|
||||
}
|
||||
|
||||
const MAX_ELSE_IF_CONDITIONS = 5; // Limit the number of else-if conditions
|
||||
|
||||
export function ConditionalGroup({
|
||||
conditionSteps,
|
||||
groupKey,
|
||||
onUpdate,
|
||||
onUpdateStep,
|
||||
onAddElse,
|
||||
onAddElseIf,
|
||||
onRemove,
|
||||
onEdit,
|
||||
onAddStep,
|
||||
agentTools,
|
||||
isLoadingTools
|
||||
}: ConditionalGroupProps) {
|
||||
const [activeConditionTab, setActiveConditionTab] = useState<string>(conditionSteps[0]?.id || '');
|
||||
|
||||
// Ensure we always have an "else" step at the end
|
||||
const hasElse = conditionSteps.some(step => step.conditions?.type === 'else');
|
||||
const elseIfCount = conditionSteps.filter(step => step.conditions?.type === 'elseif').length;
|
||||
const canAddElseIf = !hasElse && elseIfCount < MAX_ELSE_IF_CONDITIONS;
|
||||
|
||||
// Create a virtual else step if none exists to show the tab
|
||||
const displaySteps = React.useMemo(() => {
|
||||
if (!hasElse) {
|
||||
const virtualElse: ConditionalStep = {
|
||||
id: 'virtual-else',
|
||||
name: 'Else',
|
||||
description: '',
|
||||
type: 'condition',
|
||||
config: {},
|
||||
conditions: { type: 'else' },
|
||||
children: [],
|
||||
order: conditionSteps.length
|
||||
};
|
||||
return [...conditionSteps, virtualElse];
|
||||
}
|
||||
return conditionSteps;
|
||||
}, [conditionSteps, hasElse]);
|
||||
|
||||
const {
|
||||
attributes,
|
||||
|
@ -56,7 +82,25 @@ export function ConditionalGroup({
|
|||
return String.fromCharCode(65 + index);
|
||||
};
|
||||
|
||||
const activeStep = conditionSteps.find(s => s.id === activeConditionTab) || conditionSteps[0];
|
||||
// Ensure we have a valid active tab when steps change
|
||||
React.useEffect(() => {
|
||||
if (!displaySteps.find(s => s.id === activeConditionTab)) {
|
||||
setActiveConditionTab(displaySteps[0]?.id || '');
|
||||
}
|
||||
}, [displaySteps, activeConditionTab]);
|
||||
|
||||
const activeStep = displaySteps.find(s => s.id === activeConditionTab) || displaySteps[0];
|
||||
|
||||
// Handle clicking add step button
|
||||
const handleAddStepClick = React.useCallback(() => {
|
||||
if (activeStep?.id === 'virtual-else') {
|
||||
// If clicking on virtual else, create a real else step first
|
||||
onAddElse(conditionSteps[conditionSteps.length - 1].id);
|
||||
} else {
|
||||
// Call onAddStep with index and parent step ID
|
||||
onAddStep(-1, activeStep?.id);
|
||||
}
|
||||
}, [activeStep, onAddElse, conditionSteps, onAddStep]);
|
||||
|
||||
return (
|
||||
<div ref={setNodeRef} style={style} className={cn(isDragging && "opacity-50")}>
|
||||
|
@ -71,58 +115,56 @@ export function ConditionalGroup({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Gear icon for editing - appears on hover */}
|
||||
<div className="absolute top-4 right-4 z-10 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onEdit(activeStep)}
|
||||
className="h-8 w-8 p-0"
|
||||
>
|
||||
<Settings className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Condition Tabs */}
|
||||
<div className="flex items-center gap-2 flex-wrap pl-5">
|
||||
{conditionSteps.map((step, index) => {
|
||||
<div className="flex items-center gap-2 flex-wrap pl-5 pr-12">
|
||||
{displaySteps.map((step, index) => {
|
||||
const letter = getConditionLetter(index);
|
||||
const isActive = step.id === activeConditionTab;
|
||||
const conditionType = step.conditions?.type === 'if' ? 'If' :
|
||||
step.conditions?.type === 'elseif' ? 'Else If' :
|
||||
step.conditions?.type === 'else' ? 'Else' : 'If';
|
||||
|
||||
return (
|
||||
<button
|
||||
<Button
|
||||
key={step.id}
|
||||
onClick={() => setActiveConditionTab(step.id)}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium transition-all",
|
||||
"h-9 px-3 border border-dashed text-xs",
|
||||
isActive
|
||||
? "bg-blue-500 text-white border-blue-500 shadow-sm"
|
||||
: "bg-white dark:bg-zinc-800 border-zinc-200 dark:border-zinc-700 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-700"
|
||||
? "bg-blue-500 hover:bg-blue-600 "
|
||||
: "bg-white dark:bg-zinc-800 border-dashed border-zinc-300 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 hover:border-zinc-400 dark:hover:border-zinc-500 hover:bg-zinc-50 dark:hover:bg-zinc-700"
|
||||
)}
|
||||
>
|
||||
|
||||
<span className="font-mono text-xs font-bold">{letter}</span>
|
||||
<span>•</span>
|
||||
<span>{conditionType}</span>
|
||||
{step.hasIssues && (
|
||||
<AlertTriangle className="h-3 w-3 text-red-500" />
|
||||
)}
|
||||
</button>
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Add Else If button */}
|
||||
{!hasElse && (
|
||||
{/* Plus button to add Else If - always show next to Else if we can add more */}
|
||||
{canAddElseIf && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onAddElseIf(conditionSteps[conditionSteps.length - 1].id)}
|
||||
className="h-9 px-3 border-dashed text-xs"
|
||||
className="h-6 w-6 p-0 border border-dashed rounded-md border-zinc-300 dark:border-zinc-600 hover:border-zinc-400 dark:hover:border-zinc-500 bg-white dark:bg-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors"
|
||||
>
|
||||
<Plus className="h-3 w-3 mr-1" />
|
||||
Else If
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Add Else button */}
|
||||
{!hasElse && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onAddElse(conditionSteps[conditionSteps.length - 1].id)}
|
||||
className="h-9 px-3 border-dashed text-xs"
|
||||
>
|
||||
<Plus className="h-3 w-3 mr-1" />
|
||||
Else
|
||||
<Plus className="h-3 w-3 text-zinc-600 dark:text-zinc-400" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
@ -130,30 +172,8 @@ export function ConditionalGroup({
|
|||
{/* Active condition content */}
|
||||
{activeStep && (
|
||||
<div className="bg-white dark:bg-zinc-800 rounded-2xl p-4 border border-zinc-200 dark:border-zinc-700">
|
||||
{(activeStep.conditions?.type === 'if' || activeStep.conditions?.type === 'elseif') ? (
|
||||
<div className="space-y-3">
|
||||
<Label className="text-sm font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{activeStep.conditions?.type === 'if' ? 'If condition' : 'Else if condition'}
|
||||
</Label>
|
||||
<Input
|
||||
type="text"
|
||||
value={activeStep.conditions.expression || ''}
|
||||
onChange={(e) => onUpdate({
|
||||
id: activeStep.id,
|
||||
conditions: { ...activeStep.conditions, expression: e.target.value }
|
||||
})}
|
||||
placeholder="e.g., user asks about pricing"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-sm text-zinc-600 dark:text-zinc-400 font-medium">
|
||||
Otherwise (fallback condition)
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Steps within this condition */}
|
||||
<div className="mt-4 space-y-3">
|
||||
<div className="space-y-3">
|
||||
{activeStep.children && activeStep.children.length > 0 ? (
|
||||
<SortableContext items={activeStep.children.map(child => child.id)} strategy={verticalListSortingStrategy}>
|
||||
<div className="space-y-2">
|
||||
|
@ -164,7 +184,7 @@ export function ConditionalGroup({
|
|||
stepNumber={index + 1}
|
||||
isNested={true}
|
||||
onEdit={onEdit}
|
||||
onUpdate={onUpdate}
|
||||
onUpdateStep={onUpdateStep}
|
||||
agentTools={agentTools}
|
||||
isLoadingTools={isLoadingTools}
|
||||
/>
|
||||
|
@ -177,19 +197,12 @@ export function ConditionalGroup({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Add step button */}
|
||||
{/* Add step button - triggers special edit mode for adding child steps */}
|
||||
<div className="flex justify-center pt-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onEdit({
|
||||
id: 'new',
|
||||
name: 'New Step',
|
||||
description: '',
|
||||
type: 'instruction',
|
||||
config: {},
|
||||
order: activeStep.children?.length || 0
|
||||
} as ConditionalStep)}
|
||||
onClick={handleAddStepClick}
|
||||
className="border-dashed text-xs"
|
||||
>
|
||||
<Plus className="h-3 w-3 mr-1" />
|
||||
|
|
|
@ -15,7 +15,7 @@ interface StepCardProps {
|
|||
stepNumber: number;
|
||||
isNested?: boolean;
|
||||
onEdit: (step: ConditionalStep) => void;
|
||||
onUpdate: (updates: Partial<ConditionalStep>) => void;
|
||||
onUpdateStep: (updates: Partial<ConditionalStep>) => void;
|
||||
agentTools?: any;
|
||||
isLoadingTools?: boolean;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export function StepCard({
|
|||
stepNumber,
|
||||
isNested = false,
|
||||
onEdit,
|
||||
onUpdate,
|
||||
onUpdateStep,
|
||||
agentTools,
|
||||
isLoadingTools
|
||||
}: StepCardProps) {
|
||||
|
@ -44,12 +44,53 @@ export function StepCard({
|
|||
};
|
||||
|
||||
const getStepIcon = (step: ConditionalStep) => {
|
||||
const stepType = {
|
||||
category: step.config?.tool_type === 'agentpress' || step.config?.tool_type === 'mcp' ? 'tools' :
|
||||
step.type === 'condition' ? 'conditions' : 'actions',
|
||||
config: step.config,
|
||||
icon: step.type === 'condition' ? 'Settings' : 'FileText'
|
||||
};
|
||||
// Determine the proper step type based on the step configuration
|
||||
let stepType;
|
||||
|
||||
if (step.type === 'condition') {
|
||||
stepType = {
|
||||
category: 'conditions',
|
||||
config: step.config,
|
||||
icon: 'Settings'
|
||||
};
|
||||
} else if (step.config?.tool_type === 'agentpress' || step.config?.tool_type === 'mcp') {
|
||||
stepType = {
|
||||
category: 'tools',
|
||||
config: step.config,
|
||||
icon: 'FileText'
|
||||
};
|
||||
} else if (step.config?.step_type === 'mcp_configuration') {
|
||||
stepType = {
|
||||
category: 'configuration',
|
||||
config: { step_type: 'mcp_configuration' },
|
||||
icon: 'Cog'
|
||||
};
|
||||
} else if (step.config?.step_type === 'credentials_profile') {
|
||||
stepType = {
|
||||
category: 'configuration',
|
||||
config: { step_type: 'credentials_profile' },
|
||||
icon: 'Key'
|
||||
};
|
||||
} else if (step.type === 'instruction') {
|
||||
stepType = {
|
||||
category: 'actions',
|
||||
config: step.config,
|
||||
icon: 'FileText'
|
||||
};
|
||||
} else if (step.type === 'sequence') {
|
||||
stepType = {
|
||||
category: 'actions',
|
||||
config: step.config,
|
||||
icon: 'GitBranch'
|
||||
};
|
||||
} else {
|
||||
// Default fallback
|
||||
stepType = {
|
||||
category: 'actions',
|
||||
config: step.config,
|
||||
icon: 'FileText'
|
||||
};
|
||||
}
|
||||
|
||||
const { icon: IconComponent, color } = getStepIconAndColor(stepType);
|
||||
return (
|
||||
|
@ -90,7 +131,7 @@ export function StepCard({
|
|||
</div>
|
||||
)}
|
||||
{step.config?.tool_name && (
|
||||
<Badge variant="secondary" className="mt-1 text-xs">
|
||||
<Badge variant="default" className="mt-1 text-xs">
|
||||
{step.config.tool_name}
|
||||
</Badge>
|
||||
)}
|
||||
|
|
|
@ -27,12 +27,13 @@ import {
|
|||
|
||||
interface WorkflowStepsProps {
|
||||
steps: ConditionalStep[];
|
||||
onAddStep: (index: number) => void;
|
||||
onAddStep: (index: number, parentStepId?: string) => void;
|
||||
onEditStep: (step: ConditionalStep) => void;
|
||||
onUpdateStep: (updates: Partial<ConditionalStep>) => void;
|
||||
onDeleteStep: (stepId: string) => void;
|
||||
onAddElseIf: (afterStepId: string) => void;
|
||||
onAddElse: (afterStepId: string) => void;
|
||||
onStepsChange: (steps: ConditionalStep[]) => void;
|
||||
agentTools?: any;
|
||||
isLoadingTools?: boolean;
|
||||
}
|
||||
|
@ -45,6 +46,7 @@ export function WorkflowSteps({
|
|||
onDeleteStep,
|
||||
onAddElseIf,
|
||||
onAddElse,
|
||||
onStepsChange,
|
||||
agentTools,
|
||||
isLoadingTools
|
||||
}: WorkflowStepsProps) {
|
||||
|
@ -123,8 +125,7 @@ export function WorkflowSteps({
|
|||
});
|
||||
|
||||
// Call the parent's onStepsChange
|
||||
// This would need to be passed down from the parent
|
||||
// For now, we'll just update the local state
|
||||
onStepsChange(newSteps);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -145,11 +146,12 @@ export function WorkflowSteps({
|
|||
<ConditionalGroup
|
||||
conditionSteps={item}
|
||||
groupKey={`condition-group-${index}`}
|
||||
onUpdate={onUpdateStep}
|
||||
onUpdateStep={onUpdateStep}
|
||||
onAddElse={onAddElse}
|
||||
onAddElseIf={onAddElseIf}
|
||||
onRemove={onDeleteStep}
|
||||
onEdit={onEditStep}
|
||||
onAddStep={onAddStep}
|
||||
agentTools={agentTools}
|
||||
isLoadingTools={isLoadingTools}
|
||||
/>
|
||||
|
@ -159,7 +161,7 @@ export function WorkflowSteps({
|
|||
step={item}
|
||||
stepNumber={index + 1}
|
||||
onEdit={onEditStep}
|
||||
onUpdate={onUpdateStep}
|
||||
onUpdateStep={onUpdateStep}
|
||||
agentTools={agentTools}
|
||||
isLoadingTools={isLoadingTools}
|
||||
/>
|
||||
|
@ -211,7 +213,7 @@ export function WorkflowSteps({
|
|||
step={activeStep}
|
||||
stepNumber={1}
|
||||
onEdit={() => { }}
|
||||
onUpdate={() => { }}
|
||||
onUpdateStep={() => { }}
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
|
|
|
@ -53,6 +53,7 @@ export function WorkflowBuilder({
|
|||
const [selectedStep, setSelectedStep] = useState<ConditionalStep | null>(null);
|
||||
const [insertIndex, setInsertIndex] = useState<number>(-1);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [parentStepId, setParentStepId] = useState<string | null>(null);
|
||||
|
||||
const {
|
||||
handleAddStep,
|
||||
|
@ -72,7 +73,11 @@ export function WorkflowBuilder({
|
|||
setPanelMode,
|
||||
setSelectedStep,
|
||||
setInsertIndex,
|
||||
setSearchQuery
|
||||
setSearchQuery,
|
||||
selectedStep,
|
||||
insertIndex,
|
||||
parentStepId,
|
||||
setParentStepId
|
||||
});
|
||||
|
||||
const handleToggleSidePanel = () => {
|
||||
|
@ -98,7 +103,7 @@ export function WorkflowBuilder({
|
|||
onDeleteStep={handleDeleteStep}
|
||||
isLoadingTools={isLoadingTools}
|
||||
>
|
||||
<div className="h-full bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="min-h-full bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="mx-auto max-w-3xl md:px-8 min-w-0 py-8">
|
||||
{steps.length === 0 ? (
|
||||
// Empty state
|
||||
|
@ -130,6 +135,7 @@ export function WorkflowBuilder({
|
|||
onDeleteStep={handleDeleteStep}
|
||||
onAddElseIf={handleAddElseIf}
|
||||
onAddElse={handleAddElse}
|
||||
onStepsChange={onStepsChange}
|
||||
agentTools={agentTools}
|
||||
isLoadingTools={isLoadingTools}
|
||||
/>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FileText, Terminal, Rocket, Computer, Eye, Search, Globe, GitBranch, Settings, MonitorPlay } from 'lucide-react';
|
||||
import { FileText, Terminal, Rocket, Computer, Eye, Search, Globe, GitBranch, Settings, MonitorPlay, Cog, Key } from 'lucide-react';
|
||||
|
||||
export interface StepDefinition {
|
||||
id: string;
|
||||
|
@ -44,6 +44,8 @@ export const TOOL_COLORS: Record<string, string> = {
|
|||
export const ACTION_ICONS: Record<string, any> = {
|
||||
'FileText': FileText,
|
||||
'GitBranch': GitBranch,
|
||||
'Cog': Cog,
|
||||
'Key': Key,
|
||||
};
|
||||
|
||||
// Base step definitions
|
||||
|
@ -72,6 +74,24 @@ export const BASE_STEP_DEFINITIONS: StepDefinition[] = [
|
|||
category: 'conditions',
|
||||
color: 'from-orange-500/20 to-orange-600/10 border-orange-500/20 text-orange-500'
|
||||
},
|
||||
{
|
||||
id: 'mcp_configuration',
|
||||
name: 'MCP Configuration',
|
||||
description: 'Configure MCP server connections and settings',
|
||||
icon: Cog,
|
||||
category: 'configuration',
|
||||
color: 'from-indigo-500/20 to-indigo-600/10 border-indigo-500/20 text-indigo-500',
|
||||
config: { step_type: 'mcp_configuration' }
|
||||
},
|
||||
{
|
||||
id: 'credentials_profile',
|
||||
name: 'Credentials Profile',
|
||||
description: 'Select and configure credential profiles for authentication',
|
||||
icon: Key,
|
||||
category: 'configuration',
|
||||
color: 'from-emerald-500/20 to-emerald-600/10 border-emerald-500/20 text-emerald-500',
|
||||
config: { step_type: 'credentials_profile' }
|
||||
},
|
||||
];
|
||||
|
||||
// Category definitions
|
||||
|
@ -86,6 +106,11 @@ export const CATEGORY_DEFINITIONS: CategoryDefinition[] = [
|
|||
name: 'Conditions',
|
||||
description: 'Add logic and branching'
|
||||
},
|
||||
{
|
||||
id: 'configuration',
|
||||
name: 'Configuration',
|
||||
description: 'Setup and configure services'
|
||||
},
|
||||
{
|
||||
id: 'tools',
|
||||
name: 'Tools',
|
||||
|
@ -131,6 +156,14 @@ export function getStepIconAndColor(stepType: any): { icon: any; color: string }
|
|||
return { icon, color };
|
||||
} else if (stepType.category === 'conditions') {
|
||||
return { icon: Settings, color: 'from-orange-500/20 to-orange-600/10 border-orange-500/20 text-orange-500' };
|
||||
} else if (stepType.category === 'configuration') {
|
||||
const stepType_id = stepType.config?.step_type || stepType.id;
|
||||
if (stepType_id === 'mcp_configuration') {
|
||||
return { icon: Cog, color: 'from-indigo-500/20 to-indigo-600/10 border-indigo-500/20 text-indigo-500' };
|
||||
} else if (stepType_id === 'credentials_profile') {
|
||||
return { icon: Key, color: 'from-emerald-500/20 to-emerald-600/10 border-emerald-500/20 text-emerald-500' };
|
||||
}
|
||||
return { icon: Cog, color: 'from-indigo-500/20 to-indigo-600/10 border-indigo-500/20 text-indigo-500' };
|
||||
} else {
|
||||
const icon = ACTION_ICONS[stepType.icon] || FileText;
|
||||
return { icon, color: 'from-gray-500/20 to-gray-600/10 border-gray-500/20 text-gray-500' };
|
||||
|
|
|
@ -60,7 +60,7 @@ export function WorkflowLayout({
|
|||
isSaving={isSaving}
|
||||
/>
|
||||
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="flex-1 overflow-y-auto overflow-x-hidden">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -159,7 +159,7 @@ export function WorkflowSidePanel({
|
|||
<Input
|
||||
id="step-name"
|
||||
value={selectedStep.name}
|
||||
onChange={(e) => onUpdateStep({ name: e.target.value })}
|
||||
onChange={(e) => onUpdateStep({ id: selectedStep.id, name: e.target.value })}
|
||||
placeholder="Step name"
|
||||
className="mt-1"
|
||||
/>
|
||||
|
@ -170,7 +170,7 @@ export function WorkflowSidePanel({
|
|||
<Textarea
|
||||
id="step-description"
|
||||
value={selectedStep.description}
|
||||
onChange={(e) => onUpdateStep({ description: e.target.value })}
|
||||
onChange={(e) => onUpdateStep({ id: selectedStep.id, description: e.target.value })}
|
||||
placeholder="What should this step do?"
|
||||
rows={3}
|
||||
className="mt-1 resize-none"
|
||||
|
@ -197,6 +197,7 @@ export function WorkflowSidePanel({
|
|||
id="condition-expression"
|
||||
value={selectedStep.conditions?.expression || ''}
|
||||
onChange={(e) => onUpdateStep({
|
||||
id: selectedStep.id,
|
||||
conditions: {
|
||||
...selectedStep.conditions,
|
||||
type: 'if' as const,
|
||||
|
|
Loading…
Reference in New Issue