URLs in Playlist inline bearbeiten (Edit-Button + Modal)

- Neue Route POST /admin/<site>/update-playlist-item/<screen>
- Bearbeiten-Button (✎) für URL-Items in admin.html + priority.html
- Modal mit URL + Zoom-Feld, Validierung und Speichern
- Grid um Edit-Spalte erweitert (28px|1fr|90px|80px|40px|50px|32px)
This commit is contained in:
Erik Thiele
2026-07-10 16:24:47 +02:00
parent 106ec2d897
commit ce4cd4486b
3 changed files with 274 additions and 4 deletions

View File

@@ -43,11 +43,11 @@
background-color: #ffffff;
}
/* ─── Playlist-Zeile als Grid (Checkbox | Name | Typ | Größe | Löschen | Drag) ─── */
/* ─── Playlist-Zeile als Grid (Checkbox | Name | Typ | Größe | Edit | Löschen | Drag) ─── */
.playlist-row {
display: grid;
grid-template-columns:
28px 1fr 90px 80px 90px 32px;
28px 1fr 90px 80px 40px 50px 32px;
align-items: center;
gap: 0.75rem;
}
@@ -271,6 +271,18 @@
{% endif %}
</span>
<!-- Bearbeiten (nur URLs) -->
{% if file.type == "url" %}
<button type="button"
class="btn btn-ghost-secondary btn-sm edit-url-btn"
title="URL bearbeiten"
data-name="{{ file.name | e }}"
data-zoom="{{ file.zoom | default(1.0) }}"
onclick="editPlaylistItem('{{ current_site }}', '{{ screen }}', this.dataset.name, this.dataset.zoom)">
&#9998;
</button>
{% endif %}
<!-- Löschen -->
<form action="{{ url_for('delete_file', site=current_site, screen=screen, filename=file.name) }}"
method="post">
@@ -577,8 +589,138 @@ document.addEventListener("DOMContentLoaded", function () {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
/* ─── Playlist-Item bearbeiten (URL + Zoom) ─── */
var editState = { site: "", screen: "", oldName: "" };
function editPlaylistItem(site, screen, oldName, zoom) {
editState.site = site;
editState.screen = screen;
editState.oldName = oldName;
document.getElementById("edit-url-input").value = oldName;
document.getElementById("edit-zoom-input").value = zoom;
document.getElementById("edit-url-error").style.display = "none";
new bootstrap.Modal(document.getElementById("edit-url-modal")).show();
}
document.addEventListener("DOMContentLoaded", function () {
/* ─── SortableJS Drag & Drop initialisieren ─── */
function initSortable(name) {
const el = document.getElementById("playlist-" + name);
if (!el || typeof Sortable === "undefined") return;
new Sortable(el, {
animation: 150,
handle: ".drag-handle",
ghostClass: "bg-light",
chosenClass: "bg-light"
});
}
// Für jeden Screen eine Sortable-Instanz
{% for screen in screens.keys() %}
initSortable("{{ screen }}");
{% endfor %}
/* ─── Playlist-Reihenfolge speichern ─── */
function savePlaylist(site, screen) {
const items = document.querySelectorAll(
"#playlist-" + screen + " li"
);
const playlist = [];
const enabled = {};
items.forEach((item) => {
const checkbox = item.querySelector(".playlist-enabled");
const fileName = item.dataset.file;
enabled[fileName] = !!(checkbox && checkbox.checked);
playlist.push(fileName);
});
fetch("/admin/" + site + "/playlist/" + screen, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
playlist: playlist,
enabled: enabled
})
}).then(() => location.reload());
}
window.savePlaylist = savePlaylist;
/* ─── Bootstrap Tooltips initialisieren ─── */
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
/* ─── Edit-URL-Modal: Speichern-Button ─── */
document.getElementById("edit-url-save").addEventListener("click", function () {
const newUrl = document.getElementById("edit-url-input").value.trim();
const zoom = parseFloat(document.getElementById("edit-zoom-input").value) || 1.0;
const errorEl = document.getElementById("edit-url-error");
if (!newUrl.startsWith("http://") && !newUrl.startsWith("https://")) {
errorEl.textContent = "Ungültige URL (muss mit http:// oder https:// beginnen)";
errorEl.style.display = "block";
return;
}
errorEl.style.display = "none";
fetch("/admin/" + editState.site + "/update-playlist-item/" + editState.screen, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
old_name: editState.oldName,
new_url: newUrl,
zoom: zoom
})
}).then(function (r) {
if (r.ok) {
location.reload();
} else {
return r.json().then(function (d) {
errorEl.textContent = d.error || "Fehler beim Speichern";
errorEl.style.display = "block";
});
}
}).catch(function () {
errorEl.textContent = "Netzwerkfehler";
errorEl.style.display = "block";
});
});
});
</script>
<!-- Modal: URL bearbeiten -->
<div class="modal fade" id="edit-url-modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">URL bearbeiten</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">URL</label>
<input type="url" class="form-control" id="edit-url-input">
</div>
<div class="mb-3">
<label class="form-label">Zoom Faktor (z.B. 1.0, 1.5, 2.0)</label>
<input type="number" class="form-control" id="edit-zoom-input" step="0.1" min="0.1" value="1.0">
</div>
<div class="alert alert-danger py-2" id="edit-url-error" style="display:none"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="button" class="btn btn-primary" id="edit-url-save">Speichern</button>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -29,7 +29,7 @@
.playlist-row {
display: grid;
grid-template-columns:
28px 1fr 90px 80px 90px 32px;
28px 1fr 90px 80px 40px 50px 32px;
align-items: center;
gap: 0.75rem;
}
@@ -171,6 +171,18 @@
{% endif %}
</span>
<!-- Bearbeiten (nur URLs) -->
{% if file.type == "url" %}
<button type="button"
class="btn btn-ghost-secondary btn-sm edit-url-btn"
title="URL bearbeiten"
data-name="{{ file.name | e }}"
data-zoom="{{ file.zoom | default(1.0) }}"
onclick="editPlaylistItem('{{ current_site }}', 'priority', this.dataset.name, this.dataset.zoom)">
&#9998;
</button>
{% endif %}
<!-- Delete -->
<form action="{{ url_for('delete_file', site=current_site, screen='priority', filename=file.name) }}"
method="post"
@@ -278,14 +290,91 @@ document.addEventListener("DOMContentLoaded", function () {
window.savePlaylist = savePlaylist;
// Initialize tooltips
/* ─── Bootstrap Tooltips ─── */
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
/* ─── Edit-URL-Modal: Speichern-Button ─── */
document.getElementById("edit-url-save").addEventListener("click", function () {
const newUrl = document.getElementById("edit-url-input").value.trim();
const zoom = parseFloat(document.getElementById("edit-zoom-input").value) || 1.0;
const errorEl = document.getElementById("edit-url-error");
if (!newUrl.startsWith("http://") && !newUrl.startsWith("https://")) {
errorEl.textContent = "Ungültige URL (muss mit http:// oder https:// beginnen)";
errorEl.style.display = "block";
return;
}
errorEl.style.display = "none";
fetch("/admin/" + editState.site + "/update-playlist-item/" + editState.screen, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
old_name: editState.oldName,
new_url: newUrl,
zoom: zoom
})
}).then(function (r) {
if (r.ok) {
location.reload();
} else {
return r.json().then(function (d) {
errorEl.textContent = d.error || "Fehler beim Speichern";
errorEl.style.display = "block";
});
}
}).catch(function () {
errorEl.textContent = "Netzwerkfehler";
errorEl.style.display = "block";
});
});
});
/* ─── Global: editState + editPlaylistItem ─── */
var editState = { site: "", screen: "", oldName: "" };
function editPlaylistItem(site, screen, oldName, zoom) {
editState.site = site;
editState.screen = screen;
editState.oldName = oldName;
document.getElementById("edit-url-input").value = oldName;
document.getElementById("edit-zoom-input").value = zoom;
document.getElementById("edit-url-error").style.display = "none";
new bootstrap.Modal(document.getElementById("edit-url-modal")).show();
}
</script>
<!-- Modal: URL bearbeiten -->
<div class="modal fade" id="edit-url-modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">URL bearbeiten</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">URL</label>
<input type="url" class="form-control" id="edit-url-input">
</div>
<div class="mb-3">
<label class="form-label">Zoom Faktor (z.B. 1.0, 1.5, 2.0)</label>
<input type="number" class="form-control" id="edit-zoom-input" step="0.1" min="0.1" value="1.0">
</div>
<div class="alert alert-danger py-2" id="edit-url-error" style="display:none"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
<button type="button" class="btn btn-primary" id="edit-url-save">Speichern</button>
</div>
</div>
</div>
</div>
</body>
</html>