/* Powermieter — Admin / Hausverwaltung */ const { useState: useAS, useMemo: useAM, useEffect: useAE } = React; const PM_STATUS = { none: { label: 'Nicht eingeladen', tone: 'gray', dot: 'var(--text-3)' }, invited: { label: 'Eingeladen', tone: 'gray', dot: 'var(--text-3)' }, interest:{ label: 'Interesse', tone: 'accent', dot: 'var(--accent)' }, reg: { label: 'Registrierung', tone: 'accent', dot: 'var(--accent)' }, prep: { label: 'Vertrag vorbereitet', tone: 'warn', dot: 'var(--warn-500)' }, signed: { label: 'Abgeschlossen', tone: 'positive', dot: 'var(--positive)' }, active: { label: 'Aktiviert', tone: 'positive', dot: 'var(--positive-500)' }, }; const STATUS_ORDER = ['none', 'invited', 'interest', 'reg', 'prep', 'signed', 'active']; const RESIDENTS = [ { whg: 'W01', name: 'Anna Berger', auf: 'A', etage: 'EG', status: 'active', contact: 'a.berger@mail.de' }, { whg: 'W02', name: 'Familie Çelik', auf: 'A', etage: 'EG', status: 'signed', contact: '+49 170 …' }, { whg: 'W03', name: 'Tobias Wolf', auf: 'A', etage: '1. OG', status: 'prep', contact: 't.wolf@mail.de' }, { whg: 'W04', name: 'Max Mustermann', auf: 'A', etage: '2. OG', status: 'reg', contact: 'max@beispiel.de', flag: true }, { whg: 'W05', name: 'Sofia Russo', auf: 'A', etage: '2. OG', status: 'interest', contact: 's.russo@mail.de' }, { whg: 'W06', name: 'Leon Fischer', auf: 'A', etage: '3. OG', status: 'invited', contact: 'l.fischer@mail.de' }, { whg: 'W07', name: 'Hr. & Fr. Yılmaz', auf: 'B', etage: 'EG', status: 'active', contact: '+49 151 …' }, { whg: 'W08', name: 'Clara Hoffmann', auf: 'B', etage: 'EG', status: 'signed', contact: 'c.hoffmann@mail.de' }, { whg: 'W09', name: 'Jonas Krüger', auf: 'B', etage: '1. OG', status: 'interest', contact: 'j.krueger@mail.de' }, { whg: 'W10', name: '— leer stehend', auf: 'B', etage: '1. OG', status: 'none', contact: '', vacant: true }, { whg: 'W11', name: 'Mara Schulz', auf: 'B', etage: '2. OG', status: 'invited', contact: 'm.schulz@mail.de', flag: true }, { whg: 'W12', name: 'David Becker', auf: 'B', etage: '3. OG', status: 'reg', contact: 'd.becker@mail.de' }, ]; function StatusBadge({ s }) { const st = PM_STATUS[s]; return {st.label}; } function AdminView({ device, tweaks = { goLiveGate: 'offen', datenfuelle: 'lueckenhaft', quote: 58 }, model = 'mieterstrom', setModel = () => {}, initialView = 'uebersicht' }) { const [adminView, setAdminView] = useAS(() => { if (initialView && initialView !== 'uebersicht') return initialView; try { return JSON.parse(localStorage.getItem('pm_adminview') || '"uebersicht"'); } catch { return 'uebersicht'; } }); useAE(() => { if (new URLSearchParams(location.search).get('embed') === '1') return; try { localStorage.setItem('pm_adminview', JSON.stringify(adminView)); } catch {} }, [adminView]); const [wizardOpen, setWizardOpen] = useAS(false); const [wizardUnit, setWizardUnit] = useAS(null); const [drawer, setDrawer] = useAS(null); const openWizard = (u) => { setWizardUnit(u || null); setWizardOpen(true); }; const [auf, setAuf] = useAS('all'); const [etage, setEtage] = useAS('all'); const [q, setQ] = useAS(''); const [statusFilter, setStatusFilter] = useAS('all'); const [noteFor, setNoteFor] = useAS(null); const [notes, setNotes] = useAS({ W04: 'Rückruf gewünscht — Mo nachmittags.', W11: 'Zweite Einladung am 28.05. versendet.' }); const [showPoster, setShowPoster] = useAS(false); const [view, setView] = useAS('list'); const etagen = useAM(() => [...new Set(RESIDENTS.map(r => r.etage))], []); const rows = useAM(() => RESIDENTS.filter(r => (auf === 'all' || r.auf === auf) && (etage === 'all' || r.etage === etage) && (statusFilter === 'all' || r.status === statusFilter) && (!q || r.name.toLowerCase().includes(q.toLowerCase()) || r.whg.toLowerCase().includes(q.toLowerCase())) ), [auf, etage, q, statusFilter]); const total = RESIDENTS.length; const occupied = RESIDENTS.filter(r => !r.vacant).length; const interested = RESIDENTS.filter(r => ['interest', 'reg', 'prep', 'signed', 'active'].includes(r.status)).length; const contracts = RESIDENTS.filter(r => ['signed', 'active'].includes(r.status)).length; const active = RESIDENTS.filter(r => r.status === 'active').length; const quote = Math.round(contracts / occupied * 100); const todo = RESIDENTS.filter(r => ['prep', 'reg'].includes(r.status) || r.flag).length; const breakdown = STATUS_ORDER.map(s => ({ s, n: RESIDENTS.filter(r => r.status === s).length })).filter(b => b.n > 0); return (
{/* Top bar */}

Musterstraße 12

10115 Berlin · 12 Wohneinheiten · 2 Aufgänge
{[['uebersicht', '\u00dcbersicht', I.chart], ['cockpit', 'Vertrags-Cockpit', I.signature], ['lifecycle', 'Fristen & Wechsel', I.calendar], ['weg', 'WEG-Freigabe', I.users]].map(([v, l, Ic]) => ( ))}
{adminView === 'cockpit' &&
} {adminView === 'lifecycle' &&
{window.Lifecycle ? : null}
} {adminView === 'weg' &&
} {adminView === 'uebersicht' && (
{/* KPIs */}
} value={total} label="Wohnungen" sub={`${occupied} bewohnt · 1 leer`} /> } value={interested} label="Interessenten" tone="accent" sub={`${Math.round(interested / occupied * 100)}% der Haushalte`} /> } value={contracts} label="Verträge" tone="positive" sub={`${active} bereits aktiv`} /> } value={quote + '%'} label="Teilnahmequote" tone="positive" sub="Ziel: 60%" /> } value={todo} label="Handlungsbedarf" tone="warn" sub="offene Vorgänge" />
{/* Progress + action panel */}

Objekt-Fortschritt

{quote}% abgeschlossen
{breakdown.map(b => )}
{breakdown.map(b => (
{PM_STATUS[b.s].label}{b.n}
))}

Offener Handlungsbedarf

Vorgänge, die auf Sie warten.

} text="3 Verträge in Vorbereitung" sub="Daten vollständig — bereit zum Versand" /> } text="2 Einladungen ohne Reaktion" sub="seit über 7 Tagen" warn /> } text="1 leer stehende Wohnung" sub="W10 · Aufgang B" />
{/* Conversion funnel */} {/* Bewohner — Liste / Gebäude */}

Bewohner-Status

}, { value: 'building', label: 'Gebäude', icon: }, ]} />
{view === 'building' ? {}} /> : (<> {/* Filters */}
setQ(e.target.value)} />
[e, e])]} /> [s, PM_STATUS[s].label])]} />
{rows.length} von {total}
{/* Table */}
Wohnung
Bewohner
Lage
Status
Aktion
{rows.map(r => (
{r.whg}
{r.vacant ? '—' : initials(r.name)}
{r.name}{r.flag && }
{r.contact &&
{r.contact}
}
Aufgang {r.auf} · {r.etage}
{!r.vacant && (r.status === 'invited' || r.status === 'none' || r.flag ? : )}
{noteFor === r.whg && (