import { Fragment, useCallback, useEffect, useState } from 'react' import { Link } from 'react-router-dom' import { LayoutGrid } from 'lucide-react' import { api, formatFastApiDetail } from '../utils/api' export default function AdminWidgetFeatureAssignmentsPage() { const [bundle, setBundle] = useState(null) const [error, setError] = useState('') const [success, setSuccess] = useState('') const [expandedId, setExpandedId] = useState(null) const [localFeatureIds, setLocalFeatureIds] = useState([]) const [saving, setSaving] = useState(false) const load = useCallback(async () => { setError('') try { const d = await api.adminGetWidgetFeatureAssignments() setBundle(d) } catch (e) { setError(formatFastApiDetail(null, e.message)) } }, []) useEffect(() => { load() }, [load]) const openRow = (w) => { setExpandedId(w.id) setSuccess('') setError('') const base = w.uses_custom_requirements ? w.feature_ids : w.catalog_feature_ids setLocalFeatureIds([...(base || [])]) } const toggleFeature = (fid) => { setLocalFeatureIds((prev) => { const s = new Set(prev) if (s.has(fid)) s.delete(fid) else s.add(fid) return [...s].sort() }) } const saveCatalog = async (widgetId) => { setSaving(true) setError('') setSuccess('') try { await api.adminPutWidgetFeatureAssignment(widgetId, { mode: 'catalog' }) setSuccess('Auf Katalog-Fallback zurückgesetzt.') setExpandedId(null) await load() } catch (e) { setError(formatFastApiDetail(null, e.message)) } finally { setSaving(false) } } const saveCustom = async (widgetId) => { setSaving(true) setError('') setSuccess('') try { await api.adminPutWidgetFeatureAssignment(widgetId, { mode: 'custom', feature_ids: localFeatureIds, }) setSuccess('Feature-Zuordnung gespeichert (AND: alle müssen erlaubt sein).') await load() } catch (e) { setError(formatFastApiDetail(null, e.message)) } finally { setSaving(false) } } if (!bundle && !error) { return (
) } return (
← Features (Admin)

Widgets × Features

Ordnet jedes Dashboard-Widget einer oder mehreren Features aus der Registry zu. Ohne Eintrag gilt der Vorgabewert aus dem Code-Katalog (requires_feature). Mit Custom müssen{' '} alle gewählten Features für den Nutzer erlaubt sein. Leere Auswahl = Widget ohne Feature-Gate.

{error && (

{error}

)} {success && (

{success}

)}
{bundle?.widgets?.map((w) => { const expanded = expandedId === w.id const summary = w.uses_custom_requirements ? `Custom: ${w.feature_ids.length ? w.feature_ids.join(', ') : '— (kein Feature)'}` : `Katalog: ${w.catalog_feature_ids.length ? w.catalog_feature_ids.join(', ') : '—'}` return ( {expanded && ( )} ) })}
Widget Modus / Anforderungen
{w.title}
{w.id}
{summary}
{w.description && (
{w.description}
)}
Features (mehrfach, AND)
{(bundle.features || []).map((f) => ( ))}
) }