320 lines
18 KiB
HTML
Executable File
320 lines
18 KiB
HTML
Executable File
{% extends "base.html" %}
|
||
|
||
{% block title %}⚡ Zaubereiministerium – Cockpit{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="admin-wrapper">
|
||
<div class="admin-header">
|
||
<div>
|
||
<h1 class="glow-title">⚡ Großinquisitor-Cockpit</h1>
|
||
<p class="subtitle">Eingeloggt als: <strong>{{ admin_user }}</strong></p>
|
||
</div>
|
||
<div class="admin-top-actions">
|
||
<a href="/" target="_blank" class="btn btn-secondary">🌐 Zum Live-Quiz</a>
|
||
<a href="/admin/logout" class="btn btn-danger">🚪 Abmelden</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Navigation Tabs -->
|
||
<div class="tabs-nav">
|
||
<button class="tab-btn active" onclick="showTab('tab-pending')">
|
||
📥 Eingereichte Vorschläge
|
||
{% if pending_questions|length > 0 %}
|
||
<span class="badge badge-warning">{{ pending_questions|length }}</span>
|
||
{% endif %}
|
||
</button>
|
||
<button class="tab-btn" onclick="showTab('tab-active')">
|
||
📜 Aktive Quiz-Fragen
|
||
<span class="badge badge-info">{{ active_questions|length }}</span>
|
||
</button>
|
||
<button class="tab-btn" onclick="showTab('tab-new')">
|
||
➕ Neue Frage anlegen
|
||
</button>
|
||
{% if rejected_questions|length > 0 %}
|
||
<button class="tab-btn" onclick="showTab('tab-rejected')">
|
||
❌ Archiviert / Abgelehnt
|
||
<span class="badge badge-muted">{{ rejected_questions|length }}</span>
|
||
</button>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- TAB 1: Eingereichte Vorschläge (User Questions Need Test) -->
|
||
<section id="tab-pending" class="tab-content active">
|
||
<h2>📥 Eingegangene Vorschläge (Prüfung & Validierung erforderlich)</h2>
|
||
<p class="description">Diese Fragen wurden von Besuchern eingereicht. Bitte teste die Antwort und schalte die Frage frei oder lehne sie ab.</p>
|
||
|
||
{% if pending_questions|length == 0 %}
|
||
<div class="empty-state">
|
||
<p>🎉 Keine offenen Vorschläge zur Prüfung vorhanden!</p>
|
||
</div>
|
||
{% else %}
|
||
<div class="questions-list">
|
||
{% for q in pending_questions %}
|
||
<div class="card question-admin-card" id="q-card-{{ q.id }}">
|
||
<div class="q-header">
|
||
<div style="display: flex; gap: 8px; align-items: center;">
|
||
<span class="badge badge-warning">⚠️ Prüfung ausstehend</span>
|
||
<span class="badge badge-diff badge-diff-{{ q.difficulty or 'medium' }}">{{ (q.difficulty or 'medium')|capitalize }}</span>
|
||
{% if q.question_type == 'multiple_choice' %}
|
||
<span class="badge" style="background: rgba(155, 89, 182, 0.2); border-color: #9b59b6; color: #d2b4de;">🔘 Multiple-Choice</span>
|
||
{% else %}
|
||
<span class="badge badge-muted">🔤 Freitext</span>
|
||
{% endif %}
|
||
</div>
|
||
<span class="q-submitter">Von: <strong>{{ q.submitted_by or 'Gast' }}</strong> am {{ q.created_at.strftime('%d.%m.%Y %H:%M') }}</span>
|
||
</div>
|
||
|
||
<div class="q-body">
|
||
<h3>{{ q.question }}</h3>
|
||
<p><strong>Erwartete Antwort:</strong> <span class="highlight-answer">{{ q.canonical_answer }}</span></p>
|
||
{% if q.options %}
|
||
<p style="margin-top: 5px;"><strong>Auswahl-Optionen:</strong> <code>{{ q.options }}</code></p>
|
||
{% endif %}
|
||
{% if q.hint %}
|
||
<p style="margin-top: 5px;"><strong>Tipp:</strong> <em>{{ q.hint }}</em></p>
|
||
{% endif %}
|
||
|
||
<!-- Interaktives Testfeld für den Admin -->
|
||
<div class="test-matcher-box">
|
||
<label>🧪 Antwort-Validierung testen:</label>
|
||
<div class="test-input-group">
|
||
<input type="text" id="test-input-{{ q.id }}" class="input-text-sm" placeholder="Test-Eingabe (z.B. mit Tippfehler)">
|
||
<button type="button" class="btn btn-sm btn-secondary" onclick="testMatcher({{ q.id }}, '{{ q.canonical_answer|escape }}')">Testen</button>
|
||
</div>
|
||
<div id="test-result-{{ q.id }}" class="test-result-feedback"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="q-actions">
|
||
<button class="btn btn-success" onclick="approveQuestion({{ q.id }})">
|
||
✅ Freischalten & Aktivieren
|
||
</button>
|
||
<button class="btn btn-secondary" onclick="toggleEdit({{ q.id }})">
|
||
✏️ Bearbeiten
|
||
</button>
|
||
<button class="btn btn-danger" onclick="rejectQuestion({{ q.id }})">
|
||
❌ Ablehnen
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Inline Edit Form (Hidden by default) -->
|
||
<div id="edit-form-{{ q.id }}" class="inline-edit-form hidden">
|
||
<form action="/admin/questions/{{ q.id }}/edit" method="post">
|
||
<div class="form-group">
|
||
<label>Frage:</label>
|
||
<input type="text" name="question" class="input-text" value="{{ q.question }}" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Fragentyp:</label>
|
||
<select name="question_type" class="input-text">
|
||
<option value="text" {% if q.question_type != 'multiple_choice' %}selected{% endif %}>🔤 Freitext</option>
|
||
<option value="multiple_choice" {% if q.question_type == 'multiple_choice' %}selected{% endif %}>🔘 Multiple Choice</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Richtige Antwort:</label>
|
||
<input type="text" name="canonical_answer" class="input-text" value="{{ q.canonical_answer }}" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Optionen (JSON-Liste oder Komma-getrennt für Multiple Choice):</label>
|
||
<input type="text" name="options_raw" class="input-text" value="{{ q.options or '' }}">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Tipp:</label>
|
||
<input type="text" name="hint" class="input-text" value="{{ q.hint or '' }}">
|
||
</div>
|
||
<input type="hidden" name="status_val" value="active">
|
||
<button type="submit" class="btn btn-primary">Speichern & Aktivieren</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
</section>
|
||
|
||
<!-- TAB 2: Aktive Fragen -->
|
||
<section id="tab-active" class="tab-content">
|
||
<h2>📜 Aktive Fragen im Live-Quiz ({{ active_questions|length }})</h2>
|
||
<p class="description">Diese Fragen werden den Spielern in der angegebenen Reihenfolge gestellt.</p>
|
||
|
||
<div class="questions-list">
|
||
{% for q in active_questions %}
|
||
<div class="card question-admin-card" id="q-card-{{ q.id }}">
|
||
<div class="q-header">
|
||
<div style="display: flex; gap: 8px; align-items: center;">
|
||
<span class="badge badge-success">Reihenfolge #{{ q.sort_order }}</span>
|
||
<span class="badge badge-diff badge-diff-{{ q.difficulty or 'medium' }}">{{ (q.difficulty or 'medium')|capitalize }}</span>
|
||
{% if q.question_type == 'multiple_choice' %}
|
||
<span class="badge" style="background: rgba(155, 89, 182, 0.2); border-color: #9b59b6; color: #d2b4de;">🔘 Multiple-Choice</span>
|
||
{% else %}
|
||
<span class="badge badge-muted">🔤 Freitext</span>
|
||
{% endif %}
|
||
</div>
|
||
<span class="q-category">Kategorie: {{ q.category }}</span>
|
||
</div>
|
||
|
||
<div class="q-body">
|
||
<h3>{{ loop.index }}. {{ q.question }}</h3>
|
||
<p><strong>Antwort:</strong> <span class="highlight-answer">{{ q.canonical_answer }}</span></p>
|
||
{% if q.options %}
|
||
<p style="margin-top: 5px;"><strong>Optionen:</strong> <code>{{ q.options }}</code></p>
|
||
{% endif %}
|
||
{% if q.hint %}
|
||
<p style="margin-top: 5px;"><strong>Tipp:</strong> <em>{{ q.hint }}</em></p>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="q-actions">
|
||
<button class="btn btn-secondary btn-sm" onclick="toggleEdit({{ q.id }})">✏️ Bearbeiten</button>
|
||
<button class="btn btn-danger btn-sm" onclick="deleteQuestion({{ q.id }})">🗑️ Löschen</button>
|
||
</div>
|
||
|
||
<!-- Inline Edit Form -->
|
||
<div id="edit-form-{{ q.id }}" class="inline-edit-form hidden">
|
||
<form action="/admin/questions/{{ q.id }}/edit" method="post">
|
||
<div class="form-group">
|
||
<label>Frage:</label>
|
||
<input type="text" name="question" class="input-text" value="{{ q.question }}" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Fragentyp:</label>
|
||
<select name="question_type" class="input-text">
|
||
<option value="text" {% if q.question_type != 'multiple_choice' %}selected{% endif %}>🔤 Freitext</option>
|
||
<option value="multiple_choice" {% if q.question_type == 'multiple_choice' %}selected{% endif %}>🔘 Multiple Choice</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Schwierigkeitsgrad:</label>
|
||
<select name="difficulty" class="input-text">
|
||
<option value="easy" {% if q.difficulty == 'easy' %}selected{% endif %}>🟢 Leicht</option>
|
||
<option value="medium" {% if q.difficulty == 'medium' or not q.difficulty %}selected{% endif %}>🟡 Mittel</option>
|
||
<option value="hard" {% if q.difficulty == 'hard' %}selected{% endif %}>🔴 Schwer</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Richtige Antwort:</label>
|
||
<input type="text" name="canonical_answer" class="input-text" value="{{ q.canonical_answer }}" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Optionen (JSON-Liste oder Komma-getrennt für Multiple Choice):</label>
|
||
<input type="text" name="options_raw" class="input-text" value="{{ q.options or '' }}">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Tipp:</label>
|
||
<input type="text" name="hint" class="input-text" value="{{ q.hint or '' }}">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Reihenfolge (Sort Order):</label>
|
||
<input type="number" name="sort_order" class="input-text-sm" value="{{ q.sort_order }}">
|
||
</div>
|
||
<input type="hidden" name="status_val" value="active">
|
||
<button type="submit" class="btn btn-primary">Änderungen speichern</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB 3: Neue Frage anlegen -->
|
||
<section id="tab-new" class="tab-content">
|
||
<h2>➕ Neue Frage direkt zum Live-Quiz hinzufügen</h2>
|
||
<div class="card">
|
||
<form action="/admin/questions/new" method="post" class="magic-form">
|
||
<div class="form-group">
|
||
<label for="category">Kategorie:</label>
|
||
<select name="category" id="category" class="input-text">
|
||
<option value="books">📖 Buchwissen</option>
|
||
<option value="movies">🎬 Filmwissen</option>
|
||
<option value="creatures">🐉 Magische Geschöpfe</option>
|
||
<option value="spells">🪄 Zaubersprüche & Tränke</option>
|
||
<option value="characters" selected>🧙♂️ Charaktere</option>
|
||
<option value="hogwarts">🏰 Hogwarts & Orte</option>
|
||
<option value="quidditch">🧹 Quidditch</option>
|
||
<option value="lore">🔮 Artefakte & Lore</option>
|
||
<option value="general">⚡ Allgemein</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="new_diff">Schwierigkeitsgrad:</label>
|
||
<select name="difficulty" id="new_diff" class="input-text">
|
||
<option value="easy">🟢 Leicht</option>
|
||
<option value="medium" selected>🟡 Mittel</option>
|
||
<option value="hard">🔴 Schwer</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="new_type">Fragentyp:</label>
|
||
<select name="question_type" id="new_type" class="input-text" onchange="toggleAdminNewType(this.value)">
|
||
<option value="text" selected>🔤 Freitext-Eingabe (Tippen)</option>
|
||
<option value="multiple_choice">🔘 Multiple Choice (Auswahl)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="new_question">Frage:</label>
|
||
<textarea name="question" id="new_question" class="textarea" rows="2" required placeholder="z. B. Welcher Zauber entwaffnet Gegner?"></textarea>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="new_answer">Richtige Antwort:</label>
|
||
<input type="text" name="canonical_answer" id="new_answer" class="input-text" required placeholder="z. B. Expelliarmus">
|
||
</div>
|
||
|
||
<!-- Multiple Choice Optionen für Admin -->
|
||
<div id="adminMcOptions" class="form-group hidden" style="background: rgba(255,255,255,0.03); padding: 15px; border-radius: 8px; border: 1px dashed var(--border-gold);">
|
||
<label style="color: var(--gold);">Falsche Optionen für Multiple Choice (Optionen 2 bis 4):</label>
|
||
<input type="text" name="wrong_option_1" class="input-text" placeholder="Falsche Option 1 (z.B. Stupor)" style="margin-bottom: 8px;">
|
||
<input type="text" name="wrong_option_2" class="input-text" placeholder="Falsche Option 2 (z.B. Lumos)" style="margin-bottom: 8px;">
|
||
<input type="text" name="wrong_option_3" class="input-text" placeholder="Falsche Option 3 (z.B. Alohomora)">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="new_hint">Tipp (optional):</label>
|
||
<input type="text" name="hint" id="new_hint" class="input-text" placeholder="z. B. Harrys Lieblingszauber">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="new_sort">Reihenfolge (Nummerierung):</label>
|
||
<input type="number" name="sort_order" id="new_sort" class="input-text-sm" value="{{ active_questions|length + 1 }}">
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary btn-large">Frage speichern & aktivieren</button>
|
||
</form>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- TAB 4: Archiviert / Abgelehnt -->
|
||
{% if rejected_questions|length > 0 %}
|
||
<section id="tab-rejected" class="tab-content">
|
||
<h2>❌ Abgelehnte / Archivierte Vorschläge</h2>
|
||
<div class="questions-list">
|
||
{% for q in rejected_questions %}
|
||
<div class="card question-admin-card muted-card" id="q-card-{{ q.id }}">
|
||
<div class="q-header">
|
||
<span class="badge badge-danger">Abgelehnt</span>
|
||
<span class="q-submitter">Eingereicht von: {{ q.submitted_by or 'Gast' }}</span>
|
||
</div>
|
||
<div class="q-body">
|
||
<h3>{{ q.question }}</h3>
|
||
<p><strong>Antwort:</strong> {{ q.canonical_answer }}</p>
|
||
</div>
|
||
<div class="q-actions">
|
||
<button class="btn btn-sm btn-success" onclick="approveQuestion({{ q.id }})">Wieder aktivieren</button>
|
||
<button class="btn btn-sm btn-danger" onclick="deleteQuestion({{ q.id }})">Endgültig löschen</button>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</section>
|
||
{% endif %}
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<script src="/static/js/admin.js?v=2.3"></script>
|
||
{% endblock %}
|