fix(FN-3161): migrate roadmap dashboard surface to bundled plugin

- Move RoadmapsView UI, hooks, and tests from dashboard app into fusion-plugin-roadmap
- Replace the built-in roadmaps route/nav wiring with plugin view registration and host rendering
- Add bundled plugin view registration bootstrap in dashboard startup
- Update roadmap plugin build/test config and add FN-3161 removal changeset

Fusion-Task-Id: FN-3161
This commit is contained in:
Fusion
2026-05-08 16:51:04 -07:00
committed by gsxdsm
parent 7cb9ab78fe
commit d42d8ee2f2
33 changed files with 295 additions and 149 deletions

View File

@@ -1,5 +1,6 @@
import { PluginDashboardViewHost as RegistryPluginDashboardViewHost } from "./pluginViewRegistry";
import type { PluginDashboardViewContext, PluginTaskView } from "./pluginViewRegistry";
import type { PluginTaskView } from "./pluginViewRegistry";
import type { PluginDashboardViewContext } from "./types";
export function PluginDashboardViewHost({ taskView, context }: { taskView: PluginTaskView; context?: PluginDashboardViewContext }) {
return <RegistryPluginDashboardViewHost viewId={taskView} context={context} />;

View File

@@ -1,20 +1,11 @@
import type { Task, TaskDetail, WorkflowStep } from "@fusion/core";
import type { PluginDashboardViewContext } from "./types";
import { AlertTriangle } from "lucide-react";
import { lazy, Suspense, type LazyExoticComponent, type ReactElement, type ReactNode } from "react";
import { ErrorBoundary } from "../components/ErrorBoundary";
import type { DetailTaskTab } from "../hooks/useModalManager";
import "./pluginViewRegistry.css";
export type PluginTaskView = `plugin:${string}:${string}`;
export interface PluginDashboardViewContext {
projectId?: string;
tasks: Task[];
workflowSteps: WorkflowStep[];
openTaskDetail: (task: Task | TaskDetail, initialTab?: DetailTaskTab) => void;
renderTaskCard?: (task: Task | TaskDetail) => ReactNode;
}
type PluginViewComponent = LazyExoticComponent<({ context }: { context?: PluginDashboardViewContext }) => ReactElement>;
const registry = new Map<string, PluginViewComponent>();
@@ -46,25 +37,15 @@ export function getPluginViewComponent(pluginId: string, viewId: string): Plugin
return registry.get(getPluginViewId(pluginId, viewId)) ?? null;
}
export function isPluginViewRegistered(pluginId: string, viewId: string): boolean {
return registry.has(getPluginViewId(pluginId, viewId));
}
/** Test helper for clearing global registry state. */
export function __test_clearPluginViewRegistry(): void {
registry.clear();
registerBundledPluginViews();
}
function registerBundledPluginViews(): void {
registerPluginView(
"fusion-plugin-dependency-graph",
"graph",
lazy(async () => {
const mod = await import("@fusion-plugin-examples/dependency-graph/dashboard-view");
return { default: mod.DependencyGraphDashboardView };
}),
);
}
registerBundledPluginViews();
function PluginViewUnavailable({ viewId }: { viewId: string }): ReactNode {
return (
<section className="card plugin-dashboard-view-missing" data-testid="plugin-view-unavailable">

View File

@@ -0,0 +1,27 @@
import { lazy } from "react";
import { registerPluginView } from "./pluginViewRegistry";
let registered = false;
export function registerBundledPluginViews(): void {
if (registered) return;
registered = true;
registerPluginView(
"fusion-plugin-dependency-graph",
"graph",
lazy(async () => {
const mod = await import("@fusion-plugin-examples/dependency-graph/dashboard-view");
return { default: mod.DependencyGraphDashboardView };
}),
);
registerPluginView(
"roadmap-planner",
"roadmaps",
lazy(async () => {
const mod = await import("@fusion-plugin-examples/roadmap/dashboard-view");
return { default: mod.RoadmapDashboardView };
}),
);
}

View File

@@ -14,6 +14,8 @@ import type { Task, TaskDetail, WorkflowStep } from "@fusion/core";
/** Tab identifiers for the task detail modal. Mirrors the dashboard's local enum. */
export type DetailTaskTab = "definition" | "logs" | "changes" | "comments" | "model" | "workflow";
export type PluginToastType = "success" | "error" | "warning" | "info";
/** Runtime context passed to a plugin dashboard view component. */
export interface PluginDashboardViewContext {
projectId?: string;
@@ -21,6 +23,7 @@ export interface PluginDashboardViewContext {
workflowSteps: WorkflowStep[];
openTaskDetail: (task: Task | TaskDetail, initialTab?: DetailTaskTab) => void;
renderTaskCard?: (task: Task | TaskDetail) => ReactNode;
addToast?: (message: string, type?: PluginToastType) => void;
}
/** Composite view ID format: `plugin:{pluginId}:{viewId}`. */