import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router-dom'; import type { PluginView } from '@dorfteich/shared'; import { apiGet } from '../lib/api'; import { PluginFrame } from '../plugins/PluginFrame'; /** * Site-Admin preview of one installed plugin (issue #73): mounts each of the * plugin's extension points in a real sandbox frame, with the declared * permissions listed next to it. Doubles as the surface the plugin security * e2e pack drives — the sandbox here is byte-identical to what pages embed. */ export function PluginPreviewPage(): React.JSX.Element { const { t } = useTranslation('plugins'); const { pluginId } = useParams<{ pluginId: string }>(); const [plugins, setPlugins] = useState(null); const [error, setError] = useState(false); useEffect(() => { let cancelled = false; apiGet('/admin/plugins') .then((list) => { if (!cancelled) setPlugins(list); }) .catch(() => { if (!cancelled) setError(true); }); return () => { cancelled = true; }; }, []); const plugin = useMemo( () => plugins?.find((entry) => entry.id === pluginId) ?? null, [plugins, pluginId], ); if (error) return

{t('preview.loadFailed')}

; if (!plugins) return

{t('preview.loading')}

; if (!plugin) return

{t('preview.notFound')}

; return (

{t('preview.title', { name: plugin.name })}{' '} v{plugin.version}

{t('preview.permissions')}

{plugin.permissions.length === 0 ? (

{t('preview.noPermissions')}

) : (
    {plugin.permissions.map((permission) => (
  • {permission}
  • ))}
)}
{plugin.extensionPoints.map((point) => (

{t('preview.surface')} {point.id} ({point.type})

))} {plugin.extensionPoints.length === 0 ?

{t('preview.noSurfaces')}

: null}
); }