frontend_nyla/src/pages/workflows/AutomationTemplatesPage.tsx
2026-02-09 23:45:05 +01:00

217 lines
8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* AutomationTemplatesPage
*
* Page for managing automation templates (CRUD).
* Uses FormGeneratorTable for listing and AutomationEditor for editing.
*/
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 } from 'react-icons/fa';
import { useToast } from '../../contexts/ToastContext';
import styles from '../admin/Admin.module.css';
export const AutomationTemplatesPage: React.FC = () => {
const {
templates,
attributes,
loading,
error,
permissions,
refetch,
createTemplate,
updateTemplate,
deleteTemplate,
getTemplate,
} = useAutomationTemplates();
const { showSuccess, showError } = useToast();
// Editor states
const [showEditor, setShowEditor] = useState(false);
const [editingTemplate, setEditingTemplate] = useState<AutomationTemplate | null>(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: '_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<AutomationTemplate>) => {
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<boolean> => {
try {
await deleteTemplate(templateId);
showSuccess('Vorlage gelöscht');
return true;
} catch (err: any) {
showError(`Fehler: ${err.message}`);
return false;
}
};
if (error) {
return (
<div className={styles.adminPage}>
<div className={styles.errorContainer}>
<span className={styles.errorIcon}></span>
<p className={styles.errorMessage}>Fehler beim Laden der Vorlagen: {error}</p>
<button className={styles.secondaryButton} onClick={() => refetch()}>
<FaSync /> Erneut versuchen
</button>
</div>
</div>
);
}
return (
<div className={styles.adminPage}>
<div className={styles.pageHeader}>
<div>
<h1 className={styles.pageTitle}>Automation-Vorlagen</h1>
<p className={styles.pageSubtitle}>Verwalten Sie Ihre Workflow-Vorlagen</p>
</div>
<div className={styles.headerActions}>
<button
className={styles.secondaryButton}
onClick={() => refetch()}
disabled={loading}
>
<FaSync className={loading ? 'spinning' : ''} /> Aktualisieren
</button>
{canCreate && (
<button
className={styles.primaryButton}
onClick={handleCreateClick}
>
<FaPlus /> Neue Vorlage
</button>
)}
</div>
</div>
<div className={styles.tableContainer}>
{loading && (!templates || templates.length === 0) ? (
<div className={styles.loadingContainer}>
<div className={styles.spinner} />
<span>Lade Vorlagen...</span>
</div>
) : !templates || templates.length === 0 ? (
<div className={styles.emptyState}>
<FaFileAlt className={styles.emptyIcon} />
<h3 className={styles.emptyTitle}>Keine Vorlagen vorhanden</h3>
<p className={styles.emptyDescription}>
Erstellen Sie eine neue Vorlage für Ihre Workflows.
</p>
{canCreate && (
<button
className={styles.primaryButton}
onClick={handleCreateClick}
>
<FaPlus /> Vorlage erstellen
</button>
)}
</div>
) : (
<FormGeneratorTable
data={templates as any[]}
columns={columns}
apiEndpoint="/api/automation-templates"
loading={loading}
pagination={true}
pageSize={25}
searchable={true}
filterable={true}
sortable={true}
selectable={false}
actionButtons={[
...(canUpdate ? [{
type: 'edit' as const,
onAction: handleEditClick,
title: 'Bearbeiten',
}] : []),
...(canDelete ? [{
type: 'delete' as const,
title: 'Löschen',
}] : []),
]}
onDelete={(template) => handleDelete(template.id)}
hookData={{
refetch,
handleDelete,
attributes,
}}
emptyMessage="Keine Vorlagen gefunden"
/>
)}
</div>
{/* Automation Editor */}
{showEditor && (
<AutomationEditor
mode="template"
initialData={editingTemplate}
onSave={handleEditorSave}
onCancel={handleEditorCancel}
saving={saving}
/>
)}
</div>
);
};
export default AutomationTemplatesPage;