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

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

3
.gitignore vendored
View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
export { Automation2FlowEditor, Automation2FlowEditor as FlowEditor } from './editor/Automation2FlowEditor';
export { WorkflowFlowEditor, WorkflowFlowEditor as FlowEditor } from './editor/WorkflowFlowEditor';
export type { PendingFile, EditorDataSource, EditorFeatureDataSource } from './editor/EditorChatPanel';
export { 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';

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.