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 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). **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) ### 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'. - ~~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). - ~~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.base import BasePlugin
from app.plugins.manifest import ( from app.plugins.manifest import (
FrontendDashboardWidget, FrontendDashboardWidget,
FrontendDetailTab,
FrontendMenuItem, FrontendMenuItem,
FrontendPageRoute, FrontendPageRoute,
PluginManifest, PluginManifest,
@@ -66,9 +65,9 @@ class CalendarPlugin(BasePlugin):
page_routes=[ page_routes=[
FrontendPageRoute(path='/calendar', component='@/pages/Calendar', protected=True, permission='calendar:read'), FrontendPageRoute(path='/calendar', component='@/pages/Calendar', protected=True, permission='calendar:read'),
], ],
detail_tabs=[ # BUG (ghost component): ContactCalendarTab does not exist in the
FrontendDetailTab(entity_type='contact', label_key='tabs.calendar', label='Calendar', component='@/components/contact/ContactCalendarTab', icon='Calendar', order=30, permission='calendar:read'), # frontend — tab removed until implemented (Block I-D).
], detail_tabs=[],
author="LeoCRM Team", author="LeoCRM Team",
min_app_version="1.0.0", min_app_version="1.0.0",
hooks=["calendar.before_appointment", "calendar.after_appointment"], 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.base import BasePlugin
from app.plugins.manifest import ( from app.plugins.manifest import (
FrontendDetailTab,
FrontendMenuItem, FrontendMenuItem,
FrontendPageRoute, FrontendPageRoute,
PluginManifest, PluginManifest,
@@ -45,9 +44,9 @@ class DmsPlugin(BasePlugin):
FrontendPageRoute(path='/dms', component='@/pages/Dms', protected=True, permission='dms:read'), FrontendPageRoute(path='/dms', component='@/pages/Dms', protected=True, permission='dms:read'),
FrontendPageRoute(path='/dms/trash', component='@/pages/DmsTrash', protected=True, permission='dms:read'), FrontendPageRoute(path='/dms/trash', component='@/pages/DmsTrash', protected=True, permission='dms:read'),
], ],
detail_tabs=[ # BUG (ghost component): ContactFilesTab does not exist in the
FrontendDetailTab(entity_type='contact', label_key='tabs.files', label='Dateien', component='@/components/contact/ContactFilesTab', icon='FolderOpen', order=40, permission='dms:read'), # frontend — tab removed until implemented (Block I-D).
], detail_tabs=[],
author="LeoCRM Team", author="LeoCRM Team",
min_app_version="1.0.0", min_app_version="1.0.0",
hooks=["dms.before_upload"], hooks=["dms.before_upload"],
+4 -4
View File
@@ -5,7 +5,7 @@ from __future__ import annotations
from typing import Any from typing import Any
from app.plugins.base import BasePlugin from app.plugins.base import BasePlugin
from app.plugins.manifest import FrontendDetailTab, PluginManifest, PluginRouteDef from app.plugins.manifest import PluginManifest, PluginRouteDef
class EntityLinksPlugin(BasePlugin): class EntityLinksPlugin(BasePlugin):
@@ -42,9 +42,9 @@ class EntityLinksPlugin(BasePlugin):
"entity_links:delete", "entity_links:delete",
], ],
is_core=True, is_core=True,
detail_tabs=[ # BUG (ghost component): ContactLinksTab does not exist in the
FrontendDetailTab(entity_type='contact', label_key='tabs.links', label='Verknüpfungen', component='@/components/contact/ContactLinksTab', icon='Link', order=60, permission='entity_links:read'), # frontend — tab removed until implemented (Block I-D).
], detail_tabs=[],
author="LeoCRM Team", author="LeoCRM Team",
min_app_version="1.0.0", 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.base import BasePlugin
from app.plugins.manifest import ( from app.plugins.manifest import (
FrontendDetailTab,
FrontendMenuItem, FrontendMenuItem,
FrontendPageRoute, FrontendPageRoute,
FrontendSettingsPage, FrontendSettingsPage,
@@ -150,9 +149,9 @@ class MailPlugin(BasePlugin):
settings_pages=[ settings_pages=[
FrontendSettingsPage(path='mail', label_key='settings.mail', label='Mail', component='@/pages/MailSettings', icon='Mail', order=50), FrontendSettingsPage(path='mail', label_key='settings.mail', label='Mail', component='@/pages/MailSettings', icon='Mail', order=50),
], ],
detail_tabs=[ # BUG (ghost component): ContactMailTab does not exist in the
FrontendDetailTab(entity_type='contact', label_key='tabs.email', label='E-Mails', component='@/components/contact/ContactMailTab', icon='Mail', order=20, permission='mail:read'), # frontend — tab removed until implemented (Block I-D).
], detail_tabs=[],
author="LeoCRM Team", author="LeoCRM Team",
min_app_version="1.0.0", min_app_version="1.0.0",
hooks=["mail.before_send", "mail.after_send"], hooks=["mail.before_send", "mail.after_send"],
+4 -4
View File
@@ -3,7 +3,7 @@
from __future__ import annotations from __future__ import annotations
from app.plugins.base import BasePlugin from app.plugins.base import BasePlugin
from app.plugins.manifest import FrontendDetailTab, PluginManifest, PluginRouteDef from app.plugins.manifest import PluginManifest, PluginRouteDef
class TagsPlugin(BasePlugin): class TagsPlugin(BasePlugin):
@@ -31,9 +31,9 @@ class TagsPlugin(BasePlugin):
"tags:admin", "tags:admin",
], ],
is_core=True, is_core=True,
detail_tabs=[ # BUG (ghost component): ContactTagsTab does not exist in the
FrontendDetailTab(entity_type='contact', label_key='tabs.tags', label='Tags', component='@/components/contact/ContactTagsTab', icon='Tag', order=50, permission='tags:read'), # frontend — tab removed until implemented (Block I-D).
], detail_tabs=[],
author="LeoCRM Team", author="LeoCRM Team",
min_app_version="1.0.0", 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 ### 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). - ✅ 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> = { const STATIC_COMPONENT_MAP: Record<string, LazyComponentFactory> = {
// Pages // Pages
'@/pages/AgentDashboard': () => import('@/pages/AgentDashboard').then(normalizeModule), '@/pages/AgentDashboard': () => import('@/pages/AgentDashboard').then(normalizeModule),
// NOTE: @/pages/AIAssistant is declared in the backend manifest but does // BUG (ghost page) fixed in Block I-D: page now exists.
// not exist as a frontend page — ghost component, see PROGRESS.md. '@/pages/AIAssistant': () => import('@/pages/AIAssistant').then(normalizeModule),
'@/pages/AISettings': () => import('@/pages/AISettings').then(normalizeModule), '@/pages/AISettings': () => import('@/pages/AISettings').then(normalizeModule),
'@/pages/AutomationDashboard': () => import('@/pages/AutomationDashboard').then(normalizeModule), '@/pages/AutomationDashboard': () => import('@/pages/AutomationDashboard').then(normalizeModule),
'@/pages/AutomationSettings': () => import('@/pages/AutomationSettings').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>
);
}