- Introduce a new period selection feature in the dashboard for generating newsletters, allowing users to choose between "last month" and "last X days." - Refactor newsletter generation logic to support filtering by article type (new, edited, or all) and improve the handling of filters in the dashboard. - Update the dashboard template to include new input fields for period and article type, enhancing user experience. - Improve CSS styles for send notifications and sections in the dashboard, providing clearer feedback on newsletter sending status.
340 lines
18 KiB
HTML
340 lines
18 KiB
HTML
{% 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>
|
||
</div>
|
||
<label>Artikel-Auswahl
|
||
<select name="article_type">
|
||
<option value="all" {% if filters.article_type == 'all' %}selected{% endif %}>Alle (neue & 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" 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" 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 & 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>
|
||
<strong>Empfänger:</strong> {{ smtp.recipients if smtp.recipients else "— keine konfiguriert —" }}
|
||
{% 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="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" value="{{ smtp.password }}">
|
||
</label>
|
||
</div>
|
||
<label>Absender E-Mail
|
||
<input type="email" name="from_email" value="{{ smtp.from_email }}">
|
||
</label>
|
||
<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>
|
||
<label>Geplanter täglicher Versand (HH:MM)
|
||
<input type="time" name="schedule_time" value="{{ smtp.schedule_time }}">
|
||
</label>
|
||
<button type="submit">Einstellungen speichern</button>
|
||
</form>
|
||
</div>
|
||
</details>
|
||
{% endif %}
|
||
|
||
<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>
|
||
|
||
</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 %}
|