From 65207637361170ddfdde64bfbad78a8690a1c112 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 9 Jun 2026 22:59:45 +0200
Subject: [PATCH] automation fixautomation fixeses
---
src/api/workflowAutomationApi.ts | 10 +--
.../FlowEditor/editor/WorkflowFlowEditor.tsx | 6 +-
.../FlowEditor/nodes/shared/graphUtils.ts | 69 +++++++++++++++++++
.../FormGeneratorTable/FormGeneratorTable.tsx | 15 ++--
src/config/pageRegistry.tsx | 8 +--
src/hooks/useFiles.ts | 10 +--
src/hooks/useWorkflows.ts | 3 +-
src/pages/ComplianceAuditPage.tsx | 2 +-
.../WorkflowAutomationHubPage.tsx | 7 ++
src/pages/workflowAutomation/tabs/RunsTab.tsx | 8 +--
.../workflowAutomation/tabs/WorkflowsTab.tsx | 10 +--
11 files changed, 112 insertions(+), 36 deletions(-)
diff --git a/src/api/workflowAutomationApi.ts b/src/api/workflowAutomationApi.ts
index 3400e43..c0dcaa7 100644
--- a/src/api/workflowAutomationApi.ts
+++ b/src/api/workflowAutomationApi.ts
@@ -531,17 +531,18 @@ export type ApiRequestFunction = (options: ApiRequestOptions) => Promise {
- console.log(`${LOG} fetchNodeTypes: language=${language}`);
+ console.log(`${LOG} fetchNodeTypes: mandateId=${mandateId} language=${language}`);
const data = await request({
url: `${BASE}/node-types`,
method: 'get',
- params: { language },
+ params: { mandateId, language },
});
const nodeTypes = data?.nodeTypes ?? [];
const categories = data?.categories ?? [];
@@ -717,6 +718,7 @@ export async function fetchWorkflows(
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
});
if (data?.items && data?.pagination) return data;
+ if (data?.items) return data.items;
return data?.workflows ?? [];
}
@@ -990,7 +992,7 @@ export async function fetchTasks(
method: 'get',
params,
});
- return data?.tasks ?? [];
+ return data?.items ?? [];
}
export async function completeTask(
diff --git a/src/components/FlowEditor/editor/WorkflowFlowEditor.tsx b/src/components/FlowEditor/editor/WorkflowFlowEditor.tsx
index 953a740..523c80e 100644
--- a/src/components/FlowEditor/editor/WorkflowFlowEditor.tsx
+++ b/src/components/FlowEditor/editor/WorkflowFlowEditor.tsx
@@ -551,7 +551,7 @@ export const WorkflowFlowEditor: React.FC = ({ instance
setLoading(true);
setError(null);
try {
- const data = await fetchNodeTypes(request, language);
+ const data = await fetchNodeTypes(request, mandateId || '', language);
setNodeTypes(data.nodeTypes);
setCategories(data.categories);
if (data.portTypeCatalog) {
@@ -572,12 +572,12 @@ export const WorkflowFlowEditor: React.FC = ({ instance
const loadWorkflows = useCallback(async () => {
try {
- const result = await fetchWorkflows(request);
+ const result = await fetchWorkflows(request, { mandateId: mandateId || undefined });
setWorkflows(Array.isArray(result) ? result : result.items);
} catch (e) {
console.error(`${LOG} loadWorkflows failed`, e);
}
- }, [request]);
+ }, [request, mandateId]);
useEffect(() => {
loadNodeTypes();
diff --git a/src/components/FlowEditor/nodes/shared/graphUtils.ts b/src/components/FlowEditor/nodes/shared/graphUtils.ts
index 81528a1..227776a 100644
--- a/src/components/FlowEditor/nodes/shared/graphUtils.ts
+++ b/src/components/FlowEditor/nodes/shared/graphUtils.ts
@@ -100,9 +100,78 @@ export function fromApiGraph(
};
});
+ const hasPositions = nodes.length > 0 && nodes.some((n) => n.x !== 0 || n.y !== 0);
+ if (!hasPositions && nodes.length > 1) {
+ _autoLayoutTopDown(nodes, connections);
+ }
+
return { nodes, connections };
}
+const _NODE_WIDTH = 200;
+const _NODE_HEIGHT = 60;
+const _VERTICAL_GAP = 80;
+const _HORIZONTAL_GAP = 60;
+
+function _autoLayoutTopDown(nodes: CanvasNode[], connections: CanvasConnection[]): void {
+ const inDegree = new Map();
+ const children = new Map();
+ for (const n of nodes) {
+ inDegree.set(n.id, 0);
+ children.set(n.id, []);
+ }
+ for (const c of connections) {
+ inDegree.set(c.targetId, (inDegree.get(c.targetId) ?? 0) + 1);
+ children.get(c.sourceId)?.push(c.targetId);
+ }
+
+ const layers: string[][] = [];
+ const assigned = new Set();
+ const queue = nodes.filter((n) => (inDegree.get(n.id) ?? 0) === 0).map((n) => n.id);
+
+ while (queue.length > 0) {
+ const layer = [...queue];
+ layers.push(layer);
+ layer.forEach((id) => assigned.add(id));
+ const next: string[] = [];
+ for (const id of layer) {
+ for (const child of children.get(id) || []) {
+ if (assigned.has(child)) continue;
+ const remaining = connections.filter(
+ (c) => c.targetId === child && !assigned.has(c.sourceId)
+ );
+ if (remaining.length === 0) next.push(child);
+ }
+ }
+ queue.length = 0;
+ const unique = [...new Set(next)];
+ queue.push(...unique);
+ }
+
+ for (const n of nodes) {
+ if (!assigned.has(n.id)) {
+ layers.push([n.id]);
+ assigned.add(n.id);
+ }
+ }
+
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
+ let y = 40;
+ for (const layer of layers) {
+ const totalWidth = layer.length * _NODE_WIDTH + (layer.length - 1) * _HORIZONTAL_GAP;
+ let x = Math.max(40, (600 - totalWidth) / 2);
+ for (const id of layer) {
+ const node = nodeById.get(id);
+ if (node) {
+ node.x = x;
+ node.y = y;
+ x += _NODE_WIDTH + _HORIZONTAL_GAP;
+ }
+ }
+ y += _NODE_HEIGHT + _VERTICAL_GAP;
+ }
+}
+
export function toApiGraph(
nodes: CanvasNode[],
connections: CanvasConnection[]
diff --git a/src/components/FormGenerator/FormGeneratorTable/FormGeneratorTable.tsx b/src/components/FormGenerator/FormGeneratorTable/FormGeneratorTable.tsx
index dd347fb..f59cc83 100644
--- a/src/components/FormGenerator/FormGeneratorTable/FormGeneratorTable.tsx
+++ b/src/components/FormGenerator/FormGeneratorTable/FormGeneratorTable.tsx
@@ -913,13 +913,14 @@ export function FormGeneratorTable>({
return columnsRef.current;
}
- // NO COLUMNS PROVIDED - this is an error in the calling component
- // The calling component should provide columns from the /attributes/{entityType} endpoint
- console.warn(
- '⚠️ FormGeneratorTable: No columns provided! ' +
- 'Columns should come from Pydantic attribute definitions via /attributes/{entityType} endpoint. ' +
- 'Please ensure the calling component fetches and passes columns from the backend.'
- );
+ // Only warn when data is present but columns are still missing
+ if (data && data.length > 0) {
+ console.warn(
+ '⚠️ FormGeneratorTable: No columns provided! ' +
+ 'Columns should come from Pydantic attribute definitions via /attributes/{entityType} endpoint. ' +
+ 'Please ensure the calling component fetches and passes columns from the backend.'
+ );
+ }
// Return empty array - table will show no columns
return [];
diff --git a/src/config/pageRegistry.tsx b/src/config/pageRegistry.tsx
index aea2eaf..ef0ba28 100644
--- a/src/config/pageRegistry.tsx
+++ b/src/config/pageRegistry.tsx
@@ -25,7 +25,7 @@ import {
FaProjectDiagram, FaMapMarkedAlt, FaWallet, FaMoneyBillAlt, FaClock,
FaHeadset, FaVideo, FaHatWizard, FaStore, FaUserTie, FaClipboardList,
FaFileContract, FaGlobe, FaClipboardCheck,
- FaSitemap, FaCopy, FaTasks,
+ FaSitemap,
} from 'react-icons/fa';
// =============================================================================
@@ -58,11 +58,7 @@ export const PAGE_ICONS: Record = {
'page.system.ragInventory': ,
// System pages - Workflow Automation
- 'page.system.workflowAutomation.workflows': ,
- 'page.system.workflowAutomation.editor': ,
- 'page.system.workflowAutomation.templates': ,
- 'page.system.workflowAutomation.runs': ,
- 'page.system.workflowAutomation.tasks': ,
+ 'page.system.workflowAutomation': ,
// Billing pages (legacy compat)
'page.billing.dashboard': ,
diff --git a/src/hooks/useFiles.ts b/src/hooks/useFiles.ts
index d3a098e..3755d6f 100644
--- a/src/hooks/useFiles.ts
+++ b/src/hooks/useFiles.ts
@@ -133,11 +133,11 @@ export function useUserFiles() {
} catch (error: any) {
console.error('Error fetching permissions:', error);
const defaultPerms: UserPermissions = {
- view: false,
- read: 'n',
- create: 'n',
- update: 'n',
- delete: 'n',
+ view: true,
+ read: 'my',
+ create: 'my',
+ update: 'my',
+ delete: 'my',
};
setPermissions(defaultPerms);
return defaultPerms;
diff --git a/src/hooks/useWorkflows.ts b/src/hooks/useWorkflows.ts
index efa3ee5..1cae59c 100644
--- a/src/hooks/useWorkflows.ts
+++ b/src/hooks/useWorkflows.ts
@@ -34,11 +34,12 @@ async function startWorkflowApi(
workflowData: StartWorkflowRequest,
options?: { workflowId?: string; workflowMode?: 'Dynamic' | 'Automation' },
) {
+ const wfId = options?.workflowId ?? (workflowData as { workflowId?: string }).workflowId;
return await request({
url: `/api/workflow-automation/execute`,
method: 'post',
data: {
- workflowId: options?.workflowId ?? (workflowData as { workflowId?: string }).workflowId,
+ workflowId: wfId,
payload: workflowData,
},
});
diff --git a/src/pages/ComplianceAuditPage.tsx b/src/pages/ComplianceAuditPage.tsx
index 3f6f6c9..9ab7c50 100644
--- a/src/pages/ComplianceAuditPage.tsx
+++ b/src/pages/ComplianceAuditPage.tsx
@@ -575,7 +575,7 @@ export const ComplianceAuditPage: React.FC = () => {
label: t('Datei'),
sortable: true,
width: 140,
- formatter: (val: any) => (val ? `${String(val).slice(0, 8)}…` : '–'),
+ displayField: 'fileIdLabel',
},
], [t]);
diff --git a/src/pages/workflowAutomation/WorkflowAutomationHubPage.tsx b/src/pages/workflowAutomation/WorkflowAutomationHubPage.tsx
index 23a8822..db782de 100644
--- a/src/pages/workflowAutomation/WorkflowAutomationHubPage.tsx
+++ b/src/pages/workflowAutomation/WorkflowAutomationHubPage.tsx
@@ -40,6 +40,13 @@ export const WorkflowAutomationPage: React.FC = () => {
const [activeTab, setActiveTab] = useState(initialRunId ? 'detail' : initialTab);
const [selectedRunId, setSelectedRunId] = useState(initialRunId);
+
+ useEffect(() => {
+ const newTab = _TAB_ALIASES[searchParams.get('tab') || 'workflows'] || searchParams.get('tab') || 'workflows';
+ if (newTab !== activeTab) {
+ setActiveTab(newTab);
+ }
+ }, [searchParams]);
const [workflowFilter, setWorkflowFilter] = useState(null);
const [selectedMandateId, setSelectedMandateId] = useState('all');
diff --git a/src/pages/workflowAutomation/tabs/RunsTab.tsx b/src/pages/workflowAutomation/tabs/RunsTab.tsx
index 541d36d..939d668 100644
--- a/src/pages/workflowAutomation/tabs/RunsTab.tsx
+++ b/src/pages/workflowAutomation/tabs/RunsTab.tsx
@@ -338,7 +338,7 @@ export const _RunsTab: React.FC = ({ workflowFilter, onRunClick, s
if (selectedMandateId !== 'all') params.mandateId = selectedMandateId;
const resp = await api.get('/api/workflow-automation/runs', { params });
const data = resp.data;
- setRuns(data?.runs || []);
+ setRuns(data?.items || []);
const total = data?.total ?? 0;
const pageSize = pag.pageSize;
setPaginationMeta({
@@ -416,7 +416,7 @@ export const _RunsTab: React.FC = ({ workflowFilter, onRunClick, s
width: 140,
sortable: true,
filterable: true,
- displayField: 'mandateLabel',
+ displayField: 'mandateIdLabel',
},
{
key: 'featureInstanceId',
@@ -424,7 +424,7 @@ export const _RunsTab: React.FC = ({ workflowFilter, onRunClick, s
width: 140,
sortable: true,
filterable: true,
- displayField: 'instanceLabel',
+ displayField: 'featureInstanceIdLabel',
},
{
key: 'ownerId',
@@ -432,7 +432,7 @@ export const _RunsTab: React.FC = ({ workflowFilter, onRunClick, s
width: 140,
sortable: true,
filterable: true,
- displayField: 'ownerLabel',
+ displayField: 'ownerIdLabel',
},
{ key: 'status', width: 110, sortable: true, filterable: true },
{
diff --git a/src/pages/workflowAutomation/tabs/WorkflowsTab.tsx b/src/pages/workflowAutomation/tabs/WorkflowsTab.tsx
index 353c773..a80d410 100644
--- a/src/pages/workflowAutomation/tabs/WorkflowsTab.tsx
+++ b/src/pages/workflowAutomation/tabs/WorkflowsTab.tsx
@@ -220,7 +220,7 @@ export const _WorkflowsTab: React.FC = ({ onWorkflowClick, se
width: 140,
sortable: true,
filterable: true,
- displayField: 'mandateLabel',
+ displayField: 'mandateIdLabel',
},
{
key: 'featureInstanceId',
@@ -228,15 +228,15 @@ export const _WorkflowsTab: React.FC = ({ onWorkflowClick, se
width: 140,
sortable: true,
filterable: true,
- displayField: 'instanceLabel',
+ displayField: 'featureInstanceIdLabel',
},
{
- key: 'ownerId',
- label: t('Benutzer'),
+ key: 'sysCreatedBy',
+ label: t('Ersteller'),
width: 140,
sortable: true,
filterable: true,
- displayField: 'ownerLabel',
+ displayField: 'sysCreatedByLabel',
},
{
key: 'active',