- h1: text-2xl font-bold text-slate-900 - Karten-h2: text-base font-semibold text-slate-800 - th: text-xs font-medium uppercase tracking-wider text-slate-500
148 lines
8.3 KiB
HTML
148 lines
8.3 KiB
HTML
{% extends "base.html" %}
|
|
{% block titel %}{{ titel }} — Spiele-Redaktion{% endblock %}
|
|
{% block inhalt %}
|
|
<h1 class="text-2xl font-bold text-slate-900 mb-2">{{ titel }}</h1>
|
|
<p class="text-slate-600 mb-4">
|
|
Neuheitenliste aus der <a href="https://boardgamegeek.com" class="text-emerald-700 underline">BoardGameGeek</a>-Datenbank.
|
|
Erweiterungen und Prototypen werden beim Sync automatisch gefiltert.
|
|
</p>
|
|
|
|
{% if meldung %}
|
|
<div id="meldung"
|
|
class="mb-4 px-4 py-3 rounded-lg border text-sm
|
|
{% if meldung.startswith('Sync fehlgeschlagen') %}bg-red-50 border-red-200 text-red-700{% else %}bg-emerald-50 border-emerald-200 text-emerald-800{% endif %}">
|
|
{{ 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" role="alert" tabindex="-1">{{ fehler }}</div>
|
|
{% endif %}
|
|
|
|
<div class="flex flex-wrap items-end gap-3 mb-4">
|
|
{% if ist_redaktion %}
|
|
<form method="post" action="/neuheiten/sync" class="flex flex-col sm:flex-row items-stretch sm:items-end gap-2">
|
|
<div class="sm:w-72">
|
|
<label for="suchbegriff" class="block text-xs font-medium text-slate-500 mb-1">
|
|
Sofort auf BoardGameGeek suchen & importieren (optional)
|
|
</label>
|
|
<input type="text" id="suchbegriff" name="suchbegriff" placeholder="Spielname oder Suchbegriff …"
|
|
class="w-full min-h-[44px] border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500">
|
|
</div>
|
|
<button type="submit"
|
|
class="min-h-[44px] inline-flex items-center justify-center gap-1.5 bg-emerald-600 hover:bg-emerald-700 active:bg-emerald-800 text-white font-medium px-4 py-2 rounded-lg text-sm whitespace-nowrap">
|
|
{% from "icons.html" import icon_sync %}{{ icon_sync("h-4 w-4") }} Jetzt synchronisieren
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
<p class="text-xs text-slate-400 ml-auto">
|
|
Hintergrund-Sync:
|
|
{% if sync_aktiv %}<span class="text-emerald-700 font-medium">aktiv</span>{% else %}<span class="text-slate-500">aus</span>{% endif %}
|
|
· Suchbegriffe: {{ suchbegriffe }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="mb-6 bg-white rounded-xl border border-slate-200 shadow-sm p-4">
|
|
<div class="flex flex-wrap items-center gap-2 mb-3">
|
|
<h2 class="text-sm font-semibold uppercase tracking-wide text-slate-500 mr-auto">Web-Quellen</h2>
|
|
{% if ist_redaktion %}
|
|
<form method="post" action="/neuheiten/quellen-sync">
|
|
<button type="submit"
|
|
class="min-h-[44px] md:min-h-0 inline-flex items-center gap-1.5 bg-sky-600 hover:bg-sky-700 active:bg-sky-800 text-white font-medium px-3 py-1.5 rounded-lg text-xs whitespace-nowrap">
|
|
{% from "icons.html" import icon_sync %}{{ icon_sync("h-3.5 w-3.5") }} Web-Quellen synchronisieren
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full text-xs">
|
|
<thead class="text-left text-slate-400">
|
|
<tr>
|
|
<th class="px-2 py-1.5 font-medium text-xs uppercase tracking-wider">Quelle</th>
|
|
<th class="px-2 py-1.5 font-medium text-xs uppercase tracking-wider">Status</th>
|
|
<th class="px-2 py-1.5 font-medium text-xs uppercase tracking-wider">Letzter Lauf</th>
|
|
<th class="px-2 py-1.5 font-medium text-right text-xs uppercase tracking-wider">Neu</th>
|
|
<th class="px-2 py-1.5 font-medium text-right text-xs uppercase tracking-wider">Aktualisiert</th>
|
|
<th class="px-2 py-1.5 font-medium text-right text-xs uppercase tracking-wider">Gefiltert</th>
|
|
<th class="px-2 py-1.5 font-medium text-xs uppercase tracking-wider">Hinweis</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-slate-100">
|
|
{% for quelle in quellen_zeilen %}
|
|
<tr>
|
|
<td class="px-2 py-1.5">
|
|
<a href="{{ quelle.url }}" target="_blank" rel="noopener noreferrer"
|
|
class="font-medium text-slate-700 hover:text-emerald-700">{{ quelle.anzeigename }}</a>
|
|
<span class="ml-1 text-[10px] text-slate-300">{{ quelle.name }}</span>
|
|
</td>
|
|
<td class="px-2 py-1.5">
|
|
<form method="post" action="/neuheiten/quellen/{{ quelle.name }}/schalter" class="inline-flex items-center gap-2">
|
|
{% if sync_aktiv %}
|
|
<input type="hidden" name="aktiv" value="{{ '0' if quelle.aktiv else '1' }}">
|
|
<button type="submit"
|
|
class="relative inline-flex h-5 w-9 items-center rounded-full transition-colors {{ 'bg-emerald-500' if quelle.aktiv else 'bg-slate-300' }}"
|
|
title="{{ 'Quelle ausschalten' if quelle.aktiv else 'Quelle einschalten' }}">
|
|
<span class="inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform {{ 'translate-x-4' if quelle.aktiv else 'translate-x-0.5' }}"></span>
|
|
</button>
|
|
{% else %}
|
|
<span class="text-xs text-slate-400">{{ 'an' if quelle.aktiv else 'aus' }} (Env)</span>
|
|
{% endif %}
|
|
</form>
|
|
<form method="post" action="/neuheiten/quellen-sync" class="inline">
|
|
<input type="hidden" name="quelle" value="{{ quelle.name }}">
|
|
<button type="submit"
|
|
class="ml-2 text-xs text-emerald-700 hover:text-emerald-900 underline decoration-dotted"
|
|
title="Nur diese Quelle jetzt synchronisieren">jetzt sync</button>
|
|
</form>
|
|
</td>
|
|
<td class="px-2 py-1.5 whitespace-nowrap text-slate-500">
|
|
{{ quelle.letzte_laufzeit.strftime('%d.%m.%Y %H:%M') if quelle.letzte_laufzeit else '—' }}
|
|
{% if quelle.dauer_sekunden %}<span class="text-slate-300">({{ '%.1f' | format(quelle.dauer_sekunden) }} s)</span>{% endif %}
|
|
</td>
|
|
<td class="px-2 py-1.5 text-right">{{ quelle.neu }}</td>
|
|
<td class="px-2 py-1.5 text-right">{{ quelle.aktualisiert }}</td>
|
|
<td class="px-2 py-1.5 text-right">{{ quelle.gefiltert }}</td>
|
|
<td class="px-2 py-1.5">
|
|
{% if quelle.fehlermeldung %}<span class="text-red-600">{{ quelle.fehlermeldung }}</span>
|
|
{% elif quelle.unveraendert %}<span class="text-slate-400">unverändert (304) — übersprungen</span>
|
|
{% elif not quelle.aktiv %}<span class="text-slate-300">abgeschaltet</span>
|
|
{% else %}—{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<p class="mt-2 text-[11px] text-slate-400">
|
|
Quellen-Sync:
|
|
{% if quellen_sync_aktiv %}<span class="text-emerald-700 font-medium">täglich aktiv</span>{% else %}<span class="text-slate-500">ausgeschaltet</span>{% endif %}
|
|
· max. 1 Anfrage/Sekunde je Quelle · Duplikate gegen BGG werden per Titel-Match gefiltert
|
|
</p>
|
|
</div>
|
|
|
|
<form method="get" action="/neuheiten"
|
|
hx-get="/neuheiten" hx-target="#neuheiten-liste" hx-swap="outerHTML"
|
|
hx-trigger="input changed delay:300ms from:find input[name='q'], change from:find select[name='status'], change from:find select[name='jahr']"
|
|
class="grid grid-cols-1 sm:flex sm:flex-wrap gap-3 mb-4">
|
|
<input type="text" name="q" value="{{ q }}" placeholder="Suche in Titel, Verlag, Autor …"
|
|
class="w-full min-h-[44px] border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500 sm:w-80">
|
|
<select name="status" class="w-full sm:w-auto min-h-[44px] md:min-h-0 border border-slate-300 rounded-lg px-3 py-2 text-sm bg-white">
|
|
<option value="">Alle Status</option>
|
|
{% for option in status_optionen %}
|
|
<option value="{{ option }}" {% if option == status_filter %}selected{% endif %}>{{ option }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<select name="jahr" class="w-full sm:w-auto min-h-[44px] md:min-h-0 border border-slate-300 rounded-lg px-3 py-2 text-sm bg-white">
|
|
<option value="{{ jahr_standard }}">ab {{ jahr_standard }}</option>
|
|
{% for jahr in jahr_optionen %}
|
|
<option value="{{ jahr }}" {% if jahr|string == jahr_filter|string %}selected{% endif %}>{{ jahr }}</option>
|
|
{% endfor %}
|
|
<option value="" {% if not jahr_filter %}selected{% endif %}>Alle Jahre</option>
|
|
</select>
|
|
<noscript>
|
|
<button type="submit" class="bg-slate-200 hover:bg-slate-300 px-4 py-2 rounded-lg text-sm min-h-[44px] md:min-h-0">Filtern</button>
|
|
</noscript>
|
|
</form>
|
|
|
|
{% include "neuheiten/_liste.html" %}
|
|
{% endblock %}
|