/* Powermieter — shared UI primitives (global). */
const { useRef: _useRef, useState: _useState, useEffect: _useEffect } = React;
function Logo({ size = 32 }) {
/* Prefers the brand asset assets/logo.svg (drop in the real Powerhouse 360
logo there); falls back to a theme-adaptive wordmark if it is missing. */
const [imgOk, setImgOk] = _useState(true);
if (imgOk) {
return setImgOk(false)} />;
}
const m = Math.round(size * 0.92);
return (
Powerhouse 360
);
}
const STEPS = ['Interesse', 'Daten', 'SEPA-Mandat', 'Vertrag'];
function ProcessNav({ current, maxReached = 1, onJump }) {
return (
{STEPS.map((label, i) => {
const n = i + 1;
const done = n < current, active = n === current;
const clickable = onJump && n <= maxReached && n !== current;
return (
onJump(n) : undefined}>
{done ? : n}
{label}
{n < STEPS.length && }
);
})}
);
}
function Field({ label, name, value, onChange, error, type = 'text', placeholder, optional, hint, inputMode, autoComplete, col2 }) {
return (
{label}{optional && · optional }
onChange(name, e.target.value)} />
{error ? {error} : (hint && {hint} )}
);
}
function Select({ label, name, value, onChange, options, error, optional, col2 }) {
return (
{label}{optional && · optional }
onChange(name, e.target.value)}>
Bitte wählen…
{options.map((o) => {o.label} )}
{error && {error} }
);
}
function Consent({ name, checked, onChange, error, children }) {
return (
onChange(name, e.target.checked)} />
{children}
);
}
function Button({ kind = 'primary', lg, block, busy, children, ...rest }) {
return (
{busy && }{children}
);
}
function Note({ children }) {
return {children}
;
}
/* ---- Meter-reading photo (camera on mobile, downscaled client-side) ---- */
function PhotoCapture({ value, onChange, error }) {
const inputRef = _useRef(null);
const onFile = (e) => {
const file = e.target.files && e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = () => {
const img = new Image();
img.onload = () => {
const max = 1280; let { width, height } = img;
if (width > max || height > max) { const r = Math.min(max / width, max / height); width = Math.round(width * r); height = Math.round(height * r); }
const c = document.createElement('canvas'); c.width = width; c.height = height;
c.getContext('2d').drawImage(img, 0, 0, width, height);
onChange(c.toDataURL('image/jpeg', 0.7));
};
img.src = reader.result;
};
reader.readAsDataURL(file);
};
return (
{value ? (
Foto erfasst
onChange('')}> Entfernen
) : (
inputRef.current && inputRef.current.click()}>
Foto vom Zählerstand aufnehmen Kamera öffnen oder Bild wählen
)}
{error &&
{error} }
);
}
/* ---- Signature pad (built-in SES; mouse + touch) ---- */
function SignaturePad({ onChange, error }) {
const canvasRef = _useRef(null);
const [signed, setSigned] = _useState(false);
const drawing = _useRef(false);
const hasDrawn = _useRef(false);
const last = _useRef(null);
_useEffect(() => {
const c = canvasRef.current;
const ratio = window.devicePixelRatio || 1;
const resize = () => {
const r = c.getBoundingClientRect();
c.width = r.width * ratio; c.height = r.height * ratio;
const ctx = c.getContext('2d');
ctx.scale(ratio, ratio);
ctx.lineWidth = 2.2; ctx.lineCap = 'round'; ctx.lineJoin = 'round';
ctx.strokeStyle = getComputedStyle(document.documentElement).getPropertyValue('--text').trim() || '#0F1A2E';
};
resize();
window.addEventListener('resize', resize);
return () => window.removeEventListener('resize', resize);
}, []);
const pos = (e) => {
const r = canvasRef.current.getBoundingClientRect();
const t = e.touches ? e.touches[0] : e;
return { x: t.clientX - r.left, y: t.clientY - r.top };
};
const start = (e) => { e.preventDefault(); drawing.current = true; last.current = pos(e); };
const move = (e) => {
if (!drawing.current) return; e.preventDefault();
const ctx = canvasRef.current.getContext('2d'); const p = pos(e);
ctx.beginPath(); ctx.moveTo(last.current.x, last.current.y); ctx.lineTo(p.x, p.y); ctx.stroke();
last.current = p; hasDrawn.current = true; if (!signed) setSigned(true);
};
const end = () => { if (!drawing.current) return; drawing.current = false; if (hasDrawn.current) onChange(canvasRef.current.toDataURL('image/png')); };
const clear = () => { const c = canvasRef.current; c.getContext('2d').clearRect(0, 0, c.width, c.height); hasDrawn.current = false; setSigned(false); onChange(''); };
return (
✕
Hier mit dem Finger oder der Maus unterschreiben
{signed ? 'Unterschrift erfasst' : 'Rechtsgültige elektronische Unterschrift'}
{signed && Neu unterschreiben }
{error &&
{error} }
);
}
window.Logo = Logo; window.ProcessNav = ProcessNav; window.Field = Field; window.Select = Select;
window.Consent = Consent; window.Button = Button; window.Note = Note;
window.PhotoCapture = PhotoCapture; window.SignaturePad = SignaturePad;