e.stopPropagation()} style={{ maxWidth: '700px' }}>
-
- {getStatusIcon(executionModal.status)} Ausführung: {executionModal.automationLabel}
-
-
+ {getStatusIcon(executionModal.status)} Ausführung: {executionModal.automationLabel}
+
@@ -772,83 +538,49 @@ export const AutomationsPage: React.FC = () => {
{executionModal.status === 'stopped' && 'Gestoppt'}
{executionModal.status === 'error' && 'Fehler'}
- {executionModal.workflowId && (
-
- Workflow: {executionModal.workflowId}
-
- )}
+ {executionModal.workflowId && Workflow: {executionModal.workflowId}}
-
+
{executionModal.logs.map((log, index) => (
[{formatTime(log.timestamp)}]
{log.status && {log.status}:}
{log.message}
- {log.progress !== undefined && log.progress !== null && log.progress < 1 && (
- ({Math.round(log.progress * 100)}%)
- )}
+ {log.progress !== undefined && log.progress !== null && log.progress < 1 && ({Math.round(log.progress * 100)}%)}
))}
- {executionModal.status === 'running' && (
-
- )}
-
+ {executionModal.status === 'running' && }
+
)}
- {/* Logs History Modal */}
{logsModal.visible && logsModal.automation && (
setLogsModal({ visible: false, automation: null })}>
e.stopPropagation()} style={{ maxWidth: '700px' }}>
-
- Ausführungsverlauf: {logsModal.automation.label}
-
-
+ Ausführungsverlauf: {logsModal.automation.label}
+
{(!logsModal.automation.executionLogs || logsModal.automation.executionLogs.length === 0) ? (
-
-
Keine Ausführungen vorhanden
-
+
Keine Ausführungen vorhanden
) : (
{[...logsModal.automation.executionLogs].reverse().map((log, index) => (
{formatTimestamp(log.timestamp)}
-
- {log.status || 'Unbekannt'}
-
- {log.workflowId && (
-
- Workflow: {log.workflowId}
-
- )}
+ {log.status || 'Unbekannt'}
+ {log.workflowId && Workflow: {log.workflowId}}
{log.messages && log.messages.length > 0 && (
- {log.messages.map((msg, msgIndex) => (
-
{msg}
- ))}
+ {log.messages.map((msg, msgIndex) =>
{msg}
)}
)}
@@ -857,12 +589,7 @@ export const AutomationsPage: React.FC = () => {
)}
-
+
@@ -870,5 +597,3 @@ export const AutomationsPage: React.FC = () => {
);
};
-
-export default AutomationsPage;
diff --git a/src/pages/views/automation/AutomationLogsView.tsx b/src/pages/views/automation/AutomationLogsView.tsx
new file mode 100644
index 0000000..355fae8
--- /dev/null
+++ b/src/pages/views/automation/AutomationLogsView.tsx
@@ -0,0 +1,14 @@
+/**
+ * AutomationLogsView
+ *
+ * Placeholder view for automation execution logs.
+ */
+import React from 'react';
+import styles from '../../FeatureView.module.css';
+
+export const AutomationLogsView: React.FC = () => (
+
+
Execution Logs
+
Automatisierungs-Ausführungsprotokolle
+
+);
diff --git a/src/pages/views/automation/AutomationTemplatesView.tsx b/src/pages/views/automation/AutomationTemplatesView.tsx
new file mode 100644
index 0000000..5aa4cf4
--- /dev/null
+++ b/src/pages/views/automation/AutomationTemplatesView.tsx
@@ -0,0 +1,196 @@
+/**
+ * AutomationTemplatesView
+ *
+ * View for managing automation templates (CRUD).
+ * System templates (isSystem=true) are read-only for non-SysAdmin, with duplicate option.
+ * Instance templates can be managed by instance admins/editors.
+ */
+
+import React, { useState, useMemo, useEffect } from 'react';
+import { useAutomationTemplates, type AutomationTemplate } from '../../../hooks/useAutomations';
+import { FormGeneratorTable } from '../../../components/FormGenerator/FormGeneratorTable';
+import { AutomationEditor } from '../../../components/AutomationEditor';
+import { FaSync, FaPlus, FaFileAlt, FaLock } from 'react-icons/fa';
+import { useToast } from '../../../contexts/ToastContext';
+import { useCurrentUser } from '../../../hooks/useUsers';
+import styles from '../../admin/Admin.module.css';
+
+export const AutomationTemplatesView: React.FC = () => {
+ const {
+ templates,
+ attributes,
+ loading,
+ error,
+ permissions,
+ refetch,
+ createTemplate,
+ updateTemplate,
+ deleteTemplate,
+ duplicateTemplate,
+ getTemplate,
+ } = useAutomationTemplates();
+ const { user: currentUser } = useCurrentUser();
+ const isSysAdmin = currentUser?.isSysAdmin || false;
+ const { showSuccess, showError } = useToast();
+
+ const [showEditor, setShowEditor] = useState(false);
+ const [editingTemplate, setEditingTemplate] = useState
(null);
+ const [saving, setSaving] = useState(false);
+
+ useEffect(() => { refetch(); }, []);
+
+ const canCreate = permissions?.create !== 'n';
+ const canUpdate = permissions?.update !== 'n';
+ const canDelete = permissions?.delete !== 'n';
+
+ const columns = useMemo(() => [
+ { key: 'label', label: 'Label', type: 'string' as const, sortable: true, searchable: true, width: 200 },
+ { key: 'overview', label: 'Beschreibung', type: 'string' as const, width: 300 },
+ { key: 'isSystem', label: 'Typ', type: 'boolean' as const, width: 100, formatter: (value: any) =>
+ value ? System
+ : Instanz
+ },
+ { key: '_createdByUserName', label: 'Erstellt von', type: 'string' as const, width: 150 },
+ ], []);
+
+ const handleEditClick = async (template: AutomationTemplate) => {
+ const fullTemplate = await getTemplate(template.id);
+ setEditingTemplate(fullTemplate || template);
+ setShowEditor(true);
+ };
+
+ const handleCreateClick = () => {
+ setEditingTemplate(null);
+ setShowEditor(true);
+ };
+
+ const handleEditorSave = async (data: Partial) => {
+ setSaving(true);
+ try {
+ if (editingTemplate) {
+ await updateTemplate(editingTemplate.id, data);
+ showSuccess('Vorlage aktualisiert');
+ } else {
+ await createTemplate(data as any);
+ showSuccess('Vorlage erstellt');
+ }
+ setShowEditor(false);
+ setEditingTemplate(null);
+ await refetch();
+ } catch (err: any) {
+ showError(`Fehler: ${err.message}`);
+ } finally {
+ setSaving(false);
+ }
+ };
+
+ const handleEditorCancel = () => {
+ setShowEditor(false);
+ setEditingTemplate(null);
+ };
+
+ const handleDelete = async (templateId: string): Promise => {
+ try {
+ await deleteTemplate(templateId);
+ showSuccess('Vorlage gelöscht');
+ return true;
+ } catch (err: any) {
+ showError(`Fehler: ${err.message}`);
+ return false;
+ }
+ };
+
+ const handleDuplicate = async (template: AutomationTemplate) => {
+ try {
+ await duplicateTemplate(template.id);
+ showSuccess('Vorlage dupliziert');
+ await refetch();
+ } catch (err: any) {
+ showError(`Fehler beim Duplizieren: ${err.message}`);
+ }
+ };
+
+ if (error) {
+ return (
+
+
+
⚠️
+
Fehler beim Laden der Vorlagen: {error}
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
Automation-Vorlagen
+
Verwalten Sie Ihre Workflow-Vorlagen
+
+
+
+ {canCreate && (
+
+ )}
+
+
+
+
+ {loading && (!templates || templates.length === 0) ? (
+
+ ) : !templates || templates.length === 0 ? (
+
+
+
Keine Vorlagen vorhanden
+
Erstellen Sie eine neue Vorlage für Ihre Workflows.
+ {canCreate && (
+
+ )}
+
+ ) : (
+
row.isSystem && !isSysAdmin ? { disabled: true, message: 'System-Vorlagen können nur vom SysAdmin bearbeitet werden' } : !canUpdate ? { disabled: true, message: 'Keine Berechtigung' } : false },
+ { type: 'delete' as const, title: 'Löschen', disabled: (row: any) => row.isSystem && !isSysAdmin ? { disabled: true, message: 'System-Vorlagen können nur vom SysAdmin gelöscht werden' } : !canDelete ? { disabled: true, message: 'Keine Berechtigung' } : false },
+ ]}
+ onDelete={(template) => handleDelete(template.id)}
+ hookData={{ refetch, handleDelete, attributes }}
+ emptyMessage="Keine Vorlagen gefunden"
+ />
+ )}
+
+
+ {showEditor && (
+
+ )}
+
+ );
+};
diff --git a/src/pages/views/automation/index.ts b/src/pages/views/automation/index.ts
new file mode 100644
index 0000000..855bc6e
--- /dev/null
+++ b/src/pages/views/automation/index.ts
@@ -0,0 +1,7 @@
+/**
+ * Automation Views Export
+ */
+
+export { AutomationDefinitionsView } from './AutomationDefinitionsView';
+export { AutomationTemplatesView } from './AutomationTemplatesView';
+export { AutomationLogsView } from './AutomationLogsView';
diff --git a/src/pages/workflows/AutomationTemplatesPage.tsx b/src/pages/workflows/AutomationTemplatesPage.tsx
deleted file mode 100644
index d199d1d..0000000
--- a/src/pages/workflows/AutomationTemplatesPage.tsx
+++ /dev/null
@@ -1,252 +0,0 @@
-/**
- * AutomationTemplatesPage
- *
- * Page for managing automation templates (CRUD).
- * System templates (isSystem=true) are read-only for non-SysAdmin, with duplicate option.
- * Instance templates can be managed by instance admins/editors.
- */
-
-import React, { useState, useMemo, useEffect } from 'react';
-import { useAutomationTemplates, type AutomationTemplate } from '../../hooks/useAutomations';
-import { FormGeneratorTable } from '../../components/FormGenerator/FormGeneratorTable';
-import { AutomationEditor } from '../../components/AutomationEditor';
-import { FaSync, FaPlus, FaFileAlt, FaLock } from 'react-icons/fa';
-import { useToast } from '../../contexts/ToastContext';
-import { useCurrentUser } from '../../hooks/useUsers';
-import styles from '../admin/Admin.module.css';
-
-export const AutomationTemplatesPage: React.FC = () => {
- const {
- templates,
- attributes,
- loading,
- error,
- permissions,
- refetch,
- createTemplate,
- updateTemplate,
- deleteTemplate,
- duplicateTemplate,
- getTemplate,
- } = useAutomationTemplates();
- const { user: currentUser } = useCurrentUser();
- const isSysAdmin = currentUser?.isSysAdmin || false;
-
- const { showSuccess, showError } = useToast();
-
- // Editor states
- const [showEditor, setShowEditor] = useState(false);
- const [editingTemplate, setEditingTemplate] = useState(null);
- const [saving, setSaving] = useState(false);
-
- // Initial fetch
- useEffect(() => {
- refetch();
- }, []);
-
- // Check permissions
- const canCreate = permissions?.create !== 'n';
- const canUpdate = permissions?.update !== 'n';
- const canDelete = permissions?.delete !== 'n';
-
- // Table columns - FormGeneratorTable auto-renders TextMultilingual in user language
- const columns = useMemo(() => [
- { key: 'label', label: 'Label', type: 'string' as const, sortable: true, searchable: true, width: 200 },
- { key: 'overview', label: 'Beschreibung', type: 'string' as const, width: 300 },
- { key: 'isSystem', label: 'Typ', type: 'boolean' as const, width: 100, formatter: (value: any) =>
- value ? System
- : Instanz
- },
- { key: '_createdByUserName', label: 'Erstellt von', type: 'string' as const, width: 150 },
- ], []);
-
- // Handle edit click - open editor with template data
- const handleEditClick = async (template: AutomationTemplate) => {
- // Fetch full template data
- const fullTemplate = await getTemplate(template.id);
- setEditingTemplate(fullTemplate || template);
- setShowEditor(true);
- };
-
- // Handle create click - open editor for new template
- const handleCreateClick = () => {
- setEditingTemplate(null);
- setShowEditor(true);
- };
-
- // Handle editor save
- const handleEditorSave = async (data: Partial) => {
- setSaving(true);
- try {
- if (editingTemplate) {
- await updateTemplate(editingTemplate.id, data);
- showSuccess('Vorlage aktualisiert');
- } else {
- await createTemplate(data as any);
- showSuccess('Vorlage erstellt');
- }
- setShowEditor(false);
- setEditingTemplate(null);
- await refetch();
- } catch (err: any) {
- showError(`Fehler: ${err.message}`);
- } finally {
- setSaving(false);
- }
- };
-
- // Handle editor cancel
- const handleEditorCancel = () => {
- setShowEditor(false);
- setEditingTemplate(null);
- };
-
- // Handle delete by ID (used by DeleteActionButton via hookData)
- const handleDelete = async (templateId: string): Promise => {
- try {
- await deleteTemplate(templateId);
- showSuccess('Vorlage gelöscht');
- return true;
- } catch (err: any) {
- showError(`Fehler: ${err.message}`);
- return false;
- }
- };
-
- // Handle duplicate
- const handleDuplicate = async (template: AutomationTemplate) => {
- try {
- await duplicateTemplate(template.id);
- showSuccess('Vorlage dupliziert');
- await refetch();
- } catch (err: any) {
- showError(`Fehler beim Duplizieren: ${err.message}`);
- }
- };
-
- if (error) {
- return (
-
-
-
⚠️
-
Fehler beim Laden der Vorlagen: {error}
-
-
-
- );
- }
-
- return (
-
-
-
-
Automation-Vorlagen
-
Verwalten Sie Ihre Workflow-Vorlagen
-
-
-
- {canCreate && (
-
- )}
-
-
-
-
- {loading && (!templates || templates.length === 0) ? (
-
- ) : !templates || templates.length === 0 ? (
-
-
-
Keine Vorlagen vorhanden
-
- Erstellen Sie eine neue Vorlage für Ihre Workflows.
-
- {canCreate && (
-
- )}
-
- ) : (
-
row.isSystem && !isSysAdmin
- ? { disabled: true, message: 'System-Vorlagen können nur vom SysAdmin bearbeitet werden' }
- : !canUpdate
- ? { disabled: true, message: 'Keine Berechtigung' }
- : false,
- },
- {
- type: 'delete' as const,
- title: 'Löschen',
- disabled: (row: any) => row.isSystem && !isSysAdmin
- ? { disabled: true, message: 'System-Vorlagen können nur vom SysAdmin gelöscht werden' }
- : !canDelete
- ? { disabled: true, message: 'Keine Berechtigung' }
- : false,
- },
- ]}
- onDelete={(template) => handleDelete(template.id)}
- hookData={{
- refetch,
- handleDelete,
- attributes,
- }}
- emptyMessage="Keine Vorlagen gefunden"
- />
- )}
-
-
- {/* Automation Editor */}
- {showEditor && (
-
- )}
-
- );
-};
-
-export default AutomationTemplatesPage;
diff --git a/src/pages/workflows/index.ts b/src/pages/workflows/index.ts
index 2c60e26..43bc009 100644
--- a/src/pages/workflows/index.ts
+++ b/src/pages/workflows/index.ts
@@ -1,4 +1,3 @@
export { PlaygroundPage } from './PlaygroundPage';
export { WorkflowsPage } from './WorkflowsPage';
-export { AutomationsPage } from './AutomationsPage';
-export { AutomationTemplatesPage } from './AutomationTemplatesPage';
+