6.6 KiB
B2in Display – Frontend-Implementation
Ziel: Frontend-Webapp für das B2in Schaufenster-Display (9:16 Portrait, 43–55 Zoll).
Pfad: public/_cabinet/b2in/index.html
Konzept-Basis: public/_cabinet/_docs/b2in-displays.md
Ist-Zustand
Es gibt bereits drei Display-Typen unter public/_cabinet/:
| Typ | Pfad | Status | Beschreibung |
|---|---|---|---|
| Video-Display | index.html |
Live, stabil | CABINET-Branding. Videos + Footer-Rotation. API: /api/display/config |
| Offers | offers/index.html |
Entwickelt, nicht live | CABINET-Branding. iFrame-basierte Slide-Rotation mit config.json |
| Info-Tablet | info/index.html |
Entwickelt | CABINET-Branding. Store-Status/Öffnungszeiten. API: /api/cabinet-tablet/status |
| B2in Display | b2in/ |
Neu – wird jetzt gebaut | B2in-Branding. Playlist mit Videos/Bildern + Text. API: /api/b2in-display/playlist |
Architektur-Übersicht
public/_cabinet/b2in/
├── index.html ← Haupt-Webapp (Playlist-Engine + UI)
├── b2in-styles.css ← Display-spezifische Styles
└── (assets/) ← Medien werden per API/URL referenziert, nicht lokal
Shared CSS aus public/_cabinet/shared/cabinet-base.css wird nicht importiert – das B2in-Display hat ein eigenständiges Branding (dunkel, B2in statt CABINET). Es ist ein komplett eigenes Design-System.
Schritte
Schritt 1: HTML-Grundgerüst + CSS
Datei: public/_cabinet/b2in/index.html + b2in-styles.css
Layout gemäß Konzept (Abschnitt 2.1):
┌─────────────────────────┐
│ HEADER │ ← B2in-Logo (links) + Claim (rechts)
│ B2in · Claim │ Fest, immer sichtbar
├─────────────────────────┤
│ │
│ ┌─────────────────┐ │ ← Gradient oben (dunkel → transparent)
│ │ VIDEO / BILD │ │ 16:9 Content-Bereich
│ │ (16:9 Media) │ │ object-fit: cover
│ └─────────────────┘ │ ← Gradient unten (transparent → dunkel)
│ │
├─────────────────────────┤
│ TEXTFELD │ ← Headline (max 40 Zeichen)
│ Headline + Subline │ Subline (max 80 Zeichen)
├─────────────────────────┤ Wechselt synchron mit Media
│ FOOTER │ ← "Marcel Scheibe" + "b2in.de" + QR-Code
│ Name · URL · QR │ Fest, immer sichtbar
└─────────────────────────┘
Design-Entscheidungen:
- Dunkler Hintergrund (#0a0a0a) – B2in-Branding, nicht CABINET-weiß
- 9:16 Aspect Ratio Container (wie bestehende Displays)
- Gradient-Overlays oben/unten über dem Media-Bereich
- IBM Plex Sans Font (wie alle Cabinet-Displays)
- Akzentfarbe: B2in-Blau (wird aus Branding-Guide übernommen)
- Kein CABINET-Logo, kein Accent #009FE3
Schritt 2: Playlist-Engine (JavaScript)
Klasse: B2inDisplayApp
Kernfunktionalität:
- API laden →
GET /api/b2in-display/playlist(wird später gebaut, erstmal Mock/Fallback) - Playlist sortieren → nach
sort_order, nuris_active === true - Gewichtung anwenden → 70/30 Immobilien/Möbel-Verteilung berechnen
- Rotation starten → Item für Item durchspielen
Item-Wechsel-Logik:
- Video: Abspielen bis Ende → nächstes Item
- Bild:
duration_secondsabwarten → nächstes Item - Am Ende der Playlist: Von vorne beginnen
Schritt 3: Video-Handling
Bewährtes Pattern aus dem bestehenden index.html übernehmen:
autoplay muted playsinlinefür Browser-Autoplay-Policy- Memory-Management:
srcleeren +load()nach jedem Video - Preloading: Nächstes Item im Hintergrund vorladen
- Start-Timeout (10s) → bei Timeout überspringen
- Watchdog: Prüft alle 5s ob Video noch läuft
- Error-Handling: Bei Fehler → Item überspringen, nie schwarzer Screen
Schritt 4: Bild-Handling
<img>Element mitobject-fit: cover- Duration aus Item oder globaler
default_image_duration - Ken-Burns-Effekt (optionaler langsamer Zoom per CSS)
- Preload via
new Image()im Hintergrund
Schritt 5: Transitions
Drei Typen (per CMS-Setting steuerbar):
| Typ | Umsetzung |
|---|---|
fade |
Opacity 1→0, dann 0→1 |
crossfade |
Neues Element über dem alten einblenden (empfohlen, Standard) |
slide |
CSS transform translateX |
Text-Synchronisation:
- Text fade-out (400ms)
- Neuen Text setzen
- Text fade-in (400ms)
- Startet 200ms vor Media-Wechsel
Schritt 6: Polling + Stabilität
Gleicher Ansatz wie Info-Tablet, aber mit 60s Intervall:
- Lightweight Check alle 60s →
GET /api/b2in-display/check - Full Fetch nur bei Timestamp-Änderung
- Laufendes Video/Bild wird zu Ende gespielt, dann neue Playlist
- localStorage-Cache als Offline-Fallback
- Auto-Reload alle 6 Stunden
- Connection-Recovery: 3 Fehler → 5 Min Pause → Retry
- 30 Min offline → Page-Reload
Schritt 7: Standby-Modus
Wenn display_active === false:
- Nur B2in-Logo auf dunklem Hintergrund
- Kein Content, kein Textfeld, kein Footer-Text
- Polling läuft weiter (wartet auf Aktivierung)
Schritt 8: Error-Overlay
Bei kritischen Fehlern (keine Playlist, kein Media):
- Dezentes B2in-Logo auf dunklem Hintergrund
- Niemals Browser-Fehler oder weißer Screen
- Automatischer Retry im Hintergrund
API-Abhängigkeit
Das Frontend wird zunächst mit Mock-Daten gebaut. Die API (/api/b2in-display/playlist + /check) wird als separater Schritt im Backend implementiert. Das Frontend erkennt automatisch, ob die API verfügbar ist, und fällt auf eingebettete Demo-Daten zurück.
Mock-Playlist für Entwicklung:
const MOCK_PLAYLIST = {
settings: {
display_active: true,
footer_name: "Marcel Scheibe",
footer_url: "b2in.de",
transition: { type: "crossfade", duration_ms: 800 },
default_image_duration: 10
},
items: [
// Demo-Items mit Platzhalter-Medien
],
updated_at: new Date().toISOString()
};
Abgrenzung (was NICHT in diesem Schritt passiert)
- Kein CMS-Backend (Model, Migration, Controller, Admin-UI) → separater Schritt
- Kein Media-Upload → Videos/Bilder werden per URL referenziert
- Keine Gewichtungs-Logik im Backend → wird im Frontend berechnet
- Keine Änderung am bestehenden Video-Display (
index.html) → bleibt unberührt