/** * SandboxedPluginWidget * * Renders a plugin's dashboard widget using Block Kit. Sends a page_load * interaction with page="widget:" to the plugin's admin route. */ import { CircleNotch } from "@phosphor-icons/react"; import { BlockRenderer } from "@emdashcms/blocks"; import type { Block, BlockInteraction, BlockResponse } from "@emdashcms/blocks"; import { useCallback, useEffect, useState } from "react"; import { apiFetch, API_BASE } from "../lib/api/client.js"; interface SandboxedPluginWidgetProps { pluginId: string; widgetId: string; } export function SandboxedPluginWidget({ pluginId, widgetId }: SandboxedPluginWidgetProps) { const [blocks, setBlocks] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const sendInteraction = useCallback( async (interaction: BlockInteraction) => { try { const response = await apiFetch(`${API_BASE}/plugins/${pluginId}/admin`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(interaction), }); if (!response.ok) { setError(`Plugin error (${response.status})`); return; } const body = (await response.json()) as { data: BlockResponse }; const data = body.data; setBlocks(data.blocks); setError(null); } catch { setError("Failed to load widget"); } }, [pluginId], ); // Initial widget load useEffect(() => { setLoading(true); void sendInteraction({ type: "page_load", page: `widget:${widgetId}` }).finally(() => setLoading(false), ); }, [sendInteraction, widgetId]); const handleAction = useCallback( (interaction: BlockInteraction) => { void sendInteraction(interaction); }, [sendInteraction], ); if (loading) { return (
); } if (error) { return

{error}

; } if (blocks.length === 0) { return

No content

; } return ; }