/** * Runtime form fields — shared by Human Task input.form and workflow list trigger.form start. * Field rows match task.config.fields / graph node parameters.formFields shape from the backend. */ import React, { useEffect, useState } from 'react'; import { useApiRequest } from '../../../hooks/useApi'; import { loadClickupListTasksForDropdown, type ApiRequestFunction } from '../../../api/workflowApi'; import { normalizeFormFieldOptions } from '../nodes/form'; import { useLanguage } from '../../../providers/language/LanguageContext'; export type WorkflowRuntimeFormFieldRow = { name: string; type: string; label: string; required?: boolean; options?: unknown; clickupConnectionId?: string; clickupListId?: string; clickupStatusOptions?: Array<{ value: string; label: string }>; }; export function relationshipTaskIdFromFormValue(v: unknown): string { if (v && typeof v === 'object' && !Array.isArray(v) && 'add' in v) { const a = (v as { add?: unknown[] }).add; if (Array.isArray(a) && a[0] != null && String(a[0]).trim()) return String(a[0]); } return ''; } function InputFormClickupTaskField({ connectionId, listId, value, onChange, request, }: { connectionId: string; listId: string; value: unknown; onChange: (v: unknown) => void; request: ApiRequestFunction; }) { const { t } = useLanguage(); const [tasks, setTasks] = useState>([]); const [loading, setLoading] = useState(false); const [err, setErr] = useState(null); useEffect(() => { const cid = connectionId.trim(); const lid = listId.trim(); if (!cid || !lid) { setTasks([]); return; } let cancelled = false; setLoading(true); setErr(null); loadClickupListTasksForDropdown(request, cid, lid) .then((rows) => { if (!cancelled) setTasks(rows); }) .catch(() => { if (!cancelled) { setTasks([]); setErr(t('Aufgaben konnten nicht geladen werden.')); } }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [request, connectionId, listId, t]); const sel = relationshipTaskIdFromFormValue(value); if (!connectionId.trim() || !listId.trim()) { return (

{t('Für dieses Feld sind im Formular-Node ClickUp-Verbindung und Listen-ID gesetzt — bitte Workflow prüfen.')}

); } return ( <> {err ? (

{err}

) : null} {loading ? (

{t('lade Aufgaben')}

) : ( )} ); } export function useWorkflowRuntimeFormRequiredOk( fields: WorkflowRuntimeFormFieldRow[], formData: Record ): boolean { const requiredFields = fields.filter((f) => f.required); return requiredFields.every((f) => { const v = formData[f.name]; if (f.type === 'boolean') return true; if (f.type === 'clickup_tasks') { return relationshipTaskIdFromFormValue(v) !== ''; } if (f.type === 'clickup_status') { return v !== undefined && v !== null && String(v).trim() !== ''; } if ( (f.type === 'select' || f.type === 'enum') && normalizeFormFieldOptions(f.options).some((o) => String(o.value).trim() !== '') ) { return v !== undefined && v !== null && String(v).trim() !== ''; } return v !== undefined && v !== null && String(v).trim() !== ''; }); } export interface WorkflowRuntimeFormFieldsProps { fields: WorkflowRuntimeFormFieldRow[]; formData: Record; setFormData: React.Dispatch>>; formFieldsClassName: string; } /** * Renders the same controls as TaskCard input.form (no Popup — parent wraps if needed). */ export const WorkflowRuntimeFormFields: React.FC = ({ fields, formData, setFormData, formFieldsClassName, }) => { const { t } = useLanguage(); const { request } = useApiRequest(); const renderFormControl = (field: WorkflowRuntimeFormFieldRow): React.ReactNode => { const selectChoices = normalizeFormFieldOptions(field.options).filter((o) => String(o.value).trim() !== ''); if (field.type === 'boolean') { return ( setFormData((p) => ({ ...p, [field.name]: e.target.checked }))} /> ); } if (field.type === 'clickup_tasks' && request) { return ( setFormData((p) => ({ ...p, [field.name]: v }))} request={request} /> ); } if ( field.type === 'clickup_status' && Array.isArray(field.clickupStatusOptions) && field.clickupStatusOptions.length > 0 ) { return ( ); } if ((field.type === 'select' || field.type === 'enum') && selectChoices.length > 0) { return ( ); } return ( setFormData((p) => ({ ...p, [field.name]: e.target.value }))} /> ); }; return (
{fields.map((f) => (
{renderFormControl(f)}
))}
); };