Files
TK-Wiki-Newsletter/app/templates/dashboard.html
smueller 48410d39da Add editor exclusion defaults and newsletter filtering controls.
This makes it possible to exclude specific editors (e.g. Aranzinger) from edited-article results by default, including dashboard generation and scheduled SMTP runs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 10:53:01 +02:00

426 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<div class="page-header">
<h2>Wiki Newsletter</h2>
<p class="page-lead">Wiki-Änderungen filtern, Newsletter generieren und für Outlook aufbereiten.</p>
</div>
<div class="stats-row">
<div class="stat-card accent-new">
<div class="stat-value">{{ articles_new|length }}</div>
<div class="stat-label">Neue Artikel</div>
</div>
<div class="stat-card accent-edit">
<div class="stat-value">{{ articles_edited|length }}</div>
<div class="stat-label">Bearbeitete Artikel</div>
</div>
<div class="stat-card accent-total">
<div class="stat-value">{{ articles_new|length + articles_edited|length }}</div>
<div class="stat-label">Gesamt</div>
</div>
</div>
<div class="layout-2col">
<div class="main-col">
<section class="card card-accent-orange">
<div class="card-header">
<div>
<h3>Newsletter erstellen</h3>
<p class="card-desc">Zeitraum und Filter wählen, dann generieren.</p>
</div>
</div>
<form method="post" action="/dashboard" class="form-grid">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div class="form-row">
<label>Zeitraum
<select name="period">
<option value="days" {% if filters.period != 'last_month' %}selected{% endif %}>Letzte X Tage</option>
<option value="last_month" {% if filters.period == 'last_month' %}selected{% endif %}>Letzter Monat (voller Kalendermonat)</option>
</select>
</label>
<label>Tage (nur bei „Letzte X Tage“)
<input type="number" name="days" min="1" max="90" value="{{ filters.days }}">
</label>
</div>
<div class="form-row">
<label>Kategorie (optional)
<input type="text" name="category" value="{{ filters.category }}" placeholder="z. B. Linux">
</label>
<label>Bearbeiter ausschließen (optional)
<input type="text" name="excluded_users" value="{{ filters.excluded_users }}" placeholder="z. B. Aranzinger, Testuser">
</label>
</div>
<p class="hint">Nur bei „Bearbeitete Artikel“ relevant: Benutzer in dieser Liste werden aus den bearbeiteten Artikeln entfernt.</p>
<label>Artikel-Auswahl
<select name="article_type">
<option value="all" {% if filters.article_type == 'all' %}selected{% endif %}>Alle (neue &amp; bearbeitete)</option>
<option value="new" {% if filters.article_type == 'new' %}selected{% endif %}>Nur neue Artikel</option>
<option value="edited" {% if filters.article_type == 'edited' %}selected{% endif %}>Nur bearbeitete Artikel (keine neu erstellten)</option>
</select>
</label>
<fieldset class="display-options">
<legend>Anzeige im Newsletter</legend>
<label class="checkbox">
<input type="checkbox" name="show_date" value="true" {% if filters.display.show_date %}checked{% endif %}>
Datum anzeigen
</label>
<label class="checkbox">
<input type="checkbox" name="show_user" value="true" {% if filters.display.show_user %}checked{% endif %}>
Benutzer anzeigen
</label>
<label class="checkbox">
<input type="checkbox" name="show_category" value="true" {% if filters.display.show_category %}checked{% endif %}>
Kategorie anzeigen
</label>
</fieldset>
<label>Top-Highlights (optional)
<textarea name="highlights" rows="3" maxlength="{{ highlights_max_length }}" placeholder="Ein Artikeltitel pro Zeile muss exakt einem gefundenen Artikel entsprechen (max. 3)">{{ filters.highlights }}</textarea>
</label>
<p class="hint">Hervorgehobene Artikel erscheinen als „Top Highlights“-Box oben im Newsletter.</p>
<label>Redaktionsnotiz (optional)
<textarea name="editor_tip" rows="3" maxlength="{{ editor_tip_max_length }}" placeholder="Optionaler Hinweis der Redaktion für diese Ausgabe…">{{ filters.editor_tip }}</textarea>
</label>
<p class="hint">Wird nur angezeigt, wenn Text hinterlegt ist erscheint als eigene Box im Newsletter.</p>
{% if wiki_error %}
<p class="error">{{ wiki_error }}</p>
{% endif %}
{% if user.role in ["admin", "editor"] %}
<button type="submit" class="btn-primary">Newsletter generieren</button>
{% else %}
<p class="hint">Sie haben Leserechte. Bitte einen Editor oder Admin bitten, den Newsletter zu generieren.</p>
{% endif %}
</form>
</section>
<section class="card card-accent-orange">
<div class="card-header">
<div>
<h3>Vorschau &amp; Export</h3>
<p class="card-desc">Formatierten Newsletter ansehen und für Outlook kopieren.</p>
</div>
</div>
<p class="hint">Die Vorschau unten zeigt <strong>exakt</strong>, wie der Newsletter aussieht. „Vorschau kopieren“ übernimmt dieses Layout 1:1 in die Zwischenablage.</p>
<div class="copy-actions">
<button type="button" id="copy-outlook-btn" class="btn-primary">Vorschau kopieren</button>
<button type="button" id="open-outlook-btn" class="btn-secondary">Im Browser öffnen</button>
<button type="button" id="download-html-btn" class="btn-secondary">HTML herunterladen</button>
<button type="button" id="copy-html-source-btn" class="btn-outline">HTML-Quelltext</button>
<span id="copy-status" class="copy-status" aria-live="polite"></span>
</div>
<div class="preview-banner">Live-Vorschau so wird der Newsletter dargestellt</div>
<iframe id="newsletter-preview-frame" class="newsletter-preview-frame" title="Newsletter Vorschau"></iframe>
<textarea id="newsletter-html-source" class="hidden-source" hidden readonly>{{ raw_html }}</textarea>
<textarea id="newsletter-outlook-source" class="hidden-source" hidden readonly>{{ outlook_html }}</textarea>
<textarea id="newsletter-plain-source" class="hidden-source" hidden readonly>{{ plain_text }}</textarea>
{% if send_result %}
<div class="send-banner send-banner-{{ send_result.status }}">{{ send_result.message }}</div>
{% endif %}
{% if raw_html and (articles_new or articles_edited) %}
<div class="send-section">
<h4 style="margin:0 0 0.35rem;">Per E-Mail versenden</h4>
<p class="hint" style="margin:0 0 0.75rem;">Bitte oben die Vorschau prüfen. Beim Versenden wird genau dieser Newsletter (aktuelle Filter) an die Verteilerliste geschickt.</p>
<p class="hint" style="margin:0 0 0.75rem;">
<strong>Betreff:</strong> {{ subject }}<br>
{% if can_view_sensitive %}
<strong>Empfänger:</strong> {{ smtp.recipients if smtp.recipients else "— keine konfiguriert —" }}
{% else %}
<strong>Empfänger:</strong> Nur für Editor/Admin sichtbar
{% endif %}
{% if smtp.enabled != "true" %}<br><strong style="color:var(--tk-orange-dark);">SMTP ist deaktiviert</strong> bitte zuerst in „SMTP-Konfiguration“ aktivieren.{% endif %}
</p>
{% if user.role in ["admin", "editor"] %}
<form method="post" action="/newsletter/send-now" onsubmit="return confirm('Diesen Newsletter jetzt per E-Mail an die Verteilerliste senden?');">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<input type="hidden" name="period" value="{{ filters.period }}">
<input type="hidden" name="days" value="{{ filters.days }}">
<input type="hidden" name="category" value="{{ filters.category }}">
<input type="hidden" name="excluded_users" value="{{ filters.excluded_users }}">
<input type="hidden" name="article_type" value="{{ filters.article_type }}">
<input type="hidden" name="show_date" value="{{ 'true' if filters.display.show_date else 'false' }}">
<input type="hidden" name="show_user" value="{{ 'true' if filters.display.show_user else 'false' }}">
<input type="hidden" name="show_category" value="{{ 'true' if filters.display.show_category else 'false' }}">
<input type="hidden" name="highlights" value="{{ filters.highlights }}">
<input type="hidden" name="editor_tip" value="{{ filters.editor_tip }}">
<button type="submit" class="btn-primary">Newsletter jetzt senden</button>
</form>
{% else %}
<p class="hint">Sie haben Leserechte Versand nur für Editor/Admin.</p>
{% endif %}
</div>
{% endif %}
</section>
{% if filters.article_type != 'edited' %}
<section class="card card-accent-gray">
<div class="card-header">
<h3>Neue Artikel <span class="count-badge orange">{{ articles_new|length }}</span></h3>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Titel</th>
{% if filters.display.show_date %}<th>Zeit</th>{% endif %}
{% if filters.display.show_user %}<th>Benutzer</th>{% endif %}
{% if filters.display.show_category %}<th>Kategorien</th>{% endif %}
</tr>
</thead>
<tbody>
{% for item in articles_new %}
<tr>
<td><a href="{{ wiki_article_url(item.title) }}" target="_blank" rel="noopener">{{ item.title }}</a></td>
{% if filters.display.show_date %}<td>{{ item.timestamp }}</td>{% endif %}
{% if filters.display.show_user %}<td>{{ item.user }}</td>{% endif %}
{% if filters.display.show_category %}<td>{{ item.categories|join(", ") }}</td>{% endif %}
</tr>
{% else %}
<tr class="empty-row"><td colspan="4">Keine neuen Artikel im gewählten Zeitraum.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
{% endif %}
{% if filters.article_type != 'new' %}
<section class="card card-accent-gray">
<div class="card-header">
<h3>Bearbeitete Artikel <span class="count-badge">{{ articles_edited|length }}</span></h3>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Titel</th>
{% if filters.display.show_date %}<th>Zeit</th>{% endif %}
{% if filters.display.show_user %}<th>Benutzer</th>{% endif %}
{% if filters.display.show_category %}<th>Kategorien</th>{% endif %}
</tr>
</thead>
<tbody>
{% for item in articles_edited %}
<tr>
<td><a href="{{ wiki_article_url(item.title) }}" target="_blank" rel="noopener">{{ item.title }}</a></td>
{% if filters.display.show_date %}<td>{{ item.timestamp }}</td>{% endif %}
{% if filters.display.show_user %}<td>{{ item.user }}</td>{% endif %}
{% if filters.display.show_category %}<td>{{ item.categories|join(", ") }}</td>{% endif %}
</tr>
{% else %}
<tr class="empty-row"><td colspan="4">Keine bearbeiteten Artikel im gewählten Zeitraum.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
{% endif %}
<details class="card-details">
<summary>Weitere Ausgabeformate</summary>
<div class="details-body">
<h4 style="margin:1rem 0 0.5rem;font-size:0.95rem;">Nur-Text (Fallback)</h4>
<p class="hint">URLs als Klartext für klickbare Links die Vorschau nutzen.</p>
<textarea class="readonly-output" rows="10" readonly>{{ plain_text }}</textarea>
<h4 style="margin:1.25rem 0 0.5rem;font-size:0.95rem;">HTML-Quelltext</h4>
<p class="hint">Öffnet ein Fenster mit markiertem Text dort <strong>Strg+C</strong> drücken.</p>
<div class="textarea-actions">
<button type="button" id="copy-html-source-btn-2" class="btn-secondary">HTML-Quelltext kopieren</button>
<button type="button" id="download-html-source-btn" class="btn-outline">Herunterladen</button>
</div>
<textarea id="newsletter-html-display" class="readonly-output" rows="8" readonly>{{ raw_html }}</textarea>
</div>
</details>
{% if user.role == "admin" %}
<details class="card-details">
<summary>SMTP-Konfiguration</summary>
<div class="details-body">
<form method="post" action="/admin/smtp" class="form-grid">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<label class="checkbox">
<input type="checkbox" name="enabled" value="true" {% if smtp.enabled == "true" %}checked{% endif %}>
SMTP-Versand aktivieren
</label>
<div class="form-row">
<label>SMTP-Host
<input type="text" name="host" value="{{ smtp.host }}" placeholder="mail.example.com">
</label>
<label>SMTP-Port
<input type="number" name="port" value="{{ smtp.port }}">
</label>
</div>
<div class="form-row">
<label>Benutzername
<input type="text" name="username" value="{{ smtp.username }}">
</label>
<label>Passwort
<input type="password" name="password" autocomplete="new-password" placeholder="{% if smtp.password_configured == 'true' %}•••••••• (unverändert lassen){% else %}SMTP-Passwort{% endif %}">
</label>
</div>
<label>Absender-Name
<input type="text" name="from_name" value="{{ smtp.from_name }}" placeholder="Thomas-Krenn Wiki-Team">
</label>
<div class="form-row">
<label>Absender E-Mail
<input type="email" name="from_email" value="{{ smtp.from_email }}">
</label>
<label>Antwort-Adresse (Reply-To)
<input type="email" name="reply_to" value="{{ smtp.reply_to }}" placeholder="wiki-team@firma.de">
</label>
</div>
<label class="checkbox">
<input type="checkbox" name="use_tls" value="true" {% if smtp.use_tls == "true" %}checked{% endif %}>
STARTTLS verwenden
</label>
<label>Verteilerliste (kommagetrennt)
<textarea rows="3" name="recipients" placeholder="kollege@firma.de, team@firma.de">{{ smtp.recipients }}</textarea>
</label>
<fieldset class="schedule-box">
<legend>Geplanter Versand</legend>
<label class="checkbox">
<input type="checkbox" name="schedule_enabled" value="true" {% if smtp.schedule_enabled == "true" %}checked{% endif %}>
Automatischen Versand aktivieren
</label>
<label class="checkbox">
<input type="checkbox" name="schedule_acknowledged" value="true" {% if smtp.schedule_acknowledged == "true" %}checked{% endif %}>
Ich bestätige den automatischen Versand ohne manuelle Freigabe (ohne Redaktionsnotiz/Highlights)
</label>
<p class="hint" style="margin:0 0 0.6rem;">Beide Häkchen sind nötig, damit der Zeitplan greift. Für manuelle Kontrolle nur den ersten Schalter nutzen und „Newsletter jetzt senden“ verwenden.</p>
<div class="form-row">
<label>Häufigkeit
<select name="schedule_frequency">
<option value="daily" {% if smtp.schedule_frequency == 'daily' %}selected{% endif %}>Täglich</option>
<option value="weekly" {% if smtp.schedule_frequency == 'weekly' %}selected{% endif %}>Wöchentlich</option>
<option value="monthly" {% if smtp.schedule_frequency == 'monthly' %}selected{% endif %}>Monatlich</option>
</select>
</label>
<label>Uhrzeit (HH:MM)
<input type="time" name="schedule_time" value="{{ smtp.schedule_time }}">
</label>
</div>
{% set active_days = smtp.schedule_weekdays.split(',') %}
<label>Wochentage (nur bei „Wöchentlich“)</label>
<div class="weekday-row">
{% for value, label in [('0','Mo'), ('1','Di'), ('2','Mi'), ('3','Do'), ('4','Fr'), ('5','Sa'), ('6','So')] %}
<label class="weekday-chip">
<input type="checkbox" name="schedule_weekdays" value="{{ value }}" {% if value in active_days %}checked{% endif %}>
{{ label }}
</label>
{% endfor %}
</div>
<label>Tag im Monat (nur bei „Monatlich“, 128)
<input type="number" name="schedule_dom" min="1" max="28" value="{{ smtp.schedule_dom }}">
</label>
<p class="hint" style="margin:0.5rem 0 0.25rem;">Inhalt des geplanten Newsletters:</p>
<div class="form-row">
<label>Zeitraum
<select name="gen_period">
<option value="days" {% if smtp.gen_period != 'last_month' %}selected{% endif %}>Letzte X Tage</option>
<option value="last_month" {% if smtp.gen_period == 'last_month' %}selected{% endif %}>Letzter Monat</option>
</select>
</label>
<label>Tage (bei „Letzte X Tage“)
<input type="number" name="gen_days" min="1" max="90" value="{{ smtp.gen_days }}">
</label>
</div>
<div class="form-row">
<label>Artikel-Auswahl
<select name="gen_article_type">
<option value="all" {% if smtp.gen_article_type == 'all' %}selected{% endif %}>Alle</option>
<option value="new" {% if smtp.gen_article_type == 'new' %}selected{% endif %}>Nur neue</option>
<option value="edited" {% if smtp.gen_article_type == 'edited' %}selected{% endif %}>Nur bearbeitete</option>
</select>
</label>
<label>Kategorie (optional)
<input type="text" name="gen_category" value="{{ smtp.gen_category }}">
</label>
</div>
<label>Bearbeiter ausschließen (optional)
<input type="text" name="gen_excluded_users" value="{{ smtp.gen_excluded_users }}" placeholder="z. B. Aranzinger, Testuser">
</label>
<p class="hint" style="margin-top:-0.2rem;">Diese Benutzer werden bei „Bearbeitete Artikel“ ignoriert (gilt für den geplanten Versand).</p>
</fieldset>
<button type="submit">Einstellungen speichern</button>
</form>
</div>
</details>
{% endif %}
{% if can_view_sensitive %}
<section class="card">
<div class="card-header">
<h3>Versandprotokoll</h3>
<span class="hint" style="margin:0;">Letzte 20 Einträge</span>
</div>
<div class="table-wrap">
<table>
<thead>
<tr><th>Zeit</th><th>Status</th><th>Betreff</th><th>Empfänger</th><th>Details</th></tr>
</thead>
<tbody>
{% for log in send_logs %}
<tr>
<td>{{ log.created_at }}</td>
<td><span class="badge badge-{{ log.status }}">{{ log.status }}</span></td>
<td>{{ log.subject }}</td>
<td>{{ log.recipients }}</td>
<td>{{ log.detail }}</td>
</tr>
{% else %}
<tr class="empty-row"><td colspan="5">Noch keine Versände protokolliert.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
{% endif %}
</div>
<aside class="sidebar-col">
<div class="sidebar-card">
<h4>Workflow</h4>
<ol style="margin:0;padding-left:1.1rem;color:var(--tk-gray);">
<li>Filter setzen</li>
<li>Newsletter generieren</li>
<li>Vorschau prüfen</li>
<li>Für Outlook exportieren</li>
</ol>
</div>
<div class="sidebar-card">
<h4>Outlook-Tipp</h4>
<ul>
<li><strong>Vorschau kopieren</strong> übernimmt die Live-Vorschau</li>
<li>In Outlook: <strong>Strg+V</strong></li>
<li>Rechtsklick → <strong>Format beibehalten</strong></li>
<li>Bei Dunkelmodus: Outlook-Design auf „Weiß“ stellen</li>
</ul>
</div>
{% if user.role == "admin" %}
<div class="sidebar-card">
<h4>Administration</h4>
<ul>
<li><a href="/admin/users">Benutzerverwaltung</a></li>
<li>SMTP unter „SMTP-Konfiguration“</li>
</ul>
</div>
{% endif %}
</aside>
</div>
{% endblock %}
{% block scripts %}
<script src="/static/js/newsletter.js?v=8"></script>
{% endblock %}