Plugin erinnerung: Redaktionsschluss pro Ausgabe, 4-Wochen-Erinnerung
- Ausgaben (Name + Redaktionsschluss-Datum) verwalten, mehrere parallel, Anlegen/Bearbeiten/Löschen nur für Admin, mit Audit-Log-Einträgen - Erinnerung 28 Tage vor dem Redaktionsschluss an alle aktiven Rezensenten mit offener (oder fehlender) Planung, Versand über das benachrichtigung- Plugin je Kanal-Präferenz, Audit als System/benachrichtigt - Täglicher APScheduler-Job (SPIELE_ERINNERUNG_JOB_UHRZEIT, abschaltbar) plus manuelle Sofort-Prüfung in der Admin-Ansicht - Doppelschutz: Protokolltabelle mit Unique (Ausgabe, Benutzer) - Admin-UI: Ausgabenliste mit Restzeit-Badges, Erinnerungsstatus (wer wurde wann über welche Kanäle erinnert), deutsche Templates - Tests mit eingefrorener Uhr (freezegun): Fenster-Grenzen, kein Versand vor Frist/nach Schluss, Empfänger-/Rollenregeln, keine Duplikate, Scheduler-Konfiguration; Integrationstest mit den echten Plugins (167 Tests grün)
This commit is contained in:
34
plugins/erinnerung/templates/erinnerung/bearbeiten.html
Normal file
34
plugins/erinnerung/templates/erinnerung/bearbeiten.html
Normal file
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% block titel %}{{ titel_headline }} — Spiele-Redaktion{% endblock %}
|
||||
{% block inhalt %}
|
||||
<h1 class="text-2xl font-bold mb-2">Ausgabe bearbeiten</h1>
|
||||
<p class="text-slate-600 mb-4">„{{ ausgabe.name }}“ · Redaktionsschluss:
|
||||
{{ ausgabe.redaktionsschluss.strftime('%d.%m.%Y') }}
|
||||
</p>
|
||||
|
||||
<form method="post" action="/erinnerung/ausgaben/{{ ausgabe.id }}/bearbeiten"
|
||||
class="bg-white border border-slate-200 rounded-xl shadow-sm p-4 max-w-2xl space-y-4">
|
||||
<div>
|
||||
<label for="name" class="block text-xs font-medium text-slate-500 mb-1">Name/Titel der Ausgabe *</label>
|
||||
<input type="text" id="name" name="name" required value="{{ ausgabe.name }}"
|
||||
class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500">
|
||||
</div>
|
||||
<div>
|
||||
<label for="redaktionsschluss" class="block text-xs font-medium text-slate-500 mb-1">Redaktionsschluss *</label>
|
||||
<input type="date" id="redaktionsschluss" name="redaktionsschluss" required value="{{ ausgabe.redaktionsschluss.isoformat() }}"
|
||||
class="border border-slate-300 rounded-lg px-3 py-2 text-sm w-48 focus:outline-none focus:ring-2 focus:ring-emerald-500">
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<button type="submit"
|
||||
class="bg-emerald-600 hover:bg-emerald-700 text-white font-medium px-4 py-2 rounded-lg text-sm">
|
||||
Speichern
|
||||
</button>
|
||||
<a href="/erinnerung" class="text-sm text-slate-500 hover:text-slate-800 underline">Abbrechen</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="text-xs text-slate-400 mt-3 max-w-2xl">
|
||||
Hinweis: Bereits protokollierte Erinnerungen bleiben bestehen — pro Ausgabe
|
||||
und Benutzer wird nur einmal erinnert.
|
||||
</p>
|
||||
{% endblock %}
|
||||
@@ -2,9 +2,143 @@
|
||||
{% block titel %}{{ titel }} — Spiele-Redaktion{% endblock %}
|
||||
{% block inhalt %}
|
||||
<h1 class="text-2xl font-bold mb-2">{{ titel }}</h1>
|
||||
<p class="text-slate-600 max-w-2xl">
|
||||
Plugin <code class="bg-slate-200 rounded px-1 py-0.5 text-sm">{{ name }}</code>
|
||||
in Version {{ version }} ist geladen.
|
||||
Diese Seite ist ein Platzhalter — die Funktion wird in einer späteren Phase implementiert.
|
||||
<p class="text-slate-600 mb-4">
|
||||
Redaktionsschlüsse der kommenden Ausgaben. Vier Wochen vor jedem
|
||||
Redaktionsschluss erhalten alle Rezensenten eine Erinnerung, die noch offene
|
||||
Planungseinträge haben — oder noch gar keine eingetragen haben.
|
||||
</p>
|
||||
|
||||
{% if meldung %}
|
||||
<div class="mb-4 px-4 py-3 rounded-lg border bg-emerald-50 border-emerald-200 text-emerald-800 text-sm">{{ meldung }}</div>
|
||||
{% endif %}
|
||||
{% if fehler %}
|
||||
<div class="mb-4 px-4 py-3 rounded-lg border bg-red-50 border-red-200 text-red-700 text-sm">{{ fehler }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if ist_admin %}
|
||||
<div class="bg-white border border-slate-200 rounded-xl shadow-sm p-4 mb-6">
|
||||
<h2 class="font-semibold mb-3">Neue Ausgabe anlegen</h2>
|
||||
<form method="post" action="/erinnerung/ausgaben" class="flex flex-wrap items-end gap-3">
|
||||
<div>
|
||||
<label for="name" class="block text-xs font-medium text-slate-500 mb-1">Name/Titel der Ausgabe *</label>
|
||||
<input type="text" id="name" name="name" required placeholder="z. B. 3/2025"
|
||||
class="border border-slate-300 rounded-lg px-3 py-2 text-sm w-64 focus:outline-none focus:ring-2 focus:ring-emerald-500">
|
||||
</div>
|
||||
<div>
|
||||
<label for="redaktionsschluss" class="block text-xs font-medium text-slate-500 mb-1">Redaktionsschluss *</label>
|
||||
<input type="date" id="redaktionsschluss" name="redaktionsschluss" required
|
||||
class="border border-slate-300 rounded-lg px-3 py-2 text-sm w-48 focus:outline-none focus:ring-2 focus:ring-emerald-500">
|
||||
</div>
|
||||
<button type="submit"
|
||||
class="bg-emerald-600 hover:bg-emerald-700 text-white font-medium px-4 py-2 rounded-lg text-sm whitespace-nowrap">
|
||||
Ausgabe anlegen
|
||||
</button>
|
||||
</form>
|
||||
<p class="text-xs text-slate-400 mt-2">Mehrere Ausgaben können parallel laufen.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="overflow-x-auto bg-white rounded-xl border border-slate-200 shadow-sm mb-6">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-slate-50 text-left text-slate-500">
|
||||
<tr>
|
||||
<th class="px-4 py-3 font-medium">Ausgabe</th>
|
||||
<th class="px-4 py-3 font-medium">Redaktionsschluss</th>
|
||||
<th class="px-4 py-3 font-medium">Restzeit</th>
|
||||
<th class="px-4 py-3 font-medium">Erinnerungen</th>
|
||||
{% if ist_admin %}<th class="px-4 py-3 font-medium">Aktionen</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for zeile in zeilen %}
|
||||
<tr class="hover:bg-slate-50">
|
||||
<td class="px-4 py-3 font-medium">{{ zeile.ausgabe.name }}</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">{{ zeile.ausgabe.redaktionsschluss.strftime('%d.%m.%Y') }}</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap">
|
||||
{% if zeile.tage_rest > fenster_tage %}
|
||||
<span class="inline-block px-2 py-0.5 rounded-full bg-slate-100 text-slate-600 text-xs">
|
||||
noch {{ zeile.tage_rest }} Tage · Erinnerung in {{ zeile.tage_rest - fenster_tage }} Tagen
|
||||
</span>
|
||||
{% elif zeile.im_fenster %}
|
||||
<span class="inline-block px-2 py-0.5 rounded-full bg-amber-100 text-amber-800 text-xs">
|
||||
Erinnerungsphase — {{ zeile.tage_rest }} Tag{{ 'e' if zeile.tage_rest != 1 }} bis zum Redaktionsschluss
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="inline-block px-2 py-0.5 rounded-full bg-slate-200 text-slate-500 text-xs">Redaktionsschluss vorbei</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-slate-600">
|
||||
{{ zeile.erinnert_anzahl }} erinnert
|
||||
</td>
|
||||
{% if ist_admin %}
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex flex-wrap items-center gap-1.5">
|
||||
<a href="/erinnerung/ausgaben/{{ zeile.ausgabe.id }}/bearbeiten"
|
||||
class="text-xs border border-slate-300 hover:border-emerald-600 hover:text-emerald-700 px-2 py-1 rounded-lg whitespace-nowrap">Bearbeiten</a>
|
||||
<form method="post" action="/erinnerung/ausgaben/{{ zeile.ausgabe.id }}/loeschen">
|
||||
<button type="submit" class="text-xs border border-red-200 text-red-600 hover:bg-red-50 px-2 py-1 rounded-lg whitespace-nowrap">Löschen</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="{{ 5 if ist_admin else 4 }}" class="px-4 py-8 text-center text-slate-400">
|
||||
Noch keine Ausgaben angelegt.
|
||||
{% if ist_admin %}Lege oben die erste Ausgabe mit ihrem Redaktionsschluss an.{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if ist_admin %}
|
||||
<div class="bg-white border border-slate-200 rounded-xl shadow-sm p-4 mb-6 flex flex-wrap items-center gap-3">
|
||||
<div class="mr-auto">
|
||||
<h2 class="font-semibold">Sofort prüfen</h2>
|
||||
<p class="text-xs text-slate-500">Führt denselben Check aus wie der tägliche Hintergrund-Job ({{ fenster_tage }} Tage vor dem Redaktionsschluss). Doppelerinnerungen sind ausgeschlossen.</p>
|
||||
</div>
|
||||
<form method="post" action="/erinnerung/pruefen">
|
||||
<button type="submit"
|
||||
class="bg-sky-600 hover:bg-sky-700 text-white font-medium px-4 py-2 rounded-lg text-sm whitespace-nowrap">
|
||||
Jetzt prüfen und erinnern
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="bg-white border border-slate-200 rounded-xl shadow-sm p-4 mb-6">
|
||||
<h2 class="font-semibold mb-3">Erinnerungsstatus</h2>
|
||||
<p class="text-xs text-slate-500 mb-3">Wer wurde für welche Ausgabe wann erinnert? Pro Ausgabe und Benutzer wird höchstens einmal erinnert.</p>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-slate-50 text-left text-slate-500">
|
||||
<tr>
|
||||
<th class="px-4 py-3 font-medium">Ausgabe</th>
|
||||
<th class="px-4 py-3 font-medium">Rezensent</th>
|
||||
<th class="px-4 py-3 font-medium">Kanäle</th>
|
||||
<th class="px-4 py-3 font-medium">Erinnert am</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for eintrag in protokoll %}
|
||||
<tr class="hover:bg-slate-50">
|
||||
<td class="px-4 py-3 font-medium">{{ eintrag.ausgabe_name }}</td>
|
||||
<td class="px-4 py-3">{{ eintrag.benutzer }}</td>
|
||||
<td class="px-4 py-3 text-slate-600">
|
||||
{% if eintrag.eintrag.kanaele %}{{ eintrag.eintrag.kanaele.replace(',', ', ') }}{% else %}—{% endif %}
|
||||
</td>
|
||||
<td class="px-4 py-3 whitespace-nowrap text-slate-500">
|
||||
{{ eintrag.eintrag.erstellt_am.strftime('%d.%m.%Y %H:%M') if eintrag.eintrag.erstellt_am else '—' }}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="4" class="px-4 py-8 text-center text-slate-400">Noch keine Erinnerungen versendet.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user