From aa20aba2e7e7b92fc19361bc7d4547c193fec044 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Mon, 17 Aug 2026 12:11:14 +0200 Subject: [PATCH] feat(automation): own automation page with tree sidebar, separate from agents --- frontend/src/pages/Automation.tsx | 101 ++++++++++++++++++ frontend/src/pages/StartPage.tsx | 2 +- .../pages/automation/AutomationOverview.tsx | 28 +++++ .../automation/AutomationPlaceholder.tsx | 12 +++ frontend/src/routes/index.tsx | 16 +++ 5 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 frontend/src/pages/Automation.tsx create mode 100644 frontend/src/pages/automation/AutomationOverview.tsx create mode 100644 frontend/src/pages/automation/AutomationPlaceholder.tsx diff --git a/frontend/src/pages/Automation.tsx b/frontend/src/pages/Automation.tsx new file mode 100644 index 0000000..b543c74 --- /dev/null +++ b/frontend/src/pages/Automation.tsx @@ -0,0 +1,101 @@ +import React, { useState } from 'react'; +import { NavLink, Outlet } from 'react-router-dom'; +import { useUIStore } from '@/store/uiStore'; +import { ChevronRight, ChevronDown, Zap, Play, History, GitBranch, Settings2, Activity, Clock } from 'lucide-react'; + +interface AutomationNode { + title: string; + path?: string; + icon?: React.ReactNode; + children?: AutomationNode[]; +} + +const AUTOMATION_TREE: AutomationNode[] = [ + { + title: 'Automation', + icon: , + children: [ + { title: 'Übersicht', path: '/automation/overview' }, + { title: 'Ausführungen', path: '/automation/runs' }, + { title: 'Versionen', path: '/automation/versions' }, + ], + }, + { + title: 'Einstellungen', + icon: , + children: [ + { title: 'Trigger', path: '/automation/triggers' }, + { title: 'Aktionen', path: '/automation/actions' }, + { title: 'Bedingungen', path: '/automation/conditions' }, + ], + }, +]; + +function TreeItem({ node, depth }: { node: AutomationNode; depth: number }) { + const [expanded, setExpanded] = useState(depth < 1); + const hasChildren = node.children && node.children.length > 0; + const paddingLeft = depth * 16 + 12; + + if (hasChildren) { + return ( +
+ + {expanded && ( +
+ {node.children!.map((child) => ( + + ))} +
+ )} +
+ ); + } + + return ( + + `flex items-center gap-2 px-3 py-2 rounded-lg text-sm transition-colors min-h-touch ${ + isActive + ? 'bg-primary-100 text-primary-700 font-medium' + : 'text-secondary-600 hover:bg-secondary-100 hover:text-secondary-900' + }` + } + style={{ paddingLeft: paddingLeft + 20 }} + > + {node.icon} + {node.title} + + ); +} + +export function AutomationPage() { + const sidebarOpen = useUIStore((state) => state.sidebarOpen); + + return ( +
+ +
+ +
+
+ ); +} diff --git a/frontend/src/pages/StartPage.tsx b/frontend/src/pages/StartPage.tsx index 7d28da2..ee49bfb 100644 --- a/frontend/src/pages/StartPage.tsx +++ b/frontend/src/pages/StartPage.tsx @@ -41,7 +41,7 @@ export function StartPage() { { id: 'workspaces', label: 'Workspaces', icon: , active: true }, { id: 'settings', label: 'Einstellungen', icon: , onClick: () => navigate('/settings') }, { id: 'agents', label: 'Agenten', icon: , onClick: () => navigate('/agents/overview') }, - { id: 'automation', label: 'Automation', icon: , onClick: () => navigate('/agents/automation') }, + { id: 'automation', label: 'Automation', icon: , onClick: () => navigate('/automation/overview') }, { id: 'help', label: 'Hilfe', icon: , onClick: () => navigate('/help/welcome') }, ]; diff --git a/frontend/src/pages/automation/AutomationOverview.tsx b/frontend/src/pages/automation/AutomationOverview.tsx new file mode 100644 index 0000000..5c12de8 --- /dev/null +++ b/frontend/src/pages/automation/AutomationOverview.tsx @@ -0,0 +1,28 @@ +import React from 'react'; + +export function AutomationOverviewPage() { + return ( +
+

Automation Übersicht

+

+ Erstellen und verwalten Sie automatisierte Workflows. Definieren Sie Trigger, Bedingungen und Aktionen. +

+
+
+
+ +
+

Automations

+

Workflows erstellen und verwalten

+
+
+
+ +
+

Einstellungen

+

Trigger, Aktionen und Bedingungen konfigurieren

+
+
+
+ ); +} diff --git a/frontend/src/pages/automation/AutomationPlaceholder.tsx b/frontend/src/pages/automation/AutomationPlaceholder.tsx new file mode 100644 index 0000000..9be3fc6 --- /dev/null +++ b/frontend/src/pages/automation/AutomationPlaceholder.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export function AutomationPlaceholderPage() { + return ( +
+

Automation

+

+ Diese Seite wird gerade erstellt. Wählen Sie ein Thema aus dem Menü links. +

+
+ ); +} diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index 7ccf25c..1f533fa 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -80,6 +80,9 @@ const HelpPlaceholderPage = React.lazy(() => import('@/pages/help/HelpPlaceholde const AgentsPage = React.lazy(() => import('@/pages/Agents').then(m => ({ default: m.AgentsPage }))); const AgentsOverviewPage = React.lazy(() => import('@/pages/agents/AgentsOverview').then(m => ({ default: m.AgentsOverviewPage }))); const AgentsPlaceholderPage = React.lazy(() => import('@/pages/agents/AgentsPlaceholder').then(m => ({ default: m.AgentsPlaceholderPage }))); +const AutomationPage = React.lazy(() => import('@/pages/Automation').then(m => ({ default: m.AutomationPage }))); +const AutomationOverviewPage = React.lazy(() => import('@/pages/automation/AutomationOverview').then(m => ({ default: m.AutomationOverviewPage }))); +const AutomationPlaceholderPage = React.lazy(() => import('@/pages/automation/AutomationPlaceholder').then(m => ({ default: m.AutomationPlaceholderPage }))); const ApiDocsPage = React.lazy(() => import('@/pages/ApiDocs').then(m => ({ default: m.ApiDocsPage }))); /** Centered spinner fallback for lazy-loaded routes */ @@ -174,6 +177,19 @@ const router = createBrowserRouter([ { path: '*', element: withSuspense() }, ], }, + { + path: '/automation', + element: withSuspense(), + children: [ + { path: 'overview', element: withSuspense() }, + { path: 'runs', element: withSuspense() }, + { path: 'versions', element: withSuspense() }, + { path: 'triggers', element: withSuspense() }, + { path: 'actions', element: withSuspense() }, + { path: 'conditions', element: withSuspense() }, + { path: '*', element: withSuspense() }, + ], + }, { path: '/settings', element: {withSuspense()},