fix(i-d): Geister-Komponenten eliminiert — AIAssistant-Seite erstellt (Agent-Auswahl + AgentChat, STATIC_COMPONENT_MAP registriert nach C3-Pattern); 5 Ghost-Contact-Detail-Tabs aus Backend-Manifesten entfernt; Production-Build mit AIAssistant-Chunk verifiziert (AIAssistant-DVb66TSo.js); tsc exit=0; ruff clean
Check Cross-Plugin Imports / check (push) Has been cancelled

This commit is contained in:
Agent Zero
2026-08-24 21:50:21 +02:00
parent 49ca4c5fb2
commit 962e0ee1f6
9 changed files with 91 additions and 22 deletions
+2
View File
@@ -102,6 +102,8 @@
**Block C ABGESCHLOSSEN** — C1C8 implementiert, Gate-C-Checks 4+5 bewiesen; Checks 13 (Playwright gegen Build, Workspace-E2E, Normal-User-E2E) erfordern E2E-Lauf → Block E.
**Block D: D1+D2 ABGESCHLOSSEN** (6d04206) — D1: alle 9 Ziel-Suites grün (auth 10/10, abac, companies 18/18, contacts 8/8, calendar 34/34, ai_proactive 38+1s, api_tokens, backend_coverage_gaps 41/41, phase_h_wiki 42/42); 3 Produktionsbugs behoben (ContactCreate-Typ-Inferenz, CalendarContract-Zugriffe ×4, SystemSettings-Schema-Drift+Migration 0142). Offen: D3 (API-/Testpfade + ARCH-051/055/056/057), D4 (Security-Triage BUG-019/020 + ARCH-027), D5 (Marathon-Triage), D6 (Legacy-Migration ARCH-059/023).
| E/I-D | Geister-Komponenten eliminiert: @/pages/AIAssistant gebaut (minimale Seite mit Agent-Auswahl + AgentChat, in STATIC_COMPONENT_MAP registriert — C3-Pattern); 5 Contact-Detail-Tabs (ContactCalendarTab/FilesTab/LinksTab/MailTab/TagsTab) aus Backend-Manifesten entfernt (Features bleiben über Haupt-Seiten erreichbar) | ✅ tsc --noEmit exit=0; Production-Build exit=0 mit AIAssistant-Chunks (AIAssistant-DVb66TSo.js 5.92 kB); ruff clean ×6 Dateien; create_app OK (560 routes); Route /ai-assistant funktioniert statt ErrorBoundary | — |
### Bekannte Vorbestände (Block D Triage)
- ~~9 Contact/Company-Test-Failures~~ ✅ GELÖST in D1-b (ContactCreate-Typ-Inferenz, 9d8da99) — Root-Cause war BUG-008-Validator-Default type='company'.
- ~~test_mail: 28 Failures 'Unknown entity type: mail_account'~~ ✅ Root-Cause in D3-d behoben (ef90d57): Mail/DMS get_entity_models-Overrides fehlten; Rest-Failures im vollen Mail-Lauf = IMAP-Netzwerk-Calls ohne Mocking (Test-Infrastruktur-Follow-up, kein Produktionsbug).
+3 -4
View File
@@ -5,7 +5,6 @@ from __future__ import annotations
from app.plugins.base import BasePlugin
from app.plugins.manifest import (
FrontendDashboardWidget,
FrontendDetailTab,
FrontendMenuItem,
FrontendPageRoute,
PluginManifest,
@@ -66,9 +65,9 @@ class CalendarPlugin(BasePlugin):
page_routes=[
FrontendPageRoute(path='/calendar', component='@/pages/Calendar', protected=True, permission='calendar:read'),
],
detail_tabs=[
FrontendDetailTab(entity_type='contact', label_key='tabs.calendar', label='Calendar', component='@/components/contact/ContactCalendarTab', icon='Calendar', order=30, permission='calendar:read'),
],
# BUG (ghost component): ContactCalendarTab does not exist in the
# frontend — tab removed until implemented (Block I-D).
detail_tabs=[],
author="LeoCRM Team",
min_app_version="1.0.0",
hooks=["calendar.before_appointment", "calendar.after_appointment"],
+3 -4
View File
@@ -4,7 +4,6 @@ from __future__ import annotations
from app.plugins.base import BasePlugin
from app.plugins.manifest import (
FrontendDetailTab,
FrontendMenuItem,
FrontendPageRoute,
PluginManifest,
@@ -45,9 +44,9 @@ class DmsPlugin(BasePlugin):
FrontendPageRoute(path='/dms', component='@/pages/Dms', protected=True, permission='dms:read'),
FrontendPageRoute(path='/dms/trash', component='@/pages/DmsTrash', protected=True, permission='dms:read'),
],
detail_tabs=[
FrontendDetailTab(entity_type='contact', label_key='tabs.files', label='Dateien', component='@/components/contact/ContactFilesTab', icon='FolderOpen', order=40, permission='dms:read'),
],
# BUG (ghost component): ContactFilesTab does not exist in the
# frontend — tab removed until implemented (Block I-D).
detail_tabs=[],
author="LeoCRM Team",
min_app_version="1.0.0",
hooks=["dms.before_upload"],
+4 -4
View File
@@ -5,7 +5,7 @@ from __future__ import annotations
from typing import Any
from app.plugins.base import BasePlugin
from app.plugins.manifest import FrontendDetailTab, PluginManifest, PluginRouteDef
from app.plugins.manifest import PluginManifest, PluginRouteDef
class EntityLinksPlugin(BasePlugin):
@@ -42,9 +42,9 @@ class EntityLinksPlugin(BasePlugin):
"entity_links:delete",
],
is_core=True,
detail_tabs=[
FrontendDetailTab(entity_type='contact', label_key='tabs.links', label='Verknüpfungen', component='@/components/contact/ContactLinksTab', icon='Link', order=60, permission='entity_links:read'),
],
# BUG (ghost component): ContactLinksTab does not exist in the
# frontend — tab removed until implemented (Block I-D).
detail_tabs=[],
author="LeoCRM Team",
min_app_version="1.0.0",
+3 -4
View File
@@ -9,7 +9,6 @@ from typing import Any
from app.plugins.base import BasePlugin
from app.plugins.manifest import (
FrontendDetailTab,
FrontendMenuItem,
FrontendPageRoute,
FrontendSettingsPage,
@@ -150,9 +149,9 @@ class MailPlugin(BasePlugin):
settings_pages=[
FrontendSettingsPage(path='mail', label_key='settings.mail', label='Mail', component='@/pages/MailSettings', icon='Mail', order=50),
],
detail_tabs=[
FrontendDetailTab(entity_type='contact', label_key='tabs.email', label='E-Mails', component='@/components/contact/ContactMailTab', icon='Mail', order=20, permission='mail:read'),
],
# BUG (ghost component): ContactMailTab does not exist in the
# frontend — tab removed until implemented (Block I-D).
detail_tabs=[],
author="LeoCRM Team",
min_app_version="1.0.0",
hooks=["mail.before_send", "mail.after_send"],
+4 -4
View File
@@ -3,7 +3,7 @@
from __future__ import annotations
from app.plugins.base import BasePlugin
from app.plugins.manifest import FrontendDetailTab, PluginManifest, PluginRouteDef
from app.plugins.manifest import PluginManifest, PluginRouteDef
class TagsPlugin(BasePlugin):
@@ -31,9 +31,9 @@ class TagsPlugin(BasePlugin):
"tags:admin",
],
is_core=True,
detail_tabs=[
FrontendDetailTab(entity_type='contact', label_key='tabs.tags', label='Tags', component='@/components/contact/ContactTagsTab', icon='Tag', order=50, permission='tags:read'),
],
# BUG (ghost component): ContactTagsTab does not exist in the
# frontend — tab removed until implemented (Block I-D).
detail_tabs=[],
author="LeoCRM Team",
min_app_version="1.0.0",
+6
View File
@@ -1197,3 +1197,9 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te
### G3-a Nachtrag (Block I-H): dump.rdb
- ✅ Erledigt 2026-08-24 (Block E/I-H): dump.rdb aus Repo-Root entfernt (war git-ignored, kein Leak); Root-Cause bewiesen — lokaler Test-Redis hatte workdir=Repo-Root; workdir jetzt auf /tmp gesetzt. Production ist nicht betroffen (docker-compose nutzt redisdata:/data Volume).
## Geister-Komponenten — STATUS
✅ Gefixt 2026-08-24 (Block E/I-D):
- @/pages/AIAssistant: Minimale Seite erstellt (frontend/src/pages/AIAssistant.tsx, Agent-Auswahl + AgentChat), in STATIC_COMPONENT_MAP registriert (C3-Pattern); Production-Build erzeugt eigenen Chunk AIAssistant-DVb66TSo.js (5.92 kB) — Route /ai-assistant funktioniert jetzt statt ErrorBoundary.
- 5 Contact-Detail-Tabs (ContactCalendarTab/FilesTab/LinksTab/MailTab/TagsTab): Aus den Backend-Manifesten von calendar/dms/entity_links/mail/tags entfernt (detail_tabs=[] mit Erklärungskommentar). Die Features bleiben über die Haupt-Seiten erreichbar; Tabs können bei Bedarf als Feature implementiert werden.
Verifikation: tsc --noEmit exit=0, Production-Build exit=0 mit AIAssistant-Chunk, ruff clean, create_app OK (560 routes).
@@ -118,8 +118,8 @@ function normalizeModule(m: ComponentModule): { default: React.ComponentType<any
const STATIC_COMPONENT_MAP: Record<string, LazyComponentFactory> = {
// Pages
'@/pages/AgentDashboard': () => import('@/pages/AgentDashboard').then(normalizeModule),
// NOTE: @/pages/AIAssistant is declared in the backend manifest but does
// not exist as a frontend page — ghost component, see PROGRESS.md.
// BUG (ghost page) fixed in Block I-D: page now exists.
'@/pages/AIAssistant': () => import('@/pages/AIAssistant').then(normalizeModule),
'@/pages/AISettings': () => import('@/pages/AISettings').then(normalizeModule),
'@/pages/AutomationDashboard': () => import('@/pages/AutomationDashboard').then(normalizeModule),
'@/pages/AutomationSettings': () => import('@/pages/AutomationSettings').then(normalizeModule),
+64
View File
@@ -0,0 +1,64 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useAgents } from '@/api/automation';
import { Card } from '@/components/ui/Card';
import { AgentChat } from '@/components/agents/AgentChat';
/**
* AI Assistant page (BUG: ghost page fixed — was declared in the backend
* manifest but did not exist, see PROGRESS.md "Ghost components").
*
* Minimal implementation: pick an agent, chat with it. Full agent management
* lives in the Agent Dashboard.
*/
export default function AIAssistant() {
const { t } = useTranslation();
const { data: agents, isLoading } = useAgents();
const [selectedAgentId, setSelectedAgentId] = useState('');
const selectedAgent = agents?.find((a) => a.id === selectedAgentId);
return (
<div className="p-6 space-y-4" aria-label={t('ai.assistant', 'AI Assistant')}>
<h1 className="text-xl font-semibold text-secondary-900">
{t('ai.assistant', 'AI Assistant')}
</h1>
<Card className="p-4">
<label
htmlFor="assistant-agent-select"
className="block text-sm font-medium text-secondary-700 mb-1"
>
{t('agent.targetAgent', 'Target agent')}
</label>
<select
id="assistant-agent-select"
value={selectedAgentId}
onChange={(e) => setSelectedAgentId(e.target.value)}
className="w-full rounded-lg border border-secondary-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500"
>
<option value="">{t('agent.selectTargetAgent', 'Select an agent…')}</option>
{(agents ?? []).map((agent) => (
<option key={agent.id} value={agent.id}>
{agent.name}
</option>
))}
</select>
{!isLoading && (agents ?? []).length === 0 && (
<p className="mt-2 text-sm text-secondary-500">
{t(
'agent.noAgentsHint',
'No agents yet — create one in the Agent Dashboard.'
)}
</p>
)}
</Card>
{selectedAgent && (
<Card className="p-4">
<AgentChat agentId={selectedAgent.id} agentName={selectedAgent.name} />
</Card>
)}
</div>
);
}