Compare commits

..

8 Commits

Author SHA1 Message Date
Erik Thiele
1dbb88538d chore: Version auf 6.2.2 heben
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:59:53 +02:00
Erik Thiele
f8d9539959 feat: Erklaerungssatz unter die Kundenfelder ziehen
"Das System sucht automatisch nach den Kundenlogos und erstellt eine
angepasste Willkommensseite." stand bisher ganz unten bei den Angaben
zu Zielscreens und Markdown. Der Satz beschreibt aber, was mit den
eingegebenen Kundennamen passiert, und steht jetzt direkt darunter -
gefolgt vom Hinweis zum Logo-Upload und der bestehenden Trennlinie vor
"Anzeigen bis". Aus zwei Absaetzen wurde ein Satz.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:50:16 +02:00
Erik Thiele
7e2f9a404d feat: Trennlinie zwischen Ansprechpartner und Logo-Upload
Je Kundenspalte eine <hr class="mt-4 mb-3"> zwischen dem
Ansprechpartner-Feld und "Logo hochladen" - gleiche Gestaltung wie die
uebrigen Abschnittstrenner der Seite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:45:19 +02:00
Erik Thiele
7f9a1632b3 feat: gewaehlte Logo-Datei im Formular anzeigen
Ein Neuladen der Willkommensseite - etwa beim Wechsel des Standorts -
leert alle <input type="file">. Die zuvor gewaehlte Datei ist damit weg,
ohne dass es auffaellt, und das Absenden erzeugt eine Willkommensseite
ohne das hochgeladene Logo.

Unter jedem Datei-Feld steht jetzt "Keine Datei gewählt" bzw. nach der
Auswahl Name und Groesse der Datei. Damit ist vor dem Absenden
erkennbar, ob wirklich etwas angehaengt ist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 16:01:35 +02:00
Erik Thiele
556ccb211a fix: Zugriffsprotokoll von gunicorn aktivieren
Ohne --access-logfile protokolliert gunicorn keine Requests. Im Log war
dadurch nicht erkennbar, ob eine Anfrage den Container ueberhaupt
erreicht - beim Nachstellen eines Upload-Problems fehlte damit die
grundlegendste Information.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 15:57:46 +02:00
Erik Thiele
8f0e702cca fix: PYTHONUNBUFFERED im Container setzen
Ohne die Variable puffert Python stdout blockweise, sobald es kein
Terminal ist. Saemtliche print()-Ausgaben der App - darunter die
Diagnosezeilen zum Logo-Upload - erschienen dadurch nicht in
"docker compose logs", sondern blieben im Puffer haengen. In der
gesamten Log-Historie des Containers stand kein einziger APP.PY-Eintrag.
Tracebacks waren sichtbar, weil Flask sie ueber stderr ausgibt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 15:55:23 +02:00
Erik Thiele
fe2846dfa6 chore: Upload-Diagnose im Willkommensseiten-Formular
Protokolliert je Kundenfeld, ob eine Datei angekommen ist. Ein
fehlendes enctype im Formular - etwa wenn der Browser eine aeltere
Fassung der Seite aus dem Cache liefert - sieht serverseitig sonst
genauso aus wie "kein Logo hochgeladen", und der Unterschied war aus
dem Log nicht erkennbar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 15:37:44 +02:00
Erik Thiele
a25566aab4 fix: Logo nicht komplett wegradieren, wenn es dem Hintergrund aehnelt
Ein helles Logo auf hellem Grund liegt innerhalb der Farbtoleranz des
Flood Fill. Der Fuellvorgang lief vom Rand in das Logo hinein und
loeschte es mit - das Ergebnis war ein vollstaendig transparentes PNG,
auf der Willkommensseite also gar kein Logo. Ohne jede Rueckmeldung:
die bisherige Pruefung schlug nur an, wenn *nichts* entfernt wurde, nicht
wenn alles entfernt wurde.

remove_logo_background() legt jetzt vor dem Eingriff eine Kopie an und
misst danach ueber das Alpha-Histogramm, wie viel sichtbar geblieben
ist. Unter 2% gilt der Versuch als misslungen: das unveraenderte Bild
wird zurueckgegeben und die Oberflaeche nennt den Grund samt Auswegen
(transparentes PNG hochladen oder "Hintergrund behalten" ankreuzen).

Getestet mit Logos von dunkelrot bis reinweiss auf weissem Grund sowie
einem dunklen Logo auf schwarzem Grund.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 15:27:06 +02:00
7 changed files with 62 additions and 11 deletions

View File

@@ -124,7 +124,7 @@ Standorte (sites) sind die oberste Organisationsebene und gruppieren Screens.
- **Logo-Upload**: Pro Kunde kann im Willkommensseiten-Formular eine Bilddatei hochgeladen werden (`logo_file_1..3`); ein Upload hat Vorrang vor `search_customer_logo`. Ablage als `media/<site>/logo_<slot>.png`, Auslieferung über `/media/<site>/logos/<datei>` (die Route liest aus dem Standort-Verzeichnis). - **Logo-Upload**: Pro Kunde kann im Willkommensseiten-Formular eine Bilddatei hochgeladen werden (`logo_file_1..3`); ein Upload hat Vorrang vor `search_customer_logo`. Ablage als `media/<site>/logo_<slot>.png`, Auslieferung über `/media/<site>/logos/<datei>` (die Route liest aus dem Standort-Verzeichnis).
- **Keine neuen Unterordner unter `media/<site>/` anlegen.** `admin()` behandelt jeden Unterordner dort als Screen und persistiert ihn ueber `get_screen_config()` in der Konfiguration - ein Ordner `logos` erschien dadurch als Phantom-Screen, bekam eine `welcome.html` und liess sich ueber die Admin-UI bespielen und loeschen. Deshalb liegen Logos wie das Hintergrundbild als Dateien direkt im Standort-Verzeichnis. Der Name `logos` ist in der Screen-Erkennung zusaetzlich ausgenommen, damit Altbestaende nicht wieder auftauchen. Die URL traegt einen `?v=<timestamp>`, damit Player kein altes Logo aus dem Cache zeigen. - **Keine neuen Unterordner unter `media/<site>/` anlegen.** `admin()` behandelt jeden Unterordner dort als Screen und persistiert ihn ueber `get_screen_config()` in der Konfiguration - ein Ordner `logos` erschien dadurch als Phantom-Screen, bekam eine `welcome.html` und liess sich ueber die Admin-UI bespielen und loeschen. Deshalb liegen Logos wie das Hintergrundbild als Dateien direkt im Standort-Verzeichnis. Der Name `logos` ist in der Screen-Erkennung zusaetzlich ausgenommen, damit Altbestaende nicht wieder auftauchen. Die URL traegt einen `?v=<timestamp>`, damit Player kein altes Logo aus dem Cache zeigen.
- Nach dem Freistellen schneidet `trim_transparent_border()` die durchsichtigen Raender weg. Das ist kein Schoenheitsdetail: `.customer-logo` skaliert mit `object-fit: contain` auf 120px Hoehe, ein breiter Rand zaehlt dabei mit und laesst ein hochgeladenes Logo deutlich kleiner erscheinen als ein eng beschnittenes Brandfetch-Logo (gemessen: 30px statt 120px). Der Schnitt greift auch bei bereits transparenten PNGs, aber nicht bei abgewaehltem Freistellen - dort gibt es keinen transparenten Rand. - Nach dem Freistellen schneidet `trim_transparent_border()` die durchsichtigen Raender weg. Das ist kein Schoenheitsdetail: `.customer-logo` skaliert mit `object-fit: contain` auf 120px Hoehe, ein breiter Rand zaehlt dabei mit und laesst ein hochgeladenes Logo deutlich kleiner erscheinen als ein eng beschnittenes Brandfetch-Logo (gemessen: 30px statt 120px). Der Schnitt greift auch bei bereits transparenten PNGs, aber nicht bei abgewaehltem Freistellen - dort gibt es keinen transparenten Rand.
- `remove_logo_background()` arbeitet mit Pillow (Flood Fill von den Raendern, weiche Kante). **Bewusst kein ML-Modell** wie rembg: der Container laeuft mit read-only Root-FS ohne Modell-Cache. Flood Fill statt globalem Farbvergleich ist wesentlich - sonst verschwindet auch Weiss *innerhalb* des Logos. Bereits transparente Bilder werden unveraendert durchgereicht; bei unruhigem Bildrand bleibt das Bild unangetastet und die Funktion gibt einen Hinweis zurueck. - `remove_logo_background()` arbeitet mit Pillow (Flood Fill von den Raendern, weiche Kante). **Bewusst kein ML-Modell** wie rembg: der Container laeuft mit read-only Root-FS ohne Modell-Cache. Flood Fill statt globalem Farbvergleich ist wesentlich - sonst verschwindet auch Weiss *innerhalb* des Logos. Bereits transparente Bilder werden unveraendert durchgereicht; bei unruhigem Bildrand bleibt das Bild unangetastet und die Funktion gibt einen Hinweis zurueck. **Ebenso, wenn nach dem Fuellen weniger als 2% sichtbar waeren**: ein helles Logo auf hellem Grund liegt innerhalb der Toleranz, der Flood Fill frisst es mit auf und das Ergebnis waere ein leeres PNG - auf der Willkommensseite also gar kein Logo. In dem Fall wird die vor dem Eingriff angelegte Kopie zurueckgegeben.
- `welcome_data` in der Site-Config wird **feldweise** gespeichert (Slot 1-3, auch leere), nicht nur die erfolgreichen Kunden - sonst verschiebt sich die Zuordnung von Name, Logo und Ansprechpartner beim naechsten Aufruf. Ein zuvor hochgeladenes Logo wird beim erneuten Generieren wiederverwendet, solange der Kundenname im selben Feld unveraendert ist. - `welcome_data` in der Site-Config wird **feldweise** gespeichert (Slot 1-3, auch leere), nicht nur die erfolgreichen Kunden - sonst verschiebt sich die Zuordnung von Name, Logo und Ansprechpartner beim naechsten Aufruf. Ein zuvor hochgeladenes Logo wird beim erneuten Generieren wiederverwendet, solange der Kundenname im selben Feld unveraendert ist.
- **Der Schalter `welcome_enabled` wirkt sofort**: `update_screen` ruft bei einer Aenderung `sync_welcome_for_screen()`, das die vorhandene `welcome.html` in den Screen kopiert und vorne einfuegt bzw. beim Abschalten Eintrag und Datei entfernt. Kein erneutes Generieren noetig - die Seite ist je Standort identisch. Die Funktion aendert nur das uebergebene config-Dict, gespeichert wird vom Aufrufer. - **Der Schalter `welcome_enabled` wirkt sofort**: `update_screen` ruft bei einer Aenderung `sync_welcome_for_screen()`, das die vorhandene `welcome.html` in den Screen kopiert und vorne einfuegt bzw. beim Abschalten Eintrag und Datei entfernt. Kein erneutes Generieren noetig - die Seite ist je Standort identisch. Die Funktion aendert nur das uebergebene config-Dict, gespeichert wird vom Aufrufer.
- `save_welcome_page` legt zusaetzlich eine Standort-Kopie unter `media/<site>/welcome.html` an, die **nie geloescht** wird. Ohne sie waere nach dem Abschalten aller Screens keine Quelle mehr da und der Schalter wuerde stillschweigend nichts tun. - `save_welcome_page` legt zusaetzlich eine Standort-Kopie unter `media/<site>/welcome.html` an, die **nie geloescht** wird. Ohne sie waere nach dem Abschalten aller Screens keine Quelle mehr da und der Schalter wuerde stillschweigend nichts tun.

View File

@@ -14,7 +14,12 @@ RUN addgroup --system --gid 1000 app && \
USER app USER app
ENV HOME=/tmp ENV HOME=/tmp
# Ohne das puffert Python stdout blockweise, weil es kein Terminal ist -
# die print()-Ausgaben der App tauchen dann nicht in "docker compose logs" auf.
ENV PYTHONUNBUFFERED=1
EXPOSE 5005 EXPOSE 5005
CMD ["gunicorn", "-b", "0.0.0.0:5005", "-w", "4", "app:app"] # --access-logfile - schreibt jeden Request nach stdout. Ohne das protokolliert
# gunicorn keine Zugriffe, und man sieht nicht, ob eine Anfrage ueberhaupt ankommt.
CMD ["gunicorn", "-b", "0.0.0.0:5005", "-w", "4", "--access-logfile", "-", "app:app"]

View File

@@ -300,7 +300,7 @@ Maximal 3 Kunden eingeben → Logos werden via OpenAI + Brandfetch gesucht → `
**Logo-Upload:** Findet die automatische Suche kein Logo (oder passt es nicht), kann pro Kunde eine Bilddatei hochgeladen werden. Ein Upload hat Vorrang vor der Suche. Die Datei wird als `media/<site>/logo_<slot>.png` abgelegt und über `/media/<site>/logos/<datei>` ausgeliefert. **Bewusst als Datei im Standort-Verzeichnis, nicht in einem Unterordner:** `admin()` behandelt jeden Unterordner von `media/<site>/` als Screen und schreibt ihn dauerhaft in die Konfiguration – ein Ordner `logos` würde also als Phantom-Screen in der Oberfläche auftauchen. Das Hintergrundbild liegt aus demselben Grund als `media/<site>/background.*`. Der Ordnername `logos` ist zusätzlich reserviert und wird bei der Screen-Erkennung übersprungen. **Logo-Upload:** Findet die automatische Suche kein Logo (oder passt es nicht), kann pro Kunde eine Bilddatei hochgeladen werden. Ein Upload hat Vorrang vor der Suche. Die Datei wird als `media/<site>/logo_<slot>.png` abgelegt und über `/media/<site>/logos/<datei>` ausgeliefert. **Bewusst als Datei im Standort-Verzeichnis, nicht in einem Unterordner:** `admin()` behandelt jeden Unterordner von `media/<site>/` als Screen und schreibt ihn dauerhaft in die Konfiguration – ein Ordner `logos` würde also als Phantom-Screen in der Oberfläche auftauchen. Das Hintergrundbild liegt aus demselben Grund als `media/<site>/background.*`. Der Ordnername `logos` ist zusätzlich reserviert und wird bei der Screen-Erkennung übersprungen.
**Freistellen und Zuschneiden:** Beim Upload wird der Hintergrund automatisch entfernt, damit das Logo nicht in einem weissen Kasten auf dem Hintergrundbild steht. Anschliessend werden die durchsichtigen Ränder weggeschnitten (`trim_transparent_border`). Ohne diesen Schnitt erscheint ein hochgeladenes Logo kleiner als ein automatisch gefundenes: die Willkommensseite skaliert mit `object-fit: contain` auf eine feste Höhe von 120px, und ein breiter Rand zählt dabei mit – Brandfetch-Logos sind bereits eng beschnitten. Verfahren: Flood Fill von den Bildrändern her (bewusst kein globaler Farbvergleich – so bleibt Weiss *innerhalb* des Logos erhalten), weiche Kante gegen Treppeneffekte, Speicherung immer als PNG. Bereits transparente Bilder werden unverändert übernommen; ist der Bildrand nicht einfarbig, bleibt das Bild unangetastet und die Oberfläche weist darauf hin. Mit der Checkbox „Hintergrund behalten" lässt sich das Freistellen pro Kunde abschalten. Umgesetzt mit Pillow – bewusst ohne ML-Modell, da der Container mit read-only Root-FS läuft und kein Modell-Cache zur Verfügung steht. **Freistellen und Zuschneiden:** Beim Upload wird der Hintergrund automatisch entfernt, damit das Logo nicht in einem weissen Kasten auf dem Hintergrundbild steht. Anschliessend werden die durchsichtigen Ränder weggeschnitten (`trim_transparent_border`). Ohne diesen Schnitt erscheint ein hochgeladenes Logo kleiner als ein automatisch gefundenes: die Willkommensseite skaliert mit `object-fit: contain` auf eine feste Höhe von 120px, und ein breiter Rand zählt dabei mit – Brandfetch-Logos sind bereits eng beschnitten. Verfahren: Flood Fill von den Bildrändern her (bewusst kein globaler Farbvergleich – so bleibt Weiss *innerhalb* des Logos erhalten), weiche Kante gegen Treppeneffekte, Speicherung immer als PNG. Bereits transparente Bilder werden unverändert übernommen; ist der Bildrand nicht einfarbig, bleibt das Bild unangetastet und die Oberfläche weist darauf hin. Dasselbe gilt, wenn beim Freistellen fast nichts übrig bliebe – ein helles Logo auf hellem Grund liegt innerhalb der Farbtoleranz und würde sonst vollständig verschwinden, was auf der Willkommensseite als „kein Logo" ankäme. Mit der Checkbox „Hintergrund behalten" lässt sich das Freistellen pro Kunde abschalten. Umgesetzt mit Pillow – bewusst ohne ML-Modell, da der Container mit read-only Root-FS läuft und kein Modell-Cache zur Verfügung steht.
**Anzeigen bis:** Optionales Datum im Formular. Ist es gesetzt, bekommt der Eintrag eine Zeitsteuerung (`daily 00:00–23:59` mit `valid_until`) und verschwindet danach auf allen Screens von selbst. **Anzeigen bis:** Optionales Datum im Formular. Ist es gesetzt, bekommt der Eintrag eine Zeitsteuerung (`daily 00:00–23:59` mit `valid_until`) und verschwindet danach auf allen Screens von selbst.

7
app.py
View File

@@ -50,7 +50,7 @@ CONFIG_FILE = os.path.join(BASE_DIR, "config.json") # Persistente Konfiguration
USERS_FILE = os.path.join(BASE_DIR, "users.json") # User-Datenbank USERS_FILE = os.path.join(BASE_DIR, "users.json") # User-Datenbank
HISTORY_FILE = os.path.join(BASE_DIR, "history.json") # Aktivitätsverlauf HISTORY_FILE = os.path.join(BASE_DIR, "history.json") # Aktivitätsverlauf
APP_VERSION = "6.2.1" APP_VERSION = "6.2.2"
UPLOAD_EXTENSIONS = {".jpg", ".jpeg", ".png", ".mp4"} UPLOAD_EXTENSIONS = {".jpg", ".jpeg", ".png", ".mp4"}
app = Flask(__name__) app = Flask(__name__)
@@ -959,6 +959,11 @@ def add_customer():
if not name: if not name:
continue continue
datei = request.files.get(f"logo_file_{i + 1}") datei = request.files.get(f"logo_file_{i + 1}")
# Eindeutig protokollieren, ob ueberhaupt eine Datei ankam -
# ein fehlendes enctype im Formular (etwa aus dem Browser-Cache)
# sieht sonst genauso aus wie "kein Logo hochgeladen".
print(f"🔍 APP.PY - Slot {i + 1} ({name}): Upload = "
f"{datei.filename if datei and datei.filename else 'keiner'}")
if datei and datei.filename: if datei and datei.filename:
url, hinweis = generate_welcome_page.save_uploaded_logo( url, hinweis = generate_welcome_page.save_uploaded_logo(
datei, site, i + 1, datei, site, i + 1,

View File

@@ -112,6 +112,10 @@ def remove_logo_background(image, tolerance=LOGO_TOLERANCE):
return img, ("Der Bildrand ist nicht einfarbig – das Logo wurde unverändert " return img, ("Der Bildrand ist nicht einfarbig – das Logo wurde unverändert "
"übernommen. Am besten ein PNG mit transparentem Hintergrund hochladen.") "übernommen. Am besten ein PNG mit transparentem Hintergrund hochladen.")
# Vor dem Eingriff sichern: ist das Logo farblich zu nah am Hintergrund,
# frisst der Flood Fill es mit auf und wir muessen zurueckfallen koennen.
original = img.copy()
# Flood Fill von allen Randpixeln aus # Flood Fill von allen Randpixeln aus
besucht = bytearray(w * h) besucht = bytearray(w * h)
queue = deque() queue = deque()
@@ -155,7 +159,20 @@ def remove_logo_background(image, tolerance=LOGO_TOLERANCE):
if d < tolerance * 2: if d < tolerance * 2:
px[x, y] = (r, g, b, int(255 * max(0.0, (d - tolerance) / tolerance))) px[x, y] = (r, g, b, int(255 * max(0.0, (d - tolerance) / tolerance)))
if not any(px[x, y][3] == 0 for x in range(0, w, max(1, w // 50)) for y in (0, h - 1)): # Wie viel ist uebrig geblieben? Ein helles Logo auf hellem Grund liegt
# innerhalb der Toleranz und wuerde komplett mit entfernt - das Ergebnis
# waere ein leeres Bild und auf der Willkommensseite gar kein Logo.
histogramm = img.getchannel("A").histogram()
sichtbar = sum(histogramm[1:])
anteil = sichtbar / float(w * h)
if anteil < 0.02:
return original, ("Das Logo ist farblich zu nah am Hintergrund – beim Freistellen "
"wäre fast nichts übrig geblieben. Es wurde deshalb unverändert "
"übernommen. Am besten ein PNG mit transparentem Hintergrund "
"hochladen oder die Option \u201eHintergrund behalten\u201c ankreuzen.")
if anteil > 0.999:
return img, "Es wurde kein zusammenhängender Hintergrund gefunden." return img, "Es wurde kein zusammenhängender Hintergrund gefunden."
return img, None return img, None

View File

@@ -153,9 +153,13 @@
<textarea class="form-control" id="contact_{{ i + 1 }}" name="contact_{{ i + 1 }}" rows="4" <textarea class="form-control" id="contact_{{ i + 1 }}" name="contact_{{ i + 1 }}" rows="4"
placeholder="{{ slots[i][1] }}">{{ contacts[i] if contacts else '' }}</textarea> placeholder="{{ slots[i][1] }}">{{ contacts[i] if contacts else '' }}</textarea>
<label class="form-label mt-2" for="logo_file_{{ i + 1 }}">Logo hochladen (optional)</label> <hr class="mt-4 mb-3">
<input type="file" class="form-control" id="logo_file_{{ i + 1 }}"
name="logo_file_{{ i + 1 }}" accept="image/*"> <label class="form-label" for="logo_file_{{ i + 1 }}">Logo hochladen (optional)</label>
<input type="file" class="form-control logo-datei" id="logo_file_{{ i + 1 }}"
name="logo_file_{{ i + 1 }}" accept="image/*"
data-status="logo_status_{{ i + 1 }}">
<div class="form-hint" id="logo_status_{{ i + 1 }}">Keine Datei gewählt</div>
<div class="form-check mt-1"> <div class="form-check mt-1">
<input class="form-check-input" type="checkbox" <input class="form-check-input" type="checkbox"
id="logo_keep_bg_{{ i + 1 }}" name="logo_keep_bg_{{ i + 1 }}"> id="logo_keep_bg_{{ i + 1 }}" name="logo_keep_bg_{{ i + 1 }}">
@@ -165,6 +169,10 @@
{% endfor %} {% endfor %}
</div> </div>
<div class="text-muted mb-2">
Das System sucht automatisch nach den Kundenlogos und erstellt eine angepasste Willkommensseite.
</div>
<div class="text-muted small mb-3"> <div class="text-muted small mb-3">
<i class="ti ti-info-circle me-1"></i>Ein hochgeladenes Logo hat Vorrang vor der automatischen Suche. <i class="ti ti-info-circle me-1"></i>Ein hochgeladenes Logo hat Vorrang vor der automatischen Suche.
Der Hintergrund wird automatisch freigestellt, damit das Logo auf der Willkommensseite nicht in einem Der Hintergrund wird automatisch freigestellt, damit das Logo auf der Willkommensseite nicht in einem
@@ -182,8 +190,6 @@
</div> </div>
<div class="mb-3 text-muted"> <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.</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-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> <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> </div>
@@ -247,5 +253,23 @@
{% include "_footer.html" %} {% include "_footer.html" %}
</div> <!-- /page --> </div> <!-- /page -->
<script>
/* Gewaehlte Logo-Datei sichtbar machen. Ein Neuladen der Seite - etwa beim
Wechsel des Standorts - leert Datei-Felder, ohne dass man es bemerkt. */
document.querySelectorAll(".logo-datei").forEach(function (feld) {
var status = document.getElementById(feld.dataset.status);
feld.addEventListener("change", function () {
if (feld.files && feld.files.length) {
var kb = Math.round(feld.files[0].size / 1024);
status.textContent = "Gewählt: " + feld.files[0].name + " (" + kb + " KB)";
status.className = "form-hint text-success";
} else {
status.textContent = "Keine Datei gewählt";
status.className = "form-hint";
}
});
});
</script>
</body> </body>
</html> </html>

View File

@@ -253,7 +253,7 @@
<li><strong>Der Schalter wirkt sofort:</strong> Schaltest du ihn bei einem weiteren Screen ein, erscheint eine bereits erzeugte Willkommensseite dort umgehend in der Playlist – ohne dass du sie neu generieren musst. Schaltest du ihn aus, verschwindet sie dort wieder.</li> <li><strong>Der Schalter wirkt sofort:</strong> Schaltest du ihn bei einem weiteren Screen ein, erscheint eine bereits erzeugte Willkommensseite dort umgehend in der Playlist – ohne dass du sie neu generieren musst. Schaltest du ihn aus, verschwindet sie dort wieder.</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><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><strong>Logo hochladen:</strong> Findet die automatische Suche kein Logo oder passt es nicht, kann pro Kunde eine eigene Bilddatei hochgeladen werden. Ein Upload hat immer Vorrang vor der automatischen Suche.</li> <li><strong>Logo hochladen:</strong> Findet die automatische Suche kein Logo oder passt es nicht, kann pro Kunde eine eigene Bilddatei hochgeladen werden. Ein Upload hat immer Vorrang vor der automatischen Suche.</li>
<li><strong>Freistellen:</strong> Der Hintergrund eines hochgeladenen Logos wird automatisch entfernt, damit es auf der Willkommensseite nicht in einem weissen Kasten steht. Das gelingt am besten bei einem Logo auf einfarbigem Grund – ein PNG mit bereits transparentem Hintergrund wird unverändert übernommen. Bei unruhigem Bildrand bleibt das Bild unangetastet und es erscheint ein Hinweis. Mit „Hintergrund behalten" lässt sich das Freistellen abschalten.</li> <li><strong>Freistellen:</strong> Der Hintergrund eines hochgeladenen Logos wird automatisch entfernt, damit es auf der Willkommensseite nicht in einem weissen Kasten steht. Das gelingt am besten bei einem Logo auf einfarbigem Grund – ein PNG mit bereits transparentem Hintergrund wird unverändert übernommen. Bei unruhigem Bildrand – oder wenn das Logo farblich zu nah am Hintergrund liegt und beim Freistellen verschwinden würde – bleibt das Bild unangetastet und es erscheint ein Hinweis. Mit „Hintergrund behalten" lässt sich das Freistellen abschalten.</li>
<li><strong>Grösse:</strong> Nach dem Freistellen werden leere Ränder weggeschnitten, damit das hochgeladene Logo auf der Willkommensseite genauso gross erscheint wie ein automatisch gefundenes.</li> <li><strong>Grösse:</strong> Nach dem Freistellen werden leere Ränder weggeschnitten, damit das hochgeladene Logo auf der Willkommensseite genauso gross erscheint wie ein automatisch gefundenes.</li>
<li>Ein einmal hochgeladenes Logo bleibt beim erneuten Generieren erhalten, solange der Kundenname im selben Feld steht.</li> <li>Ein einmal hochgeladenes Logo bleibt beim erneuten Generieren erhalten, solange der Kundenname im selben Feld steht.</li>
<li>Pro Standort kann ein eigenes Hintergrundbild hochgeladen werden (Tab "Medien" im Standort-Dashboard)</li> <li>Pro Standort kann ein eigenes Hintergrundbild hochgeladen werden (Tab "Medien" im Standort-Dashboard)</li>