updated feature automation and feature chat playground routes
This commit is contained in:
parent
0dbcad771b
commit
df1344c228
5 changed files with 147 additions and 106 deletions
|
|
@ -102,12 +102,15 @@ function App() {
|
|||
<Route path="gdpr" element={<GDPRPage />} />
|
||||
|
||||
{/* ============================================== */}
|
||||
{/* WORKFLOWS ROUTES (global) */}
|
||||
{/* WORKFLOWS ROUTES (deprecated - redirect to /) */}
|
||||
{/* Workflows are accessed via feature routes: */}
|
||||
{/* /mandates/:mandateId/chatplayground/:id/workflows */}
|
||||
{/* /mandates/:mandateId/automation/:id/definitions */}
|
||||
{/* ============================================== */}
|
||||
<Route path="workflows">
|
||||
<Route path="playground" element={<PlaygroundPage />} />
|
||||
<Route path="list" element={<WorkflowsPage />} />
|
||||
<Route path="automations" element={<AutomationDefinitionsView />} />
|
||||
<Route path="list" element={<Navigate to="/" replace />} />
|
||||
<Route path="automations" element={<Navigate to="/" replace />} />
|
||||
</Route>
|
||||
|
||||
{/* ============================================== */}
|
||||
|
|
|
|||
|
|
@ -69,17 +69,24 @@ export type ApiRequestFunction = (options: ApiRequestOptions<any>) => Promise<an
|
|||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Fetch all workflows for the current user
|
||||
* Endpoint: GET /api/workflows/
|
||||
* Fetch workflows - feature-scoped or global (legacy)
|
||||
* When apiBaseUrl is provided: GET {apiBaseUrl}/workflows
|
||||
* Otherwise (legacy): GET /api/workflows/
|
||||
*/
|
||||
export async function fetchWorkflows(request: ApiRequestFunction): Promise<Workflow[]> {
|
||||
console.log('📤 fetchWorkflows: Making API request to /api/workflows/');
|
||||
export async function fetchWorkflows(
|
||||
request: ApiRequestFunction,
|
||||
params?: { pagination?: string },
|
||||
apiBaseUrl?: string
|
||||
): Promise<Workflow[] | { items: Workflow[]; pagination?: any }> {
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows` : '/api/workflows/';
|
||||
console.log('📤 fetchWorkflows: Making API request to', url);
|
||||
|
||||
try {
|
||||
const data = await request({
|
||||
url: '/api/workflows/',
|
||||
method: 'get'
|
||||
});
|
||||
const requestConfig: any = { url, method: 'get' as const };
|
||||
if (params?.pagination) {
|
||||
requestConfig.params = { pagination: params.pagination };
|
||||
}
|
||||
const data = await request(requestConfig);
|
||||
|
||||
console.log('📥 fetchWorkflows: API response:', data);
|
||||
|
||||
|
|
@ -129,30 +136,30 @@ export async function fetchWorkflows(request: ApiRequestFunction): Promise<Workf
|
|||
|
||||
/**
|
||||
* Fetch a single workflow by ID
|
||||
* Endpoint: GET /api/workflows/{workflowId}
|
||||
* When apiBaseUrl provided: GET {apiBaseUrl}/workflows/{workflowId}
|
||||
* Otherwise: GET /api/workflows/{workflowId}
|
||||
*/
|
||||
export async function fetchWorkflow(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string
|
||||
workflowId: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<Workflow & { messages?: WorkflowMessage[]; logs?: WorkflowLog[] }> {
|
||||
return await request({
|
||||
url: `/api/workflows/${workflowId}`,
|
||||
method: 'get'
|
||||
});
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}` : `/api/workflows/${workflowId}`;
|
||||
return await request({ url, method: 'get' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch workflow status (lightweight status check)
|
||||
* Endpoint: GET /api/workflows/{workflowId}/status
|
||||
* When apiBaseUrl provided: GET {apiBaseUrl}/workflows/{workflowId}/status
|
||||
* Otherwise: GET /api/workflows/{workflowId}/status
|
||||
*/
|
||||
export async function fetchWorkflowStatus(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string
|
||||
workflowId: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<Workflow | { status: string } | null> {
|
||||
const data = await request({
|
||||
url: `/api/workflows/${workflowId}/status`,
|
||||
method: 'get'
|
||||
});
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}/status` : `/api/workflows/${workflowId}/status`;
|
||||
const data = await request({ url, method: 'get' });
|
||||
|
||||
if (data && typeof data === 'object') {
|
||||
if (data.status) {
|
||||
|
|
@ -166,20 +173,18 @@ export async function fetchWorkflowStatus(
|
|||
|
||||
/**
|
||||
* Fetch workflow messages
|
||||
* Endpoint: GET /api/workflows/{workflowId}/messages
|
||||
* Query params: messageId (optional) - fetch only newer messages
|
||||
* When apiBaseUrl provided: GET {apiBaseUrl}/workflows/{workflowId}/messages
|
||||
* Otherwise: GET /api/workflows/{workflowId}/messages
|
||||
*/
|
||||
export async function fetchWorkflowMessages(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string,
|
||||
messageId?: string
|
||||
messageId?: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<WorkflowMessage[]> {
|
||||
const params = messageId ? { messageId } : undefined;
|
||||
const data = await request({
|
||||
url: `/api/workflows/${workflowId}/messages`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}/messages` : `/api/workflows/${workflowId}/messages`;
|
||||
const data = await request({ url, method: 'get', params });
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return data;
|
||||
|
|
@ -199,20 +204,18 @@ export async function fetchWorkflowMessages(
|
|||
|
||||
/**
|
||||
* Fetch workflow logs
|
||||
* Endpoint: GET /api/workflows/{workflowId}/logs
|
||||
* Query params: logId (optional) - fetch only newer logs
|
||||
* When apiBaseUrl provided: GET {apiBaseUrl}/workflows/{workflowId}/logs
|
||||
* Otherwise: GET /api/workflows/{workflowId}/logs
|
||||
*/
|
||||
export async function fetchWorkflowLogs(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string,
|
||||
logId?: string
|
||||
logId?: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<WorkflowLog[]> {
|
||||
const params = logId ? { logId } : undefined;
|
||||
const data = await request({
|
||||
url: `/api/workflows/${workflowId}/logs`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}/logs` : `/api/workflows/${workflowId}/logs`;
|
||||
const data = await request({ url, method: 'get', params });
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return data;
|
||||
|
|
@ -232,7 +235,7 @@ export async function fetchWorkflowLogs(
|
|||
|
||||
/**
|
||||
* Fetch unified chat data (messages, logs, stats, documents)
|
||||
* Endpoint: GET /api/chatplayground/{instanceId}/{workflowId}/chatData
|
||||
* Endpoint: GET /api/chatplayground/{instanceId}/workflows/{workflowId}/chatData
|
||||
* Query params: afterTimestamp (optional) - fetch only data created after this time
|
||||
*/
|
||||
export async function fetchChatData(
|
||||
|
|
@ -243,7 +246,7 @@ export async function fetchChatData(
|
|||
): Promise<ChatDataResponse> {
|
||||
const params = afterTimestamp ? { afterTimestamp: afterTimestamp.toString() } : undefined;
|
||||
const requestConfig = {
|
||||
url: `/api/chatplayground/${instanceId}/${workflowId}/chatData`,
|
||||
url: `/api/chatplayground/${instanceId}/workflows/${workflowId}/chatData`,
|
||||
method: 'get' as const,
|
||||
params
|
||||
};
|
||||
|
|
@ -372,7 +375,7 @@ export async function startWorkflowApi(
|
|||
|
||||
/**
|
||||
* Stop a running workflow
|
||||
* Endpoint: POST /api/chatplayground/{instanceId}/{workflowId}/stop
|
||||
* Endpoint: POST /api/chatplayground/{instanceId}/workflows/{workflowId}/stop
|
||||
*/
|
||||
export async function stopWorkflowApi(
|
||||
request: ApiRequestFunction,
|
||||
|
|
@ -380,52 +383,53 @@ export async function stopWorkflowApi(
|
|||
workflowId: string
|
||||
): Promise<void> {
|
||||
await request({
|
||||
url: `/api/chatplayground/${instanceId}/${workflowId}/stop`,
|
||||
url: `/api/chatplayground/${instanceId}/workflows/${workflowId}/stop`,
|
||||
method: 'post'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update workflow properties
|
||||
* Endpoint: PUT /api/workflows/{workflowId}
|
||||
* When apiBaseUrl provided: PUT {apiBaseUrl}/workflows/{workflowId}
|
||||
* Otherwise: PUT /api/workflows/{workflowId}
|
||||
*/
|
||||
export async function updateWorkflowApi(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string,
|
||||
updateData: Partial<{ name: string; description?: string; tags?: string[] }>
|
||||
updateData: Partial<{ name: string; description?: string; tags?: string[] }>,
|
||||
apiBaseUrl?: string
|
||||
): Promise<Workflow> {
|
||||
return await request({
|
||||
url: `/api/workflows/${workflowId}`,
|
||||
method: 'put',
|
||||
data: updateData
|
||||
});
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}` : `/api/workflows/${workflowId}`;
|
||||
return await request({ url, method: 'put', data: updateData });
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a workflow and all associated data
|
||||
* Endpoint: DELETE /api/workflows/{workflowId}
|
||||
* When apiBaseUrl provided: DELETE {apiBaseUrl}/workflows/{workflowId}
|
||||
* Otherwise: DELETE /api/workflows/{workflowId}
|
||||
*/
|
||||
export async function deleteWorkflowApi(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string
|
||||
workflowId: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<void> {
|
||||
await request({
|
||||
url: `/api/workflows/${workflowId}`,
|
||||
method: 'delete'
|
||||
});
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}` : `/api/workflows/${workflowId}`;
|
||||
await request({ url, method: 'delete' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple workflows
|
||||
* When apiBaseUrl provided, uses feature-scoped delete endpoint
|
||||
*/
|
||||
export async function deleteWorkflowsApi(
|
||||
request: ApiRequestFunction,
|
||||
workflowIds: string[]
|
||||
workflowIds: string[],
|
||||
apiBaseUrl?: string
|
||||
): Promise<void> {
|
||||
// Delete workflows one by one since there's no bulk delete endpoint
|
||||
const base = apiBaseUrl ? `${apiBaseUrl}/workflows` : '/api/workflows';
|
||||
const deletePromises = workflowIds.map(workflowId =>
|
||||
request({
|
||||
url: `/api/workflows/${workflowId}`,
|
||||
url: `${base}/${workflowId}`,
|
||||
method: 'delete'
|
||||
}).catch(error => {
|
||||
console.error(`Failed to delete workflow ${workflowId}:`, error);
|
||||
|
|
@ -438,33 +442,33 @@ export async function deleteWorkflowsApi(
|
|||
|
||||
/**
|
||||
* Delete a message from a workflow
|
||||
* Endpoint: DELETE /api/workflows/{workflowId}/messages/{messageId}
|
||||
* When apiBaseUrl provided: DELETE {apiBaseUrl}/workflows/{workflowId}/messages/{messageId}
|
||||
* Otherwise: DELETE /api/workflows/{workflowId}/messages/{messageId}
|
||||
*/
|
||||
export async function deleteMessageApi(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string,
|
||||
messageId: string
|
||||
messageId: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<void> {
|
||||
await request({
|
||||
url: `/api/workflows/${workflowId}/messages/${messageId}`,
|
||||
method: 'delete'
|
||||
});
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}/messages/${messageId}` : `/api/workflows/${workflowId}/messages/${messageId}`;
|
||||
await request({ url, method: 'delete' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file reference from a message
|
||||
* Endpoint: DELETE /api/workflows/{workflowId}/messages/{messageId}/files/{fileId}
|
||||
* When apiBaseUrl provided: DELETE {apiBaseUrl}/workflows/{workflowId}/messages/{messageId}/files/{fileId}
|
||||
* Otherwise: DELETE /api/workflows/{workflowId}/messages/{messageId}/files/{fileId}
|
||||
*/
|
||||
export async function deleteFileFromMessageApi(
|
||||
request: ApiRequestFunction,
|
||||
workflowId: string,
|
||||
messageId: string,
|
||||
fileId: string
|
||||
fileId: string,
|
||||
apiBaseUrl?: string
|
||||
): Promise<void> {
|
||||
await request({
|
||||
url: `/api/workflows/${workflowId}/messages/${messageId}/files/${fileId}`,
|
||||
method: 'delete'
|
||||
});
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows/${workflowId}/messages/${messageId}/files/${fileId}` : `/api/workflows/${workflowId}/messages/${messageId}/files/${fileId}`;
|
||||
await request({ url, method: 'delete' });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -46,8 +46,17 @@ export interface PaginationParams {
|
|||
search?: string;
|
||||
}
|
||||
|
||||
// Workflows list hook
|
||||
export function useUserWorkflows() {
|
||||
/** Get apiBaseUrl from instanceId and featureCode for feature-scoped workflow APIs */
|
||||
export function getWorkflowApiBaseUrl(instanceId: string | undefined, featureCode: string | undefined): string | undefined {
|
||||
if (!instanceId || !featureCode) return undefined;
|
||||
if (featureCode === 'chatplayground') return `/api/chatplayground/${instanceId}`;
|
||||
if (featureCode === 'automation') return `/api/automations/${instanceId}`;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Workflows list hook - pass instanceId and featureCode when in feature context for feature-scoped API
|
||||
export function useUserWorkflows(options?: { instanceId?: string; featureCode?: string }) {
|
||||
const apiBaseUrl = getWorkflowApiBaseUrl(options?.instanceId, options?.featureCode);
|
||||
const [workflows, setWorkflows] = useState<UserWorkflow[]>([]);
|
||||
const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
|
||||
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
||||
|
|
@ -113,16 +122,11 @@ export function useUserWorkflows() {
|
|||
}
|
||||
|
||||
let data: any;
|
||||
const url = apiBaseUrl ? `${apiBaseUrl}/workflows` : '/api/workflows/';
|
||||
if (Object.keys(requestParams).length > 0) {
|
||||
// Use request for paginated queries
|
||||
data = await request({
|
||||
url: '/api/workflows/',
|
||||
method: 'get',
|
||||
params: requestParams
|
||||
});
|
||||
data = await request({ url, method: 'get', params: requestParams });
|
||||
} else {
|
||||
// Use API function for simple queries
|
||||
data = await fetchWorkflowsApi(request);
|
||||
data = await fetchWorkflowsApi(request, undefined, apiBaseUrl);
|
||||
}
|
||||
|
||||
// Handle paginated response
|
||||
|
|
@ -164,7 +168,7 @@ export function useUserWorkflows() {
|
|||
setWorkflows([]);
|
||||
setPagination(null);
|
||||
}
|
||||
}, [request]);
|
||||
}, [request, apiBaseUrl]);
|
||||
|
||||
// Optimistically remove a workflow from the local state
|
||||
const removeOptimistically = (workflowId: string) => {
|
||||
|
|
@ -185,13 +189,13 @@ export function useUserWorkflows() {
|
|||
// Fetch a single workflow by ID
|
||||
const fetchWorkflowById = useCallback(async (workflowId: string): Promise<UserWorkflow | null> => {
|
||||
try {
|
||||
const workflow = await fetchWorkflowByIdApi(request, workflowId);
|
||||
const workflow = await fetchWorkflowByIdApi(request, workflowId, apiBaseUrl);
|
||||
return workflow as UserWorkflow | null;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching workflow by ID:', error);
|
||||
return null;
|
||||
}
|
||||
}, [request]);
|
||||
}, [request, apiBaseUrl]);
|
||||
|
||||
// Generate edit fields from attributes dynamically
|
||||
const generateEditFieldsFromAttributes = useCallback((): Array<{
|
||||
|
|
@ -375,8 +379,9 @@ export function useUserWorkflows() {
|
|||
};
|
||||
}
|
||||
|
||||
// Workflow operations hook
|
||||
export function useWorkflowOperations() {
|
||||
// Workflow operations hook - pass instanceId and featureCode when in feature context for feature-scoped API
|
||||
export function useWorkflowOperations(options?: { instanceId?: string; featureCode?: string }) {
|
||||
const apiBaseUrl = getWorkflowApiBaseUrl(options?.instanceId, options?.featureCode);
|
||||
const [startingWorkflow, setStartingWorkflow] = useState(false);
|
||||
const [stoppingWorkflows, setStoppingWorkflows] = useState<Set<string>>(new Set());
|
||||
const [deletingWorkflows, setDeletingWorkflows] = useState<Set<string>>(new Set());
|
||||
|
|
@ -476,7 +481,7 @@ export function useWorkflowOperations() {
|
|||
workflowId,
|
||||
setDeletingWorkflows,
|
||||
setDeleteError,
|
||||
() => deleteWorkflowApi(request, workflowId),
|
||||
() => deleteWorkflowApi(request, workflowId, apiBaseUrl),
|
||||
{
|
||||
default: 'Failed to delete workflow',
|
||||
notFound: 'Workflow not found or has already been deleted.',
|
||||
|
|
@ -512,7 +517,7 @@ export function useWorkflowOperations() {
|
|||
|
||||
try {
|
||||
// Delete workflows one by one since there's no bulk delete endpoint
|
||||
await deleteWorkflowsApi(request, workflowIds);
|
||||
await deleteWorkflowsApi(request, workflowIds, apiBaseUrl);
|
||||
|
||||
// Add a small delay to ensure backend has time to process
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
|
|
@ -547,7 +552,7 @@ export function useWorkflowOperations() {
|
|||
operationKey,
|
||||
setDeletingMessages,
|
||||
setDeleteMessageError,
|
||||
() => deleteMessageApi(request, workflowId, messageId),
|
||||
() => deleteMessageApi(request, workflowId, messageId, apiBaseUrl),
|
||||
{
|
||||
default: 'Failed to delete message',
|
||||
notFound: 'Message not found or has already been deleted.',
|
||||
|
|
@ -566,7 +571,7 @@ export function useWorkflowOperations() {
|
|||
operationKey,
|
||||
setDeletingFiles,
|
||||
setDeleteFileError,
|
||||
() => deleteFileFromMessageApi(request, workflowId, messageId, fileId),
|
||||
() => deleteFileFromMessageApi(request, workflowId, messageId, fileId, apiBaseUrl),
|
||||
{
|
||||
default: 'Failed to delete file',
|
||||
notFound: 'File not found or has already been deleted.',
|
||||
|
|
@ -580,7 +585,7 @@ export function useWorkflowOperations() {
|
|||
setEditingWorkflows(prev => new Set(prev).add(workflowId));
|
||||
|
||||
try {
|
||||
const updatedWorkflow = await updateWorkflowApi(request, workflowId, updateData);
|
||||
const updatedWorkflow = await updateWorkflowApi(request, workflowId, updateData, apiBaseUrl);
|
||||
|
||||
return { success: true, workflowData: updatedWorkflow };
|
||||
} catch (error: any) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { FaSync, FaRobot, FaRocket, FaPlus, FaFileAlt, FaStop, FaList, FaTimes,
|
|||
import { useToast } from '../../../contexts/ToastContext';
|
||||
import { useApiRequest } from '../../../hooks/useApi';
|
||||
import { useFeatureStore } from '../../../stores/featureStore';
|
||||
import { useCurrentInstance } from '../../../hooks/useCurrentInstance';
|
||||
import styles from '../../admin/Admin.module.css';
|
||||
|
||||
interface WorkflowLog {
|
||||
|
|
@ -24,11 +25,19 @@ interface WorkflowLog {
|
|||
}
|
||||
|
||||
export const AutomationDefinitionsView: React.FC = () => {
|
||||
const { instanceId: routeInstanceId, featureCode: routeFeatureCode } = useCurrentInstance();
|
||||
const { getAllInstances } = useFeatureStore();
|
||||
const instances = getAllInstances();
|
||||
const chatbotInstance = instances.find(i => i.featureCode === 'chatbot') || instances[0];
|
||||
const mandateId = chatbotInstance?.mandateId;
|
||||
const featureInstanceId = chatbotInstance?.id;
|
||||
const automationInstance = instances.find(i => i.featureCode === 'automation');
|
||||
// When under automation feature route, use route context; otherwise use featureStore
|
||||
const mandateId = routeFeatureCode === 'automation' && routeInstanceId
|
||||
? (automationInstance?.mandateId ?? chatbotInstance?.mandateId)
|
||||
: chatbotInstance?.mandateId;
|
||||
const featureInstanceId = routeFeatureCode === 'automation' && routeInstanceId
|
||||
? routeInstanceId
|
||||
: (chatbotInstance?.id ?? automationInstance?.id);
|
||||
const automationWorkflowInstanceId = routeFeatureCode === 'automation' ? routeInstanceId : undefined;
|
||||
|
||||
const {
|
||||
data: automations,
|
||||
|
|
@ -66,6 +75,7 @@ export const AutomationDefinitionsView: React.FC = () => {
|
|||
visible: boolean;
|
||||
automationId: string | null;
|
||||
automationLabel: string;
|
||||
featureInstanceId: string | null;
|
||||
workflowId: string | null;
|
||||
status: 'starting' | 'running' | 'completed' | 'stopped' | 'error';
|
||||
logs: WorkflowLog[];
|
||||
|
|
@ -73,6 +83,7 @@ export const AutomationDefinitionsView: React.FC = () => {
|
|||
visible: false,
|
||||
automationId: null,
|
||||
automationLabel: '',
|
||||
featureInstanceId: null,
|
||||
workflowId: null,
|
||||
status: 'starting',
|
||||
logs: [],
|
||||
|
|
@ -259,14 +270,16 @@ export const AutomationDefinitionsView: React.FC = () => {
|
|||
setShowEditor(true);
|
||||
};
|
||||
|
||||
const pollWorkflowLogs = useCallback(async (workflowId: string) => {
|
||||
const pollWorkflowLogs = useCallback(async (workflowId: string, instanceId: string) => {
|
||||
try {
|
||||
const contextHeaders: Record<string, string> = {};
|
||||
if (mandateId) contextHeaders['X-Mandate-Id'] = mandateId;
|
||||
const logsUrl = `/api/automations/${instanceId}/workflows/${workflowId}/logs`;
|
||||
const workflowUrl = `/api/automations/${instanceId}/workflows/${workflowId}`;
|
||||
const response = await request({
|
||||
url: `/api/workflows/${workflowId}/logs`,
|
||||
url: logsUrl,
|
||||
method: 'get',
|
||||
params: lastLogIdRef.current ? { afterId: lastLogIdRef.current } : {},
|
||||
params: lastLogIdRef.current ? { logId: lastLogIdRef.current } : {},
|
||||
additionalConfig: { headers: contextHeaders },
|
||||
});
|
||||
const logs: WorkflowLog[] = response?.items || response || [];
|
||||
|
|
@ -279,7 +292,7 @@ export const AutomationDefinitionsView: React.FC = () => {
|
|||
lastLogIdRef.current = logs[logs.length - 1].id;
|
||||
}
|
||||
const statusResponse = await request({
|
||||
url: `/api/workflows/${workflowId}`,
|
||||
url: workflowUrl,
|
||||
method: 'get',
|
||||
additionalConfig: { headers: contextHeaders },
|
||||
});
|
||||
|
|
@ -309,6 +322,7 @@ export const AutomationDefinitionsView: React.FC = () => {
|
|||
visible: true,
|
||||
automationId: automation.id,
|
||||
automationLabel: automation.label,
|
||||
featureInstanceId: automation.featureInstanceId ?? automationWorkflowInstanceId ?? null,
|
||||
workflowId: null,
|
||||
status: 'starting',
|
||||
logs: [{ id: 'init', timestamp: Date.now() / 1000, message: 'Automatisierung wird gestartet...' }],
|
||||
|
|
@ -316,14 +330,17 @@ export const AutomationDefinitionsView: React.FC = () => {
|
|||
try {
|
||||
const result = await handleAutomationExecute(automation.id);
|
||||
const workflowId = result?.id;
|
||||
if (workflowId) {
|
||||
const instanceId = automation.featureInstanceId ?? automationWorkflowInstanceId;
|
||||
if (workflowId && instanceId) {
|
||||
setExecutionModal(prev => ({
|
||||
...prev,
|
||||
workflowId,
|
||||
status: 'running',
|
||||
logs: [...prev.logs, { id: 'started', timestamp: Date.now() / 1000, message: `Workflow ${workflowId} gestartet`, status: 'running' }],
|
||||
}));
|
||||
pollIntervalRef.current = setInterval(() => pollWorkflowLogs(workflowId), 2000);
|
||||
pollIntervalRef.current = setInterval(() => pollWorkflowLogs(workflowId, instanceId), 2000);
|
||||
} else if (workflowId && !instanceId) {
|
||||
setExecutionModal(prev => ({ ...prev, status: 'error', logs: [...prev.logs, { id: 'error', timestamp: Date.now() / 1000, message: 'Keine Feature-Instanz für Polling', status: 'error' }] }));
|
||||
}
|
||||
} catch (err: any) {
|
||||
setExecutionModal(prev => ({
|
||||
|
|
@ -337,11 +354,17 @@ export const AutomationDefinitionsView: React.FC = () => {
|
|||
|
||||
const handleStopWorkflow = async () => {
|
||||
if (!executionModal.workflowId) return;
|
||||
const instanceId = executionModal.featureInstanceId ?? automationWorkflowInstanceId;
|
||||
if (!instanceId) {
|
||||
showError('Keine Feature-Instanz für Stopp verfügbar');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const stopHeaders: Record<string, string> = {};
|
||||
if (mandateId) stopHeaders['X-Mandate-Id'] = mandateId;
|
||||
const stopUrl = `/api/automations/${instanceId}/workflows/${executionModal.workflowId}/stop`;
|
||||
await request({
|
||||
url: `/api/workflows/${executionModal.workflowId}/stop`,
|
||||
url: stopUrl,
|
||||
method: 'post',
|
||||
additionalConfig: { headers: stopHeaders },
|
||||
});
|
||||
|
|
@ -363,6 +386,7 @@ export const AutomationDefinitionsView: React.FC = () => {
|
|||
visible: false,
|
||||
automationId: null,
|
||||
automationLabel: '',
|
||||
featureInstanceId: null,
|
||||
workflowId: null,
|
||||
status: 'starting',
|
||||
logs: [],
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@
|
|||
*/
|
||||
|
||||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
import { useUserWorkflows, useWorkflowOperations } from '../../hooks/useWorkflows';
|
||||
import { useUserWorkflows, useWorkflowOperations, getWorkflowApiBaseUrl } from '../../hooks/useWorkflows';
|
||||
import { FormGeneratorTable } from '../../components/FormGenerator/FormGeneratorTable';
|
||||
import { FormGeneratorForm } from '../../components/FormGenerator/FormGeneratorForm';
|
||||
import { FaSync, FaList, FaPlay } from 'react-icons/fa';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useCurrentInstance } from '../../hooks/useCurrentInstance';
|
||||
import styles from '../admin/Admin.module.css';
|
||||
|
||||
interface Workflow {
|
||||
|
|
@ -23,8 +24,12 @@ interface Workflow {
|
|||
|
||||
export const WorkflowsPage: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { instanceId, featureCode } = useCurrentInstance();
|
||||
const workflowOptions = instanceId && featureCode ? { instanceId, featureCode } : undefined;
|
||||
const apiBaseUrl = getWorkflowApiBaseUrl(instanceId, featureCode);
|
||||
const apiEndpoint = apiBaseUrl ? `${apiBaseUrl}/workflows` : '/api/workflows/';
|
||||
|
||||
// Data hook
|
||||
// Data hook - pass instance context when in feature route
|
||||
const {
|
||||
data: workflows,
|
||||
attributes,
|
||||
|
|
@ -35,16 +40,16 @@ export const WorkflowsPage: React.FC = () => {
|
|||
refetch,
|
||||
fetchWorkflowById,
|
||||
updateOptimistically,
|
||||
} = useUserWorkflows();
|
||||
} = useUserWorkflows(workflowOptions);
|
||||
|
||||
// Operations hook
|
||||
// Operations hook - pass instance context when in feature route
|
||||
const {
|
||||
handleWorkflowDelete,
|
||||
handleWorkflowDeleteMultiple,
|
||||
handleWorkflowUpdate,
|
||||
handleInlineUpdate,
|
||||
deletingWorkflows,
|
||||
} = useWorkflowOperations();
|
||||
} = useWorkflowOperations(workflowOptions);
|
||||
|
||||
const [editingWorkflow, setEditingWorkflow] = useState<Workflow | null>(null);
|
||||
|
||||
|
|
@ -173,7 +178,7 @@ export const WorkflowsPage: React.FC = () => {
|
|||
<FormGeneratorTable
|
||||
data={workflows}
|
||||
columns={columns}
|
||||
apiEndpoint="/api/workflows/"
|
||||
apiEndpoint={apiEndpoint}
|
||||
loading={loading}
|
||||
pagination={true}
|
||||
pageSize={25}
|
||||
|
|
|
|||
Loading…
Reference in a new issue