frontend_nyla/src/components/FlowEditor/nodes/start/FormStartNodeConfig.tsx

106 lines
4 KiB
TypeScript

/**
* Start node (Formular) — define fields that appear at run time (payload.*).
*/
import React, { useMemo } from 'react';
import type { NodeConfigRendererProps } from '../shared/types';
import type { FormField } from '../shared/types';
import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper';
import styles from '../../editor/Automation2FlowEditor.module.css';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
import { useLanguage } from '../../../../providers/language/LanguageContext';
function _parseFields(params: Record<string, unknown>, t: (key: string) => string): FormField[] {
const raw = params.formFields;
if (!Array.isArray(raw)) return [{ name: 'field1', label: t('Feld 1'), type: 'text' }];
return raw.map((f, i) => {
if (f && typeof f === 'object' && !Array.isArray(f)) {
const o = f as Record<string, unknown>;
const rawType = String(o.type ?? 'text');
const name = String(o.name ?? `field${i + 1}`);
const label = String(o.label ?? `${t('Feld')} ${i + 1}`);
const type = (FORM_FIELD_TYPES as readonly string[]).includes(rawType) ? rawType : 'text';
return { name, label, type } as FormField;
}
return { name: `field${i + 1}`, label: `${t('Feld')} ${i + 1}`, type: 'text' as const };
});
}
export const FormStartNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => {
const { t } = useLanguage();
const ctx = useAutomation2DataFlow();
const fieldTypeOptions = ctx?.formFieldTypes?.length
? ctx.formFieldTypes
: FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' }));
const fields = useMemo(() => _parseFields(params, t), [params, t]);
const setFields = (next: FormField[]) => {
updateParam('formFields', next);
};
return (
<div className={styles.startNodeDoc}>
<p className={styles.startNodeDocIntro}>
<strong>{t('Formular-Felder')}</strong>{' '}
{t('werden beim Start ausgefüllt und liegen unter')}{' '}
<code>payload.&lt;name&gt;</code> {t('in der Start-Ausgabe.')}
</p>
<div className={styles.formFieldsList}>
{fields.map((f, idx) => (
<div key={idx} className={styles.formFieldRow}>
<input
className={styles.startsInput}
placeholder={t('Name (Payload-Key)')}
value={f.name ?? ''}
onChange={(e) => {
const next = [...fields];
next[idx] = { ...f, name: e.target.value };
setFields(next);
}}
/>
<input
className={styles.startsInput}
placeholder={t('Beschriftung')}
value={f.label ?? ''}
onChange={(e) => {
const next = [...fields];
next[idx] = { ...f, label: e.target.value };
setFields(next);
}}
/>
<select
className={styles.startsSelect}
value={f.type ?? 'text'}
onChange={(e) => {
const next = [...fields];
next[idx] = { name: f.name, label: f.label, type: e.target.value as FormField['type'] };
setFields(next);
}}
>
{fieldTypeOptions.map((ft) => (
<option key={ft.id} value={ft.id}>{t(ft.label)}</option>
))}
</select>
<button
type="button"
className={styles.formFieldRemoveButton}
onClick={() => setFields(fields.filter((_, j) => j !== idx))}
>
</button>
</div>
))}
<button
type="button"
className={styles.startsAddBtn}
onClick={() =>
setFields([...fields, { name: `field${fields.length + 1}`, label: t('Neues Feld'), type: 'text' }])
}
>
{t('+ Feld')}
</button>
</div>
</div>
);
};