feat(frontend): Q1+Q2 — Plugin-Routen kommen aus Manifesten, statische Duplikate entfernt
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
Phase Q1 (Seiten-Routen) + Q2 (Settings-Routen): Die Plugin-Manifeste sind ab sofort die einzige Quelle fuer Plugin-Frontend-Routen. routes/index.tsx enthaelt nur noch Core-Routen + die StartLayout-Hub-Baeume (/agents, /automation, /logs, /help — verschachtelte Sub-Navigation). - PluginRouteRenderer: variante 'settings' rendert settings_pages mit bare Sub-Segments (Descendant-Matching im /settings-Subtree); Variante 'pages' (Default) behaelt absolute Pfade. Getrennte Entry-Listen verhindern Pfad-Kollisionen (settings 'mail' vs. page '/mail'). - Entfernt: 14 statische AppShell-Plugin-Routen + 9 statische Settings-Routen + 20 tote Lazy-Imports (search, calendar+kanban, dms, dms/trash, mail, mail/settings, reports, tasks, communication, workflows, import-export, tags, wiki, roles, users, groups, notifications, ai, ai-proactive, automation, documents). - Manifeste ergaenzt: Calendar +/calendar/kanban, Tags +/tags (+ Menue-Item, Route war sonst unerreichbar), Automation: /workflows-Permission auf workflows:read (Paritaet zur ersetzten statischen Route). - Automation: tote flache Manifest-Eintraeger fuer /agents + /automation entfernt (StartLayout-Hub-Baeume gewinnen diese Pfade immer — die Eintraege matchten nie). - Komponenten-Map regeneriert (37 Eintraege, CalendarKanban + Tags neu). Verifikation: tsc exit 0; production build exit 0; Vitest Dashboard + MiniAppWindow + pluginStore 35/35; Backend-Regressionen Route-Order, M5-MiniApps, N4-Scope, N3-Filtering 49/49; compileall sauber; Cross-Plugin-Checker 497 Dateien / 0 verbotene Imports; ruff clean.
This commit is contained in:
@@ -22,7 +22,19 @@ import { PluginPage } from './PluginLoader';
|
||||
* route group in routes/index.tsx:
|
||||
* { path: '*', element: <PluginRouteRenderer /> }
|
||||
*/
|
||||
export function PluginRouteRenderer() {
|
||||
/**
|
||||
* variant:
|
||||
* - 'pages' (default): renders manifest page_routes. Mounted at the
|
||||
* AppShell catch-all — descendant <Routes> match the full pathname,
|
||||
* so entries keep their absolute paths (e.g. '/mail').
|
||||
* - 'settings': renders manifest settings_pages. Mounted INSIDE the
|
||||
* /settings subtree catch-all — descendant matching works on the
|
||||
* REMAINING pathname ('/mail' after the '/settings' prefix is stripped),
|
||||
* so entries keep the bare settings sub-segment ('mail'). Mixing both
|
||||
* entry kinds in one list would create path collisions (e.g. settings
|
||||
* 'mail' vs. page '/mail' rendering the inbox).
|
||||
*/
|
||||
export function PluginRouteRenderer({ variant = 'pages' }: { variant?: 'pages' | 'settings' } = {}) {
|
||||
const location = useLocation();
|
||||
const manifests = usePluginStore((s) => s.manifests);
|
||||
const loaded = usePluginStore((s) => s.loaded);
|
||||
@@ -30,16 +42,33 @@ export function PluginRouteRenderer() {
|
||||
// Flatten manifests → route entries with their owning plugin attached.
|
||||
// Attaching the name here (instead of a later find()) stays correct even
|
||||
// if two plugins declare overlapping paths.
|
||||
const entries = useMemo(
|
||||
() =>
|
||||
manifests.flatMap((m) =>
|
||||
m.page_routes.map((r) => ({
|
||||
route: r,
|
||||
pluginName: m.display_name || m.name || r.path,
|
||||
type RendererEntry = {
|
||||
route: { path: string; component: string; permission?: string };
|
||||
pluginName: string;
|
||||
};
|
||||
|
||||
const entries = useMemo<RendererEntry[]>(() => {
|
||||
if (variant === 'settings') {
|
||||
return manifests.flatMap((m) =>
|
||||
(Array.isArray(m.settings_pages) ? m.settings_pages : []).map((p) => ({
|
||||
route: {
|
||||
// bare sub-segment ('mail') — descendant matching strips the
|
||||
// matched '/settings' prefix; tolerate 'settings/mail' too.
|
||||
path: p.path.replace(/^\/?settings\//, ''),
|
||||
component: p.component,
|
||||
permission: p.permission || undefined,
|
||||
},
|
||||
pluginName: m.display_name || m.name || p.path,
|
||||
}))
|
||||
),
|
||||
[manifests]
|
||||
);
|
||||
);
|
||||
}
|
||||
return manifests.flatMap((m) =>
|
||||
m.page_routes.map((r) => ({
|
||||
route: r,
|
||||
pluginName: m.display_name || m.name || r.path,
|
||||
}))
|
||||
);
|
||||
}, [manifests, variant]);
|
||||
|
||||
// If manifests haven't loaded yet, show a spinner (not null/blank)
|
||||
if (!loaded) {
|
||||
|
||||
Reference in New Issue
Block a user