fixed automation and trustee
This commit is contained in:
parent
cc8770dec3
commit
41e02b5a2c
4 changed files with 93 additions and 86 deletions
|
|
@ -7,6 +7,7 @@ import { ApiRequestOptions } from '../hooks/useApi';
|
|||
export interface Automation {
|
||||
id: string;
|
||||
mandateId: string;
|
||||
featureInstanceId: string;
|
||||
label: string;
|
||||
template: string | object;
|
||||
placeholders: Record<string, string>;
|
||||
|
|
@ -51,6 +52,7 @@ export interface CreateAutomationRequest {
|
|||
schedule?: string;
|
||||
active?: boolean;
|
||||
mandateId?: string;
|
||||
featureInstanceId?: string;
|
||||
}
|
||||
|
||||
export interface UpdateAutomationRequest {
|
||||
|
|
|
|||
|
|
@ -56,91 +56,33 @@ export function useAutomations() {
|
|||
const { request, isLoading: loading, error } = useApiRequest<null, Automation[]>();
|
||||
const { checkPermission } = usePermissions();
|
||||
|
||||
// Fallback attributes for automation form
|
||||
const fallbackAttributes: AttributeDefinition[] = [
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
label: 'Name',
|
||||
required: true,
|
||||
editable: true,
|
||||
visible: true,
|
||||
sortable: true,
|
||||
searchable: true,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
name: 'schedule',
|
||||
type: 'text',
|
||||
label: 'Zeitplan (Cron)',
|
||||
required: false,
|
||||
editable: true,
|
||||
visible: true,
|
||||
description: 'z.B. "0 8 * * *" für täglich 8:00 Uhr',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
name: 'template',
|
||||
type: 'textarea',
|
||||
label: 'Template',
|
||||
required: false,
|
||||
editable: true,
|
||||
visible: true,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
name: 'active',
|
||||
type: 'checkbox',
|
||||
label: 'Aktiv',
|
||||
required: false,
|
||||
editable: true,
|
||||
visible: true,
|
||||
default: true,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
type: 'text',
|
||||
label: 'Status',
|
||||
required: false,
|
||||
editable: false,
|
||||
visible: true,
|
||||
readonly: true,
|
||||
width: 100,
|
||||
},
|
||||
];
|
||||
|
||||
// Fetch attributes from backend
|
||||
// Fetch attributes from backend - no fallback, errors should be visible
|
||||
const fetchAttributes = useCallback(async () => {
|
||||
try {
|
||||
const response = await api.get('/api/attributes/AutomationDefinition');
|
||||
const response = await api.get('/api/automations/attributes');
|
||||
|
||||
let attrs: AttributeDefinition[] = [];
|
||||
if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
||||
// Backend returns: { attributes: { model: "...", attributes: [...] } }
|
||||
// So we need to access response.data.attributes.attributes
|
||||
if (response.data?.attributes?.attributes && Array.isArray(response.data.attributes.attributes)) {
|
||||
attrs = response.data.attributes.attributes;
|
||||
} else if (response.data?.attributes && Array.isArray(response.data.attributes)) {
|
||||
// Fallback: if attributes is directly an array
|
||||
attrs = response.data.attributes;
|
||||
} else if (Array.isArray(response.data)) {
|
||||
attrs = response.data;
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
const keys = Object.keys(response.data);
|
||||
for (const key of keys) {
|
||||
if (Array.isArray(response.data[key])) {
|
||||
attrs = response.data[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use fallback if no attributes returned
|
||||
if (attrs.length === 0) {
|
||||
attrs = fallbackAttributes;
|
||||
console.warn('No attributes returned from backend for AutomationDefinition');
|
||||
}
|
||||
|
||||
setAttributes(attrs);
|
||||
return attrs;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching automation attributes, using fallback:', error);
|
||||
setAttributes(fallbackAttributes);
|
||||
return fallbackAttributes;
|
||||
console.error('Error fetching automation attributes:', error);
|
||||
setAttributes([]);
|
||||
return [];
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
|
@ -322,13 +264,24 @@ export function useAutomationOperations() {
|
|||
setCreateError(null);
|
||||
|
||||
try {
|
||||
// Get mandateId from session storage
|
||||
const currentUserJson = sessionStorage.getItem('currentUser');
|
||||
if (currentUserJson) {
|
||||
const currentUser = JSON.parse(currentUserJson);
|
||||
if (currentUser.mandateId) {
|
||||
data.mandateId = currentUser.mandateId;
|
||||
// Validate required fields - mandateId and featureInstanceId must be provided
|
||||
if (!data.mandateId || !data.featureInstanceId) {
|
||||
throw new Error('mandateId and featureInstanceId are required');
|
||||
}
|
||||
|
||||
// Convert placeholders to ensure all values are strings
|
||||
if (data.placeholders) {
|
||||
const convertedPlaceholders: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(data.placeholders)) {
|
||||
if (value === null || value === undefined) {
|
||||
convertedPlaceholders[key] = '';
|
||||
} else if (typeof value === 'object') {
|
||||
convertedPlaceholders[key] = JSON.stringify(value);
|
||||
} else {
|
||||
convertedPlaceholders[key] = String(value);
|
||||
}
|
||||
}
|
||||
data.placeholders = convertedPlaceholders;
|
||||
}
|
||||
|
||||
const newAutomation = await createAutomationApi(request, data);
|
||||
|
|
|
|||
|
|
@ -127,6 +127,11 @@ export function useCanViewFeatureView(viewCode: string): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check for wildcard "_all" permission first (item=None in backend = all views)
|
||||
if (instance.permissions.views["_all"]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return instance.permissions.views[viewCode] ?? false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ import React, { useState, useMemo, useEffect, useCallback, useRef } from 'react'
|
|||
import { useAutomations, useAutomationOperations, AutomationTemplate, Automation } from '../../hooks/useAutomations';
|
||||
import { FormGeneratorTable } from '../../components/FormGenerator/FormGeneratorTable';
|
||||
import { FormGeneratorForm } from '../../components/FormGenerator/FormGeneratorForm';
|
||||
import { FaSync, FaRobot, FaPlay, FaPlus, FaToggleOn, FaToggleOff, FaFileAlt, FaStop, FaList, FaTimes, FaCheck, FaExclamationCircle, FaSpinner } from 'react-icons/fa';
|
||||
import { FaSync, FaRobot, FaRocket, FaPlus, FaPauseCircle, FaPlayCircle, FaFileAlt, FaStop, FaList, FaTimes, FaCheck, FaExclamationCircle, FaSpinner } from 'react-icons/fa';
|
||||
import { useToast } from '../../contexts/ToastContext';
|
||||
import { useApiRequest } from '../../hooks/useApi';
|
||||
import { useFeatureStore } from '../../stores/featureStore';
|
||||
import styles from '../admin/Admin.module.css';
|
||||
|
||||
|
||||
|
|
@ -25,6 +26,15 @@ interface WorkflowLog {
|
|||
}
|
||||
|
||||
export const AutomationsPage: React.FC = () => {
|
||||
// Get mandate and feature instance from store (first chatbot instance or first available)
|
||||
const { getAllInstances } = useFeatureStore();
|
||||
const instances = getAllInstances();
|
||||
|
||||
// Find first chatbot instance, or fall back to first available instance
|
||||
const chatbotInstance = instances.find(i => i.featureCode === 'chatbot') || instances[0];
|
||||
const mandateId = chatbotInstance?.mandateId;
|
||||
const featureInstanceId = chatbotInstance?.id;
|
||||
|
||||
// Data hook
|
||||
const {
|
||||
data: automations,
|
||||
|
|
@ -147,11 +157,24 @@ export const AutomationsPage: React.FC = () => {
|
|||
|
||||
// Handle create submit
|
||||
const handleCreateSubmit = async (data: Partial<Automation>) => {
|
||||
const result = await handleAutomationCreate(data as any);
|
||||
// Validate context - mandateId and featureInstanceId are required
|
||||
if (!mandateId || !featureInstanceId) {
|
||||
showError('Fehler: Kein aktiver Mandant oder Feature-Instanz gefunden');
|
||||
return;
|
||||
}
|
||||
|
||||
// Add required fields from context
|
||||
const createData = {
|
||||
...data,
|
||||
mandateId: mandateId,
|
||||
featureInstanceId: featureInstanceId,
|
||||
};
|
||||
|
||||
const result = await handleAutomationCreate(createData as any);
|
||||
if (result) {
|
||||
setShowCreateModal(false);
|
||||
showSuccess('Automatisierung erstellt');
|
||||
refetch();
|
||||
await refetch();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -162,7 +185,7 @@ export const AutomationsPage: React.FC = () => {
|
|||
if (success) {
|
||||
setEditingAutomation(null);
|
||||
showSuccess('Automatisierung aktualisiert');
|
||||
refetch();
|
||||
await refetch();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -172,7 +195,7 @@ export const AutomationsPage: React.FC = () => {
|
|||
const success = await handleAutomationDelete(automation.id);
|
||||
if (success) {
|
||||
showSuccess('Automatisierung gelöscht');
|
||||
refetch();
|
||||
await refetch();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -199,11 +222,35 @@ export const AutomationsPage: React.FC = () => {
|
|||
const handleTemplateSelect = async (template: AutomationTemplate) => {
|
||||
setShowTemplateModal(false);
|
||||
|
||||
// Pre-fill form with template data
|
||||
// Validate context - mandateId and featureInstanceId are required
|
||||
if (!mandateId || !featureInstanceId) {
|
||||
showError('Fehler: Kein aktiver Mandant oder Feature-Instanz gefunden');
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert placeholder values to strings (backend expects Dict[str, str])
|
||||
// Arrays and objects are converted to JSON strings
|
||||
const convertedPlaceholders: Record<string, string> = {};
|
||||
const templateParams = template.parameters || {};
|
||||
for (const [key, value] of Object.entries(templateParams)) {
|
||||
if (value === null || value === undefined) {
|
||||
convertedPlaceholders[key] = '';
|
||||
} else if (Array.isArray(value) || (typeof value === 'object' && value !== null)) {
|
||||
// Convert complex structures to JSON strings
|
||||
convertedPlaceholders[key] = JSON.stringify(value);
|
||||
} else {
|
||||
// Keep primitive values as strings
|
||||
convertedPlaceholders[key] = String(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-fill form with template data including required fields
|
||||
const prefillData: Partial<Automation> = {
|
||||
mandateId: mandateId,
|
||||
featureInstanceId: featureInstanceId,
|
||||
label: template.template?.overview || 'Neue Automatisierung',
|
||||
template: JSON.stringify(template.template, null, 2),
|
||||
placeholders: template.parameters || {},
|
||||
placeholders: convertedPlaceholders,
|
||||
active: false,
|
||||
schedule: '0 */4 * * *',
|
||||
};
|
||||
|
|
@ -212,7 +259,7 @@ export const AutomationsPage: React.FC = () => {
|
|||
const result = await handleAutomationCreate(prefillData as any);
|
||||
if (result) {
|
||||
showSuccess('Automatisierung aus Vorlage erstellt');
|
||||
refetch();
|
||||
await refetch();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -530,14 +577,14 @@ export const AutomationsPage: React.FC = () => {
|
|||
customActions={[
|
||||
{
|
||||
id: 'execute',
|
||||
icon: <FaPlay />,
|
||||
icon: <FaRocket />,
|
||||
onClick: handleExecute,
|
||||
title: 'Ausführen',
|
||||
loading: (row: any) => executingAutomations.has(row.id),
|
||||
},
|
||||
{
|
||||
id: 'toggleActive',
|
||||
icon: (row: any) => row.active ? <FaToggleOn /> : <FaToggleOff />,
|
||||
icon: (row: any) => row.active ? <FaPauseCircle /> : <FaPlayCircle />,
|
||||
onClick: handleToggleActive,
|
||||
title: (row: any) => row.active ? 'Deaktivieren' : 'Aktivieren',
|
||||
} as any,
|
||||
|
|
|
|||
Loading…
Reference in a new issue