Compare commits
6 commits
main
...
feat/tenan
| Author | SHA1 | Date | |
|---|---|---|---|
| f88d1c7719 | |||
| 78ccea8bac | |||
| fa7988b4b6 | |||
| eb0a58aaa7 | |||
| 849efa6ed7 | |||
| dfa36c17ef |
489 changed files with 22936 additions and 6208 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -31,7 +31,4 @@ dist-ssr
|
|||
.cursorignore
|
||||
|
||||
# Keep environment files in config/ (naming: env-<workflow>.env)
|
||||
!config/env-*.env
|
||||
|
||||
tsc-errors.txt
|
||||
scripts/i18n_missing_report.md
|
||||
!config/env-*.env
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Configuration — reads mandatory env vars set by .env (copied from config/env-*.env by CI).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,3 +1 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
export { getApiBaseUrl, getAppName } from './config';
|
||||
|
|
|
|||
2
env.d.ts
vendored
2
env.d.ts
vendored
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
|
|
|
|||
15027
scripts/i18n_missing_report.md
Normal file
15027
scripts/i18n_missing_report.md
Normal file
File diff suppressed because it is too large
Load diff
23
src/App.tsx
23
src/App.tsx
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* App.tsx
|
||||
*
|
||||
|
|
@ -41,11 +39,11 @@ import { GDPRPage } from './pages/GDPR';
|
|||
import StorePage from './pages/Store';
|
||||
import { IntegrationsOverviewPage } from './pages/IntegrationsOverviewPage';
|
||||
import { FeatureViewPage } from './pages/FeatureView';
|
||||
import { AccessManagementHub, AdminMandatesPage, AdminUsersPage, AdminUserMandatesPage, AdminFeatureAccessPage, AdminInvitationsPage, AdminMandateRolesPage, AdminFeatureRolesPage, AdminFeatureInstanceUsersPage, AdminMandateRolePermissionsPage, AdminUserAccessOverviewPage, AdminLogsPage, AdminDemoConfigPage } from './pages/admin';
|
||||
import { AccessManagementHub, AdminMandatesPage, AdminUsersPage, AdminFeatureAccessPage, AdminInvitationsPage, AdminUserRoleTemplatesPage, AdminFeatureRolesPage, AdminFeatureInstanceUsersPage, AdminUserAccessOverviewPage, AdminLogsPage, AdminDemoConfigPage } from './pages/admin';
|
||||
import { AdminMandateWizardPage, AdminInvitationWizardPage } from './pages/admin/wizards';
|
||||
import { PromptsPage, FilesPage, ConnectionsPage } from './pages/basedata';
|
||||
import { BillingDataView, BillingAdmin, BillingMandateView, AdminSubscriptionsPage } from './pages/billing';
|
||||
import { WorkflowAutomationPage } from './pages/workflowAutomation/WorkflowAutomationHubPage';
|
||||
import { AutomationsDashboardPage } from './pages/AutomationsDashboardPage';
|
||||
import { RagInventoryPage } from './pages/RagInventoryPage';
|
||||
import { ComplianceAuditPage } from './pages/ComplianceAuditPage';
|
||||
function App() {
|
||||
|
|
@ -126,9 +124,9 @@ function App() {
|
|||
</Route>
|
||||
|
||||
{/* ============================================== */}
|
||||
{/* WORKFLOW AUTOMATION (System-Komponente) */}
|
||||
{/* AUTOMATIONS DASHBOARD */}
|
||||
{/* ============================================== */}
|
||||
<Route path="workflow-automation" element={<WorkflowAutomationPage />} />
|
||||
<Route path="automations" element={<AutomationsDashboardPage />} />
|
||||
|
||||
{/* ============================================== */}
|
||||
{/* RAG INVENTORY */}
|
||||
|
|
@ -139,7 +137,6 @@ function App() {
|
|||
<Route path="pek" element={<Navigate to="/" replace />} />
|
||||
<Route path="speech" element={<Navigate to="/" replace />} />
|
||||
|
||||
|
||||
{/* ============================================== */}
|
||||
{/* FEATURE-INSTANZ ROUTES */}
|
||||
{/* /mandates/:mandateId/:featureCode/:instanceId */}
|
||||
|
|
@ -173,9 +170,13 @@ function App() {
|
|||
<Route path="templates" element={<FeatureViewPage view="templates" />} />
|
||||
<Route path="logs" element={<FeatureViewPage view="logs" />} />
|
||||
|
||||
{/* Workspace Editor */}
|
||||
{/* Workspace + Automation2 Editor */}
|
||||
<Route path="editor" element={<FeatureViewPage view="editor" />} />
|
||||
|
||||
{/* Automation2: legacy workflows URL → editor */}
|
||||
<Route path="workflows" element={<Navigate to="../editor" replace />} />
|
||||
<Route path="workflows-tasks" element={<FeatureViewPage view="workflows-tasks" />} />
|
||||
|
||||
{/* Teams Bot Feature Views */}
|
||||
<Route path="sessions" element={<FeatureViewPage view="sessions" />} />
|
||||
<Route path="settings" element={<FeatureViewPage view="settings" />} />
|
||||
|
|
@ -205,14 +206,14 @@ function App() {
|
|||
<Route index element={<Navigate to="/admin/access" replace />} />
|
||||
<Route path="mandates" element={<AdminMandatesPage />} />
|
||||
<Route path="users" element={<AdminUsersPage />} />
|
||||
<Route path="user-mandates" element={<AdminUserMandatesPage />} />
|
||||
<Route path="user-mandates" element={<Navigate to="/admin/mandates" replace />} />
|
||||
<Route path="access" element={<AccessManagementHub />} />
|
||||
<Route path="feature-instances" element={<AdminFeatureAccessPage />} />
|
||||
<Route path="feature-roles" element={<AdminFeatureRolesPage />} />
|
||||
<Route path="feature-users" element={<AdminFeatureInstanceUsersPage />} />
|
||||
<Route path="invitations" element={<AdminInvitationsPage />} />
|
||||
<Route path="mandate-roles" element={<AdminMandateRolesPage />} />
|
||||
<Route path="mandate-role-permissions" element={<AdminMandateRolePermissionsPage />} />
|
||||
<Route path="user-role-templates" element={<AdminUserRoleTemplatesPage />} />
|
||||
<Route path="mandate-roles" element={<Navigate to="/admin/user-role-templates" replace />} />
|
||||
<Route path="user-access-overview" element={<AdminUserAccessOverviewPage />} />
|
||||
<Route path="billing">
|
||||
<Route index element={<Navigate to="/billing/admin" replace />} />
|
||||
|
|
|
|||
64
src/api.ts
64
src/api.ts
|
|
@ -1,10 +1,25 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
// api.ts
|
||||
import axios from 'axios';
|
||||
import { addCSRFTokenToHeaders, getCSRFToken, generateAndStoreCSRFToken } from './utils/csrfUtils';
|
||||
import { clearUserDataCache, getUserDataCache } from './utils/userCache';
|
||||
|
||||
// Utility function to resolve hostname to IP address
|
||||
const resolveHostnameToIP = async (hostname: string): Promise<string | null> => {
|
||||
try {
|
||||
// For localhost, return as is
|
||||
if (hostname === 'localhost' || hostname === '127.0.0.1') {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
// For production domains, we can't directly resolve IP due to CORS
|
||||
// But we can show the hostname which is more useful anyway
|
||||
return hostname;
|
||||
} catch (error) {
|
||||
console.warn('Could not resolve hostname to IP:', error);
|
||||
return hostname;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract mandate/instance context from current URL.
|
||||
* URL pattern: /mandates/:mandateId/:featureCode/:instanceId/...
|
||||
|
|
@ -29,25 +44,52 @@ const getContextFromUrl = (): { mandateId?: string; instanceId?: string } => {
|
|||
|
||||
import { getApiBaseUrl } from '../config/config';
|
||||
|
||||
const _baseUrl = getApiBaseUrl();
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(`[api] Backend: ${_baseUrl} | env: ${import.meta.env.MODE}`);
|
||||
}
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: _baseUrl,
|
||||
baseURL: getApiBaseUrl(),
|
||||
withCredentials: true,
|
||||
// FastAPI expects repeat-style array query params (``?ids=1&ids=2``).
|
||||
// Axios v1.x default would render ``?ids[]=1&ids[]=2``, which FastAPI
|
||||
// silently drops -- e.g. ``trackerIds`` filters on the Redmine stats
|
||||
// endpoint never reach the route. Setting ``indexes: null`` switches
|
||||
// the URLSearchParams visitor to repeat format. Applies globally so
|
||||
// every endpoint with array query params gets it for free.
|
||||
paramsSerializer: { indexes: null },
|
||||
});
|
||||
|
||||
// Add a request interceptor to add the auth token, context headers
|
||||
// Add a request interceptor to add the auth token, context headers, and log backend IP
|
||||
api.interceptors.request.use(
|
||||
async (config) => {
|
||||
// Add auth token if available (otherwise httpOnly cookies are used automatically)
|
||||
// Log backend information
|
||||
const backendUrl = config.baseURL || getApiBaseUrl();
|
||||
console.log(`🌐 Communicating with backend: ${backendUrl}`);
|
||||
|
||||
// Try to resolve and log the IP address
|
||||
if (backendUrl) {
|
||||
try {
|
||||
const url = new URL(backendUrl);
|
||||
const hostname = url.hostname;
|
||||
const resolvedIP = await resolveHostnameToIP(hostname);
|
||||
|
||||
console.log(`📍 Backend hostname: ${hostname}`);
|
||||
console.log(`🔗 Full backend URL: ${backendUrl}`);
|
||||
console.log(`🌍 Resolved address: ${resolvedIP}`);
|
||||
|
||||
// Log environment info
|
||||
console.log(`🏗️ Environment: ${import.meta.env.MODE}`);
|
||||
console.log(`⚙️ API Base URL: ${getApiBaseUrl()}`);
|
||||
} catch (error) {
|
||||
console.warn('Could not parse backend URL:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for auth token in localStorage and add to headers
|
||||
const authToken = localStorage.getItem('authToken');
|
||||
if (authToken && config.headers) {
|
||||
config.headers.Authorization = `Bearer ${authToken}`;
|
||||
console.log('🔑 Using Bearer token for authentication');
|
||||
} else {
|
||||
// Fallback: httpOnly cookies
|
||||
console.log('🍪 Using httpOnly cookies for authentication (automatic)');
|
||||
}
|
||||
|
||||
// Send app language to backend so i18n labels match the UI
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
import type { AttributeType } from '../utils/attributeTypeMapper';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
import api from '../api';
|
||||
import { addCSRFTokenToHeaders } from '../utils/csrfUtils';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* ClickUp API — ClickUp-specific functions for the workflow automation flow editor.
|
||||
*
|
||||
* Extracted from the legacy workflowApi.ts re-export shim so each integration
|
||||
* lives in its own module.
|
||||
*/
|
||||
|
||||
import type { ApiRequestFunction } from './workflowAutomationApi';
|
||||
|
||||
function _encodedConnectionId(connectionId: string): string {
|
||||
return encodeURIComponent(connectionId);
|
||||
}
|
||||
|
||||
/** ClickUp GET /task/{taskId} — list.id for resolving list-scoped fields when only task id is known. */
|
||||
export async function fetchClickupTask(
|
||||
request: ApiRequestFunction,
|
||||
connectionId: string,
|
||||
taskId: string
|
||||
): Promise<Record<string, unknown>> {
|
||||
const data = await request({
|
||||
url: `/api/clickup/${_encodedConnectionId(connectionId)}/tasks/${encodeURIComponent(taskId)}`,
|
||||
method: 'get',
|
||||
});
|
||||
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
||||
}
|
||||
|
||||
/** ClickUp list metadata (statuses, etc.) — GET /api/clickup/{connectionId}/lists/{listId}. */
|
||||
export async function fetchClickupList(
|
||||
request: ApiRequestFunction,
|
||||
connectionId: string,
|
||||
listId: string
|
||||
): Promise<Record<string, unknown>> {
|
||||
const data = await request({
|
||||
url: `/api/clickup/${_encodedConnectionId(connectionId)}/lists/${encodeURIComponent(listId)}`,
|
||||
method: 'get',
|
||||
});
|
||||
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
||||
}
|
||||
|
||||
/** ClickUp workspace/team (members for assignees) — GET /api/clickup/{connectionId}/teams/{teamId}. */
|
||||
export async function fetchClickupTeam(
|
||||
request: ApiRequestFunction,
|
||||
connectionId: string,
|
||||
teamId: string
|
||||
): Promise<Record<string, unknown>> {
|
||||
const data = await request({
|
||||
url: `/api/clickup/${_encodedConnectionId(connectionId)}/teams/${encodeURIComponent(teamId)}`,
|
||||
method: 'get',
|
||||
});
|
||||
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
||||
}
|
||||
|
||||
/** ClickUp list custom fields (GET /api/clickup/{connectionId}/lists/{listId}/fields). */
|
||||
export async function fetchClickupListFields(
|
||||
request: ApiRequestFunction,
|
||||
connectionId: string,
|
||||
listId: string
|
||||
): Promise<{ fields?: unknown[] } & Record<string, unknown>> {
|
||||
const data = await request({
|
||||
url: `/api/clickup/${_encodedConnectionId(connectionId)}/lists/${encodeURIComponent(listId)}/fields`,
|
||||
method: 'get',
|
||||
});
|
||||
return (data && typeof data === 'object' ? data : {}) as { fields?: unknown[] } & Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** ClickUp GET /list/{id}/task page (tasks in a list for relationship dropdowns). */
|
||||
export interface ClickupListTaskItem {
|
||||
id?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export async function fetchClickupListTasks(
|
||||
request: ApiRequestFunction,
|
||||
connectionId: string,
|
||||
listId: string,
|
||||
options?: { page?: number; includeClosed?: boolean }
|
||||
): Promise<
|
||||
{ tasks?: ClickupListTaskItem[]; last_page?: boolean } & Record<string, unknown>
|
||||
> {
|
||||
const data = await request({
|
||||
url: `/api/clickup/${_encodedConnectionId(connectionId)}/lists/${encodeURIComponent(listId)}/tasks`,
|
||||
method: 'get',
|
||||
params: {
|
||||
page: options?.page ?? 0,
|
||||
include_closed: options?.includeClosed ?? false,
|
||||
},
|
||||
});
|
||||
return (data && typeof data === 'object' ? data : {}) as {
|
||||
tasks?: ClickupListTaskItem[];
|
||||
last_page?: boolean;
|
||||
} & Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** Paginated tasks in a list — for ClickUp relationship dropdowns and input.form „ClickUp-Aufgabe". */
|
||||
export async function loadClickupListTasksForDropdown(
|
||||
request: ApiRequestFunction,
|
||||
connectionId: string,
|
||||
listId: string
|
||||
): Promise<Array<{ id: string; name: string }>> {
|
||||
const acc: Array<{ id: string; name: string }> = [];
|
||||
const seen = new Set<string>();
|
||||
const maxPages = 12;
|
||||
const pageSizeHint = 100;
|
||||
for (let page = 0; page < maxPages; page++) {
|
||||
const data = await fetchClickupListTasks(request, connectionId, listId, {
|
||||
page,
|
||||
includeClosed: false,
|
||||
});
|
||||
if (data && typeof data === 'object' && 'error' in data && (data as { error?: unknown }).error) {
|
||||
const err = (data as { error?: unknown }).error;
|
||||
const body = (data as { body?: string }).body;
|
||||
throw new Error(
|
||||
typeof err === 'string' ? err + (body ? `: ${body.slice(0, 200)}` : '') : 'ClickUp API error'
|
||||
);
|
||||
}
|
||||
const tasks = Array.isArray(data.tasks) ? data.tasks : [];
|
||||
for (const t of tasks) {
|
||||
const id = t?.id != null ? String(t.id) : '';
|
||||
if (!id || seen.has(id)) continue;
|
||||
seen.add(id);
|
||||
acc.push({ id, name: String(t.name ?? id) });
|
||||
}
|
||||
const rawLast = (data as Record<string, unknown>).last_page;
|
||||
const last =
|
||||
rawLast === true ||
|
||||
rawLast === 'true' ||
|
||||
tasks.length === 0 ||
|
||||
tasks.length < pageSizeHint;
|
||||
if (last) break;
|
||||
}
|
||||
acc.sort((a, b) => a.name.localeCompare(b.name, 'de'));
|
||||
return acc;
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import api from '../api';
|
||||
import { addCSRFTokenToHeaders, getCSRFToken, generateAndStoreCSRFToken } from '../utils/csrfUtils';
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Features API
|
||||
*
|
||||
|
|
@ -172,11 +170,19 @@ export async function fetchMyFeatures(): Promise<FeaturesMyResponse> {
|
|||
}
|
||||
|
||||
try {
|
||||
console.log('📡 featuresApi: Fetching /api/features/my');
|
||||
const response = await api.get<FeaturesMyResponse>('/api/features/my');
|
||||
|
||||
// Get the actual data (response.data contains the FeaturesMyResponse)
|
||||
const data = response.data;
|
||||
|
||||
console.log('✅ featuresApi: Loaded features:', {
|
||||
mandateCount: data?.mandates?.length || 0,
|
||||
totalInstances: data?.mandates
|
||||
?.flatMap(m => m.features)
|
||||
?.flatMap(f => f.instances)
|
||||
?.length || 0,
|
||||
});
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('❌ featuresApi: Error fetching features:', error);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Neutralization API
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import api from '../api';
|
||||
import type { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Redmine API
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Store API
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import api from '../api';
|
||||
|
||||
export interface TableListViewRow {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import api from '../api';
|
||||
import type { VoiceOption } from './voiceCatalogApi';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Trustee API
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { ApiRequestOptions } from '../hooks/useApi';
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Voice / Language Catalog API.
|
||||
*
|
||||
|
|
|
|||
17
src/api/workflowApi.connectionPath.test.ts
Normal file
17
src/api/workflowApi.connectionPath.test.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
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');
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* AccessLevelSelect
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* AccessRulesEditor
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* AccessRulesTable
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* AccessRules Components
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* AddConnectionWizard
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* ChatInput -- Shared chat input component.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* ChatMessageList -- Shared chat message display component.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
export { ChatMessageList } from './ChatMessageList';
|
||||
export type { ChatMessage } from './ChatMessageList';
|
||||
export { ChatInput } from './ChatInput';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { IoIosDownload, IoIosCopy } from 'react-icons/io';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { useState, useEffect } from 'react';
|
||||
import { IoIosDownload } from 'react-icons/io';
|
||||
import { Popup, PopupAction } from '../UiComponents/Popup/Popup';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
export { ContentPreview } from './ContentPreview';
|
||||
export type { ContentPreviewProps } from './ContentPreview';
|
||||
export { UrlContentPreview } from './UrlContentPreview';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import styles from '../ContentPreview.module.css';
|
||||
|
||||
interface ApplicationRendererProps {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import styles from '../ContentPreview.module.css';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import * as XLSX from 'xlsx';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import styles from '../ContentPreview.module.css';
|
||||
|
||||
interface HtmlRendererProps {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import styles from '../ContentPreview.module.css';
|
||||
|
||||
interface ImageRendererProps {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { useState } from 'react';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import styles from '../ContentPreview.module.css';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import styles from '../ContentPreview.module.css';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
// @ts-ignore
|
||||
import * as pdfjsLib from 'pdfjs-dist';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { IoIosWarning } from 'react-icons/io';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import styles from '../ContentPreview.module.css';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import styles from '../ContentPreview.module.css';
|
||||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import styles from '../ContentPreview.module.css';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { renderAsync } from 'docx-preview';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
export { JsonRenderer } from './JsonRenderer';
|
||||
export { ImageRenderer } from './ImageRenderer';
|
||||
export { TextRenderer } from './TextRenderer';
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Workflow Flow Editor - Data flow context for Data Picker and DynamicValueField.
|
||||
* Automation2 Flow Editor - Data flow context for Data Picker and DynamicValueField.
|
||||
* Extended with portTypeCatalog and systemVariables for the Typed Port System.
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext, useMemo } from 'react';
|
||||
import type { CanvasNode, CanvasConnection } from '../editor/FlowCanvas';
|
||||
import { getAvailableSources } from '../nodes/shared/dataFlowGraph';
|
||||
import type { ApiRequestFunction, ConditionOperatorDef, FormFieldType, NodeType, PortField, PortSchema, SystemVariable } from '../../../api/workflowAutomationApi';
|
||||
import type { ApiRequestFunction, ConditionOperatorDef, FormFieldType, NodeType, PortField, PortSchema, SystemVariable } from '../../../api/workflowApi';
|
||||
|
||||
export interface WorkflowDataFlowContextValue {
|
||||
export interface Automation2DataFlowContextValue {
|
||||
currentNodeId: string;
|
||||
nodes: CanvasNode[];
|
||||
connections: CanvasConnection[];
|
||||
|
|
@ -32,13 +30,13 @@ export interface WorkflowDataFlowContextValue {
|
|||
parseGraphDefinedSchema: (parameterKey: string) => PortSchema | null;
|
||||
}
|
||||
|
||||
const WorkflowDataFlowContext = createContext<WorkflowDataFlowContextValue | null>(null);
|
||||
const Automation2DataFlowContext = createContext<Automation2DataFlowContextValue | null>(null);
|
||||
|
||||
export function useWorkflowDataFlow(): WorkflowDataFlowContextValue | null {
|
||||
return useContext(WorkflowDataFlowContext);
|
||||
export function useAutomation2DataFlow(): Automation2DataFlowContextValue | null {
|
||||
return useContext(Automation2DataFlowContext);
|
||||
}
|
||||
|
||||
interface WorkflowDataFlowProviderProps {
|
||||
interface Automation2DataFlowProviderProps {
|
||||
node: CanvasNode | null;
|
||||
nodes: CanvasNode[];
|
||||
connections: CanvasConnection[];
|
||||
|
|
@ -54,7 +52,7 @@ interface WorkflowDataFlowProviderProps {
|
|||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const WorkflowDataFlowProvider: React.FC<WorkflowDataFlowProviderProps> = ({
|
||||
export const Automation2DataFlowProvider: React.FC<Automation2DataFlowProviderProps> = ({
|
||||
node,
|
||||
nodes,
|
||||
connections,
|
||||
|
|
@ -69,7 +67,7 @@ export const WorkflowDataFlowProvider: React.FC<WorkflowDataFlowProviderProps> =
|
|||
request,
|
||||
children,
|
||||
}) => {
|
||||
const value = useMemo((): WorkflowDataFlowContextValue | null => {
|
||||
const value = useMemo((): Automation2DataFlowContextValue | null => {
|
||||
if (!node) return null;
|
||||
const formTypeToPort: Record<string, string> = Object.fromEntries(
|
||||
formFieldTypes.map((f) => [f.id, f.portType])
|
||||
|
|
@ -137,8 +135,8 @@ export const WorkflowDataFlowProvider: React.FC<WorkflowDataFlowProviderProps> =
|
|||
}, [node, nodes, connections, nodeOutputsPreview, nodeTypes, language, portTypeCatalog, systemVariables, formFieldTypes, conditionOperatorCatalog, instanceId, request]);
|
||||
|
||||
return (
|
||||
<WorkflowDataFlowContext.Provider value={value}>
|
||||
<Automation2DataFlowContext.Provider value={value}>
|
||||
{children}
|
||||
</WorkflowDataFlowContext.Provider>
|
||||
</Automation2DataFlowContext.Provider>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Workflow Flow Editor Styles
|
||||
* Automation2 Flow Editor Styles
|
||||
* Sidebar with node list + canvas area.
|
||||
*/
|
||||
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* WorkflowFlowEditor
|
||||
* Automation2FlowEditor
|
||||
*
|
||||
* 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.
|
||||
|
|
@ -25,16 +23,15 @@ import {
|
|||
createTemplateFromWorkflow,
|
||||
copyTemplate,
|
||||
importWorkflowFromFile,
|
||||
WORKFLOW_FILE_EXTENSION,
|
||||
type NodeType,
|
||||
type NodeTypeCategory,
|
||||
type WorkflowGraph,
|
||||
type WorkflowDefinition,
|
||||
type Automation2Graph,
|
||||
type Automation2Workflow,
|
||||
type ExecuteGraphResponse,
|
||||
type WorkflowEntryPoint,
|
||||
type AutoVersion,
|
||||
type AutoTemplateScope,
|
||||
} from '../../../api/workflowAutomationApi';
|
||||
} from '../../../api/workflowApi';
|
||||
import {
|
||||
FlowCanvas,
|
||||
type CanvasNode,
|
||||
|
|
@ -52,7 +49,7 @@ import { fromApiGraph, toApiGraph, switchOutputCountFromCases, trimConnectionsFo
|
|||
import { buildNodeOutputsPreview, setPortTypeCatalog as setRegistryCatalog } from '../nodes/shared/outputPreviewRegistry';
|
||||
import { findGraphErrors } from '../nodes/shared/paramValidation';
|
||||
import { getLabel as getParamLabel } from '../nodes/shared/utils';
|
||||
import { WorkflowDataFlowProvider } from '../context/WorkflowDataFlowContext';
|
||||
import { Automation2DataFlowProvider } from '../context/Automation2DataFlowContext';
|
||||
import { usePrompt } from '../../../hooks/usePrompt';
|
||||
import { EditorChatPanel } from './EditorChatPanel';
|
||||
import type { PendingFile, EditorDataSource, EditorFeatureDataSource } from './EditorChatPanel';
|
||||
|
|
@ -60,12 +57,12 @@ import { EditorWorkflowChatList } from './EditorWorkflowChatList';
|
|||
import { RunTracingPanel } from './RunTracingPanel';
|
||||
import { UnifiedDataBar } from '../../../components/UnifiedDataBar';
|
||||
import type { UdbContext, UdbTab } from '../../../components/UnifiedDataBar';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
|
||||
const LOG = '[WorkflowEditor]';
|
||||
const LOG = '[Automation2]';
|
||||
|
||||
const CANVAS_HISTORY_MAX = 50;
|
||||
|
||||
|
|
@ -81,7 +78,7 @@ function cloneCanvasSnapshot(nodes: CanvasNode[], connections: CanvasConnection[
|
|||
};
|
||||
}
|
||||
|
||||
interface WorkflowFlowEditorProps {
|
||||
interface Automation2FlowEditorProps {
|
||||
instanceId: string;
|
||||
mandateId?: string;
|
||||
language?: string;
|
||||
|
|
@ -95,7 +92,7 @@ interface WorkflowFlowEditorProps {
|
|||
onSourcesChanged?: () => void;
|
||||
}
|
||||
|
||||
export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instanceId,
|
||||
export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ instanceId,
|
||||
mandateId,
|
||||
language = 'de',
|
||||
initialWorkflowId,
|
||||
|
|
@ -113,9 +110,9 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
const [categories, setCategories] = useState<NodeTypeCategory[]>([]);
|
||||
const [portTypeCatalog, setPortTypeCatalog] = useState<Record<string, unknown>>({});
|
||||
const [systemVariables, setSystemVariables] = useState<Record<string, unknown>>({});
|
||||
const [formFieldTypes, setFormFieldTypes] = useState<import('../../../api/workflowAutomationApi').FormFieldType[]>([]);
|
||||
const [formFieldTypes, setFormFieldTypes] = useState<import('../../../api/workflowApi').FormFieldType[]>([]);
|
||||
const [conditionOperatorCatalog, setConditionOperatorCatalog] = useState<
|
||||
Record<string, import('../../../api/workflowAutomationApi').ConditionOperatorDef[]>
|
||||
Record<string, import('../../../api/workflowApi').ConditionOperatorDef[]>
|
||||
>({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
|
@ -140,7 +137,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
const [canvasStickyNotes, setCanvasStickyNotes] = useState<CanvasStickyNote[]>([]);
|
||||
const [executing, setExecuting] = useState(false);
|
||||
const [executeResult, setExecuteResult] = useState<ExecuteGraphResponse | null>(null);
|
||||
const [workflows, setWorkflows] = useState<WorkflowDefinition[]>([]);
|
||||
const [workflows, setWorkflows] = useState<Automation2Workflow[]>([]);
|
||||
const [currentWorkflowId, setCurrentWorkflowId] = useState<string | null>(null);
|
||||
const [selectedNode, setSelectedNode] = useState<CanvasNode | null>(null);
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
|
@ -156,7 +153,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
instanceId,
|
||||
mandateId: mandateId || '',
|
||||
featureInstanceId: instanceId,
|
||||
surface: 'workflowAutomation',
|
||||
surface: 'graphEditor',
|
||||
}), [instanceId, mandateId]);
|
||||
const [versions, setVersions] = useState<AutoVersion[]>([]);
|
||||
const [currentVersionId, setCurrentVersionId] = useState<string | null>(null);
|
||||
|
|
@ -304,7 +301,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
|
||||
const applyGraphWithSync = useCallback(
|
||||
(
|
||||
graph: WorkflowGraph | null | undefined,
|
||||
graph: Automation2Graph | null | undefined,
|
||||
wfInvocations: WorkflowEntryPoint[] | undefined,
|
||||
opts?: { skipHistory?: boolean }
|
||||
) => {
|
||||
|
|
@ -312,7 +309,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
pushCanvasHistoryPastFromCurrent();
|
||||
}
|
||||
setInvocations(wfInvocations ?? []);
|
||||
const g: WorkflowGraph = graph ?? { nodes: [], connections: [] };
|
||||
const g: Automation2Graph = graph ?? { nodes: [], connections: [] };
|
||||
const { nodes, connections } = fromApiGraph(g, nodeTypes);
|
||||
setCanvasNodes(nodes);
|
||||
setCanvasConnections(connections);
|
||||
|
|
@ -321,7 +318,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
);
|
||||
|
||||
const handleFromApiGraph = useCallback(
|
||||
(graph: WorkflowGraph, wfInvocations?: WorkflowEntryPoint[]) => {
|
||||
(graph: Automation2Graph, wfInvocations?: WorkflowEntryPoint[]) => {
|
||||
applyGraphWithSync(graph, wfInvocations);
|
||||
},
|
||||
[applyGraphWithSync]
|
||||
|
|
@ -357,7 +354,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
setExecuteResult(null);
|
||||
try {
|
||||
const ep = currentWorkflowId ? invocations[0]?.id : undefined;
|
||||
const result = await executeGraph(request, graph, currentWorkflowId ?? undefined, {
|
||||
const result = await executeGraph(request, instanceId, graph, currentWorkflowId ?? undefined, {
|
||||
...(ep ? { entryPointId: ep } : {}),
|
||||
});
|
||||
setExecuteResult(result);
|
||||
|
|
@ -406,7 +403,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
setSaving(true);
|
||||
try {
|
||||
if (currentWorkflowId) {
|
||||
const updated = await updateWorkflow(request, currentWorkflowId, {
|
||||
const updated = await updateWorkflow(request, instanceId, currentWorkflowId, {
|
||||
graph,
|
||||
invocations,
|
||||
targetFeatureInstanceId,
|
||||
|
|
@ -423,12 +420,11 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
setSaving(false);
|
||||
return;
|
||||
}
|
||||
const created = await createWorkflow(request, {
|
||||
const created = await createWorkflow(request, instanceId, {
|
||||
label: label.trim() || t('Neuer Workflow'),
|
||||
graph,
|
||||
invocations,
|
||||
targetFeatureInstanceId,
|
||||
mandateId,
|
||||
});
|
||||
setCurrentWorkflowId(created.id);
|
||||
setInvocations(created.invocations ?? []);
|
||||
|
|
@ -440,12 +436,12 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}, [request, mandateId, canvasNodes, canvasConnections, currentWorkflowId, promptInput, invocations, t, nodeErrors, targetFeatureInstanceId, hasCanvasStartNode]);
|
||||
}, [request, instanceId, canvasNodes, canvasConnections, currentWorkflowId, promptInput, invocations, t, nodeErrors, targetFeatureInstanceId, hasCanvasStartNode]);
|
||||
|
||||
const handleLoad = useCallback(
|
||||
async (workflowId: string) => {
|
||||
try {
|
||||
const wf = await fetchWorkflow(request, workflowId);
|
||||
const wf = await fetchWorkflow(request, instanceId, workflowId);
|
||||
if (wf.graph) {
|
||||
handleFromApiGraph(wf.graph, wf.invocations);
|
||||
} else {
|
||||
|
|
@ -467,7 +463,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
setExecuteResult(null);
|
||||
applyGraphWithSync({ nodes: [], connections: [] }, []);
|
||||
try {
|
||||
const result = await fetchWorkflows(request);
|
||||
const result = await fetchWorkflows(request, instanceId);
|
||||
setWorkflows(Array.isArray(result) ? result : result.items);
|
||||
} catch (refreshErr) {
|
||||
console.error(`${LOG} workflows refresh failed`, refreshErr);
|
||||
|
|
@ -480,7 +476,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
});
|
||||
}
|
||||
},
|
||||
[request, handleFromApiGraph, applyGraphWithSync, t]
|
||||
[request, instanceId, handleFromApiGraph, applyGraphWithSync, t]
|
||||
);
|
||||
|
||||
const handleWorkflowSelect = useCallback(
|
||||
|
|
@ -548,10 +544,11 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
);
|
||||
|
||||
const loadNodeTypes = useCallback(async () => {
|
||||
if (!instanceId) return;
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const data = await fetchNodeTypes(request, mandateId || '', language);
|
||||
const data = await fetchNodeTypes(request, instanceId, language);
|
||||
setNodeTypes(data.nodeTypes);
|
||||
setCategories(data.categories);
|
||||
if (data.portTypeCatalog) {
|
||||
|
|
@ -568,16 +565,17 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [language, request]);
|
||||
}, [instanceId, language, request]);
|
||||
|
||||
const loadWorkflows = useCallback(async () => {
|
||||
if (!instanceId) return;
|
||||
try {
|
||||
const result = await fetchWorkflows(request, { mandateId: mandateId || undefined });
|
||||
const result = await fetchWorkflows(request, instanceId);
|
||||
setWorkflows(Array.isArray(result) ? result : result.items);
|
||||
} catch (e) {
|
||||
console.error(`${LOG} loadWorkflows failed`, e);
|
||||
}
|
||||
}, [request, mandateId]);
|
||||
}, [instanceId, request]);
|
||||
|
||||
useEffect(() => {
|
||||
loadNodeTypes();
|
||||
|
|
@ -667,17 +665,17 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
);
|
||||
|
||||
const loadVersions = useCallback(async () => {
|
||||
if (!currentWorkflowId) {
|
||||
if (!instanceId || !currentWorkflowId) {
|
||||
setVersions([]);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const v = await fetchVersions(request, currentWorkflowId);
|
||||
const v = await fetchVersions(request, instanceId, currentWorkflowId);
|
||||
setVersions(v);
|
||||
} catch (e) {
|
||||
console.error(`${LOG} loadVersions failed`, e);
|
||||
}
|
||||
}, [currentWorkflowId, request]);
|
||||
}, [instanceId, currentWorkflowId, request]);
|
||||
|
||||
useEffect(() => {
|
||||
loadVersions();
|
||||
|
|
@ -698,9 +696,10 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
|
||||
const handlePublishVersion = useCallback(
|
||||
async (versionId: string) => {
|
||||
if (!instanceId) return;
|
||||
setVersionLoading(true);
|
||||
try {
|
||||
await publishVersion(request, versionId);
|
||||
await publishVersion(request, instanceId, versionId);
|
||||
await loadVersions();
|
||||
} catch (e: unknown) {
|
||||
setExecuteResult({ success: false, error: e instanceof Error ? e.message : String(e) });
|
||||
|
|
@ -708,14 +707,15 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
setVersionLoading(false);
|
||||
}
|
||||
},
|
||||
[request, loadVersions]
|
||||
[request, instanceId, loadVersions]
|
||||
);
|
||||
|
||||
const handleUnpublishVersion = useCallback(
|
||||
async (versionId: string) => {
|
||||
if (!instanceId) return;
|
||||
setVersionLoading(true);
|
||||
try {
|
||||
await unpublishVersion(request, versionId);
|
||||
await unpublishVersion(request, instanceId, versionId);
|
||||
await loadVersions();
|
||||
} catch (e: unknown) {
|
||||
setExecuteResult({ success: false, error: e instanceof Error ? e.message : String(e) });
|
||||
|
|
@ -723,14 +723,15 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
setVersionLoading(false);
|
||||
}
|
||||
},
|
||||
[request, loadVersions]
|
||||
[request, instanceId, loadVersions]
|
||||
);
|
||||
|
||||
const handleArchiveVersion = useCallback(
|
||||
async (versionId: string) => {
|
||||
if (!instanceId) return;
|
||||
setVersionLoading(true);
|
||||
try {
|
||||
await archiveVersion(request, versionId);
|
||||
await archiveVersion(request, instanceId, versionId);
|
||||
await loadVersions();
|
||||
} catch (e: unknown) {
|
||||
setExecuteResult({ success: false, error: e instanceof Error ? e.message : String(e) });
|
||||
|
|
@ -738,14 +739,14 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
setVersionLoading(false);
|
||||
}
|
||||
},
|
||||
[request, loadVersions]
|
||||
[request, instanceId, loadVersions]
|
||||
);
|
||||
|
||||
const handleCreateDraft = useCallback(async () => {
|
||||
if (!currentWorkflowId) return;
|
||||
if (!instanceId || !currentWorkflowId) return;
|
||||
setVersionLoading(true);
|
||||
try {
|
||||
const draft = await createDraftVersion(request, currentWorkflowId);
|
||||
const draft = await createDraftVersion(request, instanceId, currentWorkflowId);
|
||||
await loadVersions();
|
||||
setCurrentVersionId(draft.id);
|
||||
} catch (e: unknown) {
|
||||
|
|
@ -753,16 +754,16 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
} finally {
|
||||
setVersionLoading(false);
|
||||
}
|
||||
}, [request, currentWorkflowId, loadVersions]);
|
||||
}, [request, instanceId, currentWorkflowId, loadVersions]);
|
||||
|
||||
// Template: save current workflow as template
|
||||
const [templateSaving, setTemplateSaving] = useState(false);
|
||||
const handleSaveAsTemplate = useCallback(
|
||||
async (scope: AutoTemplateScope) => {
|
||||
if (!currentWorkflowId) return;
|
||||
if (!instanceId || !currentWorkflowId) return;
|
||||
setTemplateSaving(true);
|
||||
try {
|
||||
await createTemplateFromWorkflow(request, currentWorkflowId, scope);
|
||||
await createTemplateFromWorkflow(request, instanceId, currentWorkflowId, scope);
|
||||
setExecuteResult({ success: true, error: undefined } as unknown as ExecuteGraphResponse);
|
||||
} catch (e: unknown) {
|
||||
setExecuteResult({ success: false, error: e instanceof Error ? e.message : String(e) });
|
||||
|
|
@ -770,15 +771,16 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
setTemplateSaving(false);
|
||||
}
|
||||
},
|
||||
[request, currentWorkflowId]
|
||||
[request, instanceId, currentWorkflowId]
|
||||
);
|
||||
|
||||
// Template: new workflow from template
|
||||
const [templatePickerOpen, setTemplatePickerOpen] = useState(false);
|
||||
const handleNewFromTemplate = useCallback(
|
||||
async (templateId: string) => {
|
||||
if (!instanceId) return;
|
||||
try {
|
||||
const wf = await copyTemplate(request, templateId);
|
||||
const wf = await copyTemplate(request, instanceId, templateId);
|
||||
setWorkflows((prev) => [...prev, wf]);
|
||||
setCurrentWorkflowId(wf.id);
|
||||
if (wf.graph) handleFromApiGraph(wf.graph, wf.invocations);
|
||||
|
|
@ -787,7 +789,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
setExecuteResult({ success: false, error: e instanceof Error ? e.message : String(e) });
|
||||
}
|
||||
},
|
||||
[request, handleFromApiGraph]
|
||||
[request, instanceId, handleFromApiGraph]
|
||||
);
|
||||
|
||||
|
||||
|
|
@ -945,20 +947,12 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
activeTab={udbTab as UdbTab}
|
||||
onTabChange={(tab) => setUdbTab(tab as LeftTab)}
|
||||
hideTabs={['chats']}
|
||||
onFileSelect={async (fileId, fileName) => {
|
||||
if (fileName?.toLowerCase().endsWith(WORKFLOW_FILE_EXTENSION)) {
|
||||
try {
|
||||
const result = await importWorkflowFromFile(request, { fileId });
|
||||
await loadWorkflows();
|
||||
if (result?.workflow?.id) handleWorkflowSelect(result.workflow.id);
|
||||
} catch (e) {
|
||||
console.error('[workflowAutomation] workflow file import failed', e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
onFileSelect?.(fileId, fileName);
|
||||
}}
|
||||
onFileSelect={onFileSelect}
|
||||
onSourcesChanged={onSourcesChanged}
|
||||
onWorkflowImportedFromFile={async (workflowId) => {
|
||||
await loadWorkflows();
|
||||
handleWorkflowSelect(workflowId);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -1030,12 +1024,12 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
stickyNotes={canvasStickyNotes}
|
||||
onStickyNotesChange={setCanvasStickyNotes}
|
||||
onExternalDrop={async (mime, payload) => {
|
||||
if (mime !== 'application/json+workflow') return false;
|
||||
if (mime !== 'application/json+workflow' || !instanceId) return false;
|
||||
const p = payload as { files?: Array<{ id: string }> } | undefined;
|
||||
const fileId = p?.files?.[0]?.id;
|
||||
if (!fileId) return false;
|
||||
try {
|
||||
const result = await importWorkflowFromFile(request, { fileId });
|
||||
const result = await importWorkflowFromFile(request, instanceId, { fileId });
|
||||
await loadWorkflows();
|
||||
if (result?.workflow?.id) handleWorkflowSelect(result.workflow.id);
|
||||
return true;
|
||||
|
|
@ -1048,7 +1042,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
</div>
|
||||
{configurableSelected && selectedNode && (
|
||||
<div className={styles.nodeConfigPanelWrap} data-suppress-flow-node-hotkeys="">
|
||||
<WorkflowDataFlowProvider
|
||||
<Automation2DataFlowProvider
|
||||
node={selectedNode}
|
||||
nodes={canvasNodes}
|
||||
connections={canvasConnections}
|
||||
|
|
@ -1073,7 +1067,7 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
request={request}
|
||||
verboseSchema={verboseSchema}
|
||||
/>
|
||||
</WorkflowDataFlowProvider>
|
||||
</Automation2DataFlowProvider>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -1128,4 +1122,4 @@ export const WorkflowFlowEditor: React.FC<WorkflowFlowEditorProps> = ({ instance
|
|||
);
|
||||
};
|
||||
|
||||
export default WorkflowFlowEditor;
|
||||
export default Automation2FlowEditor;
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* CanvasHeader - Workflow controls, version selector, and execute result.
|
||||
*/
|
||||
|
|
@ -28,8 +26,8 @@ import {
|
|||
HiOutlineChatBubbleLeftEllipsis,
|
||||
HiOutlineSquares2X2,
|
||||
} from 'react-icons/hi2';
|
||||
import type { WorkflowDefinition, ExecuteGraphResponse, AutoVersion, AutoTemplateScope } from '../../../api/workflowAutomationApi';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
import type { Automation2Workflow, ExecuteGraphResponse, AutoVersion, AutoTemplateScope } from '../../../api/workflowApi';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import { getUserDataCache } from '../../../utils/userCache';
|
||||
|
|
@ -62,7 +60,7 @@ export interface CanvasHeaderCanvasEditProps {
|
|||
}
|
||||
|
||||
interface CanvasHeaderProps {
|
||||
workflows: WorkflowDefinition[];
|
||||
workflows: Automation2Workflow[];
|
||||
currentWorkflowId: string | null;
|
||||
onWorkflowSelect: (workflowId: string | null) => void;
|
||||
onNew: () => void;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* EditorChatPanel
|
||||
*
|
||||
* AI Chat sidebar for the WorkflowAutomation editor.
|
||||
* AI Chat sidebar for the GraphicalEditor.
|
||||
* Streams responses via SSE (same pattern as Workspace chat).
|
||||
* File & data-source attachment UX mirrors WorkspaceInput:
|
||||
* - Files: drag & drop from FilesTab (UDB) onto input area, or click in UDB
|
||||
|
|
@ -89,7 +87,7 @@ export const EditorChatPanel: React.FC<EditorChatPanelProps> = ({ instanceId,
|
|||
|
||||
// Load persisted chat history from the backend whenever the workflow changes.
|
||||
// The chat is stored in `ChatWorkflow.linkedWorkflowId == workflowId` and is
|
||||
// returned by `GET /api/workflow-automation/{workflowId}/chat/messages`.
|
||||
// returned by `GET /api/workflows/{instanceId}/{workflowId}/chat/messages`.
|
||||
// For an unsaved workflow (workflowId == null) we just clear the panel.
|
||||
useEffect(() => {
|
||||
if (!workflowId) {
|
||||
|
|
@ -101,7 +99,7 @@ export const EditorChatPanel: React.FC<EditorChatPanelProps> = ({ instanceId,
|
|||
setHistoryLoading(true);
|
||||
try {
|
||||
const res = await api.get<PersistedEditorChatResponse>(
|
||||
`/api/workflow-automation/${workflowId}/chat/messages`,
|
||||
`/api/workflows/${instanceId}/${workflowId}/chat/messages`,
|
||||
);
|
||||
if (cancelled) return;
|
||||
const persisted = (res.data?.messages || []).map((m): ChatMessage => ({
|
||||
|
|
@ -168,7 +166,7 @@ export const EditorChatPanel: React.FC<EditorChatPanelProps> = ({ instanceId,
|
|||
|
||||
const baseURL = api.defaults.baseURL || '';
|
||||
const cleanup = startSseStream({
|
||||
url: `${baseURL}/api/workflow-automation/${workflowId}/chat/stream`,
|
||||
url: `${baseURL}/api/workflows/${instanceId}/${workflowId}/chat/stream`,
|
||||
body,
|
||||
handlers: {
|
||||
onChunk: (event) => {
|
||||
|
|
@ -229,7 +227,7 @@ export const EditorChatPanel: React.FC<EditorChatPanelProps> = ({ instanceId,
|
|||
: m));
|
||||
}
|
||||
try {
|
||||
await api.post(`/api/workflow-automation/${workflowId}/chat/stop`);
|
||||
await api.post(`/api/workflows/${instanceId}/${workflowId}/chat/stop`);
|
||||
} catch {
|
||||
}
|
||||
abortRef.current?.();
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* EditorWorkflowChatList
|
||||
*
|
||||
* UDB "Chats" tab content for the WorkflowAutomation editor: each AutoWorkflow
|
||||
* is treated as one editor chat session. Lists workflows already loaded by the
|
||||
* parent editor (no extra fetch), supports search and "+ Neu" to start a fresh
|
||||
* UDB "Chats" tab content for the GraphicalEditor: each AutoWorkflow is treated
|
||||
* as one editor chat session. Lists workflows already loaded by the parent
|
||||
* editor (no extra fetch), supports search and "+ Neu" to start a fresh
|
||||
* workflow chat. Mirrors the spirit of the Workspace ChatsTab but uses
|
||||
* WorkflowAutomation data instead of the workspace endpoint.
|
||||
* GraphicalEditor data instead of the workspace endpoint.
|
||||
*/
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import type { WorkflowDefinition } from '../../../api/workflowAutomationApi';
|
||||
import type { Automation2Workflow } from '../../../api/workflowApi';
|
||||
|
||||
interface EditorWorkflowChatListProps {
|
||||
workflows: WorkflowDefinition[];
|
||||
workflows: Automation2Workflow[];
|
||||
currentWorkflowId: string | null;
|
||||
onSelect: (workflowId: string | null) => void;
|
||||
onNew: () => void;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* FlowCanvas - Workflow graph canvas with nodes and connection lines.
|
||||
* Nodes have 4 connection handles (one per side), drag nodes to add, connect with arrows.
|
||||
|
|
@ -15,8 +13,8 @@ import React, {
|
|||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import type { GraphDefinedSchemaRef, NodeType } from '../../../api/workflowAutomationApi';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
import type { GraphDefinedSchemaRef, NodeType } from '../../../api/workflowApi';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import { AiBadge } from '../nodes/shared/AiBadge';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* NodeConfigPanel - Generic parameter renderer for all node types.
|
||||
* Renders each parameter using FRONTEND_TYPE_RENDERERS based on frontendType.
|
||||
|
|
@ -7,15 +5,15 @@
|
|||
|
||||
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
import type { CanvasNode } from './FlowCanvas';
|
||||
import type { GraphDefinedSchemaRef, NodeType, NodeTypeParameter, PortSchema } from '../../../api/workflowAutomationApi';
|
||||
import type { ApiRequestFunction } from '../../../api/workflowAutomationApi';
|
||||
import type { GraphDefinedSchemaRef, NodeType, NodeTypeParameter, PortSchema } from '../../../api/workflowApi';
|
||||
import type { ApiRequestFunction } from '../../../api/workflowApi';
|
||||
import { getLabel } from '../nodes/shared/utils';
|
||||
import { FRONTEND_TYPE_RENDERERS } from '../nodes/frontendTypeRenderers';
|
||||
import { ContextBuilderRenderer } from '../nodes/frontendTypeRenderers/ContextBuilderRenderer';
|
||||
import { RequiredAttributePicker } from '../nodes/shared/RequiredAttributePicker';
|
||||
import { findRequiredErrors } from '../nodes/shared/paramValidation';
|
||||
import { useWorkflowDataFlow } from '../context/WorkflowDataFlowContext';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
import { useAutomation2DataFlow } from '../context/Automation2DataFlowContext';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import { AccordionList } from '../../UiComponents/AccordionList';
|
||||
|
|
@ -212,7 +210,7 @@ export const NodeConfigPanel: React.FC<NodeConfigPanelProps> = ({ node,
|
|||
[onParametersChange]
|
||||
);
|
||||
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const portTypeCatalog: Record<string, PortSchema> = (dataFlow?.portTypeCatalog as Record<string, PortSchema> | undefined) ?? {};
|
||||
|
||||
// Phase-4 Schicht-4 — Pflicht-Params zuerst sortieren, damit der User
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* NodeListItem - Draggable node type item for the sidebar.
|
||||
* Used in both regular categories and I/O sub-groups.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { NodeType } from '../../../api/workflowAutomationApi';
|
||||
import type { NodeType } from '../../../api/workflowApi';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
import { getCategoryIcon } from '../nodes/shared/utils';
|
||||
import type { GetLabelFn } from '../nodes/shared/utils';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
import { AiBadge } from '../nodes/shared/AiBadge';
|
||||
|
||||
interface NodeListItemProps {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* NodeSidebar - Sidebar with searchable, collapsible node list.
|
||||
* Groups node types by category (start, input, flow, data, ai, email, sharepoint).
|
||||
|
|
@ -7,11 +5,11 @@
|
|||
|
||||
import React, { useMemo } from 'react';
|
||||
import { FaChevronDown, FaChevronRight } from 'react-icons/fa';
|
||||
import type { NodeType, NodeTypeCategory } from '../../../api/workflowAutomationApi';
|
||||
import type { NodeType, NodeTypeCategory } from '../../../api/workflowApi';
|
||||
import { CATEGORY_ORDER, HIDDEN_NODE_IDS } from '../nodes/shared/constants';
|
||||
import { getLabel } from '../nodes/shared/utils';
|
||||
import { NodeListItem } from './NodeListItem';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* RunTracingPanel
|
||||
*
|
||||
|
|
@ -9,7 +7,7 @@
|
|||
*/
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { useApiRequest } from '../../../hooks/useApi';
|
||||
import type { AutoStepLog } from '../../../api/workflowAutomationApi';
|
||||
import type { AutoStepLog } from '../../../api/workflowApi';
|
||||
import api from '../../../api';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
@ -100,7 +98,7 @@ export const RunTracingPanel: React.FC<RunTracingPanelProps> = ({
|
|||
setLoading(true);
|
||||
try {
|
||||
const data = await request({
|
||||
url: `/api/workflow-automation/runs/${runId}/steps`,
|
||||
url: `/api/workflows/${instanceId}/runs/${runId}/steps`,
|
||||
method: 'get',
|
||||
});
|
||||
setSteps(data?.steps || []);
|
||||
|
|
@ -117,7 +115,7 @@ export const RunTracingPanel: React.FC<RunTracingPanelProps> = ({
|
|||
loadSteps();
|
||||
|
||||
const baseUrl = api.defaults.baseURL || '';
|
||||
const url = `${baseUrl}/api/workflow-automation/runs/${runId}/stream`;
|
||||
const url = `${baseUrl}/api/workflows/${instanceId}/runs/${runId}/stream`;
|
||||
const es = new EventSource(url, { withCredentials: true });
|
||||
eventSourceRef.current = es;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* TemplatePicker - modal to browse and select a workflow template for creating a new workflow.
|
||||
*/
|
||||
|
|
@ -11,8 +9,8 @@ import {
|
|||
type AutoWorkflowTemplate,
|
||||
type AutoTemplateScope,
|
||||
type ApiRequestFunction,
|
||||
} from '../../../api/workflowAutomationApi';
|
||||
import styles from './WorkflowFlowEditor.module.css';
|
||||
} from '../../../api/workflowApi';
|
||||
import styles from './Automation2FlowEditor.module.css';
|
||||
import { useLanguage } from '../../../providers/language/LanguageContext';
|
||||
|
||||
interface TemplatePickerProps {
|
||||
|
|
@ -52,7 +50,7 @@ export const TemplatePicker: React.FC<TemplatePickerProps> = ({
|
|||
setLoading(true);
|
||||
try {
|
||||
const scope = activeScope === 'all' ? undefined : activeScope;
|
||||
const result = await fetchTemplates(request, scope);
|
||||
const result = await fetchTemplates(request, instanceId, scope);
|
||||
setTemplates(Array.isArray(result) ? result : result.items);
|
||||
} catch {
|
||||
setTemplates([]);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
export { WorkflowFlowEditor, WorkflowFlowEditor as FlowEditor } from './editor/WorkflowFlowEditor';
|
||||
export { Automation2FlowEditor, Automation2FlowEditor as FlowEditor } from './editor/Automation2FlowEditor';
|
||||
export type { PendingFile, EditorDataSource, EditorFeatureDataSource } from './editor/EditorChatPanel';
|
||||
export { FlowCanvas, STICKY_NOTE_PALETTE, STICKY_NOTE_DEFAULT_COLOR_ID, STICKY_NOTE_DEFAULT_HEIGHT, getStickyNotePaletteEntry } from './editor/FlowCanvas';
|
||||
export type { CanvasNode, CanvasConnection, CanvasStickyNote, FlowCanvasHandle, FlowCanvasViewportEditState } from './editor/FlowCanvas';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* One text field per option — the text the end user sees in the dropdown.
|
||||
* Stored as { value, label } with the same string so payload and UI stay in sync.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Form node config - draggable fields, types, required toggle
|
||||
*/
|
||||
|
|
@ -8,8 +6,8 @@ import React from 'react';
|
|||
import { FaGripVertical, FaTimes } from 'react-icons/fa';
|
||||
import type { FormField, NodeConfigRendererProps } from '../shared/types';
|
||||
import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { FormFieldOptionsEditor } from './FormFieldOptionsEditor';
|
||||
import {
|
||||
deriveFormFieldPayloadKey,
|
||||
|
|
@ -21,7 +19,7 @@ import { useLanguage } from '../../../../providers/language/LanguageContext';
|
|||
|
||||
export const FormNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => {
|
||||
const { t } = useLanguage();
|
||||
const ctx = useWorkflowDataFlow();
|
||||
const ctx = useAutomation2DataFlow();
|
||||
const fieldTypeOptions = ctx?.formFieldTypes?.length
|
||||
? ctx.formFieldTypes
|
||||
: FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' }));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Helpers for optional select/multiselect rows on workflow form field definitions.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
export { FormNodeConfig } from './FormNodeConfig';
|
||||
export { FormFieldOptionsEditor } from './FormFieldOptionsEditor';
|
||||
export type { FormFieldOptionRow } from './formFieldOptionsUtils';
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Backend-driven case list for flow.switch (depends on value dataRef).
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { FieldRendererProps } from './index';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { isRef, type DataRef } from '../shared/dataRef';
|
||||
import { toApiGraph } from '../shared/graphUtils';
|
||||
import { fetchConditionMeta, type ConditionOperatorDef } from '../../../../api/workflowAutomationApi';
|
||||
import { fetchConditionMeta, type ConditionOperatorDef } from '../../../../api/workflowApi';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
|
||||
export interface SwitchCase {
|
||||
|
|
@ -118,7 +116,7 @@ export const CaseListEditor: React.FC<FieldRendererProps> = ({
|
|||
allParams,
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dependsOn =
|
||||
param.frontendOptions && typeof param.frontendOptions === 'object'
|
||||
? String((param.frontendOptions as Record<string, unknown>).dependsOn ?? 'value')
|
||||
|
|
@ -159,7 +157,7 @@ export const CaseListEditor: React.FC<FieldRendererProps> = ({
|
|||
|
||||
if (dataFlow?.instanceId && dataFlow.request) {
|
||||
setLoading(true);
|
||||
fetchConditionMeta(dataFlow.request, {
|
||||
fetchConditionMeta(dataFlow.request, dataFlow.instanceId, {
|
||||
graph: toApiGraph(dataFlow.nodes, dataFlow.connections),
|
||||
nodeId: dataFlow.currentNodeId,
|
||||
ref: { type: 'ref', nodeId: ref.nodeId, path: ref.path },
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* clickupList — hierarchical ClickUp list picker via connector browse API.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import { fetchBrowse, type BrowseEntry } from '../../../../api/workflowAutomationApi';
|
||||
import { fetchClickupList } from '../../../../api/clickupApi';
|
||||
import {
|
||||
fetchBrowse,
|
||||
fetchClickupList,
|
||||
type BrowseEntry,
|
||||
} from '../../../../api/workflowApi';
|
||||
import type { FieldRendererProps } from './index';
|
||||
import {
|
||||
clickupBrowseParentPath,
|
||||
|
|
@ -73,7 +74,7 @@ export const ClickUpListPicker: React.FC<FieldRendererProps> = ({
|
|||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const res = await fetchBrowse(request, connectionReference, 'clickup', path);
|
||||
const res = await fetchBrowse(request, instanceId, connectionReference, 'clickup', path);
|
||||
setItems(res.items);
|
||||
setBrowsePath(res.path || path);
|
||||
} catch (err: unknown) {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Backend-driven condition editor for flow.ifElse (depends on Item dataRef).
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { FieldRendererProps } from './index';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { isRef, type DataRef } from '../shared/dataRef';
|
||||
import { toApiGraph } from '../shared/graphUtils';
|
||||
import { fetchConditionMeta, type ConditionOperatorDef } from '../../../../api/workflowAutomationApi';
|
||||
import { fetchConditionMeta, type ConditionOperatorDef } from '../../../../api/workflowApi';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
|
||||
export interface StructuredCondition {
|
||||
|
|
@ -43,7 +41,7 @@ export const ConditionEditor: React.FC<FieldRendererProps> = ({
|
|||
allParams,
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const dependsOn =
|
||||
param.frontendOptions && typeof param.frontendOptions === 'object'
|
||||
? String((param.frontendOptions as Record<string, unknown>).dependsOn ?? 'Item')
|
||||
|
|
@ -85,7 +83,7 @@ export const ConditionEditor: React.FC<FieldRendererProps> = ({
|
|||
|
||||
if (dataFlow?.instanceId && dataFlow.request) {
|
||||
setLoading(true);
|
||||
fetchConditionMeta(dataFlow.request, {
|
||||
fetchConditionMeta(dataFlow.request, dataFlow.instanceId, {
|
||||
graph: toApiGraph(dataFlow.nodes, dataFlow.connections),
|
||||
nodeId: dataFlow.currentNodeId,
|
||||
ref: { type: 'ref', nodeId: ref.nodeId, path: ref.path },
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* One place to configure context.setContext rows: target key, then either
|
||||
* upstream picker, a fixed literal, or a human task.
|
||||
|
|
@ -7,7 +5,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { DataPicker } from '../shared/DataPicker';
|
||||
import { isRef, isSystemVar, type DataRef, type SystemVarRef } from '../shared/dataRef';
|
||||
import type { FieldRendererProps } from './index';
|
||||
|
|
@ -176,7 +174,7 @@ const REMOVE_BTN: React.CSSProperties = {
|
|||
|
||||
export const ContextAssignmentsEditor: React.FC<FieldRendererProps> = ({ param, value, onChange, allParams }) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const rows = normalizeRows(value, allParams);
|
||||
const [pickerRow, setPickerRow] = React.useState<number | null>(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* ContextBuilderRenderer — multi-select context binding for AI nodes.
|
||||
*
|
||||
|
|
@ -13,7 +11,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { DataPicker } from '../shared/DataPicker';
|
||||
import { isRef, type DataRef, type SystemVarRef } from '../shared/dataRef';
|
||||
import type { FieldRendererProps } from './index';
|
||||
|
|
@ -54,7 +52,7 @@ const REMOVE_BTN: React.CSSProperties = {
|
|||
|
||||
export const ContextBuilderRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const [pickerOpen, setPickerOpen] = React.useState(false);
|
||||
const dragIndex = React.useRef<number | null>(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* DataRefRenderer — Pick-not-Push attribute binding using the existing
|
||||
* hierarchical DataPicker.
|
||||
|
|
@ -12,14 +10,14 @@
|
|||
|
||||
import React from 'react';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { DataPicker } from '../shared/DataPicker';
|
||||
import { isRef, type DataRef, type SystemVarRef } from '../shared/dataRef';
|
||||
import type { FieldRendererProps } from './index';
|
||||
|
||||
export const DataRefRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const [pickerOpen, setPickerOpen] = React.useState(false);
|
||||
|
||||
const currentRef = isRef(value) ? (value as DataRef) : null;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* FeatureInstancePicker — renderer for frontendType="featureInstance".
|
||||
*
|
||||
* Modeled on ConnectionPicker. Loads mandate-scoped FeatureInstances filtered
|
||||
* by `frontendOptions.featureCode` (e.g. "trustee", "redmine") via
|
||||
* GET /api/workflow-automation/options/feature.instance?featureCode=<code>
|
||||
* GET /api/workflows/{instanceId}/options/feature.instance?featureCode=<code>
|
||||
*
|
||||
* Behavior matches the rest of the editor:
|
||||
* - 0 results -> hint to create a feature instance for this mandate
|
||||
|
|
@ -44,7 +42,7 @@ export const FeatureInstancePicker: React.FC<FieldRendererProps> = ({
|
|||
setLoading(true);
|
||||
setLoadError(null);
|
||||
request({
|
||||
url: `/api/workflow-automation/options/feature.instance?featureCode=${encodeURIComponent(featureCode)}`,
|
||||
url: `/api/workflows/${instanceId}/options/feature.instance?featureCode=${encodeURIComponent(featureCode)}`,
|
||||
method: 'get',
|
||||
})
|
||||
.then((res: unknown) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* TemplateTextarea — Freitext mit eingebetteten {{nodeId.path}} Tokens.
|
||||
* Tokens werden zur Laufzeit von resolveParameterReferences aufgeloest (Gateway).
|
||||
|
|
@ -7,11 +5,11 @@
|
|||
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import type { FieldRendererProps } from './index';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { DataPicker } from '../shared/DataPicker';
|
||||
import { formatRefLabel, isRef, isSystemVar, type DataRef, type SystemVarRef } from '../shared/dataRef';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
|
||||
const _TEMPLATE_TOKEN_RE = /\{\{\s*([^}]+?)\s*\}\}/g;
|
||||
|
||||
|
|
@ -62,7 +60,7 @@ function _parseTokensInTemplate(
|
|||
|
||||
export const TemplateTextareaRenderer: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* userFileFolder — FormGeneratorTree embedded: combobox-style trigger + expandable tree.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
clickupBrowseParentPath,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/** Parse virtual ClickUp list paths: /team/{teamId}/list/{listId} */
|
||||
|
||||
const LIST_PATH_RE = /^\/team\/([^/]+)\/list\/([^/]+)$/;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Generic FrontendType renderer registry.
|
||||
* Maps frontendType strings to React components.
|
||||
*/
|
||||
|
||||
import type { ComponentType } from 'react';
|
||||
import type { NodeTypeParameter } from '../../../../api/workflowAutomationApi';
|
||||
import type { ApiRequestFunction } from '../../../../api/workflowAutomationApi';
|
||||
import type { NodeTypeParameter } from '../../../../api/workflowApi';
|
||||
import type { ApiRequestFunction } from '../../../../api/workflowApi';
|
||||
import { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS } from '../../../../utils/attributeTypeMapper';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { FormFieldOptionsEditor } from '../form/FormFieldOptionsEditor';
|
||||
import {
|
||||
deriveFormFieldPayloadKey,
|
||||
|
|
@ -48,7 +46,7 @@ import {
|
|||
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
import { toApiGraph } from '../shared/graphUtils';
|
||||
import { postUpstreamPaths } from '../../../../api/workflowAutomationApi';
|
||||
import { postUpstreamPaths } from '../../../../api/workflowApi';
|
||||
import type { CanvasNode } from '../../editor/FlowCanvas';
|
||||
import { DataRefRenderer } from './DataRefRenderer';
|
||||
import { ContextBuilderRenderer } from './ContextBuilderRenderer';
|
||||
|
|
@ -302,7 +300,7 @@ const HiddenInput: React.FC<FieldRendererProps> = () => null;
|
|||
|
||||
const ConnectionPicker: React.FC<FieldRendererProps> = ({ param, value, onChange, instanceId, request }) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const [connections, setConnections] = React.useState<Array<{ id: string; label: string }>>([]);
|
||||
const [loadError, setLoadError] = React.useState<string | null>(null);
|
||||
const [upstreamBindOptions, setUpstreamBindOptions] = React.useState<Array<{ key: string; label: string; ref: unknown }>>([]);
|
||||
|
|
@ -312,7 +310,7 @@ const ConnectionPicker: React.FC<FieldRendererProps> = ({ param, value, onChange
|
|||
if (!instanceId || !request) return;
|
||||
const qs = authority ? `?authority=${encodeURIComponent(authority)}` : '';
|
||||
setLoadError(null);
|
||||
request({ url: `/api/workflow-automation/options/user.connection${qs}`, method: 'get' })
|
||||
request({ url: `/api/workflows/${instanceId}/options/user.connection${qs}`, method: 'get' })
|
||||
.then((res: unknown) => {
|
||||
const data = res as { options?: Array<{ value: string; label: string }> };
|
||||
setConnections((data?.options || []).map((o) => ({ id: o.value, label: o.label })));
|
||||
|
|
@ -330,7 +328,7 @@ const ConnectionPicker: React.FC<FieldRendererProps> = ({ param, value, onChange
|
|||
return;
|
||||
}
|
||||
const graph = toApiGraph(dataFlow.nodes as CanvasNode[], dataFlow.connections);
|
||||
postUpstreamPaths(request, graph, dataFlow.currentNodeId)
|
||||
postUpstreamPaths(request, instanceId, graph, dataFlow.currentNodeId)
|
||||
.then(({ paths }) => {
|
||||
const opts = paths
|
||||
.filter(
|
||||
|
|
@ -645,7 +643,7 @@ const SharepointPathPicker: React.FC<FieldRendererProps> = ({ param, value, onCh
|
|||
|
||||
const FieldBuilderEditor: React.FC<FieldRendererProps> = ({ param, value, onChange }) => {
|
||||
const { t } = useLanguage();
|
||||
const ctx = useWorkflowDataFlow();
|
||||
const ctx = useAutomation2DataFlow();
|
||||
const fieldTypeOptions = ctx?.formFieldTypes?.length
|
||||
? ctx.formFieldTypes
|
||||
: FORM_FIELD_TYPES.map((ft) => ({ id: ft, label: FORM_FIELD_TYPE_LABELS[ft] ?? ft, portType: 'str' }));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,2 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
export { ConditionEditor as IfElseNodeConfig } from '../frontendTypeRenderers/ConditionEditor';
|
||||
export type { StructuredCondition } from '../frontendTypeRenderers/ConditionEditor';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Loop node config - Datenquelle für Iteration mit benutzerfreundlichen Labels.
|
||||
* Z.B. für jedes Formularfeld, jede Datei aus Upload, jede E-Mail aus Suche.
|
||||
|
|
@ -9,7 +7,7 @@ import React from 'react';
|
|||
import type { NodeConfigRendererProps } from '../shared/types';
|
||||
import { LoopItemsSelect } from '../shared/LoopItemsSelect';
|
||||
import { createValue, isRef, isValue } from '../shared/dataRef';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
|
||||
export const LoopNodeConfig: React.FC<NodeConfigRendererProps> = ({ params, updateParam }) => {
|
||||
const value = params.items;
|
||||
|
|
|
|||
|
|
@ -1,3 +1 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
export { LoopNodeConfig } from './LoopNodeConfig';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Shared mapping: file type options (accept strings) → MIME types.
|
||||
* Used by Upload node config (allowed types) and IfElse node (mimeType comparison).
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Small label for workflow nodes that consume AI credits (LLM calls).
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Workflow Flow Editor - Schema-based Data Picker.
|
||||
* Automation2 Flow Editor - Schema-based Data Picker.
|
||||
* Builds pickable paths from portTypeCatalog + node outputPorts, or from
|
||||
* outputPorts[n].dataPickOptions when the backend defines an explicit list (authoritative).
|
||||
* Resolves Transit chains to show the real upstream schema.
|
||||
|
|
@ -11,10 +9,10 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { createRef, createSystemVar, type DataRef, type SystemVarRef, isCompatible } from './dataRef';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import type { DataPickOption, GraphDataSources, GraphDefinedSchemaRef, NodeType, PortField, PortSchema } from '../../../../api/workflowAutomationApi';
|
||||
import { fetchGraphDataSources } from '../../../../api/workflowAutomationApi';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import type { DataPickOption, GraphDataSources, GraphDefinedSchemaRef, NodeType, PortField, PortSchema } from '../../../../api/workflowApi';
|
||||
import { fetchGraphDataSources } from '../../../../api/workflowApi';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
@ -270,7 +268,7 @@ export const DataPicker: React.FC<DataPickerProps> = ({ open,
|
|||
// Default: when the consumer declares an expected type, show only compatible
|
||||
// candidates ("strict" mode). User can override per-session via the toggle.
|
||||
const [strictFilter, setStrictFilter] = useState<boolean>(Boolean(expectedParamType));
|
||||
const ctx = useWorkflowDataFlow();
|
||||
const ctx = useAutomation2DataFlow();
|
||||
|
||||
// NOTE: All hooks must be called unconditionally on every render to satisfy
|
||||
// the Rules of Hooks. The `if (!open) return null;` early-return therefore
|
||||
|
|
@ -305,7 +303,7 @@ export const DataPicker: React.FC<DataPickerProps> = ({ open,
|
|||
if (scopeFetchKey.current === key) return; // already fetched for this state
|
||||
scopeFetchKey.current = key;
|
||||
const nodeShapes = (ctx.nodes ?? []).map((n) => ({ id: n.id, type: n.type }));
|
||||
fetchGraphDataSources(ctx.request, ctx.currentNodeId, nodeShapes, connections)
|
||||
fetchGraphDataSources(ctx.request, ctx.instanceId, ctx.currentNodeId, nodeShapes, connections)
|
||||
.then(setScopeData)
|
||||
.catch(() => setScopeData(null));
|
||||
}, [open, ctx?.instanceId, ctx?.request, ctx?.currentNodeId, connections, nodesRaw]);
|
||||
|
|
@ -363,10 +361,10 @@ export const DataPicker: React.FC<DataPickerProps> = ({ open,
|
|||
onClick={(e) => e.stopPropagation()}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="workflowDataPickerTitle"
|
||||
aria-labelledby="automation2DataPickerTitle"
|
||||
>
|
||||
<div className={styles.dataPickerHeader}>
|
||||
<h4 className={styles.dataPickerTitle} id="workflowDataPickerTitle">
|
||||
<h4 className={styles.dataPickerTitle} id="automation2DataPickerTitle">
|
||||
{t('Datenquelle wählen')}
|
||||
{expectedParamType && (
|
||||
<span
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Workflow Flow Editor - Field that supports node reference only (no static value).
|
||||
* Automation2 Flow Editor - Field that supports node reference only (no static value).
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
|
@ -14,8 +12,8 @@ import {
|
|||
} from './dataRef';
|
||||
import { RefSourceSelect } from './RefSourceSelect';
|
||||
import { DataPicker } from './DataPicker';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
@ -41,7 +39,7 @@ export const DynamicValueField: React.FC<DynamicValueFieldProps> = ({ paramKey,
|
|||
variant = 'picker',
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
|
||||
const ref: DataRef | null = isRef(value) ? value : null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Text/number field: „Quelle wählen“ → Statisch (Eingabe) oder Kontext-Ref.
|
||||
* Textfeld nur bei „Statisch“, nicht bei Kontext-Referenz.
|
||||
|
|
@ -11,9 +9,9 @@ import {
|
|||
shouldShowStaticControl,
|
||||
type PathPickMode,
|
||||
} from './RefSourceSelect';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { isRef, isValue, createValue } from './dataRef';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
@ -50,7 +48,7 @@ export const HybridStaticRefField: React.FC<HybridStaticRefFieldProps> = ({ labe
|
|||
pathPickMode = 'default',
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
const hasSources =
|
||||
dataFlow &&
|
||||
dataFlow.getAvailableSourceIds().some((id) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Loop node - Datenquelle für Iteration mit benutzerfreundlichen Labels.
|
||||
* Zeigt nur iterierbare Quellen: Arrays und Objekte (Formularfelder → {name, value}).
|
||||
|
|
@ -8,8 +6,8 @@
|
|||
import React from 'react';
|
||||
import { createRef, isRef, type DataRef } from './dataRef';
|
||||
import { refToOptionValue, optionValueToRef } from './RefSourceSelect';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
@ -163,7 +161,7 @@ export const LoopItemsSelect: React.FC<LoopItemsSelectProps> = ({ value,
|
|||
placeholder,
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
if (!dataFlow) return null;
|
||||
|
||||
const sourceIds = dataFlow.getAvailableSourceIds();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Inline dropdown to select a data source (node + path) - no popup.
|
||||
* Form nodes (trigger.form / input.form): only payload.<fieldName> paths (no duplicate tree).
|
||||
|
|
@ -7,7 +5,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { createRef, isRef, isValue, createValue, type DataRef } from './dataRef';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
|
||||
/** How to build path options for StatischKontextSelect / RefSourceSelect. */
|
||||
|
|
@ -240,7 +238,7 @@ export const StatischKontextSelect: React.FC<StatischKontextSelectProps> = ({
|
|||
pathPickMode = 'default',
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
if (!dataFlow) return null;
|
||||
|
||||
const sourceIds = dataFlow.getAvailableSourceIds();
|
||||
|
|
@ -318,7 +316,7 @@ export const RefSourceSelect: React.FC<RefSourceSelectProps> = ({
|
|||
pathPickMode = 'default',
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const dataFlow = useWorkflowDataFlow();
|
||||
const dataFlow = useAutomation2DataFlow();
|
||||
if (!dataFlow) return null;
|
||||
|
||||
const sourceIds = dataFlow.getAvailableSourceIds();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* RequiredAttributePicker — Phase-4 Schicht-4 binding affordance for
|
||||
* required parameters of a Schicht-3 Adapter (Editor-Node).
|
||||
|
|
@ -17,11 +15,11 @@
|
|||
*/
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useWorkflowDataFlow } from '../../context/WorkflowDataFlowContext';
|
||||
import { useAutomation2DataFlow } from '../../context/Automation2DataFlowContext';
|
||||
import { DataPicker } from './DataPicker';
|
||||
import { createRef, formatRefLabel, isRef, type DataRef, type SystemVarRef } from './dataRef';
|
||||
import { findSourceCandidates, strictlyCompatible, type SourceCandidate } from './paramValidation';
|
||||
import styles from '../../editor/WorkflowFlowEditor.module.css';
|
||||
import styles from '../../editor/Automation2FlowEditor.module.css';
|
||||
|
||||
import { useLanguage } from '../../../../providers/language/LanguageContext';
|
||||
|
||||
|
|
@ -46,7 +44,7 @@ export const RequiredAttributePicker: React.FC<RequiredAttributePickerProps> = (
|
|||
description,
|
||||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const ctx = useWorkflowDataFlow();
|
||||
const ctx = useAutomation2DataFlow();
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
|
||||
const consumerNodeId = ctx?.currentNodeId ?? '';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Category icons for node types
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Shared condition operators for If/Else and Switch nodes.
|
||||
* Type-dependent: number gets <, >, etc.; string gets contains, equals, etc.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Workflow Flow Editor - Constants
|
||||
* Automation2 Flow Editor - Constants
|
||||
* Category ordering for node sidebar.
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
// Copyright (c) 2026 PowerOn AG
|
||||
// All rights reserved.
|
||||
/**
|
||||
* Workflow Flow Editor - Graph helpers for data flow (ancestors, topo order).
|
||||
* Automation2 Flow Editor - Graph helpers for data flow (ancestors, topo order).
|
||||
*/
|
||||
|
||||
import type { CanvasNode, CanvasConnection } from '../../editor/FlowCanvas';
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue