282 lines
10 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 %}Admin MK Control{% endblock %}
{% block content %}
<div class="container my-5">
<h1 class="mb-4">Admin-Bereich</h1>
<div class="row mb-4">
<div class="col">
<a href="{{ url_for('admin.admin_logs') }}" class="btn btn-outline-info">
<i class="bi bi-journal-text me-2"></i>Logs ansehen
</a>
</div>
<div class="col text-end d-flex gap-2 justify-content-end">
<a href="#" class="btn btn-outline-success" data-bs-toggle="modal" data-bs-target="#newConfigModal">
<i class="bi bi-plus-circle me-2"></i>Neue MK-Konfiguration
</a>
<a href="#" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#newThemeModal">
<i class="bi bi-music-note-list me-2"></i>Neues Soundboard-Theme
</a>
</div>
</div>
<h3 class="mt-5 mb-3">Vorhandene Konfigurationen</h3>
{% if configs %}
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-dark">
<tr>
<th>Name</th>
<th>Hub-ID</th>
<th>Datei</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for cfg in configs %}
<tr>
<td>{{ cfg.name }}</td>
<td>{{ cfg.hub_id | default('') }}</td>
<td><code>{{ cfg.filename }}</code></td>
<td>
<a href="{{ url_for('admin.admin_edit_config', filename=cfg.filename) }}"
class="btn btn-sm btn-primary">
<i class="bi bi-pencil"></i> Bearbeiten
</a>
<button class="btn btn-sm btn-danger delete-config-btn"
data-filename="{{ cfg.filename }}"
data-name="{{ cfg.name }}">
<i class="bi bi-trash"></i> Löschen
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="alert alert-info">
Noch keine Konfigurationen vorhanden. Erstelle eine neue.
</div>
{% endif %}
<h3 class="mt-5 mb-3">Soundboard-Themes</h3>
{% if sb_configs %}
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-dark">
<tr>
<th>Name</th>
<th>Datei</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for sb in sb_configs %}
<tr>
<td>{{ sb.name }}</td>
<td><code>{{ sb.filename }}</code></td>
<td>
<a href="{{ url_for('admin.admin_edit_theme', filename=sb.filename) }}"
class="btn btn-sm btn-primary">
<i class="bi bi-pencil"></i> Bearbeiten
</a>
<button class="btn btn-sm btn-outline-success set-default-theme-btn"
data-filename="{{ sb.filename }}">
Als Standard setzen
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="alert alert-info">Noch keine Themes vorhanden.</div>
{% endif %}
</div>
<!-- Modal für neue MK-Konfiguration -->
<div class="modal fade" id="newConfigModal" tabindex="-1" aria-labelledby="newConfigModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="newConfigModalLabel">Neue Konfiguration anlegen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="form-new-config">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Name</label>
<input class="form-control" name="name" required placeholder="z.B. Dampflok BR 01">
</div>
<div class="col-md-3">
<label class="form-label">Hub-ID</label>
<input class="form-control" name="hub_id" type="number" min="0" max="2" value="0">
</div>
<div class="col-md-3">
<label class="form-label">Hub-Typ</label>
<select class="form-select" name="hub_type">
<option value="4channel">4channel</option>
<option value="6channel">6channel</option>
</select>
</div>
<div class="col-12">
<label class="form-label">Bilddatei (optional)</label>
<input class="form-control" name="image" placeholder="z.B. dampflok1.jpg">
<small class="text-muted">Datei im configs/-Ordner ablegen.</small>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
<button type="button" class="btn btn-primary" id="btn-save-config">Anlegen</button>
</div>
</div>
</div>
</div>
<!-- Modal für neues Soundboard-Theme -->
<div class="modal fade" id="newThemeModal" tabindex="-1" aria-labelledby="newThemeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="newThemeModalLabel">Neues Soundboard-Theme anlegen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="form-new-theme">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Name</label>
<input class="form-control" name="name" required placeholder="z.B. Wilder Westen">
</div>
<div class="col-md-6">
<label class="form-label">Ordner (optional)</label>
<input class="form-control" name="folder" placeholder="wilder-westen">
<small class="text-muted">Unterordner in soundboards/. Standard: Slug des Namens.</small>
</div>
<div class="col-md-6">
<label class="form-label">Dateiname</label>
<input class="form-control" name="filename" value="theme.json">
</div>
<div class="col-md-3">
<label class="form-label">Limit/h (Random)</label>
<input class="form-control" name="random_limit_per_hour" type="number" min="1" value="2">
</div>
<div class="col-md-3">
<label class="form-label">Fenster (Min)</label>
<input class="form-control" name="random_window_minutes" type="number" min="1" value="60">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
<button type="button" class="btn btn-primary" id="btn-save-theme">Anlegen</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.delete-config-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const filename = btn.dataset.filename;
const name = btn.dataset.name;
if (!confirm(`Sicher löschen: ${name} (${filename}) ?`)) return;
try {
const res = await fetch(`/admin/delete/${filename}`, { method: 'POST' });
const data = await res.json();
if (data.success) {
alert('Gelöscht');
location.reload();
} else {
alert('Fehler: ' + data.message);
}
} catch (err) {
alert('Netzwerkfehler beim Löschen');
}
});
});
});
// Neue Config speichern
document.getElementById('btn-save-config')?.addEventListener('click', async () => {
const form = document.getElementById('form-new-config');
const data = Object.fromEntries(new FormData(form).entries());
data.hub_id = parseInt(data.hub_id || 0, 10);
try {
const res = await fetch('/admin/create_config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const text = await res.text();
let json;
try { json = JSON.parse(text); } catch (_) { throw new Error('Serverantwort ist kein JSON:\n'+text); }
if (!json.success) throw new Error(json.message || 'Fehler');
alert('Konfiguration angelegt: ' + json.filename);
location.reload();
} catch (e) {
alert('Fehler: ' + e.message);
}
});
// Neues Theme speichern
document.getElementById('btn-save-theme')?.addEventListener('click', async () => {
const form = document.getElementById('form-new-theme');
const data = Object.fromEntries(new FormData(form).entries());
data.random_limit_per_hour = parseInt(data.random_limit_per_hour || 2, 10);
data.random_window_minutes = parseInt(data.random_window_minutes || 60, 10);
try {
const res = await fetch('/admin/create_theme', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const text = await res.text();
let json;
try { json = JSON.parse(text); } catch (_) { throw new Error('Serverantwort ist kein JSON:\n'+text); }
if (!json.success) throw new Error(json.message || 'Fehler');
alert('Theme angelegt: ' + json.path);
location.reload();
} catch (e) {
alert('Fehler: ' + e.message);
}
});
// Standard-Theme setzen
document.querySelectorAll('.set-default-theme-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const filename = btn.dataset.filename;
try {
const res = await fetch('/admin/set_default_theme', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename })
});
const text = await res.text();
let json;
try { json = JSON.parse(text); } catch (_) { throw new Error('Serverantwort ist kein JSON:\n'+text); }
if (!json.success) throw new Error(json.message || 'Fehler');
alert('Standard-Theme gesetzt: ' + filename);
} catch (e) {
alert('Fehler: ' + e.message);
}
});
});
</script>
{% endblock %}