106 lines
4 KiB
TypeScript
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.<name></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>
|
|
);
|
|
};
|