122 lines
4.1 KiB
TypeScript
122 lines
4.1 KiB
TypeScript
/**
|
|
* NodeConfigPanel - Generic parameter renderer for all node types.
|
|
* Renders each parameter using FRONTEND_TYPE_RENDERERS based on frontendType.
|
|
*/
|
|
|
|
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
import type { CanvasNode } from './FlowCanvas';
|
|
import type { NodeType, NodeTypeParameter } from '../../../api/workflowApi';
|
|
import type { ApiRequestFunction } from '../../../api/workflowApi';
|
|
import { getLabel } from '../nodes/shared/utils';
|
|
import { FRONTEND_TYPE_RENDERERS } from '../nodes/frontendTypeRenderers';
|
|
import styles from './Automation2FlowEditor.module.css';
|
|
|
|
import { useLanguage } from '../../../providers/language/LanguageContext';
|
|
|
|
interface NodeConfigPanelProps {
|
|
node: CanvasNode | null;
|
|
nodeType: NodeType | undefined;
|
|
language: string;
|
|
onParametersChange: (nodeId: string, parameters: Record<string, unknown>) => void;
|
|
onMergeNodeParameters?: (nodeId: string, patch: Record<string, unknown>) => void;
|
|
onNodeUpdate?: (nodeId: string, updates: Partial<Pick<CanvasNode, 'title' | 'comment'>>) => void;
|
|
instanceId?: string;
|
|
request?: ApiRequestFunction;
|
|
}
|
|
|
|
export const NodeConfigPanel: React.FC<NodeConfigPanelProps> = ({ node,
|
|
nodeType,
|
|
language,
|
|
onParametersChange,
|
|
onMergeNodeParameters: _onMergeNodeParameters,
|
|
onNodeUpdate,
|
|
instanceId,
|
|
request,
|
|
}) => {
|
|
const { t } = useLanguage();
|
|
const [params, setParams] = useState<Record<string, unknown>>({});
|
|
const nodeIdRef = useRef<string | undefined>(undefined);
|
|
nodeIdRef.current = node?.id;
|
|
const notifyParentTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
useEffect(() => {
|
|
setParams(node?.parameters ?? {});
|
|
}, [node?.id, node?.parameters]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (notifyParentTimeoutRef.current != null) {
|
|
clearTimeout(notifyParentTimeoutRef.current);
|
|
notifyParentTimeoutRef.current = null;
|
|
}
|
|
};
|
|
}, [node?.id]);
|
|
|
|
const updateParam = useCallback(
|
|
(key: string, value: unknown) => {
|
|
setParams((prev) => {
|
|
const next = { ...prev, [key]: value };
|
|
const id = nodeIdRef.current;
|
|
if (id) {
|
|
if (notifyParentTimeoutRef.current != null) {
|
|
clearTimeout(notifyParentTimeoutRef.current);
|
|
}
|
|
notifyParentTimeoutRef.current = setTimeout(() => {
|
|
notifyParentTimeoutRef.current = null;
|
|
onParametersChange(id, next);
|
|
}, 0);
|
|
}
|
|
return next;
|
|
});
|
|
},
|
|
[onParametersChange]
|
|
);
|
|
|
|
if (!node || !nodeType) return null;
|
|
|
|
const isTrigger = node.type.startsWith('trigger.');
|
|
const showNameField = onNodeUpdate && !isTrigger;
|
|
const parameters = nodeType.parameters || [];
|
|
|
|
return (
|
|
<div className={styles.nodeConfigPanel}>
|
|
{showNameField && (
|
|
<div className={styles.nodeConfigNameRow}>
|
|
<label htmlFor="node-config-name">{t('Bezeichnung')}</label>
|
|
<input
|
|
id="node-config-name"
|
|
type="text"
|
|
value={node.title ?? ''}
|
|
onChange={(e) => onNodeUpdate(node.id, { title: e.target.value })}
|
|
placeholder={t('z.B. Kundenformular prüfen, Land')}
|
|
/>
|
|
<p className={styles.nodeConfigNameHint}>
|
|
{t('Wird im Data Picker angezeigt, um diesen Node zu identifizieren.')}
|
|
</p>
|
|
</div>
|
|
)}
|
|
<h4>{getLabel(nodeType?.label, language) || node.type}</h4>
|
|
{nodeType?.description && (
|
|
<p className={styles.nodeConfigDescription}>
|
|
{getLabel(nodeType.description, language)}
|
|
</p>
|
|
)}
|
|
{parameters.map((param: NodeTypeParameter) => {
|
|
const frontendType = param.frontendType || 'text';
|
|
const Renderer = FRONTEND_TYPE_RENDERERS[frontendType] ?? FRONTEND_TYPE_RENDERERS.text;
|
|
return (
|
|
<Renderer
|
|
key={param.name}
|
|
param={param}
|
|
value={params[param.name] ?? param.default}
|
|
onChange={(val: unknown) => updateParam(param.name, val)}
|
|
allParams={params}
|
|
instanceId={instanceId}
|
|
request={request}
|
|
nodeType={node.type}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
};
|