@iblai/web-containers 1.52.0 → 1.52.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/chat/loading-message.js +13 -0
- package/dist/cjs/components/chat/loading-message.js.map +1 -0
- package/dist/cjs/components/mentors/mentor-selection-grid.js +4 -2
- package/dist/cjs/components/mentors/mentor-selection-grid.js.map +1 -1
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-management-content.js +9 -5
- package/dist/cjs/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-management-content.js.map +1 -1
- package/dist/cjs/components/workflows/action-tooltip.js +11 -0
- package/dist/cjs/components/workflows/action-tooltip.js.map +1 -0
- package/dist/cjs/components/workflows/node-config-panel.js +576 -0
- package/dist/cjs/components/workflows/node-config-panel.js.map +1 -0
- package/dist/cjs/components/workflows/tool-dialogs.js +95 -60
- package/dist/cjs/components/workflows/tool-dialogs.js.map +1 -1
- package/dist/cjs/components/workflows/workflow-canvas.js +1028 -0
- package/dist/cjs/components/workflows/workflow-canvas.js.map +1 -0
- package/dist/cjs/components/workflows/workflow-definition.js +369 -0
- package/dist/cjs/components/workflows/workflow-definition.js.map +1 -0
- package/dist/cjs/components/workflows/workflow-editor.js +523 -0
- package/dist/cjs/components/workflows/workflow-editor.js.map +1 -0
- package/dist/cjs/components/workflows/workflow-preview-chat.js +57 -0
- package/dist/cjs/components/workflows/workflow-preview-chat.js.map +1 -0
- package/dist/cjs/components/workflows/workflow-sidebar.js +2 -2
- package/dist/cjs/components/workflows/workflow-sidebar.js.map +1 -1
- package/dist/cjs/components/workflows/workflows-list.js +115 -0
- package/dist/cjs/components/workflows/workflows-list.js.map +1 -0
- package/dist/cjs/i18n/messages/en.json.js +207 -18
- package/dist/cjs/i18n/messages/en.json.js.map +1 -1
- package/dist/cjs/i18n/messages/es.json.js +201 -12
- package/dist/cjs/i18n/messages/es.json.js.map +1 -1
- package/dist/cjs/i18n/messages/fr.json.js +201 -12
- package/dist/cjs/i18n/messages/fr.json.js.map +1 -1
- package/dist/cjs/i18n/messages/zh.json.js +201 -12
- package/dist/cjs/i18n/messages/zh.json.js.map +1 -1
- package/dist/cjs/index.js +17 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/next/index.js +10 -0
- package/dist/cjs/next/index.js.map +1 -1
- package/dist/cjs/web-containers/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/components/chat/loading-message.js +11 -0
- package/dist/esm/components/chat/loading-message.js.map +1 -0
- package/dist/esm/components/mentors/mentor-selection-grid.js +4 -2
- package/dist/esm/components/mentors/mentor-selection-grid.js.map +1 -1
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-management-content.js +9 -5
- package/dist/esm/components/modals/edit-mentor-modal/tabs/mcp-tab/connector-management-content.js.map +1 -1
- package/dist/esm/components/workflows/action-tooltip.js +9 -0
- package/dist/esm/components/workflows/action-tooltip.js.map +1 -0
- package/dist/esm/components/workflows/node-config-panel.js +574 -0
- package/dist/esm/components/workflows/node-config-panel.js.map +1 -0
- package/dist/esm/components/workflows/tool-dialogs.js +96 -61
- package/dist/esm/components/workflows/tool-dialogs.js.map +1 -1
- package/dist/esm/components/workflows/workflow-canvas.js +1026 -0
- package/dist/esm/components/workflows/workflow-canvas.js.map +1 -0
- package/dist/esm/components/workflows/workflow-definition.js +352 -0
- package/dist/esm/components/workflows/workflow-definition.js.map +1 -0
- package/dist/esm/components/workflows/workflow-editor.js +521 -0
- package/dist/esm/components/workflows/workflow-editor.js.map +1 -0
- package/dist/esm/components/workflows/workflow-preview-chat.js +55 -0
- package/dist/esm/components/workflows/workflow-preview-chat.js.map +1 -0
- package/dist/esm/components/workflows/workflow-sidebar.js +2 -2
- package/dist/esm/components/workflows/workflow-sidebar.js.map +1 -1
- package/dist/esm/components/workflows/workflows-list.js +113 -0
- package/dist/esm/components/workflows/workflows-list.js.map +1 -0
- package/dist/esm/i18n/messages/en.json.js +203 -19
- package/dist/esm/i18n/messages/en.json.js.map +1 -1
- package/dist/esm/i18n/messages/es.json.js +197 -13
- package/dist/esm/i18n/messages/es.json.js.map +1 -1
- package/dist/esm/i18n/messages/fr.json.js +197 -13
- package/dist/esm/i18n/messages/fr.json.js.map +1 -1
- package/dist/esm/i18n/messages/zh.json.js +197 -13
- package/dist/esm/i18n/messages/zh.json.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/next/index.js +5 -0
- package/dist/esm/next/index.js.map +1 -1
- package/dist/esm/web-containers/tsconfig.tsbuildinfo +1 -1
- package/dist/index.d.ts +164 -19
- package/dist/next/index.d.ts +215 -3
- package/dist/package.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
3
|
+
import { useGetMentorSettingsQuery, useGetToolsQuery } from '@iblai/data-layer';
|
|
4
|
+
import { WithFormPermissions } from '@iblai/web-utils';
|
|
5
|
+
import { useT } from '../../i18n/context.js';
|
|
6
|
+
import '../../packages/i18n/dist/index.esm.js';
|
|
7
|
+
import { Button } from '../ui/button.js';
|
|
8
|
+
import { Input } from '../ui/input.js';
|
|
9
|
+
import { Label } from '../ui/label.js';
|
|
10
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '../ui/dialog.js';
|
|
11
|
+
import { Textarea } from '../ui/textarea.js';
|
|
12
|
+
import { Switch } from '../ui/switch.js';
|
|
13
|
+
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '../ui/tooltip.js';
|
|
14
|
+
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../ui/select.js';
|
|
15
|
+
import { MentorSelectionGrid } from '../mentors/mentor-selection-grid.js';
|
|
16
|
+
import { AgentSettingsProvider } from '../modals/edit-mentor-modal/agent-settings-context.js';
|
|
17
|
+
import { AgentDatasetsTab } from '../modals/edit-mentor-modal/tabs/datasets-tab.js';
|
|
18
|
+
import { McpTab } from '../modals/edit-mentor-modal/tabs/mcp-tab/index.js';
|
|
19
|
+
import { useToggleTools } from '../modals/edit-mentor-modal/tabs/tools-tab/use-toggle-tools.js';
|
|
20
|
+
import { useCopyToClipboard } from '../chat/markdown/use-copy-to-clipboard.js';
|
|
21
|
+
import { ActionTooltip } from './action-tooltip.js';
|
|
22
|
+
import { parseVariableDefault, EXPRESSION_EXAMPLES, WHILE_DEFAULT_MAX_ITERATIONS, isValidVariableName } from './workflow-definition.js';
|
|
23
|
+
import Check from '../../node_modules/.pnpm/lucide-react@0.528.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/check.js';
|
|
24
|
+
import Copy from '../../node_modules/.pnpm/lucide-react@0.528.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/copy.js';
|
|
25
|
+
import X from '../../node_modules/.pnpm/lucide-react@0.528.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/x.js';
|
|
26
|
+
import Trash2 from '../../node_modules/.pnpm/lucide-react@0.528.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/trash-2.js';
|
|
27
|
+
import Plus from '../../node_modules/.pnpm/lucide-react@0.528.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/plus.js';
|
|
28
|
+
import Info from '../../node_modules/.pnpm/lucide-react@0.528.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/info.js';
|
|
29
|
+
import Settings from '../../node_modules/.pnpm/lucide-react@0.528.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/settings.js';
|
|
30
|
+
import Plug from '../../node_modules/.pnpm/lucide-react@0.528.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/plug.js';
|
|
31
|
+
import Cable from '../../node_modules/.pnpm/lucide-react@0.528.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/cable.js';
|
|
32
|
+
|
|
33
|
+
const INPUT_VARIABLES = [{ id: '1', name: 'input', type: 'string' }];
|
|
34
|
+
const VARIABLE_TYPES = ['String', 'Number', 'Boolean', 'Object', 'List'];
|
|
35
|
+
// Blank first rows are only shown, never saved, until the user types in them.
|
|
36
|
+
const blankCondition = () => [{ id: '1', caseName: '', expression: '' }];
|
|
37
|
+
const blankTransformRow = () => [{ id: '1', target: '', expression: '' }];
|
|
38
|
+
const blankAssignment = () => [{ id: '1', variable: '', expression: '' }];
|
|
39
|
+
const DEFAULT_INSTRUCTIONS = 'You are a helpful assistant.';
|
|
40
|
+
const DEFAULT_MENTOR_LABELS = new Set([
|
|
41
|
+
'my agent',
|
|
42
|
+
'agent',
|
|
43
|
+
'agent node',
|
|
44
|
+
'my mentor',
|
|
45
|
+
'mentor',
|
|
46
|
+
'mentor node',
|
|
47
|
+
]);
|
|
48
|
+
const isDefaultMentorLabel = (label) => {
|
|
49
|
+
if (!label)
|
|
50
|
+
return true;
|
|
51
|
+
return DEFAULT_MENTOR_LABELS.has(label.trim().toLowerCase());
|
|
52
|
+
};
|
|
53
|
+
function NodeConfigPanel({ nodeId, nodeType, nodeData, onClose, onUpdateNode, onDeleteNode, workflowVariables = [], stepReferences = [], variableUsages = {}, org, defaultMentorId, username, agentSettings, }) {
|
|
54
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
55
|
+
const t = useT();
|
|
56
|
+
const [nodeName, setNodeName] = useState(nodeData.label);
|
|
57
|
+
const [instructions, setInstructions] = useState((_a = nodeData.instructions) !== null && _a !== void 0 ? _a : DEFAULT_INSTRUCTIONS);
|
|
58
|
+
const [model, setModel] = useState((_b = nodeData.model) !== null && _b !== void 0 ? _b : '');
|
|
59
|
+
const [continueOnError, setContinueOnError] = useState((_c = nodeData.continueOnError) !== null && _c !== void 0 ? _c : false);
|
|
60
|
+
const [stateVariables, setStateVariables] = useState((_d = nodeData.stateVariables) !== null && _d !== void 0 ? _d : []);
|
|
61
|
+
// Track if we're in the middle of a local update to avoid sync loops
|
|
62
|
+
const isLocalUpdate = useRef(false);
|
|
63
|
+
// Sync state with props when nodeData changes from external source
|
|
64
|
+
// This handles cases where the workflow is reloaded from API
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
67
|
+
if (isLocalUpdate.current) {
|
|
68
|
+
isLocalUpdate.current = false;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
setNodeName(nodeData.label);
|
|
72
|
+
setInstructions((_a = nodeData.instructions) !== null && _a !== void 0 ? _a : DEFAULT_INSTRUCTIONS);
|
|
73
|
+
setModel((_b = nodeData.model) !== null && _b !== void 0 ? _b : '');
|
|
74
|
+
setContinueOnError((_c = nodeData.continueOnError) !== null && _c !== void 0 ? _c : false);
|
|
75
|
+
setStateVariables((_d = nodeData.stateVariables) !== null && _d !== void 0 ? _d : []);
|
|
76
|
+
setConditions(((_e = nodeData.conditions) === null || _e === void 0 ? void 0 : _e.length) ? nodeData.conditions : blankCondition());
|
|
77
|
+
setWhileCondition((_f = nodeData.condition) !== null && _f !== void 0 ? _f : '');
|
|
78
|
+
setMaxIterations((_h = (_g = nodeData.maxIterations) === null || _g === void 0 ? void 0 : _g.toString()) !== null && _h !== void 0 ? _h : '');
|
|
79
|
+
setUserApprovalMessage((_j = nodeData.userApprovalMessage) !== null && _j !== void 0 ? _j : '');
|
|
80
|
+
setTransformExpressions(((_k = nodeData.expressions) === null || _k === void 0 ? void 0 : _k.length) ? nodeData.expressions : blankTransformRow());
|
|
81
|
+
setAssignments(((_l = nodeData.assignments) === null || _l === void 0 ? void 0 : _l.length) ? nodeData.assignments : blankAssignment());
|
|
82
|
+
setMaxIterationsBadInput(false);
|
|
83
|
+
setMcpConnectors((_m = nodeData.mcpConnectors) !== null && _m !== void 0 ? _m : []);
|
|
84
|
+
setDatasetId((_o = nodeData.datasetId) !== null && _o !== void 0 ? _o : '');
|
|
85
|
+
setDatasetName((_p = nodeData.datasetName) !== null && _p !== void 0 ? _p : '');
|
|
86
|
+
setMaxResults((_q = nodeData.maxResults) !== null && _q !== void 0 ? _q : 10);
|
|
87
|
+
setFileSearchQuery((_r = nodeData.fileSearchQuery) !== null && _r !== void 0 ? _r : '');
|
|
88
|
+
}, [nodeData]);
|
|
89
|
+
const [showAddModal, setShowAddModal] = useState(false);
|
|
90
|
+
const [selectedType, setSelectedType] = useState('String');
|
|
91
|
+
const [newVarName, setNewVarName] = useState('');
|
|
92
|
+
const [newVarDefault, setNewVarDefault] = useState('');
|
|
93
|
+
const [showMentorModal, setShowMentorModal] = useState(false);
|
|
94
|
+
const [mentorSearchQuery, setMentorSearchQuery] = useState('');
|
|
95
|
+
const [conditions, setConditions] = useState(((_e = nodeData.conditions) === null || _e === void 0 ? void 0 : _e.length) ? nodeData.conditions : blankCondition());
|
|
96
|
+
const [whileCondition, setWhileCondition] = useState((_f = nodeData.condition) !== null && _f !== void 0 ? _f : '');
|
|
97
|
+
const [maxIterations, setMaxIterations] = useState((_h = (_g = nodeData.maxIterations) === null || _g === void 0 ? void 0 : _g.toString()) !== null && _h !== void 0 ? _h : '');
|
|
98
|
+
const [userApprovalMessage, setUserApprovalMessage] = useState((_j = nodeData.userApprovalMessage) !== null && _j !== void 0 ? _j : '');
|
|
99
|
+
const [transformExpressions, setTransformExpressions] = useState(((_k = nodeData.expressions) === null || _k === void 0 ? void 0 : _k.length) ? nodeData.expressions : blankTransformRow());
|
|
100
|
+
const [assignments, setAssignments] = useState(((_l = nodeData.assignments) === null || _l === void 0 ? void 0 : _l.length) ? nodeData.assignments : blankAssignment());
|
|
101
|
+
const [newVariableRowId, setNewVariableRowId] = useState(null);
|
|
102
|
+
const [newVariableName, setNewVariableName] = useState('');
|
|
103
|
+
const [showConnectorManagement, setShowConnectorManagement] = useState(false);
|
|
104
|
+
const [mcpConnectors, setMcpConnectors] = useState((_m = nodeData.mcpConnectors) !== null && _m !== void 0 ? _m : []);
|
|
105
|
+
const [datasetId, setDatasetId] = useState((_o = nodeData.datasetId) !== null && _o !== void 0 ? _o : '');
|
|
106
|
+
const [datasetName, setDatasetName] = useState((_p = nodeData.datasetName) !== null && _p !== void 0 ? _p : '');
|
|
107
|
+
const [maxResults, setMaxResults] = useState((_q = nodeData.maxResults) !== null && _q !== void 0 ? _q : 10);
|
|
108
|
+
const [fileSearchQuery, setFileSearchQuery] = useState((_r = nodeData.fileSearchQuery) !== null && _r !== void 0 ? _r : '');
|
|
109
|
+
const [showDatasetDialog, setShowDatasetDialog] = useState(false);
|
|
110
|
+
const pickerSettings = {
|
|
111
|
+
tenantKey: org !== null && org !== void 0 ? org : '',
|
|
112
|
+
mentorId: defaultMentorId !== null && defaultMentorId !== void 0 ? defaultMentorId : '',
|
|
113
|
+
username: username !== null && username !== void 0 ? username : '',
|
|
114
|
+
enableRBAC: (_s = agentSettings === null || agentSettings === void 0 ? void 0 : agentSettings.enableRBAC) !== null && _s !== void 0 ? _s : false,
|
|
115
|
+
rbacPermissions: agentSettings === null || agentSettings === void 0 ? void 0 : agentSettings.rbacPermissions,
|
|
116
|
+
executeGatedAction: agentSettings === null || agentSettings === void 0 ? void 0 : agentSettings.executeGatedAction,
|
|
117
|
+
dmUrl: agentSettings === null || agentSettings === void 0 ? void 0 : agentSettings.dmUrl,
|
|
118
|
+
};
|
|
119
|
+
const selectedMentorId = (_t = nodeData.mentor_id) !== null && _t !== void 0 ? _t : nodeData.entry_mentor_id;
|
|
120
|
+
const shouldFetchMentorSettings = Boolean(nodeType === 'mentor' && selectedMentorId && org && username);
|
|
121
|
+
const { data: mentorSettings, isLoading: isMentorSettingsLoading } = useGetMentorSettingsQuery({
|
|
122
|
+
mentor: selectedMentorId !== null && selectedMentorId !== void 0 ? selectedMentorId : '',
|
|
123
|
+
org: org !== null && org !== void 0 ? org : '',
|
|
124
|
+
}, { skip: !shouldFetchMentorSettings });
|
|
125
|
+
const { data: tools, isLoading: isToolsLoading } = useGetToolsQuery({
|
|
126
|
+
mentor: selectedMentorId !== null && selectedMentorId !== void 0 ? selectedMentorId : '',
|
|
127
|
+
org: org !== null && org !== void 0 ? org : '',
|
|
128
|
+
}, { skip: !shouldFetchMentorSettings });
|
|
129
|
+
const mentorToolSlugs = Array.isArray(mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.mentor_tools)
|
|
130
|
+
? mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.mentor_tools.map((tool) => tool === null || tool === void 0 ? void 0 : tool.slug).filter((slug) => Boolean(slug))
|
|
131
|
+
: [];
|
|
132
|
+
const { toggleTools, isLoading: isToggleToolsLoading } = useToggleTools({
|
|
133
|
+
tools: mentorToolSlugs,
|
|
134
|
+
activeMentorId: selectedMentorId !== null && selectedMentorId !== void 0 ? selectedMentorId : '',
|
|
135
|
+
tenantKey: org !== null && org !== void 0 ? org : '',
|
|
136
|
+
username: username !== null && username !== void 0 ? username : '',
|
|
137
|
+
});
|
|
138
|
+
const isToolsDisabled = !selectedMentorId ||
|
|
139
|
+
!org ||
|
|
140
|
+
!username ||
|
|
141
|
+
isMentorSettingsLoading ||
|
|
142
|
+
isToolsLoading ||
|
|
143
|
+
isToggleToolsLoading;
|
|
144
|
+
const forceMentorPrefill = useRef(false);
|
|
145
|
+
const updateNodeConfig = useCallback((updates) => {
|
|
146
|
+
if (onUpdateNode) {
|
|
147
|
+
// Mark that this is a local update to prevent sync effect from resetting state
|
|
148
|
+
isLocalUpdate.current = true;
|
|
149
|
+
onUpdateNode(nodeId, updates);
|
|
150
|
+
}
|
|
151
|
+
}, [nodeId, onUpdateNode]);
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (nodeType !== 'mentor' || !mentorSettings)
|
|
154
|
+
return;
|
|
155
|
+
const mentorName = mentorSettings.mentor_name || mentorSettings.display_name || nodeData.label;
|
|
156
|
+
const mentorPrompt = mentorSettings.system_prompt;
|
|
157
|
+
const mentorModel = mentorSettings.llm_name;
|
|
158
|
+
const forcePrefill = forceMentorPrefill.current;
|
|
159
|
+
const updates = {};
|
|
160
|
+
if (mentorName && (forcePrefill || isDefaultMentorLabel(nodeData.label))) {
|
|
161
|
+
if (mentorName !== nodeData.label) {
|
|
162
|
+
setNodeName(mentorName);
|
|
163
|
+
updates.label = mentorName;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (mentorPrompt &&
|
|
167
|
+
(forcePrefill || !nodeData.instructions || nodeData.instructions === DEFAULT_INSTRUCTIONS)) {
|
|
168
|
+
if (mentorPrompt !== nodeData.instructions) {
|
|
169
|
+
setInstructions(mentorPrompt);
|
|
170
|
+
updates.instructions = mentorPrompt;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (mentorModel && mentorModel !== nodeData.model) {
|
|
174
|
+
setModel(mentorModel);
|
|
175
|
+
updates.model = mentorModel;
|
|
176
|
+
}
|
|
177
|
+
if (Object.keys(updates).length > 0) {
|
|
178
|
+
updateNodeConfig(updates);
|
|
179
|
+
}
|
|
180
|
+
if (forcePrefill) {
|
|
181
|
+
forceMentorPrefill.current = false;
|
|
182
|
+
}
|
|
183
|
+
}, [
|
|
184
|
+
mentorSettings,
|
|
185
|
+
nodeType,
|
|
186
|
+
nodeData.label,
|
|
187
|
+
nodeData.instructions,
|
|
188
|
+
nodeData.model,
|
|
189
|
+
updateNodeConfig,
|
|
190
|
+
]);
|
|
191
|
+
const newVarNameTrimmed = newVarName.trim();
|
|
192
|
+
const newVarNameInvalid = newVarNameTrimmed !== '' && !isValidVariableName(newVarNameTrimmed);
|
|
193
|
+
const newVarNameTaken = stateVariables.some((variable) => variable.name === newVarNameTrimmed);
|
|
194
|
+
const parsedNewVarDefault = parseVariableDefault(selectedType.toLowerCase(), newVarDefault);
|
|
195
|
+
const canSaveVariable = newVarNameTrimmed !== '' && !newVarNameInvalid && !newVarNameTaken && parsedNewVarDefault.ok;
|
|
196
|
+
const defaultValueError = parsedNewVarDefault.ok
|
|
197
|
+
? null
|
|
198
|
+
: selectedType === 'Number'
|
|
199
|
+
? t('workflowsNodeConfigPanel.invalidNumberDefault')
|
|
200
|
+
: selectedType === 'List'
|
|
201
|
+
? t('workflowsNodeConfigPanel.invalidListDefault')
|
|
202
|
+
: t('workflowsNodeConfigPanel.invalidObjectDefault');
|
|
203
|
+
const handleSaveVariable = () => {
|
|
204
|
+
if (!canSaveVariable || !parsedNewVarDefault.ok)
|
|
205
|
+
return;
|
|
206
|
+
const newVar = {
|
|
207
|
+
id: Date.now().toString(),
|
|
208
|
+
name: newVarNameTrimmed,
|
|
209
|
+
type: selectedType.toLowerCase(),
|
|
210
|
+
defaultValue: parsedNewVarDefault.value,
|
|
211
|
+
};
|
|
212
|
+
const newStateVariables = [...stateVariables, newVar];
|
|
213
|
+
setStateVariables(newStateVariables);
|
|
214
|
+
updateNodeConfig({ stateVariables: newStateVariables });
|
|
215
|
+
setShowAddModal(false);
|
|
216
|
+
setNewVarName('');
|
|
217
|
+
setNewVarDefault('');
|
|
218
|
+
setSelectedType('String');
|
|
219
|
+
};
|
|
220
|
+
// The variable stays set after closing so the dialog keeps its text while
|
|
221
|
+
// it animates out.
|
|
222
|
+
const [variablePendingDelete, setVariablePendingDelete] = useState(null);
|
|
223
|
+
const [isDeleteVariableOpen, setIsDeleteVariableOpen] = useState(false);
|
|
224
|
+
const pendingDeleteUsages = variablePendingDelete
|
|
225
|
+
? ((_u = variableUsages[variablePendingDelete.name]) !== null && _u !== void 0 ? _u : [])
|
|
226
|
+
: [];
|
|
227
|
+
const handleRemoveVariable = (id) => {
|
|
228
|
+
const newStateVariables = stateVariables.filter((v) => v.id !== id);
|
|
229
|
+
setStateVariables(newStateVariables);
|
|
230
|
+
updateNodeConfig({ stateVariables: newStateVariables });
|
|
231
|
+
};
|
|
232
|
+
const handleNameChange = (newName) => {
|
|
233
|
+
setNodeName(newName);
|
|
234
|
+
updateNodeConfig({ label: newName });
|
|
235
|
+
};
|
|
236
|
+
const handleMentorSelect = (mentor) => {
|
|
237
|
+
const mentorId = mentor.unique_id;
|
|
238
|
+
if (!mentorId)
|
|
239
|
+
return;
|
|
240
|
+
forceMentorPrefill.current = true;
|
|
241
|
+
const updates = {
|
|
242
|
+
entry_mentor_id: mentorId,
|
|
243
|
+
mentor_id: mentorId,
|
|
244
|
+
};
|
|
245
|
+
if (mentor.name) {
|
|
246
|
+
setNodeName(mentor.name);
|
|
247
|
+
updates.label = mentor.name;
|
|
248
|
+
}
|
|
249
|
+
updateNodeConfig(updates);
|
|
250
|
+
setShowMentorModal(false);
|
|
251
|
+
setMentorSearchQuery('');
|
|
252
|
+
};
|
|
253
|
+
const handleAddCondition = () => {
|
|
254
|
+
const newCondition = {
|
|
255
|
+
id: Date.now().toString(),
|
|
256
|
+
caseName: '',
|
|
257
|
+
expression: '',
|
|
258
|
+
};
|
|
259
|
+
const newConditions = [...conditions, newCondition];
|
|
260
|
+
setConditions(newConditions);
|
|
261
|
+
updateNodeConfig({
|
|
262
|
+
conditions: newConditions,
|
|
263
|
+
conditionCount: newConditions.length,
|
|
264
|
+
});
|
|
265
|
+
};
|
|
266
|
+
const handleRemoveCondition = (id) => {
|
|
267
|
+
const newConditions = conditions.filter((c) => c.id !== id);
|
|
268
|
+
setConditions(newConditions);
|
|
269
|
+
updateNodeConfig({
|
|
270
|
+
conditions: newConditions,
|
|
271
|
+
conditionCount: newConditions.length,
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
const handleUpdateCondition = (id, updates) => {
|
|
275
|
+
const newConditions = conditions.map((c) => (c.id === id ? { ...c, ...updates } : c));
|
|
276
|
+
setConditions(newConditions);
|
|
277
|
+
updateNodeConfig({ conditions: newConditions });
|
|
278
|
+
};
|
|
279
|
+
const handleAddTransformExpression = () => {
|
|
280
|
+
const newExpressions = [
|
|
281
|
+
...transformExpressions,
|
|
282
|
+
{ id: Date.now().toString(), target: '', expression: '' },
|
|
283
|
+
];
|
|
284
|
+
setTransformExpressions(newExpressions);
|
|
285
|
+
updateNodeConfig({ expressions: newExpressions });
|
|
286
|
+
};
|
|
287
|
+
const handleRemoveTransformExpression = (id) => {
|
|
288
|
+
if (transformExpressions.length > 1) {
|
|
289
|
+
const newExpressions = transformExpressions.filter((e) => e.id !== id);
|
|
290
|
+
setTransformExpressions(newExpressions);
|
|
291
|
+
updateNodeConfig({ expressions: newExpressions });
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
const handleUpdateTransformExpression = (id, updates) => {
|
|
295
|
+
const newExpressions = transformExpressions.map((e) => e.id === id ? { ...e, ...updates } : e);
|
|
296
|
+
setTransformExpressions(newExpressions);
|
|
297
|
+
updateNodeConfig({ expressions: newExpressions });
|
|
298
|
+
};
|
|
299
|
+
const handleAddAssignment = () => {
|
|
300
|
+
const newAssignments = [
|
|
301
|
+
...assignments,
|
|
302
|
+
{ id: Date.now().toString(), variable: '', expression: '' },
|
|
303
|
+
];
|
|
304
|
+
setAssignments(newAssignments);
|
|
305
|
+
updateNodeConfig({ assignments: newAssignments });
|
|
306
|
+
};
|
|
307
|
+
const handleRemoveAssignment = (id) => {
|
|
308
|
+
if (assignments.length > 1) {
|
|
309
|
+
const newAssignments = assignments.filter((a) => a.id !== id);
|
|
310
|
+
setAssignments(newAssignments);
|
|
311
|
+
updateNodeConfig({ assignments: newAssignments });
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
const handleUpdateAssignment = (id, updates) => {
|
|
315
|
+
const newAssignments = assignments.map((a) => (a.id === id ? { ...a, ...updates } : a));
|
|
316
|
+
setAssignments(newAssignments);
|
|
317
|
+
updateNodeConfig({ assignments: newAssignments });
|
|
318
|
+
};
|
|
319
|
+
const newVariableNameTrimmed = newVariableName.trim();
|
|
320
|
+
const canAddNewVariable = isValidVariableName(newVariableNameTrimmed);
|
|
321
|
+
const closeNewVariableDialog = () => {
|
|
322
|
+
setNewVariableRowId(null);
|
|
323
|
+
setNewVariableName('');
|
|
324
|
+
};
|
|
325
|
+
const handleAddNewVariable = () => {
|
|
326
|
+
if (!newVariableRowId || !canAddNewVariable)
|
|
327
|
+
return;
|
|
328
|
+
handleUpdateAssignment(newVariableRowId, {
|
|
329
|
+
variable: newVariableNameTrimmed,
|
|
330
|
+
});
|
|
331
|
+
closeNewVariableDialog();
|
|
332
|
+
};
|
|
333
|
+
const handleRemoveConnector = (connectorId) => {
|
|
334
|
+
const newConnectors = mcpConnectors.filter((c) => c.id !== connectorId);
|
|
335
|
+
setMcpConnectors(newConnectors);
|
|
336
|
+
updateNodeConfig({ mcpConnectors: newConnectors });
|
|
337
|
+
};
|
|
338
|
+
const handleInstructionsChange = (value) => {
|
|
339
|
+
setInstructions(value);
|
|
340
|
+
updateNodeConfig({ instructions: value });
|
|
341
|
+
};
|
|
342
|
+
const handleContinueOnErrorChange = (value) => {
|
|
343
|
+
setContinueOnError(value);
|
|
344
|
+
updateNodeConfig({ continueOnError: value });
|
|
345
|
+
};
|
|
346
|
+
// An empty condition passes validation and ends the loop at once, so it is
|
|
347
|
+
// left out to get the backend's "missing required field" error instead.
|
|
348
|
+
const handleWhileConditionChange = (value) => {
|
|
349
|
+
setWhileCondition(value);
|
|
350
|
+
updateNodeConfig({ condition: value.trim() ? value : undefined });
|
|
351
|
+
};
|
|
352
|
+
// The backend rejects anything but a positive whole number; leaving it out
|
|
353
|
+
// falls back to its default instead of failing validation. Huge values
|
|
354
|
+
// serialize as 1e+21, which the backend reads as a float.
|
|
355
|
+
const toMaxIterations = (value) => {
|
|
356
|
+
const parsed = Number(value);
|
|
357
|
+
return value.trim() !== '' && Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : undefined;
|
|
358
|
+
};
|
|
359
|
+
// A number field reports letters as an empty value; validity.badInput is
|
|
360
|
+
// the only sign they were typed.
|
|
361
|
+
const [maxIterationsBadInput, setMaxIterationsBadInput] = useState(false);
|
|
362
|
+
const maxIterationsInvalid = maxIterationsBadInput ||
|
|
363
|
+
(maxIterations.trim() !== '' && toMaxIterations(maxIterations) === undefined);
|
|
364
|
+
const handleMaxIterationsChange = (value, badInput = false) => {
|
|
365
|
+
setMaxIterations(value);
|
|
366
|
+
setMaxIterationsBadInput(badInput);
|
|
367
|
+
updateNodeConfig({ maxIterations: toMaxIterations(value) });
|
|
368
|
+
};
|
|
369
|
+
const [copiedStepId, setCopiedStepId] = useState(null);
|
|
370
|
+
const { copy: copyToClipboard, status: copyStatus } = useCopyToClipboard(1500);
|
|
371
|
+
const copyStepReference = async (id) => {
|
|
372
|
+
await copyToClipboard(`step_results['${id}']`);
|
|
373
|
+
setCopiedStepId(id);
|
|
374
|
+
};
|
|
375
|
+
const handleUserApprovalMessageChange = (value) => {
|
|
376
|
+
setUserApprovalMessage(value);
|
|
377
|
+
updateNodeConfig({ userApprovalMessage: value });
|
|
378
|
+
};
|
|
379
|
+
const panelHeader = (description) => (jsxs("div", { className: "flex items-start justify-between", children: [jsxs("div", { className: "flex-1", children: [jsx("h3", { className: "text-foreground mb-0.5 text-base font-semibold", children: nodeData.label }), jsx("p", { className: "text-muted-foreground text-[11px]", children: description })] }), jsxs("div", { className: "flex items-center gap-0.5", children: [onDeleteNode && (jsx(ActionTooltip, { label: t('workflowsNodeConfigPanel.deleteNodeLabel', { label: nodeData.label }), children: jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6", "aria-label": t('workflowsNodeConfigPanel.deleteNodeLabel', { label: nodeData.label }), onClick: onDeleteNode, children: jsx(Trash2, { className: "text-muted-foreground h-3 w-3" }) }) })), jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6", "aria-label": t('workflowsNodeConfigPanel.closeLabel'), onClick: onClose, children: jsx(X, { className: "text-muted-foreground h-3 w-3" }) })] })] }));
|
|
380
|
+
const expressionHint = (jsxs(Fragment, { children: [jsx("p", { className: "text-muted-foreground text-[10px]", children: t('workflowsNodeConfigPanel.expressionHint') }), stepReferences.length > 0 && (jsxs("div", { className: "space-y-1", children: [jsx("h4", { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.stepIdsLabel') }), stepReferences.map((step) => (jsxs("div", { className: "bg-muted/50 border-border flex items-center justify-between gap-2 rounded-lg border p-1.5", children: [jsxs("div", { className: "min-w-0", children: [jsx("p", { className: "text-foreground truncate text-xs", children: step.label }), jsx("p", { id: `${nodeId}-step-ref-${step.id}`, className: "text-muted-foreground truncate font-mono text-[10px]", children: step.id })] }), jsx(ActionTooltip, { label: t('workflowsNodeConfigPanel.copyStepReference', { label: step.label }), children: jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5 shrink-0", "aria-label": t('workflowsNodeConfigPanel.copyStepReference', {
|
|
381
|
+
label: step.label,
|
|
382
|
+
}), "aria-describedby": `${nodeId}-step-ref-${step.id}`, onClick: () => void copyStepReference(step.id), children: copyStatus === 'success' && copiedStepId === step.id ? (jsx(Check, { className: "text-muted-foreground h-3 w-3" })) : (jsx(Copy, { className: "text-muted-foreground h-3 w-3" })) }) })] }, step.id)))] }))] }));
|
|
383
|
+
if (nodeType === 'start') {
|
|
384
|
+
return (jsxs(Fragment, { children: [jsx("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-20 flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: [jsxs("div", { className: "flex items-start justify-between", children: [jsxs("div", { className: "flex-1", children: [jsx("h3", { className: "text-foreground mb-0.5 text-base font-semibold", children: t('workflowsNodeConfigPanel.startTitle') }), jsx("p", { className: "text-muted-foreground text-xs", children: t('workflowsNodeConfigPanel.startDescription') })] }), jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", "aria-label": t('workflowsNodeConfigPanel.closeLabel'), onClick: onClose, children: jsx(X, { className: "h-3 w-3" }) })] }), jsxs("div", { className: "space-y-1.5", children: [jsx("h4", { className: "text-foreground text-xs font-medium", children: t('workflowsNodeConfigPanel.inputVariables') }), jsx("div", { className: "space-y-1", children: INPUT_VARIABLES.map((variable) => (jsxs("div", { className: "bg-muted/50 border-border flex items-center justify-between rounded-lg border p-1.5", children: [jsxs("div", { className: "flex items-center gap-1.5", children: [jsx("div", { className: "flex h-3.5 w-3.5 items-center justify-center rounded bg-green-500/20", children: jsx("span", { className: "font-mono text-[10px] text-green-500", children: "=" }) }), jsx("span", { className: "text-foreground font-mono text-xs", children: variable.name })] }), jsx("span", { className: "text-muted-foreground text-xs", children: variable.type })] }, variable.id))) })] }), jsxs("div", { className: "space-y-1.5", children: [jsx("h4", { className: "text-foreground text-xs font-medium", children: t('workflowsNodeConfigPanel.stateVariables') }), jsx("div", { className: "space-y-1", children: stateVariables.map((variable) => (jsxs("div", { className: "bg-muted/50 border-border flex items-center justify-between rounded-lg border p-1.5", children: [jsxs("div", { className: "flex items-center gap-1.5", children: [jsx("div", { className: "flex h-3.5 w-3.5 items-center justify-center rounded bg-purple-500/20", children: jsx("span", { className: "font-mono text-[10px] text-purple-500", children: "$" }) }), jsx("span", { className: "text-foreground font-mono text-xs", children: variable.name })] }), jsxs("div", { className: "flex items-center gap-1", children: [jsx("span", { className: "text-muted-foreground text-xs", children: variable.type }), jsx(ActionTooltip, { label: t('workflowsNodeConfigPanel.removeItemLabel', {
|
|
385
|
+
name: variable.name,
|
|
386
|
+
}), children: jsx(Button, { variant: "ghost", size: "icon", className: "h-4 w-4", "aria-label": t('workflowsNodeConfigPanel.removeItemLabel', {
|
|
387
|
+
name: variable.name,
|
|
388
|
+
}), onClick: () => {
|
|
389
|
+
setVariablePendingDelete(variable);
|
|
390
|
+
setIsDeleteVariableOpen(true);
|
|
391
|
+
}, children: jsx(Trash2, { className: "text-muted-foreground h-2.5 w-2.5" }) }) })] })] }, variable.id))) }), jsxs(Button, { variant: "secondary", size: "sm", className: "h-7 w-full gap-1.5 text-xs has-[>svg]:px-2.5", onClick: () => setShowAddModal(true), children: [jsx(Plus, { className: "mr-1 h-3 w-3" }), t('workflowsNodeConfigPanel.addButton')] })] })] }) }), jsx(Dialog, { open: isDeleteVariableOpen, onOpenChange: setIsDeleteVariableOpen, children: jsx(DialogContent, { onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: pendingDeleteUsages.length > 0 ? (jsxs(Fragment, { children: [jsxs(DialogHeader, { className: "space-y-3", children: [jsx(DialogTitle, { className: "ibl-dialog-title", children: t('workflowsNodeConfigPanel.variableInUseTitle') }), jsx(DialogDescription, { className: "text-gray-600 text-sm pt-2", children: t('workflowsNodeConfigPanel.variableInUseDescription', {
|
|
392
|
+
name: (_v = variablePendingDelete === null || variablePendingDelete === void 0 ? void 0 : variablePendingDelete.name) !== null && _v !== void 0 ? _v : '',
|
|
393
|
+
}) })] }), jsx("ul", { className: "text-foreground list-disc space-y-1 pl-5 text-sm", children: pendingDeleteUsages.map((step) => (jsx("li", { children: step.label }, step.id))) }), jsx(DialogFooter, { className: "gap-2 sm:gap-0 mt-4", children: jsx(Button, { type: "button", className: "ibl-button-primary", onClick: () => setIsDeleteVariableOpen(false), children: t('workflowsNodeConfigPanel.cancelButton') }) })] })) : (jsxs(Fragment, { children: [jsxs(DialogHeader, { className: "space-y-3", children: [jsx(DialogTitle, { className: "ibl-dialog-title", children: t('workflowsNodeConfigPanel.deleteVariableTitle') }), jsx(DialogDescription, { className: "text-gray-600 text-sm pt-2", children: t('workflowsNodeConfigPanel.deleteVariableDescription', {
|
|
394
|
+
name: (_w = variablePendingDelete === null || variablePendingDelete === void 0 ? void 0 : variablePendingDelete.name) !== null && _w !== void 0 ? _w : '',
|
|
395
|
+
}) })] }), jsxs(DialogFooter, { className: "gap-2 sm:gap-0 mt-4", children: [jsx(Button, { type: "button", variant: "outline", onClick: () => setIsDeleteVariableOpen(false), children: t('workflowsNodeConfigPanel.cancelButton') }), jsx(Button, { type: "button", className: "ibl-button-primary", onClick: () => {
|
|
396
|
+
if (variablePendingDelete) {
|
|
397
|
+
handleRemoveVariable(variablePendingDelete.id);
|
|
398
|
+
}
|
|
399
|
+
setIsDeleteVariableOpen(false);
|
|
400
|
+
}, children: t('workflowsNodeConfigPanel.deleteButton') })] })] })) }) }), jsx(Dialog, { open: showAddModal, onOpenChange: setShowAddModal, children: jsxs(DialogContent, { className: "bg-card w-[calc(100%-2rem)] rounded-lg sm:max-w-[500px]", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "sr-only", children: t('workflowsNodeConfigPanel.addStateVariableDialogTitle') }) }), jsxs("div", { className: "space-y-6 pt-4", children: [jsx("div", { className: "bg-muted flex gap-2 rounded-lg p-1", children: VARIABLE_TYPES.map((type) => (jsx("button", { type: "button", onClick: () => {
|
|
401
|
+
setSelectedType(type);
|
|
402
|
+
// Reset default value when type changes to avoid invalid data
|
|
403
|
+
if (type === 'Boolean') {
|
|
404
|
+
setNewVarDefault('false');
|
|
405
|
+
}
|
|
406
|
+
else if (type === 'Number') {
|
|
407
|
+
setNewVarDefault('');
|
|
408
|
+
}
|
|
409
|
+
else if (type === 'Object') {
|
|
410
|
+
setNewVarDefault('{}');
|
|
411
|
+
}
|
|
412
|
+
else if (type === 'List') {
|
|
413
|
+
setNewVarDefault('[]');
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
setNewVarDefault('');
|
|
417
|
+
}
|
|
418
|
+
}, className: `flex-1 rounded-md px-4 py-2 text-sm font-medium transition-colors ${selectedType === type
|
|
419
|
+
? 'bg-background text-foreground shadow-sm'
|
|
420
|
+
: 'text-muted-foreground hover:text-foreground'}`, children: type }, type))) }), jsxs("div", { className: "space-y-2", children: [jsx(Label, { htmlFor: "var-name", className: "text-sm font-medium", children: t('workflowsNodeConfigPanel.nameLabel') }), jsx(Input, { id: "var-name", placeholder: t('workflowsNodeConfigPanel.variableNamePlaceholder'), value: newVarName, onChange: (e) => setNewVarName(e.target.value), "aria-invalid": newVarNameInvalid || newVarNameTaken, className: "bg-muted border-border" }), jsx("p", { className: `text-xs ${newVarNameInvalid || newVarNameTaken ? 'text-destructive' : 'text-muted-foreground'}`, children: newVarNameTaken
|
|
421
|
+
? t('workflowsNodeConfigPanel.variableNameTaken')
|
|
422
|
+
: t('workflowsNodeConfigPanel.variableNameHint') })] }), jsxs("div", { className: "space-y-2", children: [jsxs(Label, { htmlFor: "var-default", className: "text-sm font-medium", children: [t('workflowsNodeConfigPanel.defaultValueLabel'), ' ', jsx("span", { className: "text-muted-foreground font-normal", children: t('workflowsNodeConfigPanel.defaultValueOptional') })] }), selectedType === 'Boolean' ? (jsxs("div", { className: "bg-muted border-border flex items-center gap-3 rounded-lg border p-3", children: [jsx(Switch, { id: "var-default", checked: newVarDefault === 'true', onCheckedChange: (checked) => setNewVarDefault(checked ? 'true' : 'false') }), jsx("span", { className: "text-muted-foreground text-sm", children: newVarDefault === 'true'
|
|
423
|
+
? t('workflowsNodeConfigPanel.booleanTrue')
|
|
424
|
+
: t('workflowsNodeConfigPanel.booleanFalse') })] })) : selectedType === 'Number' ? (jsx(Input, { id: "var-default", type: "number", placeholder: "0", value: newVarDefault, onChange: (e) => setNewVarDefault(e.target.value), className: "bg-muted border-border" })) : selectedType === 'Object' ? (jsx(Textarea, { id: "var-default", placeholder: '{ "key": "value" }', value: newVarDefault, onChange: (e) => setNewVarDefault(e.target.value), className: "bg-muted border-border min-h-[100px] font-mono text-sm" })) : selectedType === 'List' ? (jsx(Textarea, { id: "var-default", placeholder: '["item1", "item2"]', value: newVarDefault, onChange: (e) => setNewVarDefault(e.target.value), className: "bg-muted border-border min-h-[100px] font-mono text-sm" })) : (jsx(Input, { id: "var-default", placeholder: t('workflowsNodeConfigPanel.defaultValueStringPlaceholder'), value: newVarDefault, onChange: (e) => setNewVarDefault(e.target.value), className: "bg-muted border-border" })), defaultValueError && (jsx("p", { className: "text-destructive text-xs", children: defaultValueError }))] }), jsx("div", { className: "flex justify-end", children: jsx(Button, { onClick: handleSaveVariable, disabled: !canSaveVariable, className: "bg-background hover:bg-muted text-foreground border-border h-9 border", children: t('workflowsNodeConfigPanel.saveButton') }) })] })] }) })] }));
|
|
425
|
+
}
|
|
426
|
+
if (nodeType === 'mentor') {
|
|
427
|
+
const modelDisplay = (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider)
|
|
428
|
+
? `${mentorSettings.llm_provider}${model ? ` · ${model}` : ''}`
|
|
429
|
+
: model;
|
|
430
|
+
const enabledToolSlugs = new Set(mentorToolSlugs);
|
|
431
|
+
return (jsxs(Fragment, { children: [jsx("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-[22] flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: [panelHeader(t('workflowsNodeConfigPanel.mentorDescription')), jsxs("div", { className: "space-y-1", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.nameLabel') }), jsx(Button, { variant: "ghost", size: "sm", className: "h-6 px-2 text-[11px]", onClick: () => setShowMentorModal(true), children: selectedMentorId
|
|
432
|
+
? t('workflowsNodeConfigPanel.changeButton')
|
|
433
|
+
: t('workflowsNodeConfigPanel.selectButton') })] }), jsx(Input, { value: nodeName, onChange: (e) => handleNameChange(e.target.value), className: "bg-muted border-border h-8 text-sm" }), !selectedMentorId && (jsx("p", { className: "text-muted-foreground text-[10px]", children: t('workflowsNodeConfigPanel.selectAgentHint') }))] }), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.instructionsLabel') }), jsx(Textarea, { value: instructions, onChange: (e) => handleInstructionsChange(e.target.value), className: "bg-muted border-border min-h-[80px] resize-none text-xs" })] }), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.modelLabel') }), jsx(Input, { value: modelDisplay, readOnly: true, placeholder: selectedMentorId
|
|
434
|
+
? isMentorSettingsLoading
|
|
435
|
+
? t('workflowsNodeConfigPanel.loadingModelPlaceholder')
|
|
436
|
+
: t('workflowsNodeConfigPanel.noModelConfiguredPlaceholder')
|
|
437
|
+
: t('workflowsNodeConfigPanel.selectAgentModelPlaceholder'), className: "bg-muted border-border h-8 text-sm" })] }), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.toolsLabel') }), !selectedMentorId && (jsx("p", { className: "text-muted-foreground text-[10px]", children: t('workflowsNodeConfigPanel.selectAgentToolsHint') })), selectedMentorId && (isToolsLoading || isMentorSettingsLoading) && (jsx("p", { className: "text-muted-foreground text-[10px]", children: t('workflowsNodeConfigPanel.loadingTools') })), selectedMentorId && !isToolsLoading && (tools === null || tools === void 0 ? void 0 : tools.length) === 0 && (jsx("p", { className: "text-muted-foreground text-[10px]", children: t('workflowsNodeConfigPanel.noToolsAvailable') })), selectedMentorId && !isToolsLoading && (tools === null || tools === void 0 ? void 0 : tools.length) ? (jsx(WithFormPermissions, { name: "mentor_tools",
|
|
438
|
+
// @ts-ignore
|
|
439
|
+
permissions: (_x = mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.permissions) === null || _x === void 0 ? void 0 : _x.field, enableRBAC: (_y = agentSettings === null || agentSettings === void 0 ? void 0 : agentSettings.enableRBAC) !== null && _y !== void 0 ? _y : false, children: ({ disabled }) => (jsx("div", { className: "space-y-1", children: tools === null || tools === void 0 ? void 0 : tools.map((tool) => {
|
|
440
|
+
var _a;
|
|
441
|
+
const slug = (_a = tool === null || tool === void 0 ? void 0 : tool.slug) !== null && _a !== void 0 ? _a : '';
|
|
442
|
+
const toolLabel = (tool === null || tool === void 0 ? void 0 : tool.display_name) ||
|
|
443
|
+
(tool === null || tool === void 0 ? void 0 : tool.name) ||
|
|
444
|
+
t('workflowsNodeConfigPanel.toolFallbackLabel');
|
|
445
|
+
const isEnabled = enabledToolSlugs.has(slug);
|
|
446
|
+
const isDisabled = isToolsDisabled || disabled || !slug;
|
|
447
|
+
return (jsxs("div", { className: "bg-muted/50 border-border flex items-center justify-between rounded-lg border p-1.5", children: [jsxs("div", { className: "flex items-center gap-1.5", children: [jsx("span", { className: "text-foreground text-xs", children: toolLabel }), (tool === null || tool === void 0 ? void 0 : tool.description) ? (jsx(TooltipProvider, { children: jsxs(Tooltip, { children: [jsx(TooltipTrigger, { "aria-label": t('workflowsNodeConfigPanel.toolInfoAriaLabel', {
|
|
448
|
+
toolLabel,
|
|
449
|
+
}), children: jsx(Info, { className: "text-muted-foreground h-3 w-3" }) }), jsx(TooltipContent, { className: "ibl-tooltip-content", children: jsx("p", { children: tool === null || tool === void 0 ? void 0 : tool.description }) })] }) })) : null] }), jsx(Switch, { checked: isEnabled, onCheckedChange: () => {
|
|
450
|
+
if (!slug || isDisabled)
|
|
451
|
+
return;
|
|
452
|
+
toggleTools(slug);
|
|
453
|
+
}, disabled: isDisabled, "aria-label": t('workflowsNodeConfigPanel.toolSwitchAriaLabel', {
|
|
454
|
+
toolLabel,
|
|
455
|
+
status: isEnabled
|
|
456
|
+
? t('workflowsNodeConfigPanel.toolEnabled')
|
|
457
|
+
: t('workflowsNodeConfigPanel.toolDisabled'),
|
|
458
|
+
}) })] }, slug || toolLabel));
|
|
459
|
+
}) })) })) : null] })] }) }), jsx(Dialog, { open: showMentorModal, onOpenChange: (open) => {
|
|
460
|
+
setShowMentorModal(open);
|
|
461
|
+
if (!open) {
|
|
462
|
+
setMentorSearchQuery('');
|
|
463
|
+
}
|
|
464
|
+
}, children: jsxs(DialogContent, { className: "w-[95vw] max-w-7xl gap-0 overflow-hidden rounded-lg p-0", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: [jsx(DialogHeader, { className: "border-b border-gray-200 bg-white px-6 py-4", children: jsx(DialogTitle, { className: "text-lg font-semibold text-gray-900", children: t('workflowsNodeConfigPanel.selectAgentDialogTitle') }) }), jsx("div", { className: "px-6 py-4", children: jsx(MentorSelectionGrid, { selectedMentorIds: selectedMentorId ? [selectedMentorId] : [], onMentorSelect: handleMentorSelect, searchQuery: mentorSearchQuery, onSearchChange: setMentorSearchQuery, tenantKey: org !== null && org !== void 0 ? org : '', itemsPerPage: 8, showSearch: true, minHeight: "400px" }) })] }) })] }));
|
|
465
|
+
}
|
|
466
|
+
if (nodeType === 'guardrails') {
|
|
467
|
+
return (jsx("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-[22] flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: [panelHeader(t('workflowsNodeConfigPanel.guardrailsDescription')), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.nameLabel') }), jsx(Input, { value: nodeName, onChange: (e) => handleNameChange(e.target.value), className: "bg-muted border-border h-8 text-sm" })] }), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.inputLabel') }), jsxs("div", { className: "bg-muted border-border flex items-center gap-2 rounded-lg border p-2", children: [jsx("div", { className: "flex h-4 w-4 items-center justify-center rounded bg-green-500/20", children: jsx("span", { className: "font-mono text-[10px] text-green-500", children: "=" }) }), jsx("span", { className: "text-foreground font-mono text-xs", children: "input" }), jsxs(Select, { defaultValue: "STRING", children: [jsx(SelectTrigger, { className: "bg-background border-border ml-auto h-6 w-[90px] text-xs", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "STRING", children: "STRING" }), jsx(SelectItem, { value: "NUMBER", children: "NUMBER" }), jsx(SelectItem, { value: "BOOLEAN", children: "BOOLEAN" })] })] })] })] }), jsxs("div", { className: "space-y-2 pt-2", children: [jsxs("div", { className: "flex items-center justify-between py-1", children: [jsxs("div", { className: "flex items-center gap-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.piiLabel') }), jsx(Info, { className: "text-muted-foreground h-3 w-3" })] }), jsxs("div", { className: "flex items-center gap-1", children: [jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", children: jsx(Settings, { className: "text-muted-foreground h-3 w-3" }) }), jsx(Switch, {})] })] }), jsxs("div", { className: "flex items-center justify-between py-1", children: [jsxs("div", { className: "flex items-center gap-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.moderationLabel') }), jsx(Info, { className: "text-muted-foreground h-3 w-3" })] }), jsxs("div", { className: "flex items-center gap-1", children: [jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", children: jsx(Settings, { className: "text-muted-foreground h-3 w-3" }) }), jsx(Switch, {})] })] }), jsxs("div", { className: "flex items-center justify-between py-1", children: [jsxs("div", { className: "flex items-center gap-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.jailbreakLabel') }), jsx(Info, { className: "text-muted-foreground h-3 w-3" })] }), jsxs("div", { className: "flex items-center gap-1", children: [jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", children: jsx(Settings, { className: "text-muted-foreground h-3 w-3" }) }), jsx(Switch, {})] })] }), jsxs("div", { className: "flex items-center justify-between py-1", children: [jsxs("div", { className: "flex items-center gap-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.hallucinationLabel') }), jsx(Info, { className: "text-muted-foreground h-3 w-3" })] }), jsxs("div", { className: "flex items-center gap-1", children: [jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", children: jsx(Settings, { className: "text-muted-foreground h-3 w-3" }) }), jsx(Switch, {})] })] })] }), jsxs("div", { className: "border-border flex items-center justify-between border-t py-1 pt-2", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.continueOnErrorLabel') }), jsx(Switch, { checked: continueOnError, onCheckedChange: handleContinueOnErrorChange })] })] }) }));
|
|
468
|
+
}
|
|
469
|
+
if (nodeType === 'file-search') {
|
|
470
|
+
const handleDatasetSelect = (dataset) => {
|
|
471
|
+
const name = dataset.document_name || dataset.url;
|
|
472
|
+
setDatasetId(dataset.id);
|
|
473
|
+
setDatasetName(name);
|
|
474
|
+
updateNodeConfig({ datasetId: dataset.id, datasetName: name });
|
|
475
|
+
setShowDatasetDialog(false);
|
|
476
|
+
};
|
|
477
|
+
const handleMaxResultsChange = (value) => {
|
|
478
|
+
const num = parseInt(value, 10);
|
|
479
|
+
const val = isNaN(num) ? 10 : num;
|
|
480
|
+
setMaxResults(val);
|
|
481
|
+
updateNodeConfig({ maxResults: val });
|
|
482
|
+
};
|
|
483
|
+
const handleFileSearchQueryChange = (value) => {
|
|
484
|
+
setFileSearchQuery(value);
|
|
485
|
+
updateNodeConfig({ fileSearchQuery: value });
|
|
486
|
+
};
|
|
487
|
+
const openDatasetDialog = () => setShowDatasetDialog(true);
|
|
488
|
+
return (jsxs(Fragment, { children: [jsx("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-[22] flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: [panelHeader(t('workflowsNodeConfigPanel.fileSearchDescription')), jsxs("div", { className: "space-y-1", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.datasetLabel') }), jsx(Button, { variant: "ghost", size: "sm", className: "h-6 px-2 text-[11px]", onClick: openDatasetDialog, children: datasetId
|
|
489
|
+
? t('workflowsNodeConfigPanel.changeButton')
|
|
490
|
+
: t('workflowsNodeConfigPanel.selectButton') })] }), datasetId ? (jsx("p", { className: "text-foreground bg-muted truncate rounded-md px-2 py-1.5 text-xs", children: datasetName })) : (jsx("p", { className: "text-muted-foreground text-[10px]", children: t('workflowsNodeConfigPanel.selectDatasetHint') }))] }), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.maxResultsLabel') }), jsx(Input, { type: "number", value: maxResults, onChange: (e) => handleMaxResultsChange(e.target.value), className: "bg-muted border-border h-8 text-sm" })] }), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.queryLabel') }), jsx(Textarea, { value: fileSearchQuery, onChange: (e) => handleFileSearchQueryChange(e.target.value), placeholder: t('workflowsNodeConfigPanel.fileSearchQueryPlaceholder'), className: "bg-muted border-border min-h-[100px] resize-none text-xs" })] })] }) }), jsx(Dialog, { open: showDatasetDialog, onOpenChange: setShowDatasetDialog, children: jsxs(DialogContent, { className: "flex max-h-[80vh] w-[90vw] max-w-3xl flex-col gap-0 overflow-hidden rounded-lg p-0", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: [jsx(DialogHeader, { className: "sr-only", children: jsx(DialogTitle, { children: t('workflowsNodeConfigPanel.selectDatasetDialogTitle') }) }), jsx(AgentSettingsProvider, { ...pickerSettings, children: jsx(AgentDatasetsTab, { ...agentSettings === null || agentSettings === void 0 ? void 0 : agentSettings.datasetResourceConfig, onSelect: handleDatasetSelect, selectedDatasetId: datasetId }) })] }) })] }));
|
|
491
|
+
}
|
|
492
|
+
if (nodeType === 'mcp') {
|
|
493
|
+
const openMcpDialog = () => setShowConnectorManagement(true);
|
|
494
|
+
const closeMcpDialog = () => setShowConnectorManagement(false);
|
|
495
|
+
const handleMcpSelect = (server) => {
|
|
496
|
+
const serverId = String(server.id);
|
|
497
|
+
if (mcpConnectors.some((c) => c.id === serverId)) {
|
|
498
|
+
closeMcpDialog();
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const newConnector = {
|
|
502
|
+
id: serverId,
|
|
503
|
+
name: server.name,
|
|
504
|
+
icon: server.image || undefined,
|
|
505
|
+
};
|
|
506
|
+
const newConnectors = [...mcpConnectors, newConnector];
|
|
507
|
+
setMcpConnectors(newConnectors);
|
|
508
|
+
updateNodeConfig({ mcpConnectors: newConnectors });
|
|
509
|
+
closeMcpDialog();
|
|
510
|
+
};
|
|
511
|
+
return (jsxs(Fragment, { children: [jsx("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-[22] flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: [panelHeader(t('workflowsNodeConfigPanel.mcpDescription')), mcpConnectors.length === 0 ? (jsxs("div", { className: "flex flex-col items-center justify-center space-y-4 py-8", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx("div", { className: "border-border flex h-16 w-16 items-center justify-center rounded-2xl border bg-white shadow-sm", children: jsx(Plug, { className: "h-8 w-8 text-[#38A1E5]" }) }), jsx("div", { className: "flex h-16 w-16 items-center justify-center rounded-2xl bg-[#38A1E5] shadow-sm", children: jsx(Cable, { className: "h-8 w-8 text-white" }) })] }), jsxs(Button, { variant: "secondary", size: "sm", className: "h-8 gap-1.5 text-xs has-[>svg]:px-2.5", onClick: openMcpDialog, children: [jsx(Plus, { className: "mr-1 h-3 w-3" }), t('workflowsNodeConfigPanel.addButton')] })] })) : (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.connectedToolsLabel') }), jsx(ActionTooltip, { label: t('workflowsNodeConfigPanel.addButton'), children: jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", "aria-label": t('workflowsNodeConfigPanel.addButton'), onClick: openMcpDialog, children: jsx(Plus, { className: "text-muted-foreground h-3 w-3" }) }) })] }), jsx("div", { className: "space-y-1", children: mcpConnectors.map((connector) => (jsxs("div", { className: "bg-muted/50 border-border flex items-center justify-between rounded-lg border p-1.5", children: [jsxs("div", { className: "flex items-center gap-1.5", children: [jsx("div", { className: "bg-background flex h-6 w-6 items-center justify-center rounded", children: connector.icon ? (jsx("img", { src: connector.icon, alt: connector.name, className: "h-4 w-4 object-contain" })) : (jsx("span", { className: "text-sm", children: "\uD83D\uDD0C" })) }), jsx("span", { className: "text-foreground text-xs", children: connector.name })] }), jsx(ActionTooltip, { label: t('workflowsNodeConfigPanel.removeItemLabel', {
|
|
512
|
+
name: connector.name,
|
|
513
|
+
}), children: jsx(Button, { variant: "ghost", size: "icon", className: "h-4 w-4", "aria-label": t('workflowsNodeConfigPanel.removeItemLabel', {
|
|
514
|
+
name: connector.name,
|
|
515
|
+
}), onClick: () => handleRemoveConnector(connector.id), children: jsx(Trash2, { className: "text-muted-foreground h-2.5 w-2.5" }) }) })] }, connector.id))) })] }))] }) }), jsx(Dialog, { open: showConnectorManagement, onOpenChange: (open) => {
|
|
516
|
+
if (!open)
|
|
517
|
+
closeMcpDialog();
|
|
518
|
+
}, children: jsxs(DialogContent, { className: "flex max-h-[80vh] w-[90vw] max-w-4xl flex-col gap-0 overflow-hidden rounded-lg p-0", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: [jsx(DialogHeader, { className: "sr-only", children: jsx(DialogTitle, { children: t('workflowsNodeConfigPanel.selectMcpDialogTitle') }) }), jsx(AgentSettingsProvider, { ...pickerSettings, children: jsx(McpTab, { onSelect: handleMcpSelect }) })] }) })] }));
|
|
519
|
+
}
|
|
520
|
+
if (nodeType === 'while') {
|
|
521
|
+
return (jsx("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-[22] flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: [panelHeader(t('workflowsNodeConfigPanel.whileDescription')), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.nameLabel') }), jsx(Input, { value: nodeName, onChange: (e) => handleNameChange(e.target.value), className: "bg-muted border-border h-8 text-sm" })] }), jsxs("div", { className: "space-y-1", children: [jsx(Label, { htmlFor: `${nodeId}-while-condition`, className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.conditionLabel') }), jsx(Textarea, { id: `${nodeId}-while-condition`, value: whileCondition, onChange: (e) => handleWhileConditionChange(e.target.value), placeholder: EXPRESSION_EXAMPLES.whileCondition, className: "bg-muted border-border min-h-[100px] resize-none font-mono text-xs" }), expressionHint] }), jsxs("div", { className: "space-y-1", children: [jsx(Label, { htmlFor: `${nodeId}-max-iterations`, className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.maxIterationsLabel') }), jsx(Input, { id: `${nodeId}-max-iterations`, type: "number", min: 1, step: 1, value: maxIterations, onChange: (e) => { var _a; return handleMaxIterationsChange(e.target.value, (_a = e.target.validity) === null || _a === void 0 ? void 0 : _a.badInput); },
|
|
522
|
+
// React drops input events that leave the value '' unchanged,
|
|
523
|
+
// which is what letters in a number field look like.
|
|
524
|
+
onInput: (e) => { var _a, _b; return setMaxIterationsBadInput((_b = (_a = e.currentTarget.validity) === null || _a === void 0 ? void 0 : _a.badInput) !== null && _b !== void 0 ? _b : false); }, placeholder: String(WHILE_DEFAULT_MAX_ITERATIONS), "aria-invalid": maxIterationsInvalid, className: "bg-muted border-border h-8 text-sm" }), jsx("p", { className: `text-[10px] ${maxIterationsInvalid ? 'text-destructive' : 'text-muted-foreground'}`, children: maxIterationsInvalid
|
|
525
|
+
? t('workflowsNodeConfigPanel.maxIterationsInvalid')
|
|
526
|
+
: t('workflowsNodeConfigPanel.maxIterationsHint', {
|
|
527
|
+
count: WHILE_DEFAULT_MAX_ITERATIONS,
|
|
528
|
+
}) })] }), jsx("p", { className: "text-muted-foreground border-border border-t pt-2 text-[10px]", children: t('workflowsNodeConfigPanel.whileConnectHint') })] }) }));
|
|
529
|
+
}
|
|
530
|
+
if (nodeType === 'user-approval') {
|
|
531
|
+
return (jsx("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-[22] flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: [panelHeader(t('workflowsNodeConfigPanel.userApprovalDescription')), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.nameLabel') }), jsx(Input, { value: nodeName, onChange: (e) => handleNameChange(e.target.value), className: "bg-muted border-border h-8 text-sm" })] }), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.messageLabel') }), jsx(Textarea, { value: userApprovalMessage, onChange: (e) => handleUserApprovalMessageChange(e.target.value), placeholder: t('workflowsNodeConfigPanel.approvalMessagePlaceholder'), className: "bg-muted border-border min-h-[120px] resize-none text-xs" })] })] }) }));
|
|
532
|
+
}
|
|
533
|
+
if (nodeType === 'transform') {
|
|
534
|
+
return (jsx("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-[22] flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: [panelHeader(t('workflowsNodeConfigPanel.transformDescription')), jsxs("div", { className: "space-y-1", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: t('workflowsNodeConfigPanel.nameLabel') }), jsx(Input, { value: nodeName, onChange: (e) => handleNameChange(e.target.value), className: "bg-muted border-border h-8 text-sm" })] }), jsxs("div", { className: "space-y-2", children: [transformExpressions.map((expr) => (jsxs("div", { className: "border-border bg-muted/30 space-y-1.5 rounded-lg border p-2", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label, { htmlFor: `${nodeId}-${expr.id}-target`, className: "text-foreground text-[10px] font-medium", children: t('workflowsNodeConfigPanel.keyLabel') }), transformExpressions.length > 1 && (jsx(Button, { variant: "ghost", size: "icon", className: "h-4 w-4", "aria-label": t('workflowsNodeConfigPanel.removeRowLabel'), onClick: () => handleRemoveTransformExpression(expr.id), children: jsx(Trash2, { className: "text-muted-foreground h-2.5 w-2.5" }) }))] }), jsx(Input, { id: `${nodeId}-${expr.id}-target`, value: expr.target, onChange: (e) => handleUpdateTransformExpression(expr.id, {
|
|
535
|
+
target: e.target.value,
|
|
536
|
+
}), placeholder: EXPRESSION_EXAMPLES.transformTarget, className: "bg-background border-border h-7 text-xs" }), jsx(Label, { htmlFor: `${nodeId}-${expr.id}-expression`, className: "text-foreground text-[10px] font-medium", children: t('workflowsNodeConfigPanel.valueLabel') }), jsx(Textarea, { id: `${nodeId}-${expr.id}-expression`, value: expr.expression, onChange: (e) => handleUpdateTransformExpression(expr.id, {
|
|
537
|
+
expression: e.target.value,
|
|
538
|
+
}), placeholder: EXPRESSION_EXAMPLES.transform, className: "bg-background border-border min-h-[60px] font-mono text-xs" })] }, expr.id))), expressionHint, jsxs(Button, { variant: "secondary", size: "sm", className: "h-7 w-full gap-1.5 text-xs has-[>svg]:px-2.5", onClick: handleAddTransformExpression, children: [jsx(Plus, { className: "mr-1 h-3 w-3" }), t('workflowsNodeConfigPanel.addButton')] })] })] }) }));
|
|
539
|
+
}
|
|
540
|
+
if (nodeType === 'set-state') {
|
|
541
|
+
return (jsxs("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-[22] flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: [jsxs("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: [panelHeader(t('workflowsNodeConfigPanel.setStateDescription')), jsxs("div", { className: "space-y-2", children: [assignments.map((assignment) => {
|
|
542
|
+
const variableOptions = Array.from(new Set([...workflowVariables, assignment.variable].filter(Boolean)));
|
|
543
|
+
return (jsxs("div", { className: "border-border bg-muted/30 space-y-1.5 rounded-lg border p-2", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label, { htmlFor: `${nodeId}-${assignment.id}-expression`, className: "text-foreground text-[10px] font-medium", children: t('workflowsNodeConfigPanel.assignValueLabel') }), assignments.length > 1 && (jsx(Button, { variant: "ghost", size: "icon", className: "h-4 w-4", "aria-label": t('workflowsNodeConfigPanel.removeRowLabel'), onClick: () => handleRemoveAssignment(assignment.id), children: jsx(Trash2, { className: "text-muted-foreground h-2.5 w-2.5" }) }))] }), jsx(Textarea, { id: `${nodeId}-${assignment.id}-expression`, value: assignment.expression, onChange: (e) => handleUpdateAssignment(assignment.id, {
|
|
544
|
+
expression: e.target.value,
|
|
545
|
+
}), placeholder: EXPRESSION_EXAMPLES.setState, className: "bg-background border-border min-h-[60px] font-mono text-xs" }), jsx(Label, { className: "text-foreground text-[10px] font-medium", children: t('workflowsNodeConfigPanel.toVariableLabel') }), jsxs("div", { className: "flex gap-1", children: [jsxs(Select, { value: assignment.variable, onValueChange: (value) => handleUpdateAssignment(assignment.id, {
|
|
546
|
+
variable: value,
|
|
547
|
+
}), children: [jsx(SelectTrigger, { "aria-label": t('workflowsNodeConfigPanel.toVariableLabel'), className: "bg-background border-border h-7 flex-1 text-xs", children: jsx(SelectValue, { placeholder: t('workflowsNodeConfigPanel.selectPlaceholder') }) }), jsx(SelectContent, { children: variableOptions.length === 0 ? (jsx("p", { className: "text-muted-foreground px-2 py-1.5 text-xs", children: t('workflowsNodeConfigPanel.noVariablesYet') })) : (variableOptions.map((name) => (jsx(SelectItem, { value: name, className: "font-mono text-xs", children: name }, name)))) })] }), jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1.5 bg-transparent px-2 text-xs has-[>svg]:px-2.5", onClick: () => setNewVariableRowId(assignment.id), children: [jsx(Plus, { className: "mr-0.5 h-2.5 w-2.5" }), t('workflowsNodeConfigPanel.newButton')] })] })] }, assignment.id));
|
|
548
|
+
}), expressionHint, jsxs(Button, { variant: "secondary", size: "sm", className: "h-7 w-full gap-1.5 text-xs has-[>svg]:px-2.5", onClick: handleAddAssignment, children: [jsx(Plus, { className: "mr-1 h-3 w-3" }), t('workflowsNodeConfigPanel.addButton')] })] })] }), jsx(Dialog, { open: newVariableRowId !== null, onOpenChange: (open) => {
|
|
549
|
+
if (!open)
|
|
550
|
+
closeNewVariableDialog();
|
|
551
|
+
}, children: jsxs(DialogContent, { className: "bg-card w-[calc(100%-2rem)] rounded-lg sm:max-w-[420px]", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: t('workflowsNodeConfigPanel.newVariableDialogTitle') }) }), jsxs("form", { className: "space-y-4", onSubmit: (e) => {
|
|
552
|
+
e.preventDefault();
|
|
553
|
+
handleAddNewVariable();
|
|
554
|
+
}, children: [jsxs("div", { className: "space-y-2", children: [jsx(Label, { htmlFor: "new-variable-name", className: "text-sm font-medium", children: t('workflowsNodeConfigPanel.nameLabel') }), jsx(Input, { id: "new-variable-name", autoFocus: true, placeholder: t('workflowsNodeConfigPanel.variableNamePlaceholder'), value: newVariableName, onChange: (e) => setNewVariableName(e.target.value), "aria-invalid": newVariableNameTrimmed !== '' && !canAddNewVariable, className: "bg-muted border-border font-mono" }), jsx("p", { className: `text-xs ${newVariableNameTrimmed !== '' && !canAddNewVariable ? 'text-destructive' : 'text-muted-foreground'}`, children: t('workflowsNodeConfigPanel.variableNameHint') })] }), jsx("div", { className: "flex justify-end", children: jsx(Button, { type: "submit", disabled: !canAddNewVariable, className: "h-9", children: t('workflowsNodeConfigPanel.addButton') }) })] })] }) })] }));
|
|
555
|
+
}
|
|
556
|
+
if (nodeType === 'if-else' || nodeType === 'conditional') {
|
|
557
|
+
return (jsx("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-[22] flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: jsxs("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: [panelHeader(t('workflowsNodeConfigPanel.ifElseDescription')), conditions.map((condition, index) => (jsxs("div", { className: "bg-muted/30 border-border space-y-2 rounded-lg border p-3", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label, { className: "text-foreground text-[11px] font-medium", children: index === 0
|
|
558
|
+
? t('workflowsNodeConfigPanel.conditionIf')
|
|
559
|
+
: t('workflowsNodeConfigPanel.conditionElseIf', { index }) }), conditions.length > 1 && (jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", "aria-label": t('workflowsNodeConfigPanel.removeRowLabel'), onClick: () => handleRemoveCondition(condition.id), children: jsx(Trash2, { className: "text-muted-foreground h-3 w-3" }) }))] }), jsx(Input, { placeholder: t('workflowsNodeConfigPanel.caseNamePlaceholder'), className: "bg-background border-border h-8 text-sm", value: condition.caseName, onChange: (e) => handleUpdateCondition(condition.id, {
|
|
560
|
+
caseName: e.target.value,
|
|
561
|
+
}) }), jsx(Textarea, { "aria-label": index === 0
|
|
562
|
+
? t('workflowsNodeConfigPanel.conditionIf')
|
|
563
|
+
: t('workflowsNodeConfigPanel.conditionElseIf', { index }), placeholder: t('workflowsNodeConfigPanel.conditionPlaceholder'), className: "bg-background border-border min-h-[60px] resize-none font-mono text-xs", value: condition.expression, onChange: (e) => handleUpdateCondition(condition.id, {
|
|
564
|
+
expression: e.target.value,
|
|
565
|
+
}) })] }, condition.id))), expressionHint, jsxs(Button, { variant: "secondary", size: "sm", className: "h-7 w-full gap-1.5 text-xs has-[>svg]:px-2.5", onClick: handleAddCondition, children: [jsx(Plus, { className: "mr-1 h-3 w-3" }), t('workflowsNodeConfigPanel.addButton')] })] }) }));
|
|
566
|
+
}
|
|
567
|
+
if (nodeType === 'end') {
|
|
568
|
+
return (jsx("div", { "data-testid": "workflow-node-panel", className: "bg-card border-border absolute top-4 right-4 z-[22] flex max-h-[calc(100vh-14rem)] w-[320px] flex-col rounded-xl border shadow-xl", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), children: jsx("div", { className: "flex-1 space-y-2 overflow-y-auto p-3", children: panelHeader(t('workflowsNodeConfigPanel.endDescription')) }) }));
|
|
569
|
+
}
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export { NodeConfigPanel };
|
|
574
|
+
//# sourceMappingURL=node-config-panel.js.map
|