Version 2.0.0
This commit is contained in:
265
templates/admin.html
Normal file
265
templates/admin.html
Normal file
@@ -0,0 +1,265 @@
|
||||
<!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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user