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
Check Cross-Plugin Imports / check (push) Has been cancelled
This commit is contained in:
@@ -102,6 +102,8 @@
|
||||
**Block C ABGESCHLOSSEN** — C1–C8 implementiert, Gate-C-Checks 4+5 bewiesen; Checks 1–3 (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).
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user