diff --git a/display/frontend/src/components/EpaperPreview.tsx b/display/frontend/src/components/EpaperPreview.tsx index b5a5cc2..5efaf79 100644 --- a/display/frontend/src/components/EpaperPreview.tsx +++ b/display/frontend/src/components/EpaperPreview.tsx @@ -9,37 +9,35 @@ interface LayoutElement { width: number | null; height: number | null; font_size: number; + font_weight: string; + text_align: string; style: string; static_text: string | null; datapoint_id: number | null; + icon: string | null; } -interface DisplayJson { - display_id: number; - layout: { - id: number; - width: number; - height: number; - }; - elements: Array<{ - type: string; - x: number; - y: number; - width?: number; - height?: number; - font_size: number; - style: string; - static_text?: string; - label?: string; - value?: string; - unit?: string; - }>; +interface DisplayApiElement { + type: string; + x: number; + y: number; + width?: number; + height?: number; + font_size?: number; + font_weight?: string; + text_align?: string; + style?: string; + icon?: string; + text?: string; + label?: string; + value?: string | null; + unit?: string | null; } -interface Display { - id: number; - name: string; - layout_id: number | null; +interface DisplayApiResponse { + display: { id: number; name: string; width: number; height: number; refresh_sec: number }; + layout: { type: string }; + elements: DisplayApiElement[]; } interface EpaperPreviewProps { @@ -49,119 +47,146 @@ interface EpaperPreviewProps { layoutHeight?: number; } -const SCALE = 0.5; +const SCALE = 0.6; export default function EpaperPreview({ layoutId, elements, layoutWidth = 800, layoutHeight = 480 }: EpaperPreviewProps) { - const [displayJson, setDisplayJson] = useState(null); + const [liveElements, setLiveElements] = useState(null); + const [displayId, setDisplayId] = useState(null); + // Find a display that uses this layout useEffect(() => { - // Find a display using this layout and fetch its JSON - api.get('/api/displays') + api.get('/api/displays') .then((displays) => { - const d = displays.find((disp) => disp.layout_id === layoutId); - if (!d) return; - return api.get(`/api/display/${d.id}`).then(setDisplayJson).catch(() => {}); + const d = displays.find((disp: any) => disp.layout_id === layoutId); + setDisplayId(d ? d.id : null); }) - .catch(() => {}); + .catch(() => setDisplayId(null)); }, [layoutId]); + // Reload display data whenever elements change OR displayId is found + useEffect(() => { + if (!displayId) { setLiveElements(null); return; } + api.get(`/api/display/${displayId}`) + .then((data) => setLiveElements(data.elements)) + .catch(() => setLiveElements(null)); + }, [displayId, elements]); // elements is a new array ref after each selectLayout() + const w = layoutWidth * SCALE; const h = layoutHeight * SCALE; - const renderEl = (el: typeof elements[0], _idx?: number) => { - const left = el.x * SCALE; - const top = el.y * SCALE; - const fs = Math.max(8, el.font_size * SCALE); - - if (el.type === 'line') { - return ( -
- ); - } - if (el.type === 'rect') { - return ( -
- ); - } - - // label or value elements - const text = el.static_text ?? (el.type === 'label' ? `[DP ${el.datapoint_id ?? '?'}]` : `[Wert]`); - const isCard = el.style === 'card'; + const renderElements = liveElements || []; + const isLive = liveElements !== null; + // If no display linked and no elements, show hint + if (!isLive && elements.length === 0) { return ( -
- {text} +
+ Fuege Elemente hinzu um die Vorschau zu sehen.
); - }; + } - // Use live display JSON if available, else render from elements prop - const liveEls = displayJson?.elements; + // Build render list: use live data if available, otherwise use raw elements + const items: DisplayApiElement[] = isLive + ? renderElements + : elements.map((el) => ({ + type: el.type, + x: el.x, + y: el.y, + width: el.width ?? undefined, + height: el.height ?? undefined, + font_size: el.font_size, + font_weight: el.font_weight, + text_align: el.text_align, + style: el.style, + icon: el.icon ?? undefined, + text: el.type === 'label' ? (el.static_text || '') : undefined, + label: el.type === 'value' ? `DP #${el.datapoint_id || '?'}` : undefined, + value: el.type === 'value' ? '—' : undefined, + unit: '', + })); return (

- E-Paper Vorschau ({layoutWidth}x{layoutHeight}px){liveEls ? ' — Live-Daten' : ' — Design-Modus'} + Vorschau ({layoutWidth}x{layoutHeight}) + {isLive ? ' — Live-Daten' : ' — Layout-Struktur (kein Display zugewiesen)'}

-
- {liveEls - ? liveEls.map((el, idx) => { - const left = el.x * SCALE; - const top = el.y * SCALE; - const fs = Math.max(8, el.font_size * SCALE); +
+ {items.map((el, idx) => { + const left = el.x * SCALE; + const top = el.y * SCALE; + const fontSize = Math.max(7, (el.font_size || 24) * SCALE); + const isBold = el.font_weight === 'bold'; - if (el.type === 'line') { - return
; - } - if (el.type === 'rect') { - return
; - } + if (el.type === 'line') { + return
; + } - const text = el.static_text ?? (el.type === 'value' ? `${el.value ?? '?'} ${el.unit ?? ''}` : (el.label ?? '')); - const isCard = el.style === 'card'; + if (el.type === 'rect') { + return
; + } - return ( -
- {text} + if (el.type === 'label') { + const align = el.text_align || 'left'; + return
+ {el.text || ''} +
; + } + + if (el.type === 'value') { + const isCard = el.style === 'card'; + const elW = (el.width || 370) * SCALE; + const elH = (el.height || 90) * SCALE; + + if (isCard) { + return
+
+ {el.icon ? `[${el.icon}] ` : ''}{el.label || ''}
- ); - }) - : elements.map((el) => renderEl(el, el.id))} +
+ {el.value ?? '—'} {el.unit || ''} +
+
; + } + + return
+ {el.label}: {el.value ?? '—'} {el.unit || ''} +
; + } + + return null; + })}
);