import type { ReactNode } from "react"; import { ErrorBoundary } from "./ErrorBoundary"; import { usePluginUiSlots } from "../hooks/usePluginUiSlots"; interface PluginSlotProps { /** The slot identifier to render (e.g., "task-detail-tab", "header-action") */ slotId: string; /** Optional project ID for multi-project slot scoping */ projectId?: string; } /** * Renders placeholder divs for all plugin UI slots matching the given slotId. * * This component is non-critical: it silently fails (returns null) for loading * errors, or when no plugins are registered for the slot. Each rendered slot * is wrapped in an ErrorBoundary to isolate plugin rendering failures from the * parent dashboard UI. * * Future iterations will replace placeholder divs with dynamically loaded * components via the plugin's componentPath. */ export function PluginSlot({ slotId, projectId }: PluginSlotProps): ReactNode { const { getSlotsForId, loading, error } = usePluginUiSlots(projectId); // Non-critical failure — no visible UI when loading, errored, or no matching slots if (loading || error || !slotId) { return null; } const matchingEntries = getSlotsForId(slotId); if (matchingEntries.length === 0) { return null; } return ( <> {matchingEntries.map((entry) => (
))} ); }