/* 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 ;
}
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)} />
>}
{w.type === 'hero' && <>
onProp('headline', e.target.value)} />
onProp('cta', e.target.value)} />
>}
{w.type === 'hinweis' && <>
>}
{(w.type === 'faq' || w.type === 'checkliste' || w.type === 'kalender') && <>
{p.title !== undefined &&
onProp('title', e.target.value)} />}
onProp(p.items !== undefined ? 'items' : 'weeks', +e.target.value)} />
{p.items ?? p.weeks}
>}
{w.type === 'buttons' && <>
onProp('primary', e.target.value)} />
onProp('count', +e.target.value)} />{p.count}
>}
{w.type === 'download' && <>
onProp('label', e.target.value)} />
onProp('file', e.target.value)} />
>}
{w.type === 'social' && <>
onProp('handle', e.target.value)} />
>}
{w.type === 'kurskarte' && <>
onProp('title', e.target.value)} />
onProp('area', e.target.value)} />
>}
{w.type === 'shopteaser' && <>
onProp('product', e.target.value)} />
onProp('price', e.target.value)} />
>}
{w.type === 'video' && <>
>}
Automatische Metadaten
{[['widgetId', w.meta.widgetId], ['pageId', w.meta.pageId], ['language', w.meta.language], ['version', 'v' + w.meta.version], ['createdBy', w.meta.createdBy], ['updatedAt', w.meta.updatedAt], ['trackingKey', w.meta.trackingKey]].map(([k, v]) => (
{k}{v}
))}
);
}
function Toggle({ on }) {
const [v, setV] = useState(!!on);
return ;
}
Object.assign(window, { AdminBuilder });