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
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -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
62
src/api.ts
62
src/api.ts
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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[];
|
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',
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Automation2 Flow Editor Styles
|
* Workflow Flow Editor Styles
|
||||||
* Sidebar with node list + canvas area.
|
* Sidebar with node list + canvas area.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -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' }));
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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' }));
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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 ?? '';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Automation2 Flow Editor - Constants
|
* Workflow Flow Editor - Constants
|
||||||
* Category ordering for node sidebar.
|
* 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';
|
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.
|
* 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.
|
* 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) => ({
|
||||||
|
|
|
||||||
|
|
@ -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> = {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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. */
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Automation2 Flow Editor - Utility functions
|
* Workflow Flow Editor - Utility functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
|
|
|
||||||
|
|
@ -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' }));
|
||||||
|
|
|
||||||
|
|
@ -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 = `{
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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[] = [
|
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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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') {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
BIN
tsc-errors.txt
BIN
tsc-errors.txt
Binary file not shown.
Loading…
Reference in a new issue