266 lines
6.7 KiB
HTML
266 lines
6.7 KiB
HTML
<!doctype html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>CANCOM Simple Signage Admin</title>
|
||
|
||
<!-- Tabler CSS -->
|
||
<link rel="stylesheet"
|
||
href="https://unpkg.com/@tabler/core@1.0.0-beta20/dist/css/tabler.min.css">
|
||
|
||
<!-- Tabler JS -->
|
||
<script defer
|
||
src="https://unpkg.com/@tabler/core@1.0.0-beta20/dist/js/tabler.min.js"></script>
|
||
|
||
<!-- SortableJS -->
|
||
<script defer
|
||
src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||
|
||
<style>
|
||
body {
|
||
background-color: #F4F6F8;
|
||
color: #212121;
|
||
}
|
||
|
||
/* Top Navigation */
|
||
.navbar {
|
||
background-color: #DA002D;
|
||
}
|
||
.navbar-brand {
|
||
color: #ffffff !important;
|
||
font-weight: 600;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
|
||
/* CANCOM CI – Variante 1 */
|
||
.card-header {
|
||
background-color: #ffffff;
|
||
border-left: 6px solid #DA002D;
|
||
padding-left: 1rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.btn-primary {
|
||
background-color: #DA002D;
|
||
border-color: #DA002D;
|
||
}
|
||
.btn-primary:hover {
|
||
background-color: #B00024;
|
||
border-color: #B00024;
|
||
}
|
||
|
||
.form-check-input:checked {
|
||
background-color: #DA002D;
|
||
border-color: #DA002D;
|
||
}
|
||
|
||
.form-check-input:focus {
|
||
box-shadow: 0 0 0 0.2rem rgba(218, 0, 45, 0.25);
|
||
}
|
||
|
||
/* Playlist als Grid (eine Zeile, tabellarisch) */
|
||
.playlist-row {
|
||
display: grid;
|
||
grid-template-columns:
|
||
1fr /* Name */
|
||
90px /* Typ */
|
||
80px /* Größe */
|
||
90px /* Delete */
|
||
32px; /* Drag */
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.playlist-name {
|
||
font-weight: 600;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.playlist-size {
|
||
color: #6c757d;
|
||
font-size: 0.9em;
|
||
}
|
||
|
||
.drag-handle {
|
||
cursor: grab;
|
||
font-size: 1.2em;
|
||
text-align: center;
|
||
user-select: none;
|
||
}
|
||
.drag-handle:active {
|
||
cursor: grabbing;
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
<div class="page">
|
||
|
||
<!-- Header -->
|
||
<header class="navbar navbar-expand-md">
|
||
<div class="container-xl">
|
||
<span class="navbar-brand">CANCOM Simple Signage Admin</span>
|
||
<div class="navbar-nav ms-auto">
|
||
<a href="/logout" class="nav-link text-white">Logout</a>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Content -->
|
||
<div class="page-wrapper">
|
||
<div class="container-xl mt-4">
|
||
|
||
{% for screen, cfg in screens.items() %}
|
||
<div class="card mb-4">
|
||
|
||
<!-- Card Header -->
|
||
<div class="card-header d-flex justify-content-between align-items-center">
|
||
<span>Screen: {{ screen }}</span>
|
||
{% if screen_status[screen] == "active" %}
|
||
<span class="badge bg-success">Aktiv</span>
|
||
{% else %}
|
||
<span class="badge bg-warning text-dark">Leer</span>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="card-body">
|
||
|
||
<!-- Einstellungen -->
|
||
<form action="/admin/update/{{ screen }}" method="post">
|
||
<div class="mb-3">
|
||
<label class="form-label">Intervall (Sekunden)</label>
|
||
<input type="number" class="form-control"
|
||
name="interval" min="1" value="{{ cfg.interval }}">
|
||
</div>
|
||
|
||
<div class="form-check form-switch mb-2">
|
||
<input class="form-check-input" type="checkbox"
|
||
name="show_images"
|
||
{% if cfg.show_images %}checked{% endif %}>
|
||
<label class="form-check-label">Bilder anzeigen</label>
|
||
</div>
|
||
|
||
<div class="form-check form-switch mb-3">
|
||
<input class="form-check-input" type="checkbox"
|
||
name="show_videos"
|
||
{% if cfg.show_videos %}checked{% endif %}>
|
||
<label class="form-check-label">Videos anzeigen</label>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary">
|
||
Einstellungen speichern
|
||
</button>
|
||
</form>
|
||
|
||
<hr>
|
||
|
||
<!-- Upload -->
|
||
<h5>Medien hochladen</h5>
|
||
<form action="/admin/upload/{{ screen }}"
|
||
method="post" enctype="multipart/form-data">
|
||
<div class="input-group mb-3">
|
||
<input type="file" name="file" class="form-control" required>
|
||
<button class="btn btn-primary" type="submit">Upload</button>
|
||
</div>
|
||
</form>
|
||
|
||
<!-- Playlist -->
|
||
<h5>Playlist Reihenfolge</h5>
|
||
|
||
<ul class="list-group mb-3" id="playlist-{{ screen }}">
|
||
{% for file in media_files[screen] %}
|
||
<li class="list-group-item playlist-row"
|
||
data-file="{{ file.name }}">
|
||
|
||
<span class="playlist-name">{{ file.name }}</span>
|
||
|
||
{% if file.type == "image" %}
|
||
<span class="badge bg-blue-lt">Bild</span>
|
||
{% else %}
|
||
<span class="badge bg-orange-lt">Video</span>
|
||
{% endif %}
|
||
|
||
<span class="playlist-size">{{ file.size }} KB</span>
|
||
|
||
<!-- Delete -->
|
||
<form action="/admin/delete/{{ screen }}/{{ file.name }}"
|
||
method="post">
|
||
<button type="submit"
|
||
class="btn btn-outline-danger btn-sm"
|
||
onclick="return confirm('Datei wirklich löschen?')">
|
||
Löschen
|
||
</button>
|
||
</form>
|
||
|
||
<!-- Drag -->
|
||
<span class="drag-handle" title="Reihenfolge ändern">⇅</span>
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
|
||
<button class="btn btn-primary"
|
||
onclick="savePlaylist('{{ screen }}')">
|
||
Reihenfolge speichern
|
||
</button>
|
||
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Footer -->
|
||
<footer class="footer footer-transparent mt-4">
|
||
<div class="container-xl text-muted">
|
||
© {{ year }} CANCOM · Version {{ version }} · {{ hostname }}
|
||
</div>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
|
||
function initSortable(screen) {
|
||
const el = document.getElementById("playlist-" + screen);
|
||
if (!el || typeof Sortable === "undefined") return;
|
||
|
||
new Sortable(el, {
|
||
animation: 150,
|
||
handle: ".drag-handle",
|
||
ghostClass: "bg-light",
|
||
chosenClass: "bg-light"
|
||
});
|
||
}
|
||
|
||
function savePlaylist(screen) {
|
||
const items = document.querySelectorAll(
|
||
"#playlist-" + screen + " li"
|
||
);
|
||
|
||
fetch("/admin/playlist/" + screen, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
playlist: Array.from(items).map(i => i.dataset.file)
|
||
})
|
||
}).then(() => location.reload());
|
||
}
|
||
|
||
window.savePlaylist = savePlaylist;
|
||
|
||
{% for screen in screens.keys() %}
|
||
initSortable("{{ screen }}");
|
||
{% endfor %}
|
||
|
||
});
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|
||
|
||
|