/* admin-builder.jsx — 3-pane drag/click widget editor (Baukasten) */ let WID_SEQ = 100; function makeWidget(type) { WID_SEQ += 1; return { id: 'w_' + WID_SEQ, type, props: defaultProps(type), meta: { widgetId: 'WG-' + String(WID_SEQ).padStart(4, '0'), pageId: 'PG-001', language: 'DE', version: 1, createdBy: 'M. Krause', updatedAt: 'gerade', trackingKey: 'trk_' + type + '_' + WID_SEQ, }, }; } function defaultProps(type) { switch (type) { case 'text': return { headline: 'Überschrift', body: 'Kurzer, sachlicher Textblock.' }; case 'hero': return { headline: 'Bereit für deinen Dienststart.', cta: 'Vorbereitung starten' }; case 'hinweis': return { tone: 'info', text: 'Kein offizielles Bundeswehr-Angebot.' }; case 'faq': return { items: 3 }; case 'buttons': return { count: 2, primary: 'Starten' }; case 'checkliste': return { items: 4, title: 'Dienststart-Checkliste' }; case 'download': return { file: 'packliste.pdf', label: 'Packliste (PDF)' }; case 'social': return { network: 'Instagram', handle: '@rekruten.schmiede' }; case 'kurskarte': return { title: 'Basisfitness', area: 'Fitness' }; case 'kalender': return { weeks: 4 }; case 'shopteaser': return { product: 'Alpha-Paket', price: '49,00 €' }; case 'video': return { provider: 'YouTube', ratio: '16:9' }; default: return {}; } } const INITIAL_PAGE = () => ({ title: 'Startseite', sections: [ { id: 's1', name: 'Hero', widgets: [makeWidget('hero')] }, { id: 's2', name: 'Schnellzugriff', widgets: [makeWidget('buttons'), makeWidget('checkliste')] }, { id: 's3', name: 'Info', widgets: [makeWidget('text'), makeWidget('hinweis')] }, ], }); function WidgetPreview({ w }) { switch (w.type) { case 'hero': return (
{w.props.headline}{w.props.cta}
Bild
); case 'text': return (
{w.props.headline}
); case 'hinweis': return (
{w.props.text}
); case 'faq': return (
{Array.from({ length: w.props.items }).map((_, i) => Frage {i + 1})}
); case 'buttons': return (
{w.props.primary}{Array.from({ length: w.props.count - 1 }).map((_, i) => Aktion)}
); case 'checkliste': return (
{w.props.title}{Array.from({ length: w.props.items }).map((_, i) => Punkt {i + 1})}
); case 'download': return (
{w.props.label}{w.props.file}
); case 'social': return (
{w.props.network}{w.props.handle}
); case 'kurskarte': return (
Bild{w.props.title}{w.props.area}
); case 'kalender': return (
{Array.from({ length: w.props.weeks }).map((_, i) => KW {23 + i})}
); case 'shopteaser': return (
Bild{w.props.product}{w.props.price}
); case 'video': return (
{w.props.provider} · {w.props.ratio}
); default: return
{w.type}
; } } function AdminBuilder() { const [page, setPage] = useState(INITIAL_PAGE); const [selSection, setSelSection] = useState('s2'); const [selWidget, setSelWidget] = useState(page.sections[0].widgets[0].id); const [paletteOpen, setPaletteOpen] = useState(false); const [propsOpen, setPropsOpen] = useState(false); const allWidgets = page.sections.flatMap(s => s.widgets); const current = allWidgets.find(w => w.id === selWidget); const addWidget = (type) => { setPage(p => { const sections = p.sections.map(s => s.id === selSection ? { ...s, widgets: [...s.widgets, makeWidget(type)] } : s); return { ...p, sections }; }); }; // after add, select the new widget useEffect(() => { const sec = page.sections.find(s => s.id === selSection); if (sec && sec.widgets.length) { const last = sec.widgets[sec.widgets.length - 1]; // only auto-select if a brand-new id we haven't selected } }, [page]); const removeWidget = (id) => { setPage(p => ({ ...p, sections: p.sections.map(s => ({ ...s, widgets: s.widgets.filter(w => w.id !== id) })) })); }; const updateProp = (k, v) => { setPage(p => ({ ...p, sections: p.sections.map(s => ({ ...s, widgets: s.widgets.map(w => w.id === selWidget ? { ...w, props: { ...w.props, [k]: v } } : w) })) })); }; return (
{/* top bar */}
Baukasten / {page.title} PG-001 Auto-gespeichert · vor 8s
{/* PALETTE */} {/* CANVAS */}
Seitenstruktur · {page.sections.length} Sektionen · {allWidgets.length} Widgets
{page.sections.map(s => (
setSelSection(s.id)}>
{s.name} {s.widgets.length} Widgets {selSection === s.id && '· Ziel'}
{s.widgets.map(w => (
{ e.stopPropagation(); setSelWidget(w.id); setSelSection(s.id); }}>
{WIDGET_LABEL[w.type]} {w.meta.widgetId}
))} {!s.widgets.length &&
Leer — Widget aus der Palette hinzufügen
}
))}
{/* PROPERTIES */}
); } function PField({ label, children }) { return
{children}
; } function BuilderProps({ w, onProp }) { const p = w.props; return (
Widget · {WIDGET_LABEL[w.type]}

{p.headline || p.title || p.product || p.label || WIDGET_LABEL[w.type]}

Inhalt
{w.type === 'text' && <> onProp('headline', e.target.value)} />