cleaned servicebag and removed servicehub
Some checks failed
Deploy Nyla Frontend to Integration / deploy (push) Failing after 51s

This commit is contained in:
ValueOn AG 2026-06-08 23:35:38 +02:00
parent cd14babb2e
commit a13a158c67
61 changed files with 209 additions and 15505 deletions

5
.gitignore vendored
View file

@ -31,4 +31,7 @@ dist-ssr
.cursorignore .cursorignore
# Keep environment files in config/ (naming: env-<workflow>.env) # Keep environment files in config/ (naming: env-<workflow>.env)
!config/env-*.env !config/env-*.env
tsc-errors.txt
scripts/i18n_missing_report.md

File diff suppressed because it is too large Load diff

View file

@ -3,23 +3,6 @@ import axios from 'axios';
import { addCSRFTokenToHeaders, getCSRFToken, generateAndStoreCSRFToken } from './utils/csrfUtils'; import { addCSRFTokenToHeaders, getCSRFToken, generateAndStoreCSRFToken } from './utils/csrfUtils';
import { clearUserDataCache, getUserDataCache } from './utils/userCache'; import { clearUserDataCache, getUserDataCache } from './utils/userCache';
// Utility function to resolve hostname to IP address
const resolveHostnameToIP = async (hostname: string): Promise<string | null> => {
try {
// For localhost, return as is
if (hostname === 'localhost' || hostname === '127.0.0.1') {
return hostname;
}
// For production domains, we can't directly resolve IP due to CORS
// But we can show the hostname which is more useful anyway
return hostname;
} catch (error) {
console.warn('Could not resolve hostname to IP:', error);
return hostname;
}
};
/** /**
* Extract mandate/instance context from current URL. * Extract mandate/instance context from current URL.
* URL pattern: /mandates/:mandateId/:featureCode/:instanceId/... * URL pattern: /mandates/:mandateId/:featureCode/:instanceId/...
@ -44,52 +27,25 @@ const getContextFromUrl = (): { mandateId?: string; instanceId?: string } => {
import { getApiBaseUrl } from '../config/config'; import { getApiBaseUrl } from '../config/config';
const _baseUrl = getApiBaseUrl();
if (import.meta.env.DEV) {
console.log(`[api] Backend: ${_baseUrl} | env: ${import.meta.env.MODE}`);
}
const api = axios.create({ const api = axios.create({
baseURL: getApiBaseUrl(), baseURL: _baseUrl,
withCredentials: true, withCredentials: true,
// FastAPI expects repeat-style array query params (``?ids=1&ids=2``).
// Axios v1.x default would render ``?ids[]=1&ids[]=2``, which FastAPI
// silently drops -- e.g. ``trackerIds`` filters on the Redmine stats
// endpoint never reach the route. Setting ``indexes: null`` switches
// the URLSearchParams visitor to repeat format. Applies globally so
// every endpoint with array query params gets it for free.
paramsSerializer: { indexes: null }, paramsSerializer: { indexes: null },
}); });
// Add a request interceptor to add the auth token, context headers, and log backend IP // Add a request interceptor to add the auth token, context headers
api.interceptors.request.use( api.interceptors.request.use(
async (config) => { async (config) => {
// Log backend information // Add auth token if available (otherwise httpOnly cookies are used automatically)
const backendUrl = config.baseURL || getApiBaseUrl();
console.log(`🌐 Communicating with backend: ${backendUrl}`);
// Try to resolve and log the IP address
if (backendUrl) {
try {
const url = new URL(backendUrl);
const hostname = url.hostname;
const resolvedIP = await resolveHostnameToIP(hostname);
console.log(`📍 Backend hostname: ${hostname}`);
console.log(`🔗 Full backend URL: ${backendUrl}`);
console.log(`🌍 Resolved address: ${resolvedIP}`);
// Log environment info
console.log(`🏗️ Environment: ${import.meta.env.MODE}`);
console.log(`⚙️ API Base URL: ${getApiBaseUrl()}`);
} catch (error) {
console.warn('Could not parse backend URL:', error);
}
}
// Check for auth token in localStorage and add to headers
const authToken = localStorage.getItem('authToken'); const authToken = localStorage.getItem('authToken');
if (authToken && config.headers) { if (authToken && config.headers) {
config.headers.Authorization = `Bearer ${authToken}`; config.headers.Authorization = `Bearer ${authToken}`;
console.log('🔑 Using Bearer token for authentication');
} else {
// Fallback: httpOnly cookies
console.log('🍪 Using httpOnly cookies for authentication (automatic)');
} }
// Send app language to backend so i18n labels match the UI // Send app language to backend so i18n labels match the UI

View file

@ -1,129 +1,10 @@
/** /**
* Workflow API (WorkflowAutomation) LEGACY RE-EXPORT LAYER * ClickUp API ClickUp-specific functions for the workflow automation flow editor.
* *
* All functions have been migrated to workflowAutomationApi.ts (mandate-scoped). * Extracted from the legacy workflowApi.ts re-export shim so each integration
* This file re-exports them so existing consumers still resolve. * lives in its own module.
*
* Only ClickUp-specific functions and `deleteSystemWorkflow` remain here
* because their endpoints are not yet covered by the new unified API.
*/ */
// ============================================================================
// RE-EXPORTS — Types & Interfaces
// ============================================================================
export type {
NodeTypeParameter,
PortField,
PortSchema,
DataPickOption,
OutputPickHint,
InputPortDef,
GraphDefinedSchemaRef,
OutputPortDef,
NodeType,
NodeTypeCategory,
SystemVariable,
FormFieldType,
ConditionOperatorDef,
NodeTypesResponse,
Automation2GraphNode,
Automation2Connection,
Automation2Graph,
ExecuteGraphResponse,
WorkflowEntryPoint,
Automation2Workflow,
AutoVersion,
AutoRun,
AutoWorkflow,
AutoTask,
AutoStepLog,
UpstreamPathEntry,
ConditionMetaResponse,
ConditionMetaRequest,
GraphDataSources,
ExecuteGraphOptions,
Automation2Run,
CompletedRun,
Automation2Task,
AutoWorkflowTemplate,
UserConnection,
ConnectionService,
BrowseEntry,
WorkflowMetrics,
WorkflowFileEnvelope,
ImportWorkflowResponse,
ImportWorkflowOptions,
ExportWorkflowResult,
WorkspaceRun,
WorkspaceRunDetail,
ApiRequestFunction,
} from './workflowAutomationApi';
export type {
AutoWorkflowStatus,
AutoRunStatus,
AutoStepStatus,
AutoTaskStatus,
AutoTemplateScope,
} from './workflowAutomationApi';
// ============================================================================
// RE-EXPORTS — Constants
// ============================================================================
export {
WORKFLOW_FILE_SCHEMA_VERSION,
WORKFLOW_FILE_KIND,
WORKFLOW_FILE_EXTENSION,
} from './workflowAutomationApi';
// ============================================================================
// RE-EXPORTS — API Functions (canonical implementations in workflowAutomationApi)
// ============================================================================
export {
fetchNodeTypes,
fetchConditionMeta,
postUpstreamPaths,
fetchGraphDataSources,
getUpstreamPathsSaved,
executeGraph,
fetchWorkflows,
fetchWorkflow,
createWorkflow,
updateWorkflow,
deleteWorkflow,
importWorkflowFromFile,
exportWorkflowToFile,
isWorkflowFileContent,
workflowFileNameFor,
fetchWorkflowRuns,
fetchCompletedRuns,
fetchTasks,
completeTask,
cancelPendingTaskStopRun,
fetchVersions,
createDraftVersion,
publishVersion,
unpublishVersion,
archiveVersion,
fetchTemplates,
createTemplateFromWorkflow,
copyTemplate,
shareTemplate,
fetchConnections,
fetchConnectionServices,
fetchBrowse,
fetchMetrics,
fetchWorkspaceRuns,
fetchRunDetail as fetchWorkspaceRunDetail,
} from './workflowAutomationApi';
// ============================================================================
// KEPT — ClickUp-specific functions (not in workflow-automation API)
// ============================================================================
import type { ApiRequestFunction } from './workflowAutomationApi'; import type { ApiRequestFunction } from './workflowAutomationApi';
function _encodedConnectionId(connectionId: string): string { function _encodedConnectionId(connectionId: string): string {
@ -250,18 +131,3 @@ export async function loadClickupListTasksForDropdown(
acc.sort((a, b) => a.name.localeCompare(b.name, 'de')); acc.sort((a, b) => a.name.localeCompare(b.name, 'de'));
return acc; return acc;
} }
// ============================================================================
// KEPT — deleteSystemWorkflow (uses /api/system/... not covered by new API)
// ============================================================================
/** Delete by workflow ID only (Automations dashboard / orphan rows without featureInstanceId). */
export async function deleteSystemWorkflow(
request: ApiRequestFunction,
workflowId: string,
): Promise<void> {
await request({
url: `/api/workflow-automation/workflows/${workflowId}`,
method: 'delete',
});
}

View file

@ -170,19 +170,11 @@ export async function fetchMyFeatures(): Promise<FeaturesMyResponse> {
} }
try { try {
console.log('📡 featuresApi: Fetching /api/features/my');
const response = await api.get<FeaturesMyResponse>('/api/features/my'); const response = await api.get<FeaturesMyResponse>('/api/features/my');
// Get the actual data (response.data contains the FeaturesMyResponse) // Get the actual data (response.data contains the FeaturesMyResponse)
const data = response.data; const data = response.data;
console.log('✅ featuresApi: Loaded features:', {
mandateCount: data?.mandates?.length || 0,
totalInstances: data?.mandates
?.flatMap(m => m.features)
?.flatMap(f => f.instances)
?.length || 0,
});
return data; return data;
} catch (error) { } catch (error) {
console.error('❌ featuresApi: Error fetching features:', error); console.error('❌ featuresApi: Error fetching features:', error);

View file

@ -1,17 +0,0 @@
import { describe, expect, it } from 'vitest';
/** Mirrors _encodedConnectionId in workflowApi.ts for browse/services URL paths. */
function encodedConnectionId(connectionId: string): string {
return encodeURIComponent(connectionId);
}
describe('connection path encoding for workflow browse', () => {
it('encodes spaces and colons in connection:clickup:username references', () => {
const ref = 'connection:clickup:Stephan Schellworth';
const segment = encodedConnectionId(ref);
expect(segment).toBe('connection%3Aclickup%3AStephan%20Schellworth');
const url = `/api/workflows/inst/connections/${segment}/browse`;
expect(url).not.toContain(' ');
expect(url).toContain('%20');
});
});

View file

@ -139,7 +139,7 @@ export interface NodeTypesResponse {
formFieldTypes?: FormFieldType[]; formFieldTypes?: FormFieldType[];
} }
export interface Automation2GraphNode { export interface WorkflowGraphNode {
id: string; id: string;
type: string; type: string;
parameters?: Record<string, unknown>; parameters?: Record<string, unknown>;
@ -147,16 +147,16 @@ export interface Automation2GraphNode {
outputPorts?: Array<{ name: string; schema: string | GraphDefinedSchemaRef }>; outputPorts?: Array<{ name: string; schema: string | GraphDefinedSchemaRef }>;
} }
export interface Automation2Connection { export interface WorkflowConnection {
source: string; source: string;
target: string; target: string;
sourceOutput?: number; sourceOutput?: number;
targetInput?: number; targetInput?: number;
} }
export interface Automation2Graph { export interface WorkflowGraph {
nodes: Automation2GraphNode[]; nodes: WorkflowGraphNode[];
connections: Automation2Connection[]; connections: WorkflowConnection[];
} }
export interface ExecuteGraphResponse { export interface ExecuteGraphResponse {
@ -184,10 +184,10 @@ export interface WorkflowEntryPoint {
config: Record<string, unknown>; config: Record<string, unknown>;
} }
export interface Automation2Workflow { export interface WorkflowDefinition {
id: string; id: string;
label: string; label: string;
graph: Automation2Graph; graph: WorkflowGraph;
active?: boolean; active?: boolean;
/** Target feature instance for execution data scope (NULL for templates) */ /** Target feature instance for execution data scope (NULL for templates) */
targetFeatureInstanceId?: string | null; targetFeatureInstanceId?: string | null;
@ -224,7 +224,7 @@ export interface AutoVersion {
workflowId: string; workflowId: string;
versionNumber: number; versionNumber: number;
status: AutoWorkflowStatus; status: AutoWorkflowStatus;
graph: Automation2Graph; graph: WorkflowGraph;
invocations?: WorkflowEntryPoint[]; invocations?: WorkflowEntryPoint[];
publishedAt?: number; publishedAt?: number;
publishedBy?: string; publishedBy?: string;
@ -261,7 +261,7 @@ export interface AutoWorkflow {
active: boolean; active: boolean;
eventId?: string; eventId?: string;
notifyOnFailure?: boolean; notifyOnFailure?: boolean;
graph: Automation2Graph; graph: WorkflowGraph;
invocations?: WorkflowEntryPoint[]; invocations?: WorkflowEntryPoint[];
sysCreatedBy?: string; sysCreatedBy?: string;
sysCreatedAt?: number; sysCreatedAt?: number;
@ -319,7 +319,7 @@ export interface ConditionMetaResponse {
} }
export interface ConditionMetaRequest { export interface ConditionMetaRequest {
graph: Automation2Graph; graph: WorkflowGraph;
nodeId?: string; nodeId?: string;
ref: { type: 'ref'; nodeId: string; path: (string | number)[] }; ref: { type: 'ref'; nodeId: string; path: (string | number)[] };
} }
@ -343,7 +343,7 @@ export interface ExecuteGraphOptions {
payload?: Record<string, unknown>; payload?: Record<string, unknown>;
} }
export interface Automation2Run { export interface WorkflowRun {
id: string; id: string;
workflowId: string; workflowId: string;
status: string; status: string;
@ -351,13 +351,13 @@ export interface Automation2Run {
currentNodeId?: string; currentNodeId?: string;
} }
export interface CompletedRun extends Automation2Run { export interface CompletedRun extends WorkflowRun {
workflowLabel?: string; workflowLabel?: string;
sysModifiedAt?: number; sysModifiedAt?: number;
sysCreatedAt?: number; sysCreatedAt?: number;
} }
export interface Automation2Task { export interface WorkflowTask {
id: string; id: string;
runId: string; runId: string;
workflowId: string; workflowId: string;
@ -374,7 +374,7 @@ export interface Automation2Task {
dueAt?: number; dueAt?: number;
} }
export interface AutoWorkflowTemplate extends Automation2Workflow { export interface AutoWorkflowTemplate extends WorkflowDefinition {
isTemplate: boolean; isTemplate: boolean;
templateScope?: AutoTemplateScope; templateScope?: AutoTemplateScope;
templateSourceId?: string; templateSourceId?: string;
@ -434,7 +434,7 @@ export interface WorkflowFileEnvelope {
templateScope?: AutoTemplateScope; templateScope?: AutoTemplateScope;
sharedReadOnly?: boolean; sharedReadOnly?: boolean;
notifyOnFailure?: boolean; notifyOnFailure?: boolean;
graph: Automation2Graph; graph: WorkflowGraph;
invocations?: WorkflowEntryPoint[]; invocations?: WorkflowEntryPoint[];
} }
@ -582,7 +582,7 @@ export async function fetchConditionMeta(
*/ */
export async function postUpstreamPaths( export async function postUpstreamPaths(
request: ApiRequestFunction, request: ApiRequestFunction,
graph: Automation2Graph, graph: WorkflowGraph,
nodeId: string nodeId: string
): Promise<{ paths: UpstreamPathEntry[] }> { ): Promise<{ paths: UpstreamPathEntry[] }> {
const data = await request({ const data = await request({
@ -661,12 +661,12 @@ export async function fetchFeatureInstanceOptions(
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
/** /**
* Execute an automation2 graph. * Execute a workflow graph.
* POST /api/workflow-automation/workflows/{workflowId}/execute * POST /api/workflow-automation/workflows/{workflowId}/execute
*/ */
export async function executeGraph( export async function executeGraph(
request: ApiRequestFunction, request: ApiRequestFunction,
graph: Automation2Graph, graph: WorkflowGraph,
workflowId?: string, workflowId?: string,
options?: ExecuteGraphOptions options?: ExecuteGraphOptions
): Promise<ExecuteGraphResponse> { ): Promise<ExecuteGraphResponse> {
@ -704,7 +704,7 @@ export async function executeGraph(
export async function fetchWorkflows( export async function fetchWorkflows(
request: ApiRequestFunction, request: ApiRequestFunction,
params?: { active?: boolean; pagination?: any; mandateId?: string } params?: { active?: boolean; pagination?: any; mandateId?: string }
): Promise<Automation2Workflow[] | { items: Automation2Workflow[]; pagination: any }> { ): Promise<WorkflowDefinition[] | { items: WorkflowDefinition[]; pagination: any }> {
const queryParams: Record<string, any> = {}; const queryParams: Record<string, any> = {};
if (params?.active !== undefined) queryParams.active = params.active; if (params?.active !== undefined) queryParams.active = params.active;
if (params?.pagination) queryParams.pagination = JSON.stringify(params.pagination); if (params?.pagination) queryParams.pagination = JSON.stringify(params.pagination);
@ -721,7 +721,7 @@ export async function fetchWorkflows(
export async function fetchWorkflow( export async function fetchWorkflow(
request: ApiRequestFunction, request: ApiRequestFunction,
workflowId: string workflowId: string
): Promise<Automation2Workflow> { ): Promise<WorkflowDefinition> {
return await request({ return await request({
url: `${BASE}/workflows/${workflowId}`, url: `${BASE}/workflows/${workflowId}`,
method: 'get', method: 'get',
@ -732,12 +732,12 @@ export async function createWorkflow(
request: ApiRequestFunction, request: ApiRequestFunction,
body: { body: {
label: string; label: string;
graph: Automation2Graph; graph: WorkflowGraph;
invocations?: WorkflowEntryPoint[]; invocations?: WorkflowEntryPoint[];
targetFeatureInstanceId?: string | null; targetFeatureInstanceId?: string | null;
mandateId?: string; mandateId?: string;
} }
): Promise<Automation2Workflow> { ): Promise<WorkflowDefinition> {
return await request({ return await request({
url: `${BASE}/workflows`, url: `${BASE}/workflows`,
method: 'post', method: 'post',
@ -750,13 +750,13 @@ export async function updateWorkflow(
workflowId: string, workflowId: string,
body: { body: {
label?: string; label?: string;
graph?: Automation2Graph; graph?: WorkflowGraph;
invocations?: WorkflowEntryPoint[]; invocations?: WorkflowEntryPoint[];
active?: boolean; active?: boolean;
notifyOnFailure?: boolean; notifyOnFailure?: boolean;
targetFeatureInstanceId?: string | null; targetFeatureInstanceId?: string | null;
} }
): Promise<Automation2Workflow> { ): Promise<WorkflowDefinition> {
return await request({ return await request({
url: `${BASE}/workflows/${workflowId}`, url: `${BASE}/workflows/${workflowId}`,
method: 'put', method: 'put',
@ -848,7 +848,7 @@ export function workflowFileNameFor(label: string): string {
export async function fetchWorkflowRuns( export async function fetchWorkflowRuns(
request: ApiRequestFunction, request: ApiRequestFunction,
workflowId: string workflowId: string
): Promise<Automation2Run[]> { ): Promise<WorkflowRun[]> {
const data = await request({ const data = await request({
url: `${BASE}/runs`, url: `${BASE}/runs`,
method: 'get', method: 'get',
@ -887,7 +887,7 @@ export async function fetchRuns(
limit?: number; limit?: number;
offset?: number; offset?: number;
} }
): Promise<{ runs: Automation2Run[]; total?: number }> { ): Promise<{ runs: WorkflowRun[]; total?: number }> {
const data = await request({ const data = await request({
url: `${BASE}/runs`, url: `${BASE}/runs`,
method: 'get', method: 'get',
@ -982,7 +982,7 @@ export async function fetchWorkspaceRuns(
export async function fetchTasks( export async function fetchTasks(
request: ApiRequestFunction, request: ApiRequestFunction,
params?: { workflowId?: string; status?: string } params?: { workflowId?: string; status?: string }
): Promise<Automation2Task[]> { ): Promise<WorkflowTask[]> {
const data = await request({ const data = await request({
url: `${BASE}/tasks`, url: `${BASE}/tasks`,
method: 'get', method: 'get',
@ -1110,7 +1110,7 @@ export async function createTemplateFromWorkflow(
export async function copyTemplate( export async function copyTemplate(
request: ApiRequestFunction, request: ApiRequestFunction,
templateId: string templateId: string
): Promise<Automation2Workflow> { ): Promise<WorkflowDefinition> {
return await request({ return await request({
url: `${BASE}/templates/${templateId}/copy`, url: `${BASE}/templates/${templateId}/copy`,
method: 'post', method: 'post',

View file

@ -1,14 +1,14 @@
/** /**
* Automation2 Flow Editor - Data flow context for Data Picker and DynamicValueField. * Workflow Flow Editor - Data flow context for Data Picker and DynamicValueField.
* Extended with portTypeCatalog and systemVariables for the Typed Port System. * Extended with portTypeCatalog and systemVariables for the Typed Port System.
*/ */
import React, { createContext, useContext, useMemo } from 'react'; import React, { createContext, useContext, useMemo } from 'react';
import type { CanvasNode, CanvasConnection } from '../editor/FlowCanvas'; import type { CanvasNode, CanvasConnection } from '../editor/FlowCanvas';
import { getAvailableSources } from '../nodes/shared/dataFlowGraph'; import { getAvailableSources } from '../nodes/shared/dataFlowGraph';
import type { ApiRequestFunction, ConditionOperatorDef, FormFieldType, NodeType, PortField, PortSchema, SystemVariable } from '../../../api/workflowApi'; import type { ApiRequestFunction, ConditionOperatorDef, FormFieldType, NodeType, PortField, PortSchema, SystemVariable } from '../../../api/workflowAutomationApi';
export interface Automation2DataFlowContextValue { export interface WorkflowDataFlowContextValue {
currentNodeId: string; currentNodeId: string;
nodes: CanvasNode[]; nodes: CanvasNode[];
connections: CanvasConnection[]; connections: CanvasConnection[];
@ -30,13 +30,13 @@ export interface Automation2DataFlowContextValue {
parseGraphDefinedSchema: (parameterKey: string) => PortSchema | null; parseGraphDefinedSchema: (parameterKey: string) => PortSchema | null;
} }
const Automation2DataFlowContext = createContext<Automation2DataFlowContextValue | null>(null); const WorkflowDataFlowContext = createContext<WorkflowDataFlowContextValue | null>(null);
export function useAutomation2DataFlow(): Automation2DataFlowContextValue | null { export function useWorkflowDataFlow(): WorkflowDataFlowContextValue | null {
return useContext(Automation2DataFlowContext); return useContext(WorkflowDataFlowContext);
} }
interface Automation2DataFlowProviderProps { interface WorkflowDataFlowProviderProps {
node: CanvasNode | null; node: CanvasNode | null;
nodes: CanvasNode[]; nodes: CanvasNode[];
connections: CanvasConnection[]; connections: CanvasConnection[];
@ -52,7 +52,7 @@ interface Automation2DataFlowProviderProps {
children: React.ReactNode; children: React.ReactNode;
} }
export const Automation2DataFlowProvider: React.FC<Automation2DataFlowProviderProps> = ({ export const WorkflowDataFlowProvider: React.FC<WorkflowDataFlowProviderProps> = ({
node, node,
nodes, nodes,
connections, connections,
@ -67,7 +67,7 @@ export const Automation2DataFlowProvider: React.FC<Automation2DataFlowProviderPr
request, request,
children, children,
}) => { }) => {
const value = useMemo((): Automation2DataFlowContextValue | null => { const value = useMemo((): WorkflowDataFlowContextValue | null => {
if (!node) return null; if (!node) return null;
const formTypeToPort: Record<string, string> = Object.fromEntries( const formTypeToPort: Record<string, string> = Object.fromEntries(
formFieldTypes.map((f) => [f.id, f.portType]) formFieldTypes.map((f) => [f.id, f.portType])
@ -135,8 +135,8 @@ export const Automation2DataFlowProvider: React.FC<Automation2DataFlowProviderPr
}, [node, nodes, connections, nodeOutputsPreview, nodeTypes, language, portTypeCatalog, systemVariables, formFieldTypes, conditionOperatorCatalog, instanceId, request]); }, [node, nodes, connections, nodeOutputsPreview, nodeTypes, language, portTypeCatalog, systemVariables, formFieldTypes, conditionOperatorCatalog, instanceId, request]);
return ( return (
<Automation2DataFlowContext.Provider value={value}> <WorkflowDataFlowContext.Provider value={value}>
{children} {children}
</Automation2DataFlowContext.Provider> </WorkflowDataFlowContext.Provider>
); );
}; };

View file

@ -26,8 +26,8 @@ import {
HiOutlineChatBubbleLeftEllipsis, HiOutlineChatBubbleLeftEllipsis,
HiOutlineSquares2X2, HiOutlineSquares2X2,
} from 'react-icons/hi2'; } from 'react-icons/hi2';
import type { Automation2Workflow, ExecuteGraphResponse, AutoVersion, AutoTemplateScope } from '../../../api/workflowApi'; import type { WorkflowDefinition, ExecuteGraphResponse, AutoVersion, AutoTemplateScope } from '../../../api/workflowAutomationApi';
import styles from './Automation2FlowEditor.module.css'; import styles from './WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';
import { getUserDataCache } from '../../../utils/userCache'; import { getUserDataCache } from '../../../utils/userCache';
@ -60,7 +60,7 @@ export interface CanvasHeaderCanvasEditProps {
} }
interface CanvasHeaderProps { interface CanvasHeaderProps {
workflows: Automation2Workflow[]; workflows: WorkflowDefinition[];
currentWorkflowId: string | null; currentWorkflowId: string | null;
onWorkflowSelect: (workflowId: string | null) => void; onWorkflowSelect: (workflowId: string | null) => void;
onNew: () => void; onNew: () => void;

View file

@ -8,10 +8,10 @@
* WorkflowAutomation data instead of the workspace endpoint. * WorkflowAutomation data instead of the workspace endpoint.
*/ */
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import type { Automation2Workflow } from '../../../api/workflowApi'; import type { WorkflowDefinition } from '../../../api/workflowAutomationApi';
interface EditorWorkflowChatListProps { interface EditorWorkflowChatListProps {
workflows: Automation2Workflow[]; workflows: WorkflowDefinition[];
currentWorkflowId: string | null; currentWorkflowId: string | null;
onSelect: (workflowId: string | null) => void; onSelect: (workflowId: string | null) => void;
onNew: () => void; onNew: () => void;

View file

@ -13,8 +13,8 @@ import React, {
useRef, useRef,
useState, useState,
} from 'react'; } from 'react';
import type { GraphDefinedSchemaRef, NodeType } from '../../../api/workflowApi'; import type { GraphDefinedSchemaRef, NodeType } from '../../../api/workflowAutomationApi';
import styles from './Automation2FlowEditor.module.css'; import styles from './WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';
import { AiBadge } from '../nodes/shared/AiBadge'; import { AiBadge } from '../nodes/shared/AiBadge';

View file

@ -5,15 +5,15 @@
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import type { CanvasNode } from './FlowCanvas'; import type { CanvasNode } from './FlowCanvas';
import type { GraphDefinedSchemaRef, NodeType, NodeTypeParameter, PortSchema } from '../../../api/workflowApi'; import type { GraphDefinedSchemaRef, NodeType, NodeTypeParameter, PortSchema } from '../../../api/workflowAutomationApi';
import type { ApiRequestFunction } from '../../../api/workflowApi'; import type { ApiRequestFunction } from '../../../api/workflowAutomationApi';
import { getLabel } from '../nodes/shared/utils'; import { getLabel } from '../nodes/shared/utils';
import { FRONTEND_TYPE_RENDERERS } from '../nodes/frontendTypeRenderers'; import { FRONTEND_TYPE_RENDERERS } from '../nodes/frontendTypeRenderers';
import { ContextBuilderRenderer } from '../nodes/frontendTypeRenderers/ContextBuilderRenderer'; import { ContextBuilderRenderer } from '../nodes/frontendTypeRenderers/ContextBuilderRenderer';
import { RequiredAttributePicker } from '../nodes/shared/RequiredAttributePicker'; import { RequiredAttributePicker } from '../nodes/shared/RequiredAttributePicker';
import { findRequiredErrors } from '../nodes/shared/paramValidation'; import { findRequiredErrors } from '../nodes/shared/paramValidation';
import { useAutomation2DataFlow } from '../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../context/WorkflowDataFlowContext';
import styles from './Automation2FlowEditor.module.css'; import styles from './WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';
import { AccordionList } from '../../UiComponents/AccordionList'; import { AccordionList } from '../../UiComponents/AccordionList';
@ -210,7 +210,7 @@ export const NodeConfigPanel: React.FC<NodeConfigPanelProps> = ({ node,
[onParametersChange] [onParametersChange]
); );
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
const portTypeCatalog: Record<string, PortSchema> = (dataFlow?.portTypeCatalog as Record<string, PortSchema> | undefined) ?? {}; const portTypeCatalog: Record<string, PortSchema> = (dataFlow?.portTypeCatalog as Record<string, PortSchema> | undefined) ?? {};
// Phase-4 Schicht-4 — Pflicht-Params zuerst sortieren, damit der User // Phase-4 Schicht-4 — Pflicht-Params zuerst sortieren, damit der User

View file

@ -4,11 +4,11 @@
*/ */
import React from 'react'; import React from 'react';
import type { NodeType } from '../../../api/workflowApi'; import type { NodeType } from '../../../api/workflowAutomationApi';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';
import { getCategoryIcon } from '../nodes/shared/utils'; import { getCategoryIcon } from '../nodes/shared/utils';
import type { GetLabelFn } from '../nodes/shared/utils'; import type { GetLabelFn } from '../nodes/shared/utils';
import styles from './Automation2FlowEditor.module.css'; import styles from './WorkflowFlowEditor.module.css';
import { AiBadge } from '../nodes/shared/AiBadge'; import { AiBadge } from '../nodes/shared/AiBadge';
interface NodeListItemProps { interface NodeListItemProps {

View file

@ -5,11 +5,11 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { FaChevronDown, FaChevronRight } from 'react-icons/fa'; import { FaChevronDown, FaChevronRight } from 'react-icons/fa';
import type { NodeType, NodeTypeCategory } from '../../../api/workflowApi'; import type { NodeType, NodeTypeCategory } from '../../../api/workflowAutomationApi';
import { CATEGORY_ORDER, HIDDEN_NODE_IDS } from '../nodes/shared/constants'; import { CATEGORY_ORDER, HIDDEN_NODE_IDS } from '../nodes/shared/constants';
import { getLabel } from '../nodes/shared/utils'; import { getLabel } from '../nodes/shared/utils';
import { NodeListItem } from './NodeListItem'; import { NodeListItem } from './NodeListItem';
import styles from './Automation2FlowEditor.module.css'; import styles from './WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';

View file

@ -7,7 +7,7 @@
*/ */
import React, { useState, useEffect, useCallback, useRef } from 'react'; import React, { useState, useEffect, useCallback, useRef } from 'react';
import { useApiRequest } from '../../../hooks/useApi'; import { useApiRequest } from '../../../hooks/useApi';
import type { AutoStepLog } from '../../../api/workflowApi'; import type { AutoStepLog } from '../../../api/workflowAutomationApi';
import api from '../../../api'; import api from '../../../api';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';

View file

@ -9,8 +9,8 @@ import {
type AutoWorkflowTemplate, type AutoWorkflowTemplate,
type AutoTemplateScope, type AutoTemplateScope,
type ApiRequestFunction, type ApiRequestFunction,
} from '../../../api/workflowApi'; } from '../../../api/workflowAutomationApi';
import styles from './Automation2FlowEditor.module.css'; import styles from './WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';
interface TemplatePickerProps { interface TemplatePickerProps {

View file

@ -1,5 +1,5 @@
/** /**
* Automation2 Flow Editor Styles * Workflow Flow Editor Styles
* Sidebar with node list + canvas area. * Sidebar with node list + canvas area.
*/ */

View file

@ -1,5 +1,5 @@
/** /**
* Automation2FlowEditor * WorkflowFlowEditor
* *
* n8n-style flow builder with backend-driven node list and categories. * n8n-style flow builder with backend-driven node list and categories.
* Start nodes come from the API (category `start`); invocations are synced on the server from the graph. * Start nodes come from the API (category `start`); invocations are synced on the server from the graph.
@ -26,13 +26,13 @@ import {
WORKFLOW_FILE_EXTENSION, WORKFLOW_FILE_EXTENSION,
type NodeType, type NodeType,
type NodeTypeCategory, type NodeTypeCategory,
type Automation2Graph, type WorkflowGraph,
type Automation2Workflow, type WorkflowDefinition,
type ExecuteGraphResponse, type ExecuteGraphResponse,
type WorkflowEntryPoint, type WorkflowEntryPoint,
type AutoVersion, type AutoVersion,
type AutoTemplateScope, type AutoTemplateScope,
} from '../../../api/workflowApi'; } from '../../../api/workflowAutomationApi';
import { import {
FlowCanvas, FlowCanvas,
type CanvasNode, type CanvasNode,
@ -50,7 +50,7 @@ import { fromApiGraph, toApiGraph, switchOutputCountFromCases, trimConnectionsFo
import { buildNodeOutputsPreview, setPortTypeCatalog as setRegistryCatalog } from '../nodes/shared/outputPreviewRegistry'; import { buildNodeOutputsPreview, setPortTypeCatalog as setRegistryCatalog } from '../nodes/shared/outputPreviewRegistry';
import { findGraphErrors } from '../nodes/shared/paramValidation'; import { findGraphErrors } from '../nodes/shared/paramValidation';
import { getLabel as getParamLabel } from '../nodes/shared/utils'; import { getLabel as getParamLabel } from '../nodes/shared/utils';
import { Automation2DataFlowProvider } from '../context/Automation2DataFlowContext'; import { WorkflowDataFlowProvider } from '../context/WorkflowDataFlowContext';
import { usePrompt } from '../../../hooks/usePrompt'; import { usePrompt } from '../../../hooks/usePrompt';
import { EditorChatPanel } from './EditorChatPanel'; import { EditorChatPanel } from './EditorChatPanel';
import type { PendingFile, EditorDataSource, EditorFeatureDataSource } from './EditorChatPanel'; import type { PendingFile, EditorDataSource, EditorFeatureDataSource } from './EditorChatPanel';
@ -58,12 +58,12 @@ import { EditorWorkflowChatList } from './EditorWorkflowChatList';
import { RunTracingPanel } from './RunTracingPanel'; import { RunTracingPanel } from './RunTracingPanel';
import { UnifiedDataBar } from '../../../components/UnifiedDataBar'; import { UnifiedDataBar } from '../../../components/UnifiedDataBar';
import type { UdbContext, UdbTab } from '../../../components/UnifiedDataBar'; import type { UdbContext, UdbTab } from '../../../components/UnifiedDataBar';
import styles from './Automation2FlowEditor.module.css'; import styles from './WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';
const LOG = '[Automation2]'; const LOG = '[WorkflowEditor]';
const CANVAS_HISTORY_MAX = 50; const CANVAS_HISTORY_MAX = 50;
@ -79,7 +79,7 @@ function cloneCanvasSnapshot(nodes: CanvasNode[], connections: CanvasConnection[
}; };
} }
interface Automation2FlowEditorProps { interface WorkflowFlowEditorProps {
instanceId: string; instanceId: string;
mandateId?: string; mandateId?: string;
language?: string; language?: string;
@ -93,7 +93,7 @@ interface Automation2FlowEditorProps {
onSourcesChanged?: () => void; onSourcesChanged?: () => void;
} }
export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ instanceId, export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instanceId,
mandateId, mandateId,
language = 'de', language = 'de',
initialWorkflowId, initialWorkflowId,
@ -111,9 +111,9 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
const [categories, setCategories] = useState<NodeTypeCategory[]>([]); const [categories, setCategories] = useState<NodeTypeCategory[]>([]);
const [portTypeCatalog, setPortTypeCatalog] = useState<Record<string, unknown>>({}); const [portTypeCatalog, setPortTypeCatalog] = useState<Record<string, unknown>>({});
const [systemVariables, setSystemVariables] = useState<Record<string, unknown>>({}); const [systemVariables, setSystemVariables] = useState<Record<string, unknown>>({});
const [formFieldTypes, setFormFieldTypes] = useState<import('../../../api/workflowApi').FormFieldType[]>([]); const [formFieldTypes, setFormFieldTypes] = useState<import('../../../api/workflowAutomationApi').FormFieldType[]>([]);
const [conditionOperatorCatalog, setConditionOperatorCatalog] = useState< const [conditionOperatorCatalog, setConditionOperatorCatalog] = useState<
Record<string, import('../../../api/workflowApi').ConditionOperatorDef[]> Record<string, import('../../../api/workflowAutomationApi').ConditionOperatorDef[]>
>({}); >({});
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@ -138,7 +138,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
const [canvasStickyNotes, setCanvasStickyNotes] = useState<CanvasStickyNote[]>([]); const [canvasStickyNotes, setCanvasStickyNotes] = useState<CanvasStickyNote[]>([]);
const [executing, setExecuting] = useState(false); const [executing, setExecuting] = useState(false);
const [executeResult, setExecuteResult] = useState<ExecuteGraphResponse | null>(null); const [executeResult, setExecuteResult] = useState<ExecuteGraphResponse | null>(null);
const [workflows, setWorkflows] = useState<Automation2Workflow[]>([]); const [workflows, setWorkflows] = useState<WorkflowDefinition[]>([]);
const [currentWorkflowId, setCurrentWorkflowId] = useState<string | null>(null); const [currentWorkflowId, setCurrentWorkflowId] = useState<string | null>(null);
const [selectedNode, setSelectedNode] = useState<CanvasNode | null>(null); const [selectedNode, setSelectedNode] = useState<CanvasNode | null>(null);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
@ -302,7 +302,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
const applyGraphWithSync = useCallback( const applyGraphWithSync = useCallback(
( (
graph: Automation2Graph | null | undefined, graph: WorkflowGraph | null | undefined,
wfInvocations: WorkflowEntryPoint[] | undefined, wfInvocations: WorkflowEntryPoint[] | undefined,
opts?: { skipHistory?: boolean } opts?: { skipHistory?: boolean }
) => { ) => {
@ -310,7 +310,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
pushCanvasHistoryPastFromCurrent(); pushCanvasHistoryPastFromCurrent();
} }
setInvocations(wfInvocations ?? []); setInvocations(wfInvocations ?? []);
const g: Automation2Graph = graph ?? { nodes: [], connections: [] }; const g: WorkflowGraph = graph ?? { nodes: [], connections: [] };
const { nodes, connections } = fromApiGraph(g, nodeTypes); const { nodes, connections } = fromApiGraph(g, nodeTypes);
setCanvasNodes(nodes); setCanvasNodes(nodes);
setCanvasConnections(connections); setCanvasConnections(connections);
@ -319,7 +319,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
); );
const handleFromApiGraph = useCallback( const handleFromApiGraph = useCallback(
(graph: Automation2Graph, wfInvocations?: WorkflowEntryPoint[]) => { (graph: WorkflowGraph, wfInvocations?: WorkflowEntryPoint[]) => {
applyGraphWithSync(graph, wfInvocations); applyGraphWithSync(graph, wfInvocations);
}, },
[applyGraphWithSync] [applyGraphWithSync]
@ -1046,7 +1046,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
</div> </div>
{configurableSelected && selectedNode && ( {configurableSelected && selectedNode && (
<div className={styles.nodeConfigPanelWrap} data-suppress-flow-node-hotkeys=""> <div className={styles.nodeConfigPanelWrap} data-suppress-flow-node-hotkeys="">
<Automation2DataFlowProvider <WorkflowDataFlowProvider
node={selectedNode} node={selectedNode}
nodes={canvasNodes} nodes={canvasNodes}
connections={canvasConnections} connections={canvasConnections}
@ -1071,7 +1071,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
request={request} request={request}
verboseSchema={verboseSchema} verboseSchema={verboseSchema}
/> />
</Automation2DataFlowProvider> </WorkflowDataFlowProvider>
</div> </div>
)} )}
</div> </div>
@ -1126,4 +1126,4 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
); );
}; };
export default Automation2FlowEditor; export default WorkflowFlowEditor;

View file

@ -1,4 +1,4 @@
export { Automation2FlowEditor, Automation2FlowEditor as FlowEditor } from './editor/Automation2FlowEditor'; export { WorkflowFlowEditor, WorkflowFlowEditor as FlowEditor } from './editor/WorkflowFlowEditor';
export type { PendingFile, EditorDataSource, EditorFeatureDataSource } from './editor/EditorChatPanel'; export type { PendingFile, EditorDataSource, EditorFeatureDataSource } from './editor/EditorChatPanel';
export { FlowCanvas, STICKY_NOTE_PALETTE, STICKY_NOTE_DEFAULT_COLOR_ID, STICKY_NOTE_DEFAULT_HEIGHT, getStickyNotePaletteEntry } from './editor/FlowCanvas'; export { FlowCanvas, STICKY_NOTE_PALETTE, STICKY_NOTE_DEFAULT_COLOR_ID, STICKY_NOTE_DEFAULT_HEIGHT, getStickyNotePaletteEntry } from './editor/FlowCanvas';
export type { CanvasNode, CanvasConnection, CanvasStickyNote, FlowCanvasHandle, FlowCanvasViewportEditState } from './editor/FlowCanvas'; export type { CanvasNode, CanvasConnection, CanvasStickyNote, FlowCanvasHandle, FlowCanvasViewportEditState } from './editor/FlowCanvas';

View file

@ -6,8 +6,8 @@ import React from 'react';
import { FaGripVertical, FaTimes } from 'react-icons/fa'; import { FaGripVertical, FaTimes } from 'react-icons/fa';
import type { FormField, NodeConfigRendererProps } from '../shared/types'; import type { FormField, NodeConfigRendererProps } from '../shared/types';
import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper'; import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper';
import styles from '../../editor/Automation2FlowEditor.module.css'; import styles from '../../editor/WorkflowFlowEditor.module.css';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { FormFieldOptionsEditor } from './FormFieldOptionsEditor'; import { FormFieldOptionsEditor } from './FormFieldOptionsEditor';
import { import {
deriveFormFieldPayloadKey, deriveFormFieldPayloadKey,
@ -19,7 +19,7 @@ import { useLanguage } from '../../../../providers/language/LanguageContext';
export const FormNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => { export const FormNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const ctx = useAutomation2DataFlow(); const ctx = useWorkflowDataFlow();
const fieldTypeOptions = ctx?.formFieldTypes?.length const fieldTypeOptions = ctx?.formFieldTypes?.length
? ctx.formFieldTypes ? ctx.formFieldTypes
: FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' })); : FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' }));

View file

@ -4,10 +4,10 @@
import React from 'react'; import React from 'react';
import type { FieldRendererProps } from './index'; import type { FieldRendererProps } from './index';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { isRef, type DataRef } from '../shared/dataRef'; import { isRef, type DataRef } from '../shared/dataRef';
import { toApiGraph } from '../shared/graphUtils'; import { toApiGraph } from '../shared/graphUtils';
import { fetchConditionMeta, type ConditionOperatorDef } from '../../../../api/workflowApi'; import { fetchConditionMeta, type ConditionOperatorDef } from '../../../../api/workflowAutomationApi';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
export interface SwitchCase { export interface SwitchCase {
@ -116,7 +116,7 @@ export const CaseListEditor: React.FC<FieldRendererProps> = ({
allParams, allParams,
}) => { }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
const dependsOn = const dependsOn =
param.frontendOptions && typeof param.frontendOptions === 'object' param.frontendOptions && typeof param.frontendOptions === 'object'
? String((param.frontendOptions as Record<string, unknown>).dependsOn ?? 'value') ? String((param.frontendOptions as Record<string, unknown>).dependsOn ?? 'value')

View file

@ -4,11 +4,8 @@
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
import { import { fetchBrowse, type BrowseEntry } from '../../../../api/workflowAutomationApi';
fetchBrowse, import { fetchClickupList } from '../../../../api/clickupApi';
fetchClickupList,
type BrowseEntry,
} from '../../../../api/workflowApi';
import type { FieldRendererProps } from './index'; import type { FieldRendererProps } from './index';
import { import {
clickupBrowseParentPath, clickupBrowseParentPath,

View file

@ -4,10 +4,10 @@
import React from 'react'; import React from 'react';
import type { FieldRendererProps } from './index'; import type { FieldRendererProps } from './index';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { isRef, type DataRef } from '../shared/dataRef'; import { isRef, type DataRef } from '../shared/dataRef';
import { toApiGraph } from '../shared/graphUtils'; import { toApiGraph } from '../shared/graphUtils';
import { fetchConditionMeta, type ConditionOperatorDef } from '../../../../api/workflowApi'; import { fetchConditionMeta, type ConditionOperatorDef } from '../../../../api/workflowAutomationApi';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
export interface StructuredCondition { export interface StructuredCondition {
@ -41,7 +41,7 @@ export const ConditionEditor: React.FC<FieldRendererProps> = ({
allParams, allParams,
}) => { }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
const dependsOn = const dependsOn =
param.frontendOptions && typeof param.frontendOptions === 'object' param.frontendOptions && typeof param.frontendOptions === 'object'
? String((param.frontendOptions as Record<string, unknown>).dependsOn ?? 'Item') ? String((param.frontendOptions as Record<string, unknown>).dependsOn ?? 'Item')

View file

@ -5,7 +5,7 @@
import React from 'react'; import React from 'react';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { DataPicker } from '../shared/DataPicker'; import { DataPicker } from '../shared/DataPicker';
import { isRef, isSystemVar, type DataRef, type SystemVarRef } from '../shared/dataRef'; import { isRef, isSystemVar, type DataRef, type SystemVarRef } from '../shared/dataRef';
import type { FieldRendererProps } from './index'; import type { FieldRendererProps } from './index';
@ -174,7 +174,7 @@ const REMOVE_BTN: React.CSSProperties = {
export const ContextAssignmentsEditor: React.FC<FieldRendererProps> = ({ param, value, onChange, allParams }) => { export const ContextAssignmentsEditor: React.FC<FieldRendererProps> = ({ param, value, onChange, allParams }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
const rows = normalizeRows(value, allParams); const rows = normalizeRows(value, allParams);
const [pickerRow, setPickerRow] = React.useState<number | null>(null); const [pickerRow, setPickerRow] = React.useState<number | null>(null);

View file

@ -11,7 +11,7 @@
import React from 'react'; import React from 'react';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { DataPicker } from '../shared/DataPicker'; import { DataPicker } from '../shared/DataPicker';
import { isRef, type DataRef, type SystemVarRef } from '../shared/dataRef'; import { isRef, type DataRef, type SystemVarRef } from '../shared/dataRef';
import type { FieldRendererProps } from './index'; import type { FieldRendererProps } from './index';
@ -52,7 +52,7 @@ const REMOVE_BTN: React.CSSProperties = {
export const ContextBuilderRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => { export const ContextBuilderRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
const [pickerOpen, setPickerOpen] = React.useState(false); const [pickerOpen, setPickerOpen] = React.useState(false);
const dragIndex = React.useRef<number | null>(null); const dragIndex = React.useRef<number | null>(null);

View file

@ -10,14 +10,14 @@
import React from 'react'; import React from 'react';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { DataPicker } from '../shared/DataPicker'; import { DataPicker } from '../shared/DataPicker';
import { isRef, type DataRef, type SystemVarRef } from '../shared/dataRef'; import { isRef, type DataRef, type SystemVarRef } from '../shared/dataRef';
import type { FieldRendererProps } from './index'; import type { FieldRendererProps } from './index';
export const DataRefRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => { export const DataRefRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
const [pickerOpen, setPickerOpen] = React.useState(false); const [pickerOpen, setPickerOpen] = React.useState(false);
const currentRef = isRef(value) ? (value as DataRef) : null; const currentRef = isRef(value) ? (value as DataRef) : null;

View file

@ -5,11 +5,11 @@
import React, { useCallback, useMemo, useRef, useState } from 'react'; import React, { useCallback, useMemo, useRef, useState } from 'react';
import type { FieldRendererProps } from './index'; import type { FieldRendererProps } from './index';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { DataPicker } from '../shared/DataPicker'; import { DataPicker } from '../shared/DataPicker';
import { formatRefLabel, isRef, isSystemVar, type DataRef, type SystemVarRef } from '../shared/dataRef'; import { formatRefLabel, isRef, isSystemVar, type DataRef, type SystemVarRef } from '../shared/dataRef';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
import styles from '../../editor/Automation2FlowEditor.module.css'; import styles from '../../editor/WorkflowFlowEditor.module.css';
const _TEMPLATE_TOKEN_RE = /\{\{\s*([^}]+?)\s*\}\}/g; const _TEMPLATE_TOKEN_RE = /\{\{\s*([^}]+?)\s*\}\}/g;
@ -60,7 +60,7 @@ function _parseTokensInTemplate(
export const TemplateTextareaRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => { export const TemplateTextareaRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
const textareaRef = useRef<HTMLTextAreaElement>(null); const textareaRef = useRef<HTMLTextAreaElement>(null);
const [pickerOpen, setPickerOpen] = useState(false); const [pickerOpen, setPickerOpen] = useState(false);

View file

@ -4,10 +4,10 @@
*/ */
import type { ComponentType } from 'react'; import type { ComponentType } from 'react';
import type { NodeTypeParameter } from '../../../../api/workflowApi'; import type { NodeTypeParameter } from '../../../../api/workflowAutomationApi';
import type { ApiRequestFunction } from '../../../../api/workflowApi'; import type { ApiRequestFunction } from '../../../../api/workflowAutomationApi';
import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper'; import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { FormFieldOptionsEditor } from '../form/FormFieldOptionsEditor'; import { FormFieldOptionsEditor } from '../form/FormFieldOptionsEditor';
import { import {
deriveFormFieldPayloadKey, deriveFormFieldPayloadKey,
@ -46,7 +46,7 @@ import {
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
import { toApiGraph } from '../shared/graphUtils'; import { toApiGraph } from '../shared/graphUtils';
import { postUpstreamPaths } from '../../../../api/workflowApi'; import { postUpstreamPaths } from '../../../../api/workflowAutomationApi';
import type { CanvasNode } from '../../editor/FlowCanvas'; import type { CanvasNode } from '../../editor/FlowCanvas';
import { DataRefRenderer } from './DataRefRenderer'; import { DataRefRenderer } from './DataRefRenderer';
import { ContextBuilderRenderer } from './ContextBuilderRenderer'; import { ContextBuilderRenderer } from './ContextBuilderRenderer';
@ -300,7 +300,7 @@ const HiddenInput: React.FC<FieldRendererProps> = () => null;
const ConnectionPicker: React.FC<FieldRendererProps> = ({ param, value, onChange, instanceId, request }) => { const ConnectionPicker: React.FC<FieldRendererProps> = ({ param, value, onChange, instanceId, request }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
const [connections, setConnections] = React.useState<Array<{ id: string; label: string }>>([]); const [connections, setConnections] = React.useState<Array<{ id: string; label: string }>>([]);
const [loadError, setLoadError] = React.useState<string | null>(null); const [loadError, setLoadError] = React.useState<string | null>(null);
const [upstreamBindOptions, setUpstreamBindOptions] = React.useState<Array<{ key: string; label: string; ref: unknown }>>([]); const [upstreamBindOptions, setUpstreamBindOptions] = React.useState<Array<{ key: string; label: string; ref: unknown }>>([]);
@ -643,7 +643,7 @@ const SharepointPathPicker: React.FC<FieldRendererProps> = ({ param, value, onCh
const FieldBuilderEditor: React.FC<FieldRendererProps> = ({ param, value, onChange }) => { const FieldBuilderEditor: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const ctx = useAutomation2DataFlow(); const ctx = useWorkflowDataFlow();
const fieldTypeOptions = ctx?.formFieldTypes?.length const fieldTypeOptions = ctx?.formFieldTypes?.length
? ctx.formFieldTypes ? ctx.formFieldTypes
: FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' })); : FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' }));

View file

@ -7,7 +7,7 @@ import React from 'react';
import type { NodeConfigRendererProps } from '../shared/types'; import type { NodeConfigRendererProps } from '../shared/types';
import { LoopItemsSelect } from '../shared/LoopItemsSelect'; import { LoopItemsSelect } from '../shared/LoopItemsSelect';
import { createValue, isRef, isValue } from '../shared/dataRef'; import { createValue, isRef, isValue } from '../shared/dataRef';
import styles from '../../editor/Automation2FlowEditor.module.css'; import styles from '../../editor/WorkflowFlowEditor.module.css';
export const LoopNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => { export const LoopNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => {
const value = params.items; const value = params.items;

View file

@ -1,5 +1,5 @@
/** /**
* Automation2 Flow Editor - Schema-based Data Picker. * Workflow Flow Editor - Schema-based Data Picker.
* Builds pickable paths from portTypeCatalog + node outputPorts, or from * Builds pickable paths from portTypeCatalog + node outputPorts, or from
* outputPorts[n].dataPickOptions when the backend defines an explicit list (authoritative). * outputPorts[n].dataPickOptions when the backend defines an explicit list (authoritative).
* Resolves Transit chains to show the real upstream schema. * Resolves Transit chains to show the real upstream schema.
@ -9,10 +9,10 @@
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { createRef, createSystemVar, type DataRef, type SystemVarRef, isCompatible } from './dataRef'; import { createRef, createSystemVar, type DataRef, type SystemVarRef, isCompatible } from './dataRef';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import type { DataPickOption, GraphDataSources, GraphDefinedSchemaRef, NodeType, PortField, PortSchema } from '../../../../api/workflowApi'; import type { DataPickOption, GraphDataSources, GraphDefinedSchemaRef, NodeType, PortField, PortSchema } from '../../../../api/workflowAutomationApi';
import { fetchGraphDataSources } from '../../../../api/workflowApi'; import { fetchGraphDataSources } from '../../../../api/workflowAutomationApi';
import styles from '../../editor/Automation2FlowEditor.module.css'; import styles from '../../editor/WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
@ -268,7 +268,7 @@ export const DataPicker: React.FC<DataPickerProps> = ({ open,
// Default: when the consumer declares an expected type, show only compatible // Default: when the consumer declares an expected type, show only compatible
// candidates ("strict" mode). User can override per-session via the toggle. // candidates ("strict" mode). User can override per-session via the toggle.
const [strictFilter, setStrictFilter] = useState<boolean>(Boolean(expectedParamType)); const [strictFilter, setStrictFilter] = useState<boolean>(Boolean(expectedParamType));
const ctx = useAutomation2DataFlow(); const ctx = useWorkflowDataFlow();
// NOTE: All hooks must be called unconditionally on every render to satisfy // NOTE: All hooks must be called unconditionally on every render to satisfy
// the Rules of Hooks. The `if (!open) return null;` early-return therefore // the Rules of Hooks. The `if (!open) return null;` early-return therefore
@ -361,10 +361,10 @@ export const DataPicker: React.FC<DataPickerProps> = ({ open,
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-labelledby="automation2DataPickerTitle" aria-labelledby="workflowDataPickerTitle"
> >
<div className={styles.dataPickerHeader}> <div className={styles.dataPickerHeader}>
<h4 className={styles.dataPickerTitle} id="automation2DataPickerTitle"> <h4 className={styles.dataPickerTitle} id="workflowDataPickerTitle">
{t('Datenquelle wählen')} {t('Datenquelle wählen')}
{expectedParamType && ( {expectedParamType && (
<span <span

View file

@ -1,5 +1,5 @@
/** /**
* Automation2 Flow Editor - Field that supports node reference only (no static value). * Workflow Flow Editor - Field that supports node reference only (no static value).
*/ */
import React, { useState } from 'react'; import React, { useState } from 'react';
@ -12,8 +12,8 @@ import {
} from './dataRef'; } from './dataRef';
import { RefSourceSelect } from './RefSourceSelect'; import { RefSourceSelect } from './RefSourceSelect';
import { DataPicker } from './DataPicker'; import { DataPicker } from './DataPicker';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import styles from '../../editor/Automation2FlowEditor.module.css'; import styles from '../../editor/WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
@ -39,7 +39,7 @@ export const DynamicValueField: React.FC<DynamicValueFieldProps> = ({ paramKey,
variant = 'picker', variant = 'picker',
}) => { }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
const [pickerOpen, setPickerOpen] = useState(false); const [pickerOpen, setPickerOpen] = useState(false);
const ref: DataRef | null = isRef(value) ? value : null; const ref: DataRef | null = isRef(value) ? value : null;

View file

@ -9,9 +9,9 @@ import {
shouldShowStaticControl, shouldShowStaticControl,
type PathPickMode, type PathPickMode,
} from './RefSourceSelect'; } from './RefSourceSelect';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { isRef, isValue, createValue } from './dataRef'; import { isRef, isValue, createValue } from './dataRef';
import styles from '../../editor/Automation2FlowEditor.module.css'; import styles from '../../editor/WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
@ -48,7 +48,7 @@ export const HybridStaticRefField: React.FC<HybridStaticRefFieldProps> = ({ labe
pathPickMode = 'default', pathPickMode = 'default',
}) => { }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
const hasSources = const hasSources =
dataFlow && dataFlow &&
dataFlow.getAvailableSourceIds().some((id) => { dataFlow.getAvailableSourceIds().some((id) => {

View file

@ -6,8 +6,8 @@
import React from 'react'; import React from 'react';
import { createRef, isRef, type DataRef } from './dataRef'; import { createRef, isRef, type DataRef } from './dataRef';
import { refToOptionValue, optionValueToRef } from './RefSourceSelect'; import { refToOptionValue, optionValueToRef } from './RefSourceSelect';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import styles from '../../editor/Automation2FlowEditor.module.css'; import styles from '../../editor/WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
@ -161,7 +161,7 @@ export const LoopItemsSelect: React.FC<LoopItemsSelectProps> = ({ value,
placeholder, placeholder,
}) => { }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
if (!dataFlow) return null; if (!dataFlow) return null;
const sourceIds = dataFlow.getAvailableSourceIds(); const sourceIds = dataFlow.getAvailableSourceIds();

View file

@ -5,7 +5,7 @@
import React from 'react'; import React from 'react';
import { createRef, isRef, isValue, createValue, type DataRef } from './dataRef'; import { createRef, isRef, isValue, createValue, type DataRef } from './dataRef';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
/** How to build path options for StatischKontextSelect / RefSourceSelect. */ /** How to build path options for StatischKontextSelect / RefSourceSelect. */
@ -238,7 +238,7 @@ export const StatischKontextSelect: React.FC<StatischKontextSelectProps> = ({
pathPickMode = 'default', pathPickMode = 'default',
}) => { }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
if (!dataFlow) return null; if (!dataFlow) return null;
const sourceIds = dataFlow.getAvailableSourceIds(); const sourceIds = dataFlow.getAvailableSourceIds();
@ -316,7 +316,7 @@ export const RefSourceSelect: React.FC<RefSourceSelectProps> = ({
pathPickMode = 'default', pathPickMode = 'default',
}) => { }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const dataFlow = useAutomation2DataFlow(); const dataFlow = useWorkflowDataFlow();
if (!dataFlow) return null; if (!dataFlow) return null;
const sourceIds = dataFlow.getAvailableSourceIds(); const sourceIds = dataFlow.getAvailableSourceIds();

View file

@ -15,11 +15,11 @@
*/ */
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { DataPicker } from './DataPicker'; import { DataPicker } from './DataPicker';
import { createRef, formatRefLabel, isRef, type DataRef, type SystemVarRef } from './dataRef'; import { createRef, formatRefLabel, isRef, type DataRef, type SystemVarRef } from './dataRef';
import { findSourceCandidates, strictlyCompatible, type SourceCandidate } from './paramValidation'; import { findSourceCandidates, strictlyCompatible, type SourceCandidate } from './paramValidation';
import styles from '../../editor/Automation2FlowEditor.module.css'; import styles from '../../editor/WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
@ -44,7 +44,7 @@ export const RequiredAttributePicker: React.FC<RequiredAttributePickerProps> = (
description, description,
}) => { }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const ctx = useAutomation2DataFlow(); const ctx = useWorkflowDataFlow();
const [pickerOpen, setPickerOpen] = useState(false); const [pickerOpen, setPickerOpen] = useState(false);
const consumerNodeId = ctx?.currentNodeId ?? ''; const consumerNodeId = ctx?.currentNodeId ?? '';

View file

@ -1,5 +1,5 @@
/** /**
* Automation2 Flow Editor - Constants * Workflow Flow Editor - Constants
* Category ordering for node sidebar. * Category ordering for node sidebar.
*/ */

View file

@ -1,5 +1,5 @@
/** /**
* Automation2 Flow Editor - Graph helpers for data flow (ancestors, topo order). * Workflow Flow Editor - Graph helpers for data flow (ancestors, topo order).
*/ */
import type { CanvasNode, CanvasConnection } from '../../editor/FlowCanvas'; import type { CanvasNode, CanvasConnection } from '../../editor/FlowCanvas';

View file

@ -1,5 +1,5 @@
/** /**
* Automation2 Flow Editor - Data reference format and helpers. * Workflow Flow Editor - Data reference format and helpers.
* All dynamic values use structured ref/value objects, not plain strings. * All dynamic values use structured ref/value objects, not plain strings.
*/ */

View file

@ -1,15 +1,15 @@
/** /**
* Automation2 Flow Editor - Graph conversion utilities * Workflow Flow Editor - Graph conversion utilities
* Converts between API graph format and canvas internal format. * Converts between API graph format and canvas internal format.
*/ */
import type { import type {
NodeType, NodeType,
Automation2Graph, WorkflowGraph,
Automation2GraphNode, WorkflowGraphNode,
Automation2Connection, WorkflowConnection,
GraphDefinedSchemaRef, GraphDefinedSchemaRef,
} from '../../../../api/workflowApi'; } from '../../../../api/workflowAutomationApi';
import type { CanvasNode, CanvasConnection } from '../../editor/FlowCanvas'; import type { CanvasNode, CanvasConnection } from '../../editor/FlowCanvas';
/** Switch: one output per case plus a default (``Sonst``) port. */ /** Switch: one output per case plus a default (``Sonst``) port. */
@ -46,7 +46,7 @@ export function switchOutputLabel(
} }
export function fromApiGraph( export function fromApiGraph(
graph: Automation2Graph, graph: WorkflowGraph,
nodeTypes: NodeType[] nodeTypes: NodeType[]
): { nodes: CanvasNode[]; connections: CanvasConnection[] } { ): { nodes: CanvasNode[]; connections: CanvasConnection[] } {
const nodeMap = new Map<string, { inputs: number; outputs: number }>(); const nodeMap = new Map<string, { inputs: number; outputs: number }>();
@ -54,7 +54,7 @@ export function fromApiGraph(
nodeMap.set(nt.id, { inputs: nt.inputs ?? 1, outputs: nt.outputs ?? 1 }); nodeMap.set(nt.id, { inputs: nt.inputs ?? 1, outputs: nt.outputs ?? 1 });
}); });
const nodes: CanvasNode[] = (graph.nodes || []).map((n: Automation2GraphNode) => { const nodes: CanvasNode[] = (graph.nodes || []).map((n: WorkflowGraphNode) => {
const io = nodeMap.get(n.type) ?? { inputs: 1, outputs: 1 }; const io = nodeMap.get(n.type) ?? { inputs: 1, outputs: 1 };
let outputs = io.outputs; let outputs = io.outputs;
if (n.type === 'flow.switch') { if (n.type === 'flow.switch') {
@ -85,7 +85,7 @@ export function fromApiGraph(
}); });
const connId = (s: string, t: string, so: number, ti: number) => `c_${s}_${so}_${t}_${ti}`; const connId = (s: string, t: string, so: number, ti: number) => `c_${s}_${so}_${t}_${ti}`;
const connections: CanvasConnection[] = (graph.connections || []).map((c: Automation2Connection) => { const connections: CanvasConnection[] = (graph.connections || []).map((c: WorkflowConnection) => {
const srcNode = nodes.find((n) => n.id === c.source); const srcNode = nodes.find((n) => n.id === c.source);
const sourceOutput = c.sourceOutput ?? 0; const sourceOutput = c.sourceOutput ?? 0;
const sourceHandle = srcNode ? srcNode.inputs + sourceOutput : 0; const sourceHandle = srcNode ? srcNode.inputs + sourceOutput : 0;
@ -104,7 +104,7 @@ export function fromApiGraph(
export function toApiGraph( export function toApiGraph(
nodes: CanvasNode[], nodes: CanvasNode[],
connections: CanvasConnection[] connections: CanvasConnection[]
): Automation2Graph { ): WorkflowGraph {
const nodeMap = new Map(nodes.map((n) => [n.id, n])); const nodeMap = new Map(nodes.map((n) => [n.id, n]));
return { return {
nodes: nodes.map((n) => ({ nodes: nodes.map((n) => ({

View file

@ -1,10 +1,10 @@
/** /**
* Automation2 Flow Editor - Schema-based output preview builders. * Workflow Flow Editor - Schema-based output preview builders.
* Derives preview trees from portTypeCatalog + node outputPorts. * Derives preview trees from portTypeCatalog + node outputPorts.
*/ */
import type { CanvasNode } from '../../editor/FlowCanvas'; import type { CanvasNode } from '../../editor/FlowCanvas';
import type { PortSchema, NodeType } from '../../../../api/workflowApi'; import type { PortSchema, NodeType } from '../../../../api/workflowAutomationApi';
let _portTypeCatalog: Record<string, PortSchema> = {}; let _portTypeCatalog: Record<string, PortSchema> = {};

View file

@ -18,7 +18,7 @@
*/ */
import type { CanvasConnection, CanvasNode } from '../../editor/FlowCanvas'; import type { CanvasConnection, CanvasNode } from '../../editor/FlowCanvas';
import type { GraphDefinedSchemaRef, NodeType, NodeTypeParameter, OutputPortDef, PortSchema } from '../../../../api/workflowApi'; import type { GraphDefinedSchemaRef, NodeType, NodeTypeParameter, OutputPortDef, PortSchema } from '../../../../api/workflowAutomationApi';
import { isCompatible, isRef, isSystemVar, isValue } from './dataRef'; import { isCompatible, isRef, isSystemVar, isValue } from './dataRef';
import { getAvailableSources } from './dataFlowGraph'; import { getAvailableSources } from './dataFlowGraph';

View file

@ -2,7 +2,7 @@
* Shared types for node config renderers * Shared types for node config renderers
*/ */
import type { ApiRequestFunction } from '../../../../api/workflowApi'; import type { ApiRequestFunction } from '../../../../api/workflowAutomationApi';
import type { AttributeType } from '../../../../utils/attributeTypeMapper'; import type { AttributeType } from '../../../../utils/attributeTypeMapper';
/** input.form / trigger.form field row. */ /** input.form / trigger.form field row. */

View file

@ -1,5 +1,5 @@
/** /**
* Automation2 Flow Editor - Utility functions * Workflow Flow Editor - Utility functions
*/ */
import type React from 'react'; import type React from 'react';

View file

@ -6,8 +6,8 @@ import React, { useMemo } from 'react';
import type { NodeConfigRendererProps } from '../shared/types'; import type { NodeConfigRendererProps } from '../shared/types';
import type { FormField } from '../shared/types'; import type { FormField } from '../shared/types';
import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper'; import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper';
import styles from '../../editor/Automation2FlowEditor.module.css'; import styles from '../../editor/WorkflowFlowEditor.module.css';
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext'; import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
import { FormFieldOptionsEditor } from '../form/FormFieldOptionsEditor'; import { FormFieldOptionsEditor } from '../form/FormFieldOptionsEditor';
import { import {
deriveFormFieldPayloadKey, deriveFormFieldPayloadKey,
@ -37,7 +37,7 @@ function _parseFields(params: Record<string, unknown>, t: (key: string) => strin
export const FormStartNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => { export const FormStartNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const ctx = useAutomation2DataFlow(); const ctx = useWorkflowDataFlow();
const fieldTypeOptions = ctx?.formFieldTypes?.length const fieldTypeOptions = ctx?.formFieldTypes?.length
? ctx.formFieldTypes ? ctx.formFieldTypes
: FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' })); : FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' }));

View file

@ -5,7 +5,7 @@
import React from 'react'; import React from 'react';
import type { NodeConfigRendererProps } from '../shared/types'; import type { NodeConfigRendererProps } from '../shared/types';
import styles from '../../editor/Automation2FlowEditor.module.css'; import styles from '../../editor/WorkflowFlowEditor.module.css';
import { useLanguage } from '../../../../providers/language/LanguageContext'; import { useLanguage } from '../../../../providers/language/LanguageContext';
const SCHEMA_EXAMPLE = `{ const SCHEMA_EXAMPLE = `{

View file

@ -5,7 +5,8 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useApiRequest } from '../../../hooks/useApi'; import { useApiRequest } from '../../../hooks/useApi';
import { loadClickupListTasksForDropdown, type ApiRequestFunction } from '../../../api/workflowApi'; import type { ApiRequestFunction } from '../../../api/workflowAutomationApi';
import { loadClickupListTasksForDropdown } from '../../../api/clickupApi';
import { normalizeFormFieldOptions } from '../nodes/form'; import { normalizeFormFieldOptions } from '../nodes/form';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';

View file

@ -1555,18 +1555,6 @@ export function FormGeneratorTable<T extends Record<string, any>>({
const totalPages = useMemo(() => { const totalPages = useMemo(() => {
// If pagination object exists, use totalPages from backend // If pagination object exists, use totalPages from backend
if (hookData?.pagination) { if (hookData?.pagination) {
// Debug logging
if (import.meta.env.DEV) {
console.log('📊 FormGeneratorTable pagination data:', {
totalPages: hookData.pagination.totalPages,
totalItems: hookData.pagination.totalItems,
currentPageSize,
calculatedPages: hookData.pagination.totalItems && currentPageSize
? Math.ceil(hookData.pagination.totalItems / currentPageSize)
: 'N/A'
});
}
if (hookData.pagination.totalPages) { if (hookData.pagination.totalPages) {
return hookData.pagination.totalPages; return hookData.pagination.totalPages;
} }

View file

@ -1,8 +0,0 @@
/**
* FlowEditor re-export shim.
*
* Allows gradual migration of imports to the workflowAutomation folder
* without breaking anything. All exports proxy through to ../../FlowEditor.
*/
export * from '../../FlowEditor';

View file

@ -8,7 +8,9 @@ import { WorkflowAutomationPage } from '../pages/workflowAutomation/WorkflowAuto
export const KEEP_ALIVE_ROUTES: KeepAliveEntry[] = [ export const KEEP_ALIVE_ROUTES: KeepAliveEntry[] = [
{ {
id: 'workflow-automation-editor', id: 'workflow-automation-editor',
pathRegex: /\/workflow-automation(?:\?.*tab=editor|$)/, pathRegex: /^\/workflow-automation$/,
matchLocation: (_pathname: string, search: string) =>
new URLSearchParams(search).get('tab') === 'editor',
render: () => <WorkflowAutomationPage />, render: () => <WorkflowAutomationPage />,
}, },
{ {
@ -39,6 +41,10 @@ export const KEEP_ALIVE_ROUTES: KeepAliveEntry[] = [
}, },
]; ];
export function hideFeatureOutlet(pathname: string): boolean { export function hideFeatureOutlet(pathname: string, search: string = ''): boolean {
return KEEP_ALIVE_ROUTES.some((e) => e.pathRegex.test(pathname)); return KEEP_ALIVE_ROUTES.some((e) => {
if (!e.pathRegex.test(pathname)) return false;
if (e.matchLocation) return e.matchLocation(pathname, search);
return true;
});
} }

View file

@ -84,39 +84,10 @@ export function useApiRequest<RequestData = any, ResponseData = any>() {
// Check if we have a valid cached request for GET requests // Check if we have a valid cached request for GET requests
if (cacheKey && requestCache.has(cacheKey) && isCacheValid(cacheKey)) { if (cacheKey && requestCache.has(cacheKey) && isCacheValid(cacheKey)) {
console.log('🔧 useApiRequest: Using cached request', { url, method, cacheKey });
setIsLoading(false); setIsLoading(false);
return await requestCache.get(cacheKey)!; return await requestCache.get(cacheKey)!;
} }
console.log('🔧 useApiRequest: Making request', {
url,
method,
hasData: !!data,
hasParams: !!params,
cacheKey,
dataStructure: data ? {
data,
dataType: typeof data,
dataKeys: Object.keys(data),
dataEntries: Object.entries(data).map(([key, value]) => ({
key,
value,
valueType: typeof value,
valueIsObject: typeof value === 'object' && value !== null && !Array.isArray(value),
valueStringified: typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value)
})),
dataStringified: JSON.stringify(data, null, 2)
} : null,
fullRequestConfig: {
url,
method,
data,
params,
...additionalConfig
}
});
// Create the request promise // Create the request promise
const requestPromise = api({ const requestPromise = api({
url, url,
@ -125,7 +96,6 @@ export function useApiRequest<RequestData = any, ResponseData = any>() {
params, params,
...additionalConfig ...additionalConfig
}).then(response => { }).then(response => {
console.log('🔧 useApiRequest: Request successful', { url, status: response.status, hasData: !!response.data });
// For blob responses, return the blob data directly // For blob responses, return the blob data directly
if (additionalConfig.responseType === 'blob') { if (additionalConfig.responseType === 'blob') {

View file

@ -31,19 +31,11 @@ export function useCurrentUser() {
// Check if we already have user data in sessionStorage cache // Check if we already have user data in sessionStorage cache
const cachedUser = getUserDataCache(); const cachedUser = getUserDataCache();
if (cachedUser && cachedUser.username) { if (cachedUser && cachedUser.username) {
// Use cached user data - permissions are checked via RBAC API, not client-side
// Note: roleLabels is deprecated in Multi-Tenant architecture - use isSysAdmin/isPlatformAdmin flags instead
setUser(cachedUser); setUser(cachedUser);
console.log('✅ Using cached user data from sessionStorage (persists during session):', {
username: cachedUser.username,
isSysAdmin: cachedUser.isSysAdmin,
isPlatformAdmin: cachedUser.isPlatformAdmin
});
return; return;
} }
// JWT tokens are now stored in httpOnly cookies, so we fetch user data from API // JWT tokens are now stored in httpOnly cookies, so we fetch user data from API
console.log('🍪 JWT tokens are in httpOnly cookies, fetching user data from API');
// Determine the correct endpoint based on authentication authority // Determine the correct endpoint based on authentication authority
const authAuthority = sessionStorage.getItem('auth_authority'); const authAuthority = sessionStorage.getItem('auth_authority');
@ -55,28 +47,13 @@ export function useCurrentUser() {
endpoint = '/api/google/me'; endpoint = '/api/google/me';
} }
console.log('🔍 Fetching user data from API:', {
endpoint,
authAuthority,
hasAuthCookies: document.cookie.includes('access_token') || document.cookie.includes('refresh_token')
});
// Add a small delay to ensure cookies are properly set after authentication // Add a small delay to ensure cookies are properly set after authentication
if (authAuthority === 'msft' || authAuthority === 'google') { if (authAuthority === 'msft' || authAuthority === 'google') {
console.log('⏳ Adding delay for OAuth authentication cookie propagation...');
await new Promise(resolve => setTimeout(resolve, 500)); await new Promise(resolve => setTimeout(resolve, 500));
} }
const data = await fetchCurrentUserApi(request, authAuthority || undefined); const data = await fetchCurrentUserApi(request, authAuthority || undefined);
// Log response for debugging
console.log('📦 User data received from API:', {
username: data?.username,
isSysAdmin: data?.isSysAdmin,
isPlatformAdmin: data?.isPlatformAdmin,
allKeys: data ? Object.keys(data) : []
});
// Validate user data // Validate user data
if (!data || !data.username) { if (!data || !data.username) {
console.error('❌ User data from API is invalid:', { console.error('❌ User data from API is invalid:', {
@ -195,14 +172,7 @@ export function useCurrentUser() {
// Try to load user from sessionStorage cache first for faster initial load // Try to load user from sessionStorage cache first for faster initial load
const cachedUser = getUserDataCache(); const cachedUser = getUserDataCache();
if (cachedUser && cachedUser.username) { if (cachedUser && cachedUser.username) {
// Use cached user data - permissions are checked via RBAC API
// Note: roleLabels is deprecated in Multi-Tenant architecture - use isSysAdmin/isPlatformAdmin flags instead
setUser(cachedUser); setUser(cachedUser);
console.log('✅ Using cached user data from sessionStorage on mount:', {
username: cachedUser.username,
isSysAdmin: cachedUser.isSysAdmin,
isPlatformAdmin: cachedUser.isPlatformAdmin
});
} }
// For OAuth authentication, wait a bit longer before fetching user data // For OAuth authentication, wait a bit longer before fetching user data

View file

@ -29,11 +29,13 @@ const keepAliveShellStyle = (isVisible: boolean, shellOverflowHidden: boolean):
...(shellOverflowHidden ? { overflow: 'hidden' as const } : {}), ...(shellOverflowHidden ? { overflow: 'hidden' as const } : {}),
}); });
const RoutedKeepAliveUnscoped: React.FC<{ entry: KeepAliveUnscopedEntry; pathname: string }> = ({ const RoutedKeepAliveUnscoped: React.FC<{ entry: KeepAliveUnscopedEntry; pathname: string; search: string }> = ({
entry, entry,
pathname, pathname,
search,
}) => { }) => {
const isVisible = entry.pathRegex.test(pathname); const isVisible = entry.pathRegex.test(pathname) &&
(!entry.matchLocation || entry.matchLocation(pathname, search));
return ( return (
<div style={keepAliveShellStyle(isVisible, true)}> <div style={keepAliveShellStyle(isVisible, true)}>
{entry.render()} {entry.render()}
@ -41,11 +43,13 @@ const RoutedKeepAliveUnscoped: React.FC<{ entry: KeepAliveUnscopedEntry; pathnam
); );
}; };
const RoutedKeepAliveScoped: React.FC<{ entry: KeepAliveScopedEntry; pathname: string }> = ({ const RoutedKeepAliveScoped: React.FC<{ entry: KeepAliveScopedEntry; pathname: string; search: string }> = ({
entry, entry,
pathname, pathname,
search,
}) => { }) => {
const isVisible = entry.pathRegex.test(pathname); const isVisible = entry.pathRegex.test(pathname) &&
(!entry.matchLocation || entry.matchLocation(pathname, search));
const { const {
scopeRegex, scopeRegex,
requireMandateForMount = true, requireMandateForMount = true,
@ -82,14 +86,15 @@ const RoutedKeepAliveScoped: React.FC<{ entry: KeepAliveScopedEntry; pathname: s
); );
}; };
const RoutedKeepAliveSlot: React.FC<{ entry: KeepAliveEntry; pathname: string }> = ({ const RoutedKeepAliveSlot: React.FC<{ entry: KeepAliveEntry; pathname: string; search: string }> = ({
entry, entry,
pathname, pathname,
search,
}) => { }) => {
if (!isKeepAliveScoped(entry)) { if (!isKeepAliveScoped(entry)) {
return <RoutedKeepAliveUnscoped entry={entry} pathname={pathname} />; return <RoutedKeepAliveUnscoped entry={entry} pathname={pathname} search={search} />;
} }
return <RoutedKeepAliveScoped entry={entry} pathname={pathname} />; return <RoutedKeepAliveScoped entry={entry} pathname={pathname} search={search} />;
}; };
// ============================================================================= // =============================================================================
@ -102,7 +107,7 @@ const MainLayoutInner: React.FC = () => {
const { loadFeatures, initialized, loading, error } = useFeatureStore(); const { loadFeatures, initialized, loading, error } = useFeatureStore();
const location = useLocation(); const location = useLocation();
const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false); const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
const hideOutletShell = hideFeatureOutlet(location.pathname); const hideOutletShell = hideFeatureOutlet(location.pathname, location.search);
// Features laden beim Mount // Features laden beim Mount
useEffect(() => { useEffect(() => {
@ -172,7 +177,7 @@ const MainLayoutInner: React.FC = () => {
</div> </div>
{KEEP_ALIVE_ROUTES.map((routeEntry) => ( {KEEP_ALIVE_ROUTES.map((routeEntry) => (
<RoutedKeepAliveSlot key={routeEntry.id} entry={routeEntry} pathname={location.pathname} /> <RoutedKeepAliveSlot key={routeEntry.id} entry={routeEntry} pathname={location.pathname} search={location.search} />
))} ))}
<div <div

View file

@ -189,7 +189,7 @@ export const FeatureViewPage: React.FC<FeatureViewPageProps> = ({ view }) => {
// Feature outlet is hidden for paths configured in KEEP_ALIVE_ROUTES (rendered in MainLayout). // Feature outlet is hidden for paths configured in KEEP_ALIVE_ROUTES (rendered in MainLayout).
// Add new persistent workspace URLs there if needed. // Add new persistent workspace URLs there if needed.
if (hideFeatureOutlet(location.pathname)) { if (hideFeatureOutlet(location.pathname, location.search)) {
return null; return null;
} }

View file

@ -17,7 +17,7 @@ interface TrusteeGraphNode {
position: { x: number; y: number }; position: { x: number; y: number };
} }
/** Matches automation2 ``buildConnectionMap`` (``sourceOutput`` / ``targetInput``). */ /** Matches workflow ``buildConnectionMap`` (``sourceOutput`` / ``targetInput``). */
interface TrusteeGraphConnection { interface TrusteeGraphConnection {
source: string; source: string;
sourceOutput: number; sourceOutput: number;

View file

@ -11,7 +11,7 @@ import React, { useState, useCallback, useEffect, useRef } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { useInstanceId, useMandateId } from '../../../hooks/useCurrentInstance'; import { useInstanceId, useMandateId } from '../../../hooks/useCurrentInstance';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';
import { Automation2FlowEditor as FlowEditor } from '../../../components/FlowEditor'; import { WorkflowFlowEditor as FlowEditor } from '../../../components/FlowEditor';
import type { PendingFile, EditorDataSource, EditorFeatureDataSource } from '../../../components/FlowEditor'; import type { PendingFile, EditorDataSource, EditorFeatureDataSource } from '../../../components/FlowEditor';
import api from '../../../api'; import api from '../../../api';

View file

@ -21,7 +21,7 @@ import {
updateWorkflow, updateWorkflow,
type AutoWorkflowTemplate, type AutoWorkflowTemplate,
type AutoTemplateScope, type AutoTemplateScope,
} from '../../../api/workflowApi'; } from '../../../api/workflowAutomationApi';
import { fetchAttributes } from '../../../api/attributesApi'; import { fetchAttributes } from '../../../api/attributesApi';
import type { AttributeDefinition } from '../../../api/attributesApi'; import type { AttributeDefinition } from '../../../api/attributesApi';
import { resolveColumnTypes } from '../../../utils/columnTypeResolver'; import { resolveColumnTypes } from '../../../utils/columnTypeResolver';
@ -84,10 +84,10 @@ export const WorkflowTemplatesPage: React.FC<WorkflowTemplatesPageProps> = ({
const [backendAttributes, setBackendAttributes] = useState<AttributeDefinition[]>([]); const [backendAttributes, setBackendAttributes] = useState<AttributeDefinition[]>([]);
useEffect(() => { useEffect(() => {
fetchAttributes(request, 'Automation2WorkflowView') fetchAttributes(request, 'AutoWorkflowView')
.then(setBackendAttributes) .then(setBackendAttributes)
.catch((err) => { .catch((err) => {
console.error('[workflowAutomation] fetchAttributes Automation2WorkflowView failed', err); console.error('[workflowAutomation] fetchAttributes AutoWorkflowView failed', err);
}); });
}, [request]); }, [request]);

View file

@ -8,7 +8,7 @@
import React, { useState, useCallback, useEffect } from 'react'; import React, { useState, useCallback, useEffect } from 'react';
import { FaDownload } from 'react-icons/fa'; import { FaDownload } from 'react-icons/fa';
import { useApiRequest } from '../../../hooks/useApi'; import { useApiRequest } from '../../../hooks/useApi';
import { fetchWorkspaceRunDetail } from '../../../api/workflowApi'; import { fetchRunDetail } from '../../../api/workflowAutomationApi';
import api from '../../../api'; import api from '../../../api';
import { useLanguage } from '../../../providers/language/LanguageContext'; import { useLanguage } from '../../../providers/language/LanguageContext';
import styles from '../../admin/Admin.module.css'; import styles from '../../admin/Admin.module.css';
@ -186,13 +186,13 @@ export interface RunDetailTabProps {
export const _RunDetailTab: React.FC<RunDetailTabProps> = ({ runId, onBack }) => { export const _RunDetailTab: React.FC<RunDetailTabProps> = ({ runId, onBack }) => {
const { t } = useLanguage(); const { t } = useLanguage();
const { request } = useApiRequest(); const { request } = useApiRequest();
const [runDetail, setRunDetail] = useState<Awaited<ReturnType<typeof fetchWorkspaceRunDetail>> | null>(null); const [runDetail, setRunDetail] = useState<Awaited<ReturnType<typeof fetchRunDetail>> | null>(null);
const [detailLoading, setDetailLoading] = useState(false); const [detailLoading, setDetailLoading] = useState(false);
const _loadDetail = useCallback(async (id: string) => { const _loadDetail = useCallback(async (id: string) => {
setDetailLoading(true); setDetailLoading(true);
try { try {
const detail = await fetchWorkspaceRunDetail(request, id); const detail = await fetchRunDetail(request, id);
setRunDetail(detail); setRunDetail(detail);
} catch (e) { } catch (e) {
console.error('Workspace run detail failed', e); console.error('Workspace run detail failed', e);
@ -211,7 +211,7 @@ export const _RunDetailTab: React.FC<RunDetailTabProps> = ({ runId, onBack }) =>
const status = runDetail.run?.status; const status = runDetail.run?.status;
if (status && _TERMINAL_STATUSES.has(status)) return; if (status && _TERMINAL_STATUSES.has(status)) return;
const timer = setInterval(() => { const timer = setInterval(() => {
fetchWorkspaceRunDetail(request, runId) fetchRunDetail(request, runId)
.then(detail => setRunDetail(detail)) .then(detail => setRunDetail(detail))
.catch(() => {}); .catch(() => {});
}, _POLL_INTERVAL_MS); }, _POLL_INTERVAL_MS);

View file

@ -14,7 +14,7 @@ import {
fetchTasks, fetchTasks,
completeTask, completeTask,
cancelPendingTaskStopRun, cancelPendingTaskStopRun,
type Automation2Task, type WorkflowTask,
} from '../../../api/workflowAutomationApi'; } from '../../../api/workflowAutomationApi';
import styles from '../../admin/Admin.module.css'; import styles from '../../admin/Admin.module.css';
import { _formatTs } from '../types'; import { _formatTs } from '../types';
@ -41,7 +41,7 @@ export const _TasksTab: React.FC<TasksTabProps> = ({ selectedMandateId = 'all' }
const { request } = useApiRequest(); const { request } = useApiRequest();
const { showSuccess, showError } = useToast(); const { showSuccess, showError } = useToast();
const [tasks, setTasks] = useState<Automation2Task[]>([]); const [tasks, setTasks] = useState<WorkflowTask[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [actionId, setActionId] = useState<string | null>(null); const [actionId, setActionId] = useState<string | null>(null);

View file

@ -12,7 +12,7 @@ import { FormGeneratorTable, type ColumnConfig } from '../../../components/FormG
import { useToast } from '../../../contexts/ToastContext'; import { useToast } from '../../../contexts/ToastContext';
import { usePrompt } from '../../../hooks/usePrompt'; import { usePrompt } from '../../../hooks/usePrompt';
import { useApiRequest } from '../../../hooks/useApi'; import { useApiRequest } from '../../../hooks/useApi';
import { updateWorkflow, executeGraph, deleteSystemWorkflow } from '../../../api/workflowApi'; import { updateWorkflow, executeGraph, deleteWorkflow } from '../../../api/workflowAutomationApi';
import { fetchAttributes } from '../../../api/attributesApi'; import { fetchAttributes } from '../../../api/attributesApi';
import type { AttributeDefinition } from '../../../api/attributesApi'; import type { AttributeDefinition } from '../../../api/attributesApi';
import { resolveColumnTypes } from '../../../utils/columnTypeResolver'; import { resolveColumnTypes } from '../../../utils/columnTypeResolver';
@ -49,9 +49,9 @@ export const _WorkflowsTab: React.FC<WorkflowsTabProps> = ({ onWorkflowClick, se
const [backendAttributes, setBackendAttributes] = useState<AttributeDefinition[]>([]); const [backendAttributes, setBackendAttributes] = useState<AttributeDefinition[]>([]);
useEffect(() => { useEffect(() => {
fetchAttributes(request, 'Automation2WorkflowView') fetchAttributes(request, 'AutoWorkflowView')
.then(setBackendAttributes) .then(setBackendAttributes)
.catch((err) => { console.error('[workflowAutomation] fetchAttributes Automation2WorkflowView failed', err); }); .catch((err) => { console.error('[workflowAutomation] fetchAttributes AutoWorkflowView failed', err); });
}, [request]); }, [request]);
const _load = useCallback(async (paginationParams?: any) => { const _load = useCallback(async (paginationParams?: any) => {
@ -105,7 +105,7 @@ export const _WorkflowsTab: React.FC<WorkflowsTabProps> = ({ onWorkflowClick, se
const _handleDelete = useCallback(async (workflowId: string): Promise<boolean> => { const _handleDelete = useCallback(async (workflowId: string): Promise<boolean> => {
try { try {
await deleteSystemWorkflow(request, workflowId); await deleteWorkflow(request, workflowId);
showSuccess(t('Workflow gelöscht')); showSuccess(t('Workflow gelöscht'));
await _load(); await _load();
return true; return true;

View file

@ -17,6 +17,7 @@ export interface KeepAliveScopedEntry {
requireMandateForMount?: boolean; requireMandateForMount?: boolean;
/** Commcoach shell omits overflow:hidden; other routes use hidden. */ /** Commcoach shell omits overflow:hidden; other routes use hidden. */
shellOverflowHidden?: boolean; shellOverflowHidden?: boolean;
matchLocation?: (pathname: string, search: string) => boolean;
render: (ctx: KeepAliveRenderContext) => ReactNode; render: (ctx: KeepAliveRenderContext) => ReactNode;
} }
@ -24,6 +25,7 @@ export interface KeepAliveScopedEntry {
export interface KeepAliveUnscopedEntry { export interface KeepAliveUnscopedEntry {
id: string; id: string;
pathRegex: RegExp; pathRegex: RegExp;
matchLocation?: (pathname: string, search: string) => boolean;
render: () => ReactNode; render: () => ReactNode;
} }

Binary file not shown.