cleaned servicebag and removed servicehub
Some checks failed
Deploy Nyla Frontend to Integration / deploy (push) Failing after 51s
Some checks failed
Deploy Nyla Frontend to Integration / deploy (push) Failing after 51s
This commit is contained in:
parent
cd14babb2e
commit
a13a158c67
61 changed files with 209 additions and 15505 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -32,3 +32,6 @@ dist-ssr
|
|||
|
||||
# Keep environment files in config/ (naming: env-<workflow>.env)
|
||||
!config/env-*.env
|
||||
|
||||
tsc-errors.txt
|
||||
scripts/i18n_missing_report.md
|
||||
File diff suppressed because it is too large
Load diff
62
src/api.ts
62
src/api.ts
|
|
@ -3,23 +3,6 @@ import axios from 'axios';
|
|||
import { addCSRFTokenToHeaders, getCSRFToken, generateAndStoreCSRFToken } from './utils/csrfUtils';
|
||||
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.
|
||||
* URL pattern: /mandates/:mandateId/:featureCode/:instanceId/...
|
||||
|
|
@ -44,52 +27,25 @@ const getContextFromUrl = (): { mandateId?: string; instanceId?: string } => {
|
|||
|
||||
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({
|
||||
baseURL: getApiBaseUrl(),
|
||||
baseURL: _baseUrl,
|
||||
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 },
|
||||
});
|
||||
|
||||
// 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(
|
||||
async (config) => {
|
||||
// Log backend information
|
||||
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
|
||||
// Add auth token if available (otherwise httpOnly cookies are used automatically)
|
||||
const authToken = localStorage.getItem('authToken');
|
||||
if (authToken && config.headers) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
* This file re-exports them so existing consumers still resolve.
|
||||
*
|
||||
* Only ClickUp-specific functions and `deleteSystemWorkflow` remain here
|
||||
* because their endpoints are not yet covered by the new unified API.
|
||||
* Extracted from the legacy workflowApi.ts re-export shim so each integration
|
||||
* lives in its own module.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// 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';
|
||||
|
||||
function _encodedConnectionId(connectionId: string): string {
|
||||
|
|
@ -250,18 +131,3 @@ export async function loadClickupListTasksForDropdown(
|
|||
acc.sort((a, b) => a.name.localeCompare(b.name, 'de'));
|
||||
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',
|
||||
});
|
||||
}
|
||||
|
|
@ -170,19 +170,11 @@ export async function fetchMyFeatures(): Promise<FeaturesMyResponse> {
|
|||
}
|
||||
|
||||
try {
|
||||
console.log('📡 featuresApi: Fetching /api/features/my');
|
||||
const response = await api.get<FeaturesMyResponse>('/api/features/my');
|
||||
|
||||
// Get the actual data (response.data contains the FeaturesMyResponse)
|
||||
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;
|
||||
} catch (error) {
|
||||
console.error('❌ featuresApi: Error fetching features:', error);
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
@ -139,7 +139,7 @@ export interface NodeTypesResponse {
|
|||
formFieldTypes?: FormFieldType[];
|
||||
}
|
||||
|
||||
export interface Automation2GraphNode {
|
||||
export interface WorkflowGraphNode {
|
||||
id: string;
|
||||
type: string;
|
||||
parameters?: Record<string, unknown>;
|
||||
|
|
@ -147,16 +147,16 @@ export interface Automation2GraphNode {
|
|||
outputPorts?: Array<{ name: string; schema: string | GraphDefinedSchemaRef }>;
|
||||
}
|
||||
|
||||
export interface Automation2Connection {
|
||||
export interface WorkflowConnection {
|
||||
source: string;
|
||||
target: string;
|
||||
sourceOutput?: number;
|
||||
targetInput?: number;
|
||||
}
|
||||
|
||||
export interface Automation2Graph {
|
||||
nodes: Automation2GraphNode[];
|
||||
connections: Automation2Connection[];
|
||||
export interface WorkflowGraph {
|
||||
nodes: WorkflowGraphNode[];
|
||||
connections: WorkflowConnection[];
|
||||
}
|
||||
|
||||
export interface ExecuteGraphResponse {
|
||||
|
|
@ -184,10 +184,10 @@ export interface WorkflowEntryPoint {
|
|||
config: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface Automation2Workflow {
|
||||
export interface WorkflowDefinition {
|
||||
id: string;
|
||||
label: string;
|
||||
graph: Automation2Graph;
|
||||
graph: WorkflowGraph;
|
||||
active?: boolean;
|
||||
/** Target feature instance for execution data scope (NULL for templates) */
|
||||
targetFeatureInstanceId?: string | null;
|
||||
|
|
@ -224,7 +224,7 @@ export interface AutoVersion {
|
|||
workflowId: string;
|
||||
versionNumber: number;
|
||||
status: AutoWorkflowStatus;
|
||||
graph: Automation2Graph;
|
||||
graph: WorkflowGraph;
|
||||
invocations?: WorkflowEntryPoint[];
|
||||
publishedAt?: number;
|
||||
publishedBy?: string;
|
||||
|
|
@ -261,7 +261,7 @@ export interface AutoWorkflow {
|
|||
active: boolean;
|
||||
eventId?: string;
|
||||
notifyOnFailure?: boolean;
|
||||
graph: Automation2Graph;
|
||||
graph: WorkflowGraph;
|
||||
invocations?: WorkflowEntryPoint[];
|
||||
sysCreatedBy?: string;
|
||||
sysCreatedAt?: number;
|
||||
|
|
@ -319,7 +319,7 @@ export interface ConditionMetaResponse {
|
|||
}
|
||||
|
||||
export interface ConditionMetaRequest {
|
||||
graph: Automation2Graph;
|
||||
graph: WorkflowGraph;
|
||||
nodeId?: string;
|
||||
ref: { type: 'ref'; nodeId: string; path: (string | number)[] };
|
||||
}
|
||||
|
|
@ -343,7 +343,7 @@ export interface ExecuteGraphOptions {
|
|||
payload?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface Automation2Run {
|
||||
export interface WorkflowRun {
|
||||
id: string;
|
||||
workflowId: string;
|
||||
status: string;
|
||||
|
|
@ -351,13 +351,13 @@ export interface Automation2Run {
|
|||
currentNodeId?: string;
|
||||
}
|
||||
|
||||
export interface CompletedRun extends Automation2Run {
|
||||
export interface CompletedRun extends WorkflowRun {
|
||||
workflowLabel?: string;
|
||||
sysModifiedAt?: number;
|
||||
sysCreatedAt?: number;
|
||||
}
|
||||
|
||||
export interface Automation2Task {
|
||||
export interface WorkflowTask {
|
||||
id: string;
|
||||
runId: string;
|
||||
workflowId: string;
|
||||
|
|
@ -374,7 +374,7 @@ export interface Automation2Task {
|
|||
dueAt?: number;
|
||||
}
|
||||
|
||||
export interface AutoWorkflowTemplate extends Automation2Workflow {
|
||||
export interface AutoWorkflowTemplate extends WorkflowDefinition {
|
||||
isTemplate: boolean;
|
||||
templateScope?: AutoTemplateScope;
|
||||
templateSourceId?: string;
|
||||
|
|
@ -434,7 +434,7 @@ export interface WorkflowFileEnvelope {
|
|||
templateScope?: AutoTemplateScope;
|
||||
sharedReadOnly?: boolean;
|
||||
notifyOnFailure?: boolean;
|
||||
graph: Automation2Graph;
|
||||
graph: WorkflowGraph;
|
||||
invocations?: WorkflowEntryPoint[];
|
||||
}
|
||||
|
||||
|
|
@ -582,7 +582,7 @@ export async function fetchConditionMeta(
|
|||
*/
|
||||
export async function postUpstreamPaths(
|
||||
request: ApiRequestFunction,
|
||||
graph: Automation2Graph,
|
||||
graph: WorkflowGraph,
|
||||
nodeId: string
|
||||
): Promise<{ paths: UpstreamPathEntry[] }> {
|
||||
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
|
||||
*/
|
||||
export async function executeGraph(
|
||||
request: ApiRequestFunction,
|
||||
graph: Automation2Graph,
|
||||
graph: WorkflowGraph,
|
||||
workflowId?: string,
|
||||
options?: ExecuteGraphOptions
|
||||
): Promise<ExecuteGraphResponse> {
|
||||
|
|
@ -704,7 +704,7 @@ export async function executeGraph(
|
|||
export async function fetchWorkflows(
|
||||
request: ApiRequestFunction,
|
||||
params?: { active?: boolean; pagination?: any; mandateId?: string }
|
||||
): Promise<Automation2Workflow[] | { items: Automation2Workflow[]; pagination: any }> {
|
||||
): Promise<WorkflowDefinition[] | { items: WorkflowDefinition[]; pagination: any }> {
|
||||
const queryParams: Record<string, any> = {};
|
||||
if (params?.active !== undefined) queryParams.active = params.active;
|
||||
if (params?.pagination) queryParams.pagination = JSON.stringify(params.pagination);
|
||||
|
|
@ -721,7 +721,7 @@ export async function fetchWorkflows(
|
|||
export async function fetchWorkflow(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string
|
||||
): Promise<Automation2Workflow> {
|
||||
): Promise<WorkflowDefinition> {
|
||||
return await request({
|
||||
url: `${BASE}/workflows/${workflowId}`,
|
||||
method: 'get',
|
||||
|
|
@ -732,12 +732,12 @@ export async function createWorkflow(
|
|||
request: ApiRequestFunction,
|
||||
body: {
|
||||
label: string;
|
||||
graph: Automation2Graph;
|
||||
graph: WorkflowGraph;
|
||||
invocations?: WorkflowEntryPoint[];
|
||||
targetFeatureInstanceId?: string | null;
|
||||
mandateId?: string;
|
||||
}
|
||||
): Promise<Automation2Workflow> {
|
||||
): Promise<WorkflowDefinition> {
|
||||
return await request({
|
||||
url: `${BASE}/workflows`,
|
||||
method: 'post',
|
||||
|
|
@ -750,13 +750,13 @@ export async function updateWorkflow(
|
|||
workflowId: string,
|
||||
body: {
|
||||
label?: string;
|
||||
graph?: Automation2Graph;
|
||||
graph?: WorkflowGraph;
|
||||
invocations?: WorkflowEntryPoint[];
|
||||
active?: boolean;
|
||||
notifyOnFailure?: boolean;
|
||||
targetFeatureInstanceId?: string | null;
|
||||
}
|
||||
): Promise<Automation2Workflow> {
|
||||
): Promise<WorkflowDefinition> {
|
||||
return await request({
|
||||
url: `${BASE}/workflows/${workflowId}`,
|
||||
method: 'put',
|
||||
|
|
@ -848,7 +848,7 @@ export function workflowFileNameFor(label: string): string {
|
|||
export async function fetchWorkflowRuns(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string
|
||||
): Promise<Automation2Run[]> {
|
||||
): Promise<WorkflowRun[]> {
|
||||
const data = await request({
|
||||
url: `${BASE}/runs`,
|
||||
method: 'get',
|
||||
|
|
@ -887,7 +887,7 @@ export async function fetchRuns(
|
|||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
): Promise<{ runs: Automation2Run[]; total?: number }> {
|
||||
): Promise<{ runs: WorkflowRun[]; total?: number }> {
|
||||
const data = await request({
|
||||
url: `${BASE}/runs`,
|
||||
method: 'get',
|
||||
|
|
@ -982,7 +982,7 @@ export async function fetchWorkspaceRuns(
|
|||
export async function fetchTasks(
|
||||
request: ApiRequestFunction,
|
||||
params?: { workflowId?: string; status?: string }
|
||||
): Promise<Automation2Task[]> {
|
||||
): Promise<WorkflowTask[]> {
|
||||
const data = await request({
|
||||
url: `${BASE}/tasks`,
|
||||
method: 'get',
|
||||
|
|
@ -1110,7 +1110,7 @@ export async function createTemplateFromWorkflow(
|
|||
export async function copyTemplate(
|
||||
request: ApiRequestFunction,
|
||||
templateId: string
|
||||
): Promise<Automation2Workflow> {
|
||||
): Promise<WorkflowDefinition> {
|
||||
return await request({
|
||||
url: `${BASE}/templates/${templateId}/copy`,
|
||||
method: 'post',
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext, useMemo } from 'react';
|
||||
import type { CanvasNode, CanvasConnection } from '../editor/FlowCanvas';
|
||||
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;
|
||||
nodes: CanvasNode[];
|
||||
connections: CanvasConnection[];
|
||||
|
|
@ -30,13 +30,13 @@ export interface Automation2DataFlowContextValue {
|
|||
parseGraphDefinedSchema: (parameterKey: string) => PortSchema | null;
|
||||
}
|
||||
|
||||
const Automation2DataFlowContext = createContext<Automation2DataFlowContextValue | null>(null);
|
||||
const WorkflowDataFlowContext = createContext<WorkflowDataFlowContextValue | null>(null);
|
||||
|
||||
export function useAutomation2DataFlow(): Automation2DataFlowContextValue | null {
|
||||
return useContext(Automation2DataFlowContext);
|
||||
export function useWorkflowDataFlow(): WorkflowDataFlowContextValue | null {
|
||||
return useContext(WorkflowDataFlowContext);
|
||||
}
|
||||
|
||||
interface Automation2DataFlowProviderProps {
|
||||
interface WorkflowDataFlowProviderProps {
|
||||
node: CanvasNode | null;
|
||||
nodes: CanvasNode[];
|
||||
connections: CanvasConnection[];
|
||||
|
|
@ -52,7 +52,7 @@ interface Automation2DataFlowProviderProps {
|
|||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Automation2DataFlowProvider: React.FC<Automation2DataFlowProviderProps> = ({
|
||||
export const WorkflowDataFlowProvider: React.FC<WorkflowDataFlowProviderProps> = ({
|
||||
node,
|
||||
nodes,
|
||||
connections,
|
||||
|
|
@ -67,7 +67,7 @@ export const Automation2DataFlowProvider: React.FC<Automation2DataFlowProviderPr
|
|||
request,
|
||||
children,
|
||||
}) => {
|
||||
const value = useMemo((): Automation2DataFlowContextValue | null => {
|
||||
const value = useMemo((): WorkflowDataFlowContextValue | null => {
|
||||
if (!node) return null;
|
||||
const formTypeToPort: Record<string, string> = Object.fromEntries(
|
||||
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]);
|
||||
|
||||
return (
|
||||
<Automation2DataFlowContext.Provider value={value}>
|
||||
<WorkflowDataFlowContext.Provider value={value}>
|
||||
{children}
|
||||
</Automation2DataFlowContext.Provider>
|
||||
</WorkflowDataFlowContext.Provider>
|
||||
);
|
||||
};
|
||||
|
|
@ -26,8 +26,8 @@ import {
|
|||
HiOutlineChatBubbleLeftEllipsis,
|
||||
HiOutlineSquares2X2,
|
||||
} from 'react-icons/hi2';
|
||||
import type { Automation2Workflow, ExecuteGraphResponse, AutoVersion, AutoTemplateScope } from '../../../api/workflowApi';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
import type { WorkflowDefinition, ExecuteGraphResponse, AutoVersion, AutoTemplateScope } from '../../../api/workflowAutomationApi';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import { getUserDataCache } from '../../../utils/userCache';
|
||||
|
|
@ -60,7 +60,7 @@ export interface CanvasHeaderCanvasEditProps {
|
|||
}
|
||||
|
||||
interface CanvasHeaderProps {
|
||||
workflows: Automation2Workflow[];
|
||||
workflows: WorkflowDefinition[];
|
||||
currentWorkflowId: string | null;
|
||||
onWorkflowSelect: (workflowId: string | null) => void;
|
||||
onNew: () => void;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@
|
|||
* WorkflowAutomation data instead of the workspace endpoint.
|
||||
*/
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import type { Automation2Workflow } from '../../../api/workflowApi';
|
||||
import type { WorkflowDefinition } from '../../../api/workflowAutomationApi';
|
||||
|
||||
interface EditorWorkflowChatListProps {
|
||||
workflows: Automation2Workflow[];
|
||||
workflows: WorkflowDefinition[];
|
||||
currentWorkflowId: string | null;
|
||||
onSelect: (workflowId: string | null) => void;
|
||||
onNew: () => void;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import React, {
|
|||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import type { GraphDefinedSchemaRef, NodeType } from '../../../api/workflowApi';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
import type { GraphDefinedSchemaRef, NodeType } from '../../../api/workflowAutomationApi';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import { AiBadge } from '../nodes/shared/AiBadge';
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
import type { CanvasNode } from './FlowCanvas';
|
||||
import type { GraphDefinedSchemaRef, NodeType, NodeTypeParameter, PortSchema } from '../../../api/workflowApi';
|
||||
import type { ApiRequestFunction } from '../../../api/workflowApi';
|
||||
import type { GraphDefinedSchemaRef, NodeType, NodeTypeParameter, PortSchema } from '../../../api/workflowAutomationApi';
|
||||
import type { ApiRequestFunction } from '../../../api/workflowAutomationApi';
|
||||
import { getLabel } from '../nodes/shared/utils';
|
||||
import { FRONTEND_TYPE_RENDERERS } from '../nodes/frontendTypeRenderers';
|
||||
import { ContextBuilderRenderer } from '../nodes/frontendTypeRenderers/ContextBuilderRenderer';
|
||||
import { RequiredAttributePicker } from '../nodes/shared/RequiredAttributePicker';
|
||||
import { findRequiredErrors } from '../nodes/shared/paramValidation';
|
||||
import { useAutomation2DataFlow } from '../context/Automation2DataFlowContext';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
import { useWorkflowDataFlow } from '../context/WorkflowDataFlowContext';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import { AccordionList } from '../../UiComponents/AccordionList';
|
||||
|
|
@ -210,7 +210,7 @@ export const NodeConfigPanel: React.FC<NodeConfigPanelProps> = ({ node,
|
|||
[onParametersChange]
|
||||
);
|
||||
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const portTypeCatalog: Record<string, PortSchema> = (dataFlow?.portTypeCatalog as Record<string, PortSchema> | undefined) ?? {};
|
||||
|
||||
// Phase-4 Schicht-4 — Pflicht-Params zuerst sortieren, damit der User
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { NodeType } from '../../../api/workflowApi';
|
||||
import type { NodeType } from '../../../api/workflowAutomationApi';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import { getCategoryIcon } 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';
|
||||
|
||||
interface NodeListItemProps {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
import React, { useMemo } from 'react';
|
||||
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 { getLabel } from '../nodes/shared/utils';
|
||||
import { NodeListItem } from './NodeListItem';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useApiRequest } from '../../../hooks/useApi';
|
||||
import type { AutoStepLog } from '../../../api/workflowApi';
|
||||
import type { AutoStepLog } from '../../../api/workflowAutomationApi';
|
||||
import api from '../../../api';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import {
|
|||
type AutoWorkflowTemplate,
|
||||
type AutoTemplateScope,
|
||||
type ApiRequestFunction,
|
||||
} from '../../../api/workflowApi';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
} from '../../../api/workflowAutomationApi';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
interface TemplatePickerProps {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Automation2 Flow Editor Styles
|
||||
* Workflow Flow Editor Styles
|
||||
* Sidebar with node list + canvas area.
|
||||
*/
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Automation2FlowEditor
|
||||
* WorkflowFlowEditor
|
||||
*
|
||||
* 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.
|
||||
|
|
@ -26,13 +26,13 @@ import {
|
|||
WORKFLOW_FILE_EXTENSION,
|
||||
type NodeType,
|
||||
type NodeTypeCategory,
|
||||
type Automation2Graph,
|
||||
type Automation2Workflow,
|
||||
type WorkflowGraph,
|
||||
type WorkflowDefinition,
|
||||
type ExecuteGraphResponse,
|
||||
type WorkflowEntryPoint,
|
||||
type AutoVersion,
|
||||
type AutoTemplateScope,
|
||||
} from '../../../api/workflowApi';
|
||||
} from '../../../api/workflowAutomationApi';
|
||||
import {
|
||||
FlowCanvas,
|
||||
type CanvasNode,
|
||||
|
|
@ -50,7 +50,7 @@ import { fromApiGraph, toApiGraph, switchOutputCountFromCases, trimConnectionsFo
|
|||
import { buildNodeOutputsPreview, setPortTypeCatalog as setRegistryCatalog } from '../nodes/shared/outputPreviewRegistry';
|
||||
import { findGraphErrors } from '../nodes/shared/paramValidation';
|
||||
import { getLabel as getParamLabel } from '../nodes/shared/utils';
|
||||
import { Automation2DataFlowProvider } from '../context/Automation2DataFlowContext';
|
||||
import { WorkflowDataFlowProvider } from '../context/WorkflowDataFlowContext';
|
||||
import { usePrompt } from '../../../hooks/usePrompt';
|
||||
import { EditorChatPanel } from './EditorChatPanel';
|
||||
import type { PendingFile, EditorDataSource, EditorFeatureDataSource } from './EditorChatPanel';
|
||||
|
|
@ -58,12 +58,12 @@ import { EditorWorkflowChatList } from './EditorWorkflowChatList';
|
|||
import { RunTracingPanel } from './RunTracingPanel';
|
||||
import { UnifiedDataBar } 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';
|
||||
|
||||
|
||||
const LOG = '[Automation2]';
|
||||
const LOG = '[WorkflowEditor]';
|
||||
|
||||
const CANVAS_HISTORY_MAX = 50;
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ function cloneCanvasSnapshot(nodes: CanvasNode[], connections: CanvasConnection[
|
|||
};
|
||||
}
|
||||
|
||||
interface Automation2FlowEditorProps {
|
||||
interface WorkflowFlowEditorProps {
|
||||
instanceId: string;
|
||||
mandateId?: string;
|
||||
language?: string;
|
||||
|
|
@ -93,7 +93,7 @@ interface Automation2FlowEditorProps {
|
|||
onSourcesChanged?: () => void;
|
||||
}
|
||||
|
||||
export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ instanceId,
|
||||
export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instanceId,
|
||||
mandateId,
|
||||
language = 'de',
|
||||
initialWorkflowId,
|
||||
|
|
@ -111,9 +111,9 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
const [categories, setCategories] = useState<NodeTypeCategory[]>([]);
|
||||
const [portTypeCatalog, setPortTypeCatalog] = 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<
|
||||
Record<string, import('../../../api/workflowApi').ConditionOperatorDef[]>
|
||||
Record<string, import('../../../api/workflowAutomationApi').ConditionOperatorDef[]>
|
||||
>({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
|
@ -138,7 +138,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
const [canvasStickyNotes, setCanvasStickyNotes] = useState<CanvasStickyNote[]>([]);
|
||||
const [executing, setExecuting] = useState(false);
|
||||
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 [selectedNode, setSelectedNode] = useState<CanvasNode | null>(null);
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
|
@ -302,7 +302,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
|
||||
const applyGraphWithSync = useCallback(
|
||||
(
|
||||
graph: Automation2Graph | null | undefined,
|
||||
graph: WorkflowGraph | null | undefined,
|
||||
wfInvocations: WorkflowEntryPoint[] | undefined,
|
||||
opts?: { skipHistory?: boolean }
|
||||
) => {
|
||||
|
|
@ -310,7 +310,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
pushCanvasHistoryPastFromCurrent();
|
||||
}
|
||||
setInvocations(wfInvocations ?? []);
|
||||
const g: Automation2Graph = graph ?? { nodes: [], connections: [] };
|
||||
const g: WorkflowGraph = graph ?? { nodes: [], connections: [] };
|
||||
const { nodes, connections } = fromApiGraph(g, nodeTypes);
|
||||
setCanvasNodes(nodes);
|
||||
setCanvasConnections(connections);
|
||||
|
|
@ -319,7 +319,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
);
|
||||
|
||||
const handleFromApiGraph = useCallback(
|
||||
(graph: Automation2Graph, wfInvocations?: WorkflowEntryPoint[]) => {
|
||||
(graph: WorkflowGraph, wfInvocations?: WorkflowEntryPoint[]) => {
|
||||
applyGraphWithSync(graph, wfInvocations);
|
||||
},
|
||||
[applyGraphWithSync]
|
||||
|
|
@ -1046,7 +1046,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
</div>
|
||||
{configurableSelected && selectedNode && (
|
||||
<div className={styles.nodeConfigPanelWrap} data-suppress-flow-node-hotkeys="">
|
||||
<Automation2DataFlowProvider
|
||||
<WorkflowDataFlowProvider
|
||||
node={selectedNode}
|
||||
nodes={canvasNodes}
|
||||
connections={canvasConnections}
|
||||
|
|
@ -1071,7 +1071,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
request={request}
|
||||
verboseSchema={verboseSchema}
|
||||
/>
|
||||
</Automation2DataFlowProvider>
|
||||
</WorkflowDataFlowProvider>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -1126,4 +1126,4 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
);
|
||||
};
|
||||
|
||||
export default Automation2FlowEditor;
|
||||
export default WorkflowFlowEditor;
|
||||
|
|
@ -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 { 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';
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import React from 'react';
|
|||
import { FaGripVertical, FaTimes } from 'react-icons/fa';
|
||||
import type { FormField, NodeConfigRendererProps } from '../shared/types';
|
||||
import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { FormFieldOptionsEditor } from './FormFieldOptionsEditor';
|
||||
import {
|
||||
deriveFormFieldPayloadKey,
|
||||
|
|
@ -19,7 +19,7 @@ import { useLanguage } from '../../../../providers/language/LanguageContext';
|
|||
|
||||
export const FormNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => {
|
||||
const { t } = useLanguage();
|
||||
const ctx = useAutomation2DataFlow();
|
||||
const ctx = useWorkflowDataFlow();
|
||||
const fieldTypeOptions = ctx?.formFieldTypes?.length
|
||||
? ctx.formFieldTypes
|
||||
: FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' }));
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
import React from 'react';
|
||||
import type { FieldRendererProps } from './index';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { isRef, type DataRef } from '../shared/dataRef';
|
||||
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';
|
||||
|
||||
export interface SwitchCase {
|
||||
|
|
@ -116,7 +116,7 @@ export const CaseListEditor: React.FC<FieldRendererProps> = ({
|
|||
allParams,
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dependsOn =
|
||||
param.frontendOptions && typeof param.frontendOptions === 'object'
|
||||
? String((param.frontendOptions as Record<string, unknown>).dependsOn ?? 'value')
|
||||
|
|
|
|||
|
|
@ -4,11 +4,8 @@
|
|||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import {
|
||||
fetchBrowse,
|
||||
fetchClickupList,
|
||||
type BrowseEntry,
|
||||
} from '../../../../api/workflowApi';
|
||||
import { fetchBrowse, type BrowseEntry } from '../../../../api/workflowAutomationApi';
|
||||
import { fetchClickupList } from '../../../../api/clickupApi';
|
||||
import type { FieldRendererProps } from './index';
|
||||
import {
|
||||
clickupBrowseParentPath,
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
import React from 'react';
|
||||
import type { FieldRendererProps } from './index';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { isRef, type DataRef } from '../shared/dataRef';
|
||||
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';
|
||||
|
||||
export interface StructuredCondition {
|
||||
|
|
@ -41,7 +41,7 @@ export const ConditionEditor: React.FC<FieldRendererProps> = ({
|
|||
allParams,
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dependsOn =
|
||||
param.frontendOptions && typeof param.frontendOptions === 'object'
|
||||
? String((param.frontendOptions as Record<string, unknown>).dependsOn ?? 'Item')
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { DataPicker } from '../shared/DataPicker';
|
||||
import { isRef, isSystemVar, type DataRef, type SystemVarRef } from '../shared/dataRef';
|
||||
import type { FieldRendererProps } from './index';
|
||||
|
|
@ -174,7 +174,7 @@ const REMOVE_BTN: React.CSSProperties = {
|
|||
|
||||
export const ContextAssignmentsEditor: React.FC<FieldRendererProps> = ({ param, value, onChange, allParams }) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const rows = normalizeRows(value, allParams);
|
||||
const [pickerRow, setPickerRow] = React.useState<number | null>(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { DataPicker } from '../shared/DataPicker';
|
||||
import { isRef, type DataRef, type SystemVarRef } from '../shared/dataRef';
|
||||
import type { FieldRendererProps } from './index';
|
||||
|
|
@ -52,7 +52,7 @@ const REMOVE_BTN: React.CSSProperties = {
|
|||
|
||||
export const ContextBuilderRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const [pickerOpen, setPickerOpen] = React.useState(false);
|
||||
const dragIndex = React.useRef<number | null>(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@
|
|||
|
||||
import React from 'react';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { DataPicker } from '../shared/DataPicker';
|
||||
import { isRef, type DataRef, type SystemVarRef } from '../shared/dataRef';
|
||||
import type { FieldRendererProps } from './index';
|
||||
|
||||
export const DataRefRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const [pickerOpen, setPickerOpen] = React.useState(false);
|
||||
|
||||
const currentRef = isRef(value) ? (value as DataRef) : null;
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import type { FieldRendererProps } from './index';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { DataPicker } from '../shared/DataPicker';
|
||||
import { formatRefLabel, isRef, isSystemVar, type DataRef, type SystemVarRef } from '../shared/dataRef';
|
||||
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;
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ function _parseTokensInTemplate(
|
|||
|
||||
export const TemplateTextareaRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
*/
|
||||
|
||||
import type { ComponentType } from 'react';
|
||||
import type { NodeTypeParameter } from '../../../../api/workflowApi';
|
||||
import type { ApiRequestFunction } from '../../../../api/workflowApi';
|
||||
import type { NodeTypeParameter } from '../../../../api/workflowAutomationApi';
|
||||
import type { ApiRequestFunction } from '../../../../api/workflowAutomationApi';
|
||||
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 {
|
||||
deriveFormFieldPayloadKey,
|
||||
|
|
@ -46,7 +46,7 @@ import {
|
|||
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import { toApiGraph } from '../shared/graphUtils';
|
||||
import { postUpstreamPaths } from '../../../../api/workflowApi';
|
||||
import { postUpstreamPaths } from '../../../../api/workflowAutomationApi';
|
||||
import type { CanvasNode } from '../../editor/FlowCanvas';
|
||||
import { DataRefRenderer } from './DataRefRenderer';
|
||||
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 { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const [connections, setConnections] = React.useState<Array<{ id: string; label: string }>>([]);
|
||||
const [loadError, setLoadError] = React.useState<string | null>(null);
|
||||
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 { t } = useLanguage();
|
||||
const ctx = useAutomation2DataFlow();
|
||||
const ctx = useWorkflowDataFlow();
|
||||
const fieldTypeOptions = ctx?.formFieldTypes?.length
|
||||
? ctx.formFieldTypes
|
||||
: FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' }));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import React from 'react';
|
|||
import type { NodeConfigRendererProps } from '../shared/types';
|
||||
import { LoopItemsSelect } from '../shared/LoopItemsSelect';
|
||||
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 }) => {
|
||||
const value = params.items;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
* outputPorts[n].dataPickOptions when the backend defines an explicit list (authoritative).
|
||||
* Resolves Transit chains to show the real upstream schema.
|
||||
|
|
@ -9,10 +9,10 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { createRef, createSystemVar, type DataRef, type SystemVarRef, isCompatible } from './dataRef';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import type { DataPickOption, GraphDataSources, GraphDefinedSchemaRef, NodeType, PortField, PortSchema } from '../../../../api/workflowApi';
|
||||
import { fetchGraphDataSources } from '../../../../api/workflowApi';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import type { DataPickOption, GraphDataSources, GraphDefinedSchemaRef, NodeType, PortField, PortSchema } from '../../../../api/workflowAutomationApi';
|
||||
import { fetchGraphDataSources } from '../../../../api/workflowAutomationApi';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
|
||||
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
|
||||
// candidates ("strict" mode). User can override per-session via the toggle.
|
||||
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
|
||||
// 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()}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="automation2DataPickerTitle"
|
||||
aria-labelledby="workflowDataPickerTitle"
|
||||
>
|
||||
<div className={styles.dataPickerHeader}>
|
||||
<h4 className={styles.dataPickerTitle} id="automation2DataPickerTitle">
|
||||
<h4 className={styles.dataPickerTitle} id="workflowDataPickerTitle">
|
||||
{t('Datenquelle wählen')}
|
||||
{expectedParamType && (
|
||||
<span
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
@ -12,8 +12,8 @@ import {
|
|||
} from './dataRef';
|
||||
import { RefSourceSelect } from './RefSourceSelect';
|
||||
import { DataPicker } from './DataPicker';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ export const DynamicValueField: React.FC<DynamicValueFieldProps> = ({ paramKey,
|
|||
variant = 'picker',
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
|
||||
const ref: DataRef | null = isRef(value) ? value : null;
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import {
|
|||
shouldShowStaticControl,
|
||||
type PathPickMode,
|
||||
} from './RefSourceSelect';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
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';
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ export const HybridStaticRefField: React.FC<HybridStaticRefFieldProps> = ({ labe
|
|||
pathPickMode = 'default',
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const hasSources =
|
||||
dataFlow &&
|
||||
dataFlow.getAvailableSourceIds().some((id) => {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
import React from 'react';
|
||||
import { createRef, isRef, type DataRef } from './dataRef';
|
||||
import { refToOptionValue, optionValueToRef } from './RefSourceSelect';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
@ -161,7 +161,7 @@ export const LoopItemsSelect: React.FC<LoopItemsSelectProps> = ({ value,
|
|||
placeholder,
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
if (!dataFlow) return null;
|
||||
|
||||
const sourceIds = dataFlow.getAvailableSourceIds();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import React from 'react';
|
||||
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';
|
||||
|
||||
/** How to build path options for StatischKontextSelect / RefSourceSelect. */
|
||||
|
|
@ -238,7 +238,7 @@ export const StatischKontextSelect: React.FC<StatischKontextSelectProps> = ({
|
|||
pathPickMode = 'default',
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
if (!dataFlow) return null;
|
||||
|
||||
const sourceIds = dataFlow.getAvailableSourceIds();
|
||||
|
|
@ -316,7 +316,7 @@ export const RefSourceSelect: React.FC<RefSourceSelectProps> = ({
|
|||
pathPickMode = 'default',
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
if (!dataFlow) return null;
|
||||
|
||||
const sourceIds = dataFlow.getAvailableSourceIds();
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@
|
|||
*/
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { DataPicker } from './DataPicker';
|
||||
import { createRef, formatRefLabel, isRef, type DataRef, type SystemVarRef } from './dataRef';
|
||||
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';
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ export const RequiredAttributePicker: React.FC<RequiredAttributePickerProps> = (
|
|||
description,
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const ctx = useAutomation2DataFlow();
|
||||
const ctx = useWorkflowDataFlow();
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
|
||||
const consumerNodeId = ctx?.currentNodeId ?? '';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Automation2 Flow Editor - Constants
|
||||
* Workflow Flow Editor - Constants
|
||||
* Category ordering for node sidebar.
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
||||
import type {
|
||||
NodeType,
|
||||
Automation2Graph,
|
||||
Automation2GraphNode,
|
||||
Automation2Connection,
|
||||
WorkflowGraph,
|
||||
WorkflowGraphNode,
|
||||
WorkflowConnection,
|
||||
GraphDefinedSchemaRef,
|
||||
} from '../../../../api/workflowApi';
|
||||
} from '../../../../api/workflowAutomationApi';
|
||||
import type { CanvasNode, CanvasConnection } from '../../editor/FlowCanvas';
|
||||
|
||||
/** Switch: one output per case plus a default (``Sonst``) port. */
|
||||
|
|
@ -46,7 +46,7 @@ export function switchOutputLabel(
|
|||
}
|
||||
|
||||
export function fromApiGraph(
|
||||
graph: Automation2Graph,
|
||||
graph: WorkflowGraph,
|
||||
nodeTypes: NodeType[]
|
||||
): { nodes: CanvasNode[]; connections: CanvasConnection[] } {
|
||||
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 });
|
||||
});
|
||||
|
||||
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 };
|
||||
let outputs = io.outputs;
|
||||
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 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 sourceOutput = c.sourceOutput ?? 0;
|
||||
const sourceHandle = srcNode ? srcNode.inputs + sourceOutput : 0;
|
||||
|
|
@ -104,7 +104,7 @@ export function fromApiGraph(
|
|||
export function toApiGraph(
|
||||
nodes: CanvasNode[],
|
||||
connections: CanvasConnection[]
|
||||
): Automation2Graph {
|
||||
): WorkflowGraph {
|
||||
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
|
||||
return {
|
||||
nodes: nodes.map((n) => ({
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
||||
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> = {};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
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 { getAvailableSources } from './dataFlowGraph';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Shared types for node config renderers
|
||||
*/
|
||||
|
||||
import type { ApiRequestFunction } from '../../../../api/workflowApi';
|
||||
import type { ApiRequestFunction } from '../../../../api/workflowAutomationApi';
|
||||
import type { AttributeType } from '../../../../utils/attributeTypeMapper';
|
||||
|
||||
/** input.form / trigger.form field row. */
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Automation2 Flow Editor - Utility functions
|
||||
* Workflow Flow Editor - Utility functions
|
||||
*/
|
||||
|
||||
import type React from 'react';
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import React, { useMemo } from 'react';
|
|||
import type { NodeConfigRendererProps } from '../shared/types';
|
||||
import type { FormField } from '../shared/types';
|
||||
import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { FormFieldOptionsEditor } from '../form/FormFieldOptionsEditor';
|
||||
import {
|
||||
deriveFormFieldPayloadKey,
|
||||
|
|
@ -37,7 +37,7 @@ function _parseFields(params: Record<string, unknown>, t: (key: string) => strin
|
|||
|
||||
export const FormStartNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => {
|
||||
const { t } = useLanguage();
|
||||
const ctx = useAutomation2DataFlow();
|
||||
const ctx = useWorkflowDataFlow();
|
||||
const fieldTypeOptions = ctx?.formFieldTypes?.length
|
||||
? ctx.formFieldTypes
|
||||
: FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' }));
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import React from 'react';
|
||||
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';
|
||||
|
||||
const SCHEMA_EXAMPLE = `{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
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 { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
|
|||
|
|
@ -1555,18 +1555,6 @@ export function FormGeneratorTable<T extends Record<string, any>>({
|
|||
const totalPages = useMemo(() => {
|
||||
// If pagination object exists, use totalPages from backend
|
||||
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) {
|
||||
return hookData.pagination.totalPages;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
@ -8,7 +8,9 @@ import { WorkflowAutomationPage } from '../pages/workflowAutomation/WorkflowAuto
|
|||
export const KEEP_ALIVE_ROUTES: KeepAliveEntry[] = [
|
||||
{
|
||||
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 />,
|
||||
},
|
||||
{
|
||||
|
|
@ -39,6 +41,10 @@ export const KEEP_ALIVE_ROUTES: KeepAliveEntry[] = [
|
|||
},
|
||||
];
|
||||
|
||||
export function hideFeatureOutlet(pathname: string): boolean {
|
||||
return KEEP_ALIVE_ROUTES.some((e) => e.pathRegex.test(pathname));
|
||||
export function hideFeatureOutlet(pathname: string, search: string = ''): boolean {
|
||||
return KEEP_ALIVE_ROUTES.some((e) => {
|
||||
if (!e.pathRegex.test(pathname)) return false;
|
||||
if (e.matchLocation) return e.matchLocation(pathname, search);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,39 +84,10 @@ export function useApiRequest<RequestData = any, ResponseData = any>() {
|
|||
|
||||
// Check if we have a valid cached request for GET requests
|
||||
if (cacheKey && requestCache.has(cacheKey) && isCacheValid(cacheKey)) {
|
||||
console.log('🔧 useApiRequest: Using cached request', { url, method, cacheKey });
|
||||
setIsLoading(false);
|
||||
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
|
||||
const requestPromise = api({
|
||||
url,
|
||||
|
|
@ -125,7 +96,6 @@ export function useApiRequest<RequestData = any, ResponseData = any>() {
|
|||
params,
|
||||
...additionalConfig
|
||||
}).then(response => {
|
||||
console.log('🔧 useApiRequest: Request successful', { url, status: response.status, hasData: !!response.data });
|
||||
|
||||
// For blob responses, return the blob data directly
|
||||
if (additionalConfig.responseType === 'blob') {
|
||||
|
|
|
|||
|
|
@ -31,19 +31,11 @@ export function useCurrentUser() {
|
|||
// Check if we already have user data in sessionStorage cache
|
||||
const cachedUser = getUserDataCache();
|
||||
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);
|
||||
console.log('✅ Using cached user data from sessionStorage (persists during session):', {
|
||||
username: cachedUser.username,
|
||||
isSysAdmin: cachedUser.isSysAdmin,
|
||||
isPlatformAdmin: cachedUser.isPlatformAdmin
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
const authAuthority = sessionStorage.getItem('auth_authority');
|
||||
|
|
@ -55,28 +47,13 @@ export function useCurrentUser() {
|
|||
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
|
||||
if (authAuthority === 'msft' || authAuthority === 'google') {
|
||||
console.log('⏳ Adding delay for OAuth authentication cookie propagation...');
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
|
||||
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
|
||||
if (!data || !data.username) {
|
||||
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
|
||||
const cachedUser = getUserDataCache();
|
||||
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);
|
||||
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
|
||||
|
|
|
|||
|
|
@ -29,11 +29,13 @@ const keepAliveShellStyle = (isVisible: boolean, shellOverflowHidden: boolean):
|
|||
...(shellOverflowHidden ? { overflow: 'hidden' as const } : {}),
|
||||
});
|
||||
|
||||
const RoutedKeepAliveUnscoped: React.FC<{ entry: KeepAliveUnscopedEntry; pathname: string }> = ({
|
||||
const RoutedKeepAliveUnscoped: React.FC<{ entry: KeepAliveUnscopedEntry; pathname: string; search: string }> = ({
|
||||
entry,
|
||||
pathname,
|
||||
search,
|
||||
}) => {
|
||||
const isVisible = entry.pathRegex.test(pathname);
|
||||
const isVisible = entry.pathRegex.test(pathname) &&
|
||||
(!entry.matchLocation || entry.matchLocation(pathname, search));
|
||||
return (
|
||||
<div style={keepAliveShellStyle(isVisible, true)}>
|
||||
{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,
|
||||
pathname,
|
||||
search,
|
||||
}) => {
|
||||
const isVisible = entry.pathRegex.test(pathname);
|
||||
const isVisible = entry.pathRegex.test(pathname) &&
|
||||
(!entry.matchLocation || entry.matchLocation(pathname, search));
|
||||
const {
|
||||
scopeRegex,
|
||||
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,
|
||||
pathname,
|
||||
search,
|
||||
}) => {
|
||||
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 location = useLocation();
|
||||
const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
|
||||
const hideOutletShell = hideFeatureOutlet(location.pathname);
|
||||
const hideOutletShell = hideFeatureOutlet(location.pathname, location.search);
|
||||
|
||||
// Features laden beim Mount
|
||||
useEffect(() => {
|
||||
|
|
@ -172,7 +177,7 @@ const MainLayoutInner: React.FC = () => {
|
|||
</div>
|
||||
|
||||
{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
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
// Add new persistent workspace URLs there if needed.
|
||||
if (hideFeatureOutlet(location.pathname)) {
|
||||
if (hideFeatureOutlet(location.pathname, location.search)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ interface TrusteeGraphNode {
|
|||
position: { x: number; y: number };
|
||||
}
|
||||
|
||||
/** Matches automation2 ``buildConnectionMap`` (``sourceOutput`` / ``targetInput``). */
|
||||
/** Matches workflow ``buildConnectionMap`` (``sourceOutput`` / ``targetInput``). */
|
||||
interface TrusteeGraphConnection {
|
||||
source: string;
|
||||
sourceOutput: number;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import React, { useState, useCallback, useEffect, useRef } from 'react';
|
|||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useInstanceId, useMandateId } from '../../../hooks/useCurrentInstance';
|
||||
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 api from '../../../api';
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import {
|
|||
updateWorkflow,
|
||||
type AutoWorkflowTemplate,
|
||||
type AutoTemplateScope,
|
||||
} from '../../../api/workflowApi';
|
||||
} from '../../../api/workflowAutomationApi';
|
||||
import { fetchAttributes } from '../../../api/attributesApi';
|
||||
import type { AttributeDefinition } from '../../../api/attributesApi';
|
||||
import { resolveColumnTypes } from '../../../utils/columnTypeResolver';
|
||||
|
|
@ -84,10 +84,10 @@ export const WorkflowTemplatesPage: React.FC<WorkflowTemplatesPageProps> = ({
|
|||
const [backendAttributes, setBackendAttributes] = useState<AttributeDefinition[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAttributes(request, 'Automation2WorkflowView')
|
||||
fetchAttributes(request, 'AutoWorkflowView')
|
||||
.then(setBackendAttributes)
|
||||
.catch((err) => {
|
||||
console.error('[workflowAutomation] fetchAttributes Automation2WorkflowView failed', err);
|
||||
console.error('[workflowAutomation] fetchAttributes AutoWorkflowView failed', err);
|
||||
});
|
||||
}, [request]);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import { FaDownload } from 'react-icons/fa';
|
||||
import { useApiRequest } from '../../../hooks/useApi';
|
||||
import { fetchWorkspaceRunDetail } from '../../../api/workflowApi';
|
||||
import { fetchRunDetail } from '../../../api/workflowAutomationApi';
|
||||
import api from '../../../api';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import styles from '../../admin/Admin.module.css';
|
||||
|
|
@ -186,13 +186,13 @@ export interface RunDetailTabProps {
|
|||
export const _RunDetailTab: React.FC<RunDetailTabProps> = ({ runId, onBack }) => {
|
||||
const { t } = useLanguage();
|
||||
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 _loadDetail = useCallback(async (id: string) => {
|
||||
setDetailLoading(true);
|
||||
try {
|
||||
const detail = await fetchWorkspaceRunDetail(request, id);
|
||||
const detail = await fetchRunDetail(request, id);
|
||||
setRunDetail(detail);
|
||||
} catch (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;
|
||||
if (status && _TERMINAL_STATUSES.has(status)) return;
|
||||
const timer = setInterval(() => {
|
||||
fetchWorkspaceRunDetail(request, runId)
|
||||
fetchRunDetail(request, runId)
|
||||
.then(detail => setRunDetail(detail))
|
||||
.catch(() => {});
|
||||
}, _POLL_INTERVAL_MS);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
fetchTasks,
|
||||
completeTask,
|
||||
cancelPendingTaskStopRun,
|
||||
type Automation2Task,
|
||||
type WorkflowTask,
|
||||
} from '../../../api/workflowAutomationApi';
|
||||
import styles from '../../admin/Admin.module.css';
|
||||
import { _formatTs } from '../types';
|
||||
|
|
@ -41,7 +41,7 @@ export const _TasksTab: React.FC<TasksTabProps> = ({ selectedMandateId = 'all' }
|
|||
const { request } = useApiRequest();
|
||||
const { showSuccess, showError } = useToast();
|
||||
|
||||
const [tasks, setTasks] = useState<Automation2Task[]>([]);
|
||||
const [tasks, setTasks] = useState<WorkflowTask[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [actionId, setActionId] = useState<string | null>(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { FormGeneratorTable, type ColumnConfig } from '../../../components/FormG
|
|||
import { useToast } from '../../../contexts/ToastContext';
|
||||
import { usePrompt } from '../../../hooks/usePrompt';
|
||||
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 type { AttributeDefinition } from '../../../api/attributesApi';
|
||||
import { resolveColumnTypes } from '../../../utils/columnTypeResolver';
|
||||
|
|
@ -49,9 +49,9 @@ export const _WorkflowsTab: React.FC<WorkflowsTabProps> = ({ onWorkflowClick, se
|
|||
const [backendAttributes, setBackendAttributes] = useState<AttributeDefinition[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAttributes(request, 'Automation2WorkflowView')
|
||||
fetchAttributes(request, 'AutoWorkflowView')
|
||||
.then(setBackendAttributes)
|
||||
.catch((err) => { console.error('[workflowAutomation] fetchAttributes Automation2WorkflowView failed', err); });
|
||||
.catch((err) => { console.error('[workflowAutomation] fetchAttributes AutoWorkflowView failed', err); });
|
||||
}, [request]);
|
||||
|
||||
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> => {
|
||||
try {
|
||||
await deleteSystemWorkflow(request, workflowId);
|
||||
await deleteWorkflow(request, workflowId);
|
||||
showSuccess(t('Workflow gelöscht'));
|
||||
await _load();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export interface KeepAliveScopedEntry {
|
|||
requireMandateForMount?: boolean;
|
||||
/** Commcoach shell omits overflow:hidden; other routes use hidden. */
|
||||
shellOverflowHidden?: boolean;
|
||||
matchLocation?: (pathname: string, search: string) => boolean;
|
||||
render: (ctx: KeepAliveRenderContext) => ReactNode;
|
||||
}
|
||||
|
||||
|
|
@ -24,6 +25,7 @@ export interface KeepAliveScopedEntry {
|
|||
export interface KeepAliveUnscopedEntry {
|
||||
id: string;
|
||||
pathRegex: RegExp;
|
||||
matchLocation?: (pathname: string, search: string) => boolean;
|
||||
render: () => ReactNode;
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
tsc-errors.txt
BIN
tsc-errors.txt
Binary file not shown.
Loading…
Reference in a new issue