diff --git a/PROGRESS.md b/PROGRESS.md index 299d28e..cc69a2e 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -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). diff --git a/app/plugins/builtins/calendar/plugin.py b/app/plugins/builtins/calendar/plugin.py index 2b869a2..74006d9 100644 --- a/app/plugins/builtins/calendar/plugin.py +++ b/app/plugins/builtins/calendar/plugin.py @@ -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"], diff --git a/app/plugins/builtins/dms/plugin.py b/app/plugins/builtins/dms/plugin.py index 58b6f21..3326efc 100644 --- a/app/plugins/builtins/dms/plugin.py +++ b/app/plugins/builtins/dms/plugin.py @@ -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"], diff --git a/app/plugins/builtins/entity_links/plugin.py b/app/plugins/builtins/entity_links/plugin.py index 3ba55b2..7871b11 100644 --- a/app/plugins/builtins/entity_links/plugin.py +++ b/app/plugins/builtins/entity_links/plugin.py @@ -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", diff --git a/app/plugins/builtins/mail/plugin.py b/app/plugins/builtins/mail/plugin.py index bdc1057..7a7a818 100644 --- a/app/plugins/builtins/mail/plugin.py +++ b/app/plugins/builtins/mail/plugin.py @@ -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"], diff --git a/app/plugins/builtins/tags/plugin.py b/app/plugins/builtins/tags/plugin.py index e9cfbc5..8fdebf9 100644 --- a/app/plugins/builtins/tags/plugin.py +++ b/app/plugins/builtins/tags/plugin.py @@ -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", diff --git a/docs/test-bugs.md b/docs/test-bugs.md index f6accb8..8769cdc 100644 --- a/docs/test-bugs.md +++ b/docs/test-bugs.md @@ -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). diff --git a/frontend/src/components/plugins/PluginLoader.tsx b/frontend/src/components/plugins/PluginLoader.tsx index 46c84fd..dab0f2f 100644 --- a/frontend/src/components/plugins/PluginLoader.tsx +++ b/frontend/src/components/plugins/PluginLoader.tsx @@ -118,8 +118,8 @@ function normalizeModule(m: ComponentModule): { default: React.ComponentType = { // 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), diff --git a/frontend/src/pages/AIAssistant.tsx b/frontend/src/pages/AIAssistant.tsx new file mode 100644 index 0000000..ca6ee26 --- /dev/null +++ b/frontend/src/pages/AIAssistant.tsx @@ -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 ( +
+

+ {t('ai.assistant', 'AI Assistant')} +

+ + + + + {!isLoading && (agents ?? []).length === 0 && ( +

+ {t( + 'agent.noAgentsHint', + 'No agents yet — create one in the Agent Dashboard.' + )} +

+ )} +
+ + {selectedAgent && ( + + + + )} +
+ ); +}