feat: Willkommensseite auf mehreren Screens anzeigen
Die Willkommensseite ging bisher fest in die Lobby-Playlist. Welche
Screens Begruessungsschirme sind, ist aber eine Eigenschaft der
Installation und nicht des einzelnen Kundenbesuchs - deshalb ein Flag
pro Screen statt einer Auswahl bei jedem Vorgang.
- Neue Screen-Einstellung "welcome_enabled" (Checkbox im Tab
Einstellungen). Die generierte welcome.html wird in jeden markierten
Screen des Standorts geschrieben und dort an Position 0 gesetzt.
- welcome_target_screens() faellt nur auf "lobby" zurueck, wenn kein
Screen des Standorts den Schluessel besitzt. Bestehende Standorte
verhalten sich damit ohne Konfigurationsaufwand unveraendert.
- add_customer_to_playlists() (frueher add_customer_to_lobby_playlist)
raeumt welcome.html aus allen Screens des Standorts und fuegt sie nur
in den Zielscreens wieder ein. Ein abgewaehlter Screen bleibt so nicht
mit einer alten Begruessung stehen.
- Eingefuegt wird ein Dict statt eines Strings, damit der Eintrag
enabled und schedule tragen kann.
- Optionales "Anzeigen bis" im Formular: setzt eine Zeitsteuerung, die
Seite verschwindet danach auf allen Screens von selbst.
- Das Formular zeigt die aktuellen Zielscreens an und meldet
verstaendlich, wenn keiner markiert ist.
- screens.setdefault("lobby", {}) entfernt - das legte an Standorten
ohne Lobby stillschweigend einen Screen an. Es werden nur noch
vorhandene Screens bespielt.
Die Seite referenziert alle Bilder absolut (/static/..., /media/...),
ist also ortsunabhaengig; je Screen liegt eine identische Kopie.
README, AGENTS.md und die Hilfeseite nachgezogen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -110,7 +110,7 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
|
||||
- URL playlist items are stored as dicts like `{"url": "https://...", "zoom": 0.8}` and the zoom value must survive save/reorder flows. File items may also be dicts (`{"name": "bild.jpg", ...}`); `save_playlist` copies existing dicts, so extra keys like `schedule` survive reordering.
|
||||
- `.html` items in `media/` are rendered inline as content, not in an iframe.
|
||||
- `config.priority.enabled` makes the priority playlist show on every screen.
|
||||
- `POST /api/customer` generates `welcome.html` and inserts it at the front of the lobby playlist for the specified site.
|
||||
- `POST /api/customer` generates `welcome.html` and inserts it at the front of the playlist of every target screen for the specified site. Target screens are those with `welcome_enabled`; if no screen of the site has the key at all, the old behaviour applies (`lobby` only). Optional `valid_until` (`"YYYY-MM-DD"`) attaches a schedule so the page disappears on its own.
|
||||
- New standorte can be added by creating `media/<neuer-standort>/<screen>/` directories and optionally adding config to `config.json["sites"][<neuer-standort>]`.
|
||||
- Priority playlist (`config.priority`) is global and affects all sites/screens.
|
||||
- Willkommensseite (`customer.html`) accepts up to 3 customer names; logos are fetched via OpenAI→Brandfetch and displayed in a flex row (portrait: column).
|
||||
@@ -119,7 +119,9 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
|
||||
- `customer_names` are preserved in form fields after POST (via `value`-Attribute).
|
||||
- Admin-UI nutzt keyadmin-Design: `brand-surface` (#2b2f36), `nav-surface` (rot #DA002D), Dark Mode per `localStorage("signage-theme")`.
|
||||
- Gemeinsame HTML-Bausteine: `_header.html`, `_footer.html`, `_styles.html` (CSS-Variablen `--ccm-*`, Dark Mode, Card-Border-Radius 1rem).
|
||||
- `add_customer_to_lobby_playlist` entfernt `welcome.html` sowohl als String als auch als Dict aus der Playlist vor dem Einfügen.
|
||||
- `add_customer_to_playlists` (früher `add_customer_to_lobby_playlist`) entfernt `welcome.html` als String *und* als Dict aus **allen** Screens des Standorts und fügt sie danach nur in den Zielscreens an Position 0 wieder ein – ein abgewählter Screen bleibt so nicht mit einer alten Begrüßung stehen. Eingefügt wird ein Dict, kein String, damit `enabled` und `schedule` mitgeführt werden können.
|
||||
- `welcome_target_screens(config, site)` fällt nur dann auf `lobby` zurück, wenn **kein** Screen des Standorts den Schlüssel `welcome_enabled` besitzt. Wurde er überall bewusst auf `False` gesetzt, ist die leere Liste das gewollte Ergebnis.
|
||||
- Weder `save_welcome_page` noch `add_customer_to_playlists` legen Screens an. Das frühere `screens.setdefault("lobby", {})` erzeugte an Standorten ohne Lobby stillschweigend einen Screen.
|
||||
- `add_screen` legt ein Verzeichnis unter `media/<site>/<screen>/` an und einen Config-Eintrag.
|
||||
- `delete_screen` entfernt den Screen aus der Config und löscht das Verzeichnis rekursiv.
|
||||
- Custom-URL-Button: pro Screen konfigurierbar (`custom_url` + `custom_url_label` + `custom_url_enabled` + `custom_url_target` + `custom_url_position`) im Admin-Formular (Aktionen-Tab); wird im Player als Button an wählbarer Position (9 Positionen: oben/mitte/unten × links/mitte/rechts) angezeigt und öffnet die URL wahlweise in einem iframe-Overlay mit Zurück-Button (`overlay`) oder per Direkt-Weiterleitung (`redirect`); Player pausiert während das Overlay geöffnet ist. Die unteren Positionen weichen automatisch 16px über dem Newsticker-Balken aus.
|
||||
|
||||
@@ -10,7 +10,7 @@ Browserbasiertes Digital-Signage-System für interne Info-Screens.
|
||||
- Bilder, Videos (MP4/H.264), HTML-Seiten, URL-Playlist-Einträge
|
||||
- **Multi-Standort**: Standorte (sites) gruppieren Screens
|
||||
- **Priority-Playlist**: globale Inhalte wirken auf alle Player
|
||||
- **Willkommensseite**: bis zu 3 Kundenlogos via OpenAI + Brandfetch, pro Standort konfigurierbares Hintergrundbild
|
||||
- **Willkommensseite**: bis zu 3 Kundenlogos via OpenAI + Brandfetch, pro Standort konfigurierbares Hintergrundbild; erscheint auf allen Screens mit `welcome_enabled` (mehrere möglich), optional mit Ablaufdatum
|
||||
- Pro Screen eigene Playlist mit Drag-&-Drop-Reihenfolge
|
||||
- Auto-Reload bei Playlist-Änderungen
|
||||
- Newsticker pro Screen
|
||||
@@ -418,6 +418,7 @@ OPENAI_API_KEY=sk-...
|
||||
| `custom_url_target` | string | `"overlay"` (iframe) oder `"redirect"` |
|
||||
| `custom_url_position` | string | Position des Buttons: `top-left`, `top-center`, `top-right`, `middle-left`, `middle-center`, `middle-right`, `bottom-left`, `bottom-center`, `bottom-right` |
|
||||
| `stay_on_first` | bool | Player bleibt auf erstem Element stehen (kein Refresh) |
|
||||
| `welcome_enabled` | bool | Willkommensseite wird auf diesem Screen vorne in die Playlist gesetzt |
|
||||
| `voice_agent_enabled` | bool | Voice-Agent-Button im Player anzeigen |
|
||||
| `voice_agent_show_image` | bool | Bild (dva.png) über dem Button anzeigen |
|
||||
| `voice_agent_label` | string | Button-Beschriftung (Default: "Digitaler Assistent") |
|
||||
@@ -496,6 +497,7 @@ Optional pro Eintrag. Ohne `schedule` läuft der Eintrag immer (sofern `enabled`
|
||||
- `_in_validity_range(schedule, day)` – optionalen Gültigkeitszeitraum prüfen
|
||||
- `schedule_text(schedule)` – Jinja-Filter: Klartext einer Zeitsteuerung für Tooltips
|
||||
- `load_priority_files()` – Priority-Playlist + Dateien laden
|
||||
- `welcome_target_screens(config, site)` – Zielscreens der Willkommensseite (Fallback `lobby`)
|
||||
- `prio_redirect(site)` – Redirect-Pfad zur Priority-Seite
|
||||
- `get_background_url(site)` – URL zum benutzerdefinierten Hintergrundbild oder `None`
|
||||
- `load_users()` / `save_users()` / `get_user()` – User aus `users.json`
|
||||
|
||||
45
app.py
45
app.py
@@ -892,13 +892,15 @@ def add_customer():
|
||||
Formular für die Willkommensseite:
|
||||
- Bis zu 3 Kundennamen eingeben
|
||||
- Logos per OpenAI → Brandfetch suchen
|
||||
- welcome.html wird generiert und in der Lobby-Playlist vorne platziert
|
||||
- welcome.html wird generiert und in den Playlists aller Zielscreens
|
||||
vorne platziert (Screens mit "welcome_enabled")
|
||||
"""
|
||||
error = None
|
||||
success = None
|
||||
logo_urls = [None, None, None]
|
||||
customer_names = ["", "", ""]
|
||||
contacts = ["", "", ""]
|
||||
valid_until = (request.form.get("valid_until") or "").strip()
|
||||
site = request.args.get("site") or request.form.get("site") or ""
|
||||
|
||||
# Fallback auf ersten für den User zugänglichen Standort
|
||||
@@ -959,20 +961,34 @@ def add_customer():
|
||||
break
|
||||
|
||||
bg_url = get_background_url(site)
|
||||
html_filename = generate_welcome_page.save_welcome_page(valid_names, valid_urls, contacts=valid_contacts, site=site, background_url=bg_url)
|
||||
targets = generate_welcome_page.welcome_target_screens(load_config(), site)
|
||||
|
||||
if not targets:
|
||||
error = ("Kein Screen für die Willkommensseite ausgewählt – "
|
||||
"im Tab 'Einstellungen' eines Screens aktivieren")
|
||||
html_filename = None
|
||||
else:
|
||||
html_filename = generate_welcome_page.save_welcome_page(
|
||||
valid_names, valid_urls, contacts=valid_contacts,
|
||||
site=site, background_url=bg_url, screens=targets)
|
||||
|
||||
if html_filename:
|
||||
if generate_welcome_page.add_customer_to_lobby_playlist(html_filename, site=site):
|
||||
if generate_welcome_page.add_customer_to_playlists(
|
||||
html_filename, site=site, screens=targets, valid_until=valid_until):
|
||||
# Letzte Kundendaten für Background-Update speichern
|
||||
config = load_config()
|
||||
site_cfg = config.setdefault("sites", {}).setdefault(site, {})
|
||||
site_cfg["welcome_data"] = {"names": valid_names, "logo_urls": valid_urls, "contacts": valid_contacts}
|
||||
save_config(config)
|
||||
add_history_entry("customer_added", f"Kunden ({', '.join(valid_names)}) zur Willkommensseite an Standort '{site}' hinzugefügt", current_user.email)
|
||||
success = f"✅ Willkommensseite mit {len(valid_names)} Kunden erstellt!"
|
||||
add_history_entry("customer_added", f"Kunden ({', '.join(valid_names)}) zur Willkommensseite an Standort '{site}' hinzugefügt (Screens: {', '.join(targets)})", current_user.email)
|
||||
success = (f"✅ Willkommensseite mit {len(valid_names)} Kunden erstellt "
|
||||
f"– angezeigt auf: {', '.join(targets)}")
|
||||
if valid_until:
|
||||
success += f" (bis einschliesslich {valid_until})"
|
||||
else:
|
||||
error = "Fehler beim Hinzufügen zur Playliste"
|
||||
else:
|
||||
elif targets:
|
||||
# Ohne Zielscreens steht die Meldung dazu bereits in error
|
||||
error = "Fehler beim Speichern der Willkommensseite"
|
||||
except ValueError as e:
|
||||
error = f"Konfigurationsfehler: {str(e)}"
|
||||
@@ -1002,6 +1018,7 @@ def add_customer():
|
||||
logo_urls=logo_urls,
|
||||
customer_names=customer_names,
|
||||
contacts=contacts,
|
||||
welcome_screens=generate_welcome_page.welcome_target_screens(config, site) if site else [],
|
||||
site=site,
|
||||
site_list=site_list,
|
||||
current_site=site,
|
||||
@@ -1047,18 +1064,27 @@ def api_add_customer():
|
||||
|
||||
contact = data.get("contact", "").strip()
|
||||
|
||||
html_filename = generate_welcome_page.save_welcome_page([customer_name], [logo_url], contacts=[contact], site=site)
|
||||
valid_until = (data.get("valid_until") or "").strip()
|
||||
targets = generate_welcome_page.welcome_target_screens(load_config(), site)
|
||||
|
||||
if not targets:
|
||||
return jsonify({"error": "No welcome screen configured for this site"}), 400
|
||||
|
||||
html_filename = generate_welcome_page.save_welcome_page(
|
||||
[customer_name], [logo_url], contacts=[contact], site=site, screens=targets)
|
||||
|
||||
if not html_filename:
|
||||
return jsonify({"error": "Failed to create welcome page"}), 500
|
||||
|
||||
if generate_welcome_page.add_customer_to_lobby_playlist(html_filename, site=site):
|
||||
if generate_welcome_page.add_customer_to_playlists(
|
||||
html_filename, site=site, screens=targets, valid_until=valid_until):
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": f"Customer '{customer_name}' added successfully",
|
||||
"welcome_page": html_filename,
|
||||
"logo_url": logo_url,
|
||||
"site": site
|
||||
"site": site,
|
||||
"screens": targets
|
||||
}), 201
|
||||
else:
|
||||
return jsonify({"error": "Failed to add to playlist"}), 500
|
||||
@@ -1527,6 +1553,7 @@ def update_screen(site, screen):
|
||||
cfg["newsticker_text"] = request.form.get("newsticker_text", "")
|
||||
cfg["newsticker_enabled"] = "newsticker_enabled" in request.form
|
||||
cfg["stay_on_first"] = "stay_on_first" in request.form
|
||||
cfg["welcome_enabled"] = "welcome_enabled" in request.form
|
||||
|
||||
save_config(config)
|
||||
return redirect(f"/admin/{site}")
|
||||
|
||||
@@ -296,57 +296,134 @@ def generate_welcome_html(customer_names, logo_urls, contacts=None, site="stuttg
|
||||
# Speichern + Playlist-Einfügung
|
||||
# -------------------------------------------------
|
||||
|
||||
def save_welcome_page(customer_names, logo_urls, contacts=None, site="stuttgart", background_url=None):
|
||||
"""Speichert die generierte Willkommensseite als welcome.html im Lobby-Verzeichnis.
|
||||
def welcome_target_screens(config, site):
|
||||
"""Ermittelt die Screens eines Standorts, auf denen die Willkommensseite laufen soll.
|
||||
|
||||
Ein Screen ist Ziel, wenn "welcome_enabled" gesetzt ist. Ist der Schluessel bei
|
||||
keinem Screen des Standorts vorhanden, greift das alte Verhalten: nur "lobby".
|
||||
Damit bleiben bestehende Standorte ohne Konfigurationsaufwand unveraendert."""
|
||||
screens = config.get("sites", {}).get(site, {}).get("screens", {})
|
||||
|
||||
configured = [name for name, cfg in screens.items()
|
||||
if isinstance(cfg, dict) and cfg.get("welcome_enabled")]
|
||||
if configured:
|
||||
return sorted(configured)
|
||||
|
||||
# Kein Screen markiert: nur dann auf "lobby" zurückfallen, wenn noch nirgends
|
||||
# eine bewusste Entscheidung getroffen wurde. Wurde das Häkchen überall
|
||||
# entfernt, ist "keine Screens" das gewollte Ergebnis.
|
||||
entschieden = any(isinstance(cfg, dict) and "welcome_enabled" in cfg
|
||||
for cfg in screens.values())
|
||||
if entschieden:
|
||||
return []
|
||||
|
||||
return ["lobby"] if "lobby" in screens else []
|
||||
|
||||
|
||||
def save_welcome_page(customer_names, logo_urls, contacts=None, site="stuttgart",
|
||||
background_url=None, screens=None):
|
||||
"""Speichert die generierte Willkommensseite als welcome.html in den Zielscreens.
|
||||
|
||||
screens: Liste der Screen-Namen; None → Zielscreens aus der Konfiguration.
|
||||
contacts: Liste der Ansprechpartner-Texte (Markdown, optional)
|
||||
background_url: Benutzerdefiniertes Hintergrundbild oder None (Standard).
|
||||
Gibt den Dateinamen zurück oder None bei Fehler."""
|
||||
try:
|
||||
welcome_dir = os.path.join(MEDIA_DIR, site, "lobby")
|
||||
os.makedirs(welcome_dir, exist_ok=True)
|
||||
filepath = os.path.join(os.path.abspath(welcome_dir), WELCOME_FILENAME)
|
||||
html_content = generate_welcome_html(customer_names, logo_urls, contacts=contacts, site=site, background_url=background_url)
|
||||
Gibt den Dateinamen zurück oder None bei Fehler.
|
||||
|
||||
Die Seite referenziert alle Bilder absolut (/static/..., /media/<site>/...),
|
||||
ist also ortsunabhängig - eine Kopie je Screen rendert identisch."""
|
||||
try:
|
||||
from app import load_config
|
||||
|
||||
if screens is None:
|
||||
screens = welcome_target_screens(load_config(), site)
|
||||
|
||||
if not screens:
|
||||
print(f"⚠️ Generate - Kein Zielscreen für die Willkommensseite (site: {site})")
|
||||
return None
|
||||
|
||||
html_content = generate_welcome_html(customer_names, logo_urls, contacts=contacts,
|
||||
site=site, background_url=background_url)
|
||||
|
||||
for screen in screens:
|
||||
welcome_dir = os.path.join(MEDIA_DIR, site, screen)
|
||||
os.makedirs(welcome_dir, exist_ok=True)
|
||||
filepath = os.path.join(os.path.abspath(welcome_dir), WELCOME_FILENAME)
|
||||
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
f.write(html_content)
|
||||
if os.path.getsize(filepath) == 0:
|
||||
raise IOError("File not written")
|
||||
print(f"✅ Generate - Willkommensseite gespeichert in: {filepath}")
|
||||
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
f.write(html_content)
|
||||
if os.path.getsize(filepath) == 0:
|
||||
raise IOError("File not written")
|
||||
names_str = ", ".join(customer_names)
|
||||
print(f"✅ Generate - Willkommensseite generiert für: {names_str} (site: {site})")
|
||||
print(f"✅ Generate - Willkommensseite gespeichert in: {filepath}")
|
||||
print(f"✅ Generate - Willkommensseite generiert für: {names_str} "
|
||||
f"(site: {site}, screens: {', '.join(screens)})")
|
||||
return WELCOME_FILENAME
|
||||
except Exception as e:
|
||||
print(f"❌ Error saving welcome page: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def add_customer_to_lobby_playlist(html_filename, site="stuttgart"):
|
||||
"""Fügt die Willkommensseite (welcome.html) an Position 0 in die Lobby-Playlist ein.
|
||||
Entfernt vorher alle vorhandenen welcome.html-Einträge (als String oder Dict).
|
||||
def _strip_welcome(playlist, filename):
|
||||
"""Entfernt alle welcome.html-Einträge aus einer Playlist (String- und Dict-Form)."""
|
||||
playlist[:] = [
|
||||
item for item in playlist
|
||||
if not (
|
||||
(isinstance(item, str) and item == filename) or
|
||||
(isinstance(item, dict) and item.get("name") == filename)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def add_customer_to_playlists(html_filename, site="stuttgart", screens=None, valid_until=None):
|
||||
"""Setzt die Willkommensseite an Position 0 der Playlist jedes Zielscreens.
|
||||
|
||||
screens: Liste der Zielscreens; None → aus der Konfiguration ermitteln.
|
||||
valid_until: optionales Datum "YYYY-MM-DD". Ist es gesetzt, bekommt der
|
||||
Eintrag eine Zeitsteuerung und verschwindet danach von selbst.
|
||||
|
||||
Aus allen übrigen Screens des Standorts wird die Seite entfernt, damit ein
|
||||
abgewählter Screen nicht mit einer alten Begrüßung stehen bleibt.
|
||||
Importiert load_config/save_config aus app.py (kein Zirkelimport)."""
|
||||
try:
|
||||
from app import load_config, save_config
|
||||
|
||||
config = load_config()
|
||||
sites_cfg = config.setdefault("sites", {})
|
||||
site_cfg = sites_cfg.setdefault(site, {"screens": {}})
|
||||
screens = site_cfg.setdefault("screens", {})
|
||||
lobby = screens.setdefault("lobby", {})
|
||||
playlist = lobby.setdefault("playlist", [])
|
||||
site_cfg = config.setdefault("sites", {}).setdefault(site, {"screens": {}})
|
||||
all_screens = site_cfg.setdefault("screens", {})
|
||||
|
||||
if screens is None:
|
||||
screens = welcome_target_screens(config, site)
|
||||
|
||||
# Nur Screens, die es wirklich gibt - kein stillschweigendes Anlegen
|
||||
targets = [name for name in screens if name in all_screens]
|
||||
|
||||
entry = {"name": html_filename, "enabled": True}
|
||||
if valid_until:
|
||||
# Ganztaegig bis einschliesslich valid_until
|
||||
entry["schedule"] = {
|
||||
"mode": "daily",
|
||||
"start": "00:00",
|
||||
"end": "23:59",
|
||||
"valid_until": valid_until
|
||||
}
|
||||
|
||||
for name, screen_cfg in all_screens.items():
|
||||
if not isinstance(screen_cfg, dict):
|
||||
continue
|
||||
playlist = screen_cfg.setdefault("playlist", [])
|
||||
_strip_welcome(playlist, html_filename)
|
||||
if name in targets:
|
||||
playlist.insert(0, dict(entry))
|
||||
|
||||
full_filename = f"{html_filename}"
|
||||
# Alte welcome.html-Einträge entfernen (sowohl String- als auch Dict-Form)
|
||||
playlist[:] = [
|
||||
item for item in playlist
|
||||
if not (
|
||||
(isinstance(item, str) and item == full_filename) or
|
||||
(isinstance(item, dict) and item.get("name") == full_filename)
|
||||
)
|
||||
]
|
||||
playlist.insert(0, full_filename)
|
||||
save_config(config)
|
||||
print(f"✅ Generate - Willkommensseite in Lobby-Playliste platziert (site: {site})")
|
||||
return True
|
||||
|
||||
if targets:
|
||||
print(f"✅ Generate - Willkommensseite platziert (site: {site}, "
|
||||
f"screens: {', '.join(targets)})")
|
||||
else:
|
||||
print(f"⚠️ Generate - Kein Zielscreen vorhanden (site: {site})")
|
||||
return bool(targets)
|
||||
except Exception as e:
|
||||
print(f"❌ Generate - Error adding to playlist: {e}")
|
||||
return False
|
||||
|
||||
@@ -344,13 +344,21 @@
|
||||
<label class="form-check-label">Videos anzeigen</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch mb-4">
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
name="stay_on_first"
|
||||
{% if cfg.stay_on_first %}checked{% endif %}>
|
||||
<label class="form-check-label">Auf erstem Element stehen bleiben (kein Refresh)</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch mb-1">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
name="welcome_enabled"
|
||||
{% if cfg.welcome_enabled %}checked{% endif %}>
|
||||
<label class="form-check-label">Willkommensseite hier anzeigen</label>
|
||||
</div>
|
||||
<div class="form-hint mb-4">Erzeugte Willkommensseiten werden auf allen so markierten Screens dieses Standorts vorne in die Playlist gesetzt.</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Einstellungen speichern
|
||||
</button>
|
||||
|
||||
@@ -153,9 +153,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="mt-4 mb-3">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="valid_until">Anzeigen bis <span class="text-muted">(optional)</span></label>
|
||||
<input type="date" class="form-control" id="valid_until" name="valid_until"
|
||||
value="{{ request.form.get('valid_until', '') }}" style="max-width: 260px;">
|
||||
<div class="form-hint">Ohne Angabe bleibt die Willkommensseite stehen, bis sie von Hand entfernt wird. Mit Datum verschwindet sie nach diesem Tag auf allen Screens von selbst.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 text-muted">
|
||||
<p class="mb-1">Das System sucht automatisch nach den Kundenlogos</p>
|
||||
<p class="mb-0">und erstellt eine angepasste Willkommensseite für die Lobby-Playlist.</p>
|
||||
<p class="mb-0">und erstellt eine angepasste Willkommensseite.</p>
|
||||
<p class="mb-1">Sie erscheint auf allen Screens dieses Standorts, bei denen im Tab „Einstellungen" die Option <em>Willkommensseite hier anzeigen</em> aktiv ist{% if welcome_screens %} – aktuell: <strong>{{ welcome_screens | join(', ') }}</strong>{% else %} – <strong>aktuell keiner</strong>{% endif %}.</p>
|
||||
<p class="mb-0 small"><i class="ti ti-markdown me-1"></i>Ansprechpartner unterstützen <strong>fett</strong>, <em>kursiv</em>, <code>Code</code>, Aufzählungen (<code>- </code>) sowie Zeilenumbrüche.</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -249,7 +249,9 @@
|
||||
<p>Auf der Willkommensseite können bis zu 3 Kundennamen eingegeben werden. Die Logos werden automatisch via OpenAI + Brandfetch gesucht.</p>
|
||||
<ul>
|
||||
<li>Aufruf über <code>/willkommen?site=<standort></code> (oder Menüpunkt)</li>
|
||||
<li>Die generierte <code>welcome.html</code> wird automatisch an den Anfang der Lobby-Playlist gesetzt</li>
|
||||
<li><strong>Zielscreens:</strong> Die generierte <code>welcome.html</code> wird automatisch an den Anfang der Playlist <em>jedes</em> Screens gesetzt, bei dem im Tab "Einstellungen" die Option <em>Willkommensseite hier anzeigen</em> aktiv ist. So lässt sich dieselbe Begrüßung auf mehreren Screens zeigen, ohne sie mehrfach anzulegen. Ist an einem Standort nichts eingestellt, geht sie wie bisher in die Lobby.</li>
|
||||
<li>Wird ein Screen später abgewählt, verschwindet die Willkommensseite dort beim nächsten Generieren automatisch.</li>
|
||||
<li><strong>Anzeigen bis:</strong> Optionales Datum im Formular. Ist es gesetzt, verschwindet die Willkommensseite nach diesem Tag auf allen Screens von selbst – kein manuelles Aufräumen nach dem Besuch. Ohne Angabe bleibt sie stehen.</li>
|
||||
<li>Pro Standort kann ein eigenes Hintergrundbild hochgeladen werden (Tab "Medien" im Standort-Dashboard)</li>
|
||||
<li>Pro Kunde können <strong>Ansprechpartner</strong> eingegeben werden (Markdown-Format):
|
||||
<ul>
|
||||
|
||||
Reference in New Issue
Block a user