665 lines
26 KiB
JavaScript
665 lines
26 KiB
JavaScript
// =====================================================================
|
|
// WebGIS Moderation Portal — Application Logic
|
|
// Initializes Map Preview, loads Contributions from the API,
|
|
// handles CRUD Workflow, sorting and filtering for Contributions,
|
|
// Comments and News, and manages all UI Interactions
|
|
//
|
|
// Depends on: ADMIN_CONFIG Object set in Moderation Page
|
|
// =====================================================================
|
|
|
|
|
|
// =====================================================================
|
|
// Block 1: Status Filter for Contributions
|
|
// =====================================================================
|
|
let currentFilter = 'all';
|
|
|
|
|
|
// =====================================================================
|
|
// Restores active Tab after Page Reload
|
|
// =====================================================================
|
|
const savedTab = sessionStorage.getItem('admin_active_tab');
|
|
if (savedTab) {
|
|
// Delays to ensure DOM is ready
|
|
setTimeout(function () {
|
|
const tabBtn = document.querySelector('.page-tab[onclick*="' + savedTab + '"]');
|
|
if (tabBtn) tabBtn.click();
|
|
}, 100);
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 3: Page Tab Navigation
|
|
// =====================================================================
|
|
|
|
// Wechselt den sichtbaren Haupt-Tab (Beiträge, Kommentare, Neuigkeiten usw.).
|
|
// Speichert den aktiven Tab in sessionStorage für Persistenz nach Reload.
|
|
function showPageTab(tabName) {
|
|
|
|
sessionStorage.setItem('admin_active_tab', tabName);
|
|
|
|
document.querySelectorAll('.page-tab-content').forEach(function (el) {
|
|
el.style.display = 'none';
|
|
});
|
|
|
|
document.querySelectorAll('.page-tab').forEach(function (el) {
|
|
el.classList.remove('active');
|
|
});
|
|
|
|
document.getElementById('tab-' + tabName).style.display = 'block';
|
|
event.currentTarget.classList.add('active');
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 4: Aufklappbare Zeilen (Collapsible Rows)
|
|
// =====================================================================
|
|
|
|
// Öffnet oder schließt eine Beitrags-/Kommentar-Zeile.
|
|
// Jeweils nur eine Zeile gleichzeitig geöffnet.
|
|
// Lädt bei Bedarf die Karten-Vorschau (Map Preview).
|
|
function toggleRow(row) {
|
|
const wasOpen = row.classList.contains('open');
|
|
|
|
// Alle offenen Zeilen schließen
|
|
document.querySelectorAll('.contribution-row.open').forEach(function (el) {
|
|
el.classList.remove('open');
|
|
});
|
|
|
|
if (!wasOpen) {
|
|
row.classList.add('open');
|
|
|
|
// Karten-Vorschau laden, falls noch nicht geschehen
|
|
const mapDiv = row.querySelector('.detail-map');
|
|
if (mapDiv && !mapDiv.dataset.loaded) {
|
|
loadMapPreview(mapDiv);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 5: Detail-Slider (Karte / Foto)
|
|
// =====================================================================
|
|
|
|
// Wechselt zwischen den Slides (Karte und Foto) in einer Beitrags-Zeile.
|
|
// direction: -1 für zurück, +1 für vor.
|
|
// Wrapped circular: nach dem letzten Slide kommt der erste.
|
|
function slideDetail(contributionId, direction) {
|
|
const slider = document.getElementById('slider-' + contributionId);
|
|
if (!slider) return;
|
|
|
|
const slides = slider.querySelectorAll('.detail-slide');
|
|
let activeIndex = -1;
|
|
|
|
slides.forEach(function (slide, i) {
|
|
if (slide.style.display !== 'none') activeIndex = i;
|
|
});
|
|
|
|
const nextIndex = (activeIndex + direction + slides.length) % slides.length;
|
|
|
|
slides.forEach(function (slide) { slide.style.display = 'none'; });
|
|
slides[nextIndex].style.display = 'block';
|
|
|
|
// Karte beim Zurückwechseln zum Map-Slide laden, falls noch nicht geschehen
|
|
if (slides[nextIndex].dataset.slide === 'map') {
|
|
const mapDiv = slides[nextIndex].querySelector('.detail-map');
|
|
if (mapDiv && !mapDiv.dataset.loaded) {
|
|
loadMapPreview(mapDiv);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 6: Leaflet Karten-Vorschau (Mini-Map pro Beitrag)
|
|
// =====================================================================
|
|
|
|
// Erstellt eine Leaflet-Mini-Map in einem Beitrags-Detail-Container.
|
|
// Lädt alle Beiträge via API und zeigt die Geometrie des entsprechenden Beitrags.
|
|
// Markiert die Map als geladen (data-loaded="true"), um doppeltes Laden zu verhindern.
|
|
function loadMapPreview(mapDiv) {
|
|
const contributionId = mapDiv.dataset.contributionId;
|
|
|
|
const formData = new FormData();
|
|
formData.append('action', 'read');
|
|
formData.append('municipality_id', ADMIN_CONFIG.municipalityId);
|
|
formData.append('status', 'all');
|
|
|
|
fetch(ADMIN_CONFIG.apiUrl, { method: 'POST', body: formData })
|
|
.then(function (r) { return r.json(); })
|
|
.then(function (data) {
|
|
if (!data.features) return;
|
|
|
|
const feature = data.features.find(function (f) {
|
|
return f.properties.contribution_id == contributionId;
|
|
});
|
|
|
|
if (!feature) {
|
|
mapDiv.innerHTML = '<div style="padding:20px;color:#999;text-align:center;font-size:0.8rem;">Geometrie nicht gefunden.</div>';
|
|
return;
|
|
}
|
|
|
|
const miniMap = L.map(mapDiv, {
|
|
zoomControl: false,
|
|
attributionControl: false,
|
|
dragging: true,
|
|
scrollWheelZoom: false
|
|
});
|
|
|
|
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
|
|
maxZoom: 20
|
|
}).addTo(miniMap);
|
|
|
|
const geojsonLayer = L.geoJSON(feature, {
|
|
style: {
|
|
color: ADMIN_CONFIG.primaryColor,
|
|
weight: 3,
|
|
fillOpacity: 0.2
|
|
},
|
|
pointToLayer: function (f, latlng) {
|
|
return L.circleMarker(latlng, {
|
|
radius: 8,
|
|
color: '#ffffff',
|
|
weight: 2,
|
|
fillColor: ADMIN_CONFIG.primaryColor,
|
|
fillOpacity: 0.9
|
|
});
|
|
}
|
|
}).addTo(miniMap);
|
|
|
|
const bounds = geojsonLayer.getBounds();
|
|
if (bounds.isValid()) {
|
|
miniMap.fitBounds(bounds, { padding: [25, 25], maxZoom: 17 });
|
|
} else {
|
|
miniMap.setView(ADMIN_CONFIG.municipalityCenter, 15);
|
|
}
|
|
|
|
mapDiv.dataset.loaded = 'true';
|
|
})
|
|
.catch(function () {
|
|
mapDiv.innerHTML = '<div style="padding:20px;color:#999;text-align:center;font-size:0.8rem;">Karte nicht verfügbar.</div>';
|
|
});
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 7: Beitrags-Filter und Sortierung
|
|
// =====================================================================
|
|
|
|
// Filtert Beitrags-Zeilen nach Status (pending, approved, rejected, all).
|
|
// Aktualisiert die sichtbare Anzahl im Sort-Control.
|
|
function filterByStatus(status, tabButton) {
|
|
currentFilter = status;
|
|
|
|
document.querySelectorAll('.filter-tab').forEach(function (el) {
|
|
el.classList.remove('active');
|
|
});
|
|
tabButton.classList.add('active');
|
|
|
|
let visibleCount = 0;
|
|
document.querySelectorAll('#contributions-container .contribution-row').forEach(function (row) {
|
|
if (status === 'all' || row.dataset.status === status) {
|
|
row.style.display = '';
|
|
visibleCount++;
|
|
} else {
|
|
row.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
document.getElementById('visible-count').textContent = visibleCount + ' Beiträge';
|
|
}
|
|
|
|
// Sortiert Beitrags-Zeilen im DOM nach Datum (auf-/absteigend) oder Kategorie.
|
|
function sortContributions(sortBy) {
|
|
const container = document.getElementById('contributions-container');
|
|
const rows = Array.from(container.querySelectorAll('.contribution-row'));
|
|
|
|
rows.sort(function (a, b) {
|
|
if (sortBy === 'date-desc') return new Date(b.dataset.date) - new Date(a.dataset.date);
|
|
if (sortBy === 'date-asc') return new Date(a.dataset.date) - new Date(b.dataset.date);
|
|
if (sortBy === 'category') return a.dataset.category.localeCompare(b.dataset.category);
|
|
return 0;
|
|
});
|
|
|
|
rows.forEach(function (row) { container.appendChild(row); });
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 8: Kommentar-Filter und Sortierung
|
|
// =====================================================================
|
|
|
|
// Filtert Kommentar-Zeilen nach Status (pending, approved, rejected, all).
|
|
// Arbeitet ausschließlich auf dem #comment-filter-tabs Container,
|
|
// damit keine Kollision mit dem Beitrags-Filter entsteht.
|
|
function filterCommentsByStatus(status, tabButton) {
|
|
document.querySelectorAll('#comment-filter-tabs .filter-tab').forEach(function (el) {
|
|
el.classList.remove('active');
|
|
});
|
|
tabButton.classList.add('active');
|
|
|
|
let visibleCount = 0;
|
|
document.querySelectorAll('.comment-mod-row').forEach(function (row) {
|
|
if (status === 'all' || row.dataset.status === status) {
|
|
row.style.display = '';
|
|
visibleCount++;
|
|
} else {
|
|
row.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
document.getElementById('comment-visible-count').textContent = visibleCount + ' Kommentare';
|
|
}
|
|
|
|
// Sortiert Kommentar-Zeilen nach Datum oder Beitrags-Titel.
|
|
function sortCommentRows(sortBy) {
|
|
const container = document.getElementById('comments-mod-container');
|
|
const rows = Array.from(container.querySelectorAll('.comment-mod-row'));
|
|
|
|
rows.sort(function (a, b) {
|
|
if (sortBy === 'date-desc') return new Date(b.dataset.date) - new Date(a.dataset.date);
|
|
if (sortBy === 'date-asc') return new Date(a.dataset.date) - new Date(b.dataset.date);
|
|
if (sortBy === 'contribution') return a.dataset.contribution.localeCompare(b.dataset.contribution);
|
|
return 0;
|
|
});
|
|
|
|
rows.forEach(function (row) { container.appendChild(row); });
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 9: API-Hilfsfunktion
|
|
// =====================================================================
|
|
|
|
// Sendet einen POST-Request an die API und gibt ein Promise zurück.
|
|
// Analog zu apiCall() in app.js, jedoch Promise-basiert statt Callback-basiert,
|
|
// da admin.js keine asynchrone Verkettung mit Callback-Chains benötigt.
|
|
function apiCall(data) {
|
|
const formData = new FormData();
|
|
for (const key in data) {
|
|
formData.append(key, data[key]);
|
|
}
|
|
return fetch(ADMIN_CONFIG.apiUrl, { method: 'POST', body: formData })
|
|
.then(function (r) { return r.json(); });
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 10: Hilfsfunktion HTML-Escaping
|
|
// =====================================================================
|
|
|
|
// Verhindert XSS in dynamisch erzeugten SweetAlert-Dialogen.
|
|
// Identisch zu escapeHtml() in app.js — beide Dateien arbeiten unabhängig,
|
|
// daher keine gemeinsame Abhängigkeit eingeführt.
|
|
function escapeHtml(text) {
|
|
if (!text) return '';
|
|
const div = document.createElement('div');
|
|
div.appendChild(document.createTextNode(text));
|
|
return div.innerHTML;
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 11: Beitrags-Status ändern
|
|
// =====================================================================
|
|
|
|
// Ändert den Status eines Beitrags (approved / rejected / pending).
|
|
// Zeigt eine SweetAlert-Bestätigung und lädt die Seite nach Erfolg neu.
|
|
function changeStatus(contributionId, newStatus) {
|
|
const labels = { approved: 'freigeben', rejected: 'ablehnen', pending: 'zurücksetzen' };
|
|
|
|
Swal.fire({
|
|
title: 'Beitrag ' + labels[newStatus] + '?',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Ja',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: ADMIN_CONFIG.primaryColor
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'update',
|
|
contribution_id: contributionId,
|
|
status: newStatus
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
location.reload();
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 12: Beitrag bearbeiten
|
|
// =====================================================================
|
|
|
|
// Öffnet einen SweetAlert-Dialog zum Bearbeiten von Titel und Beschreibung.
|
|
// Werte werden mit escapeHtml() gesichert, bevor sie ins HTML eingefügt werden.
|
|
function editContribution(contributionId, currentTitle, currentDescription) {
|
|
Swal.fire({
|
|
title: 'Beitrag bearbeiten',
|
|
html:
|
|
'<div style="text-align:left;">' +
|
|
'<div style="margin-bottom:12px;">' +
|
|
'<label style="display:block;font-weight:600;font-size:1.15rem;margin-bottom:4px;">Titel</label>' +
|
|
'<input id="swal-title" class="swal2-input" style="margin:0;width:100%;" value="' + escapeHtml(currentTitle) + '">' +
|
|
'</div>' +
|
|
'<div>' +
|
|
'<label style="display:block;font-weight:600;font-size:1.15rem;margin-bottom:4px;">Beschreibung</label>' +
|
|
'<textarea id="swal-description" class="swal2-textarea" style="margin:0;width:100%;">' + escapeHtml(currentDescription) + '</textarea>' +
|
|
'</div>' +
|
|
'</div>',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Speichern',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: ADMIN_CONFIG.primaryColor,
|
|
preConfirm: function () {
|
|
return {
|
|
title: document.getElementById('swal-title').value.trim(),
|
|
description: document.getElementById('swal-description').value.trim()
|
|
};
|
|
}
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'update',
|
|
contribution_id: contributionId,
|
|
title: result.value.title,
|
|
description: result.value.description
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
Swal.fire('Gespeichert!', 'Beitrag wurde aktualisiert.', 'success')
|
|
.then(function () { location.reload(); });
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 13: Beitrag löschen
|
|
// =====================================================================
|
|
|
|
// Zeigt eine Lösch-Bestätigung und entfernt den Beitrag dauerhaft.
|
|
function deleteContribution(contributionId) {
|
|
Swal.fire({
|
|
title: 'Beitrag löschen?',
|
|
text: 'Diese Aktion kann nicht rückgängig gemacht werden.',
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Beitrag löschen',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: '#c62828'
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'delete',
|
|
contribution_id: contributionId
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
Swal.fire('Gelöscht!', 'Beitrag wurde gelöscht.', 'success')
|
|
.then(function () { location.reload(); });
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 14: Kommentar-Status ändern
|
|
// =====================================================================
|
|
|
|
// Ändert den Status eines Kommentars (approved / rejected / pending).
|
|
function changeCommentStatus(commentId, newStatus) {
|
|
const labels = { approved: 'akzeptieren', rejected: 'ablehnen', pending: 'zurücksetzen' };
|
|
|
|
Swal.fire({
|
|
title: 'Kommentar ' + labels[newStatus] + '?',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Ja',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: ADMIN_CONFIG.primaryColor
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'update_comment',
|
|
comment_id: commentId,
|
|
status: newStatus
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
location.reload();
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 15: Kommentar bearbeiten
|
|
// =====================================================================
|
|
|
|
// Öffnet einen Dialog zum Bearbeiten des Kommentar-Inhalts.
|
|
function editModComment(commentId, currentContent) {
|
|
Swal.fire({
|
|
title: 'Kommentar bearbeiten',
|
|
html:
|
|
'<div style="text-align:left;">' +
|
|
'<label style="display:block;font-weight:600;font-size:1.15rem;margin-bottom:4px;">Inhalt</label>' +
|
|
'<textarea id="swal-comment-content" class="swal2-textarea" style="margin:0;width:100%;">' + escapeHtml(currentContent) + '</textarea>' +
|
|
'</div>',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Speichern',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: ADMIN_CONFIG.primaryColor,
|
|
preConfirm: function () {
|
|
return { content: document.getElementById('swal-comment-content').value.trim() };
|
|
}
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'update_comment',
|
|
comment_id: commentId,
|
|
content: result.value.content
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
Swal.fire('Gespeichert!', 'Kommentar wurde aktualisiert.', 'success')
|
|
.then(function () { location.reload(); });
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 16: Kommentar löschen
|
|
// =====================================================================
|
|
|
|
// Zeigt eine Lösch-Bestätigung und entfernt den Kommentar dauerhaft.
|
|
function deleteModComment(commentId) {
|
|
Swal.fire({
|
|
title: 'Kommentar löschen?',
|
|
text: 'Diese Aktion kann nicht rückgängig gemacht werden.',
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Löschen',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: '#c62828'
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'delete_comment',
|
|
comment_id: commentId
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
Swal.fire('Gelöscht!', 'Kommentar wurde entfernt.', 'success')
|
|
.then(function () { location.reload(); });
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 17: Neuigkeit erstellen
|
|
// =====================================================================
|
|
|
|
// Öffnet einen Dialog zum Erstellen einer neuen Neuigkeit.
|
|
// Titel und Inhalt sind Pflichtfelder, Autor hat einen Standardwert.
|
|
function createNews() {
|
|
Swal.fire({
|
|
title: 'Neuigkeit hinzufügen',
|
|
html:
|
|
'<div style="text-align:left;">' +
|
|
'<div style="margin-bottom:12px;">' +
|
|
'<label style="display:block;font-weight:600;font-size:1.15rem;margin-bottom:4px;">Titel</label>' +
|
|
'<input id="swal-news-title" class="swal2-input" style="margin:0;width:100%;" placeholder="Titel der Neuigkeit">' +
|
|
'</div>' +
|
|
'<div style="margin-bottom:12px;">' +
|
|
'<label style="display:block;font-weight:600;font-size:1.15rem;margin-bottom:4px;">Inhalt</label>' +
|
|
'<textarea id="swal-news-content" class="swal2-textarea" style="margin:0;width:100%;" placeholder="Neuigkeit verfassen..."></textarea>' +
|
|
'</div>' +
|
|
'<div>' +
|
|
'<label style="display:block;font-weight:600;font-size:1.15rem;margin-bottom:4px;">Autor</label>' +
|
|
'<input id="swal-news-author" class="swal2-input" style="margin:0;width:100%;" value="Stadtverwaltung">' +
|
|
'</div>' +
|
|
'</div>',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Veröffentlichen',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: ADMIN_CONFIG.primaryColor,
|
|
preConfirm: function () {
|
|
const title = document.getElementById('swal-news-title').value.trim();
|
|
const content = document.getElementById('swal-news-content').value.trim();
|
|
const author = document.getElementById('swal-news-author').value.trim() || 'Stadtverwaltung';
|
|
if (!title || !content) {
|
|
Swal.showValidationMessage('Titel und Inhalt sind Pflichtfelder.');
|
|
return false;
|
|
}
|
|
return { title, content, author_name: author };
|
|
}
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'create_news',
|
|
municipality_id: ADMIN_CONFIG.municipalityId,
|
|
title: result.value.title,
|
|
content: result.value.content,
|
|
author_name: result.value.author_name
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
Swal.fire('Veröffentlicht!', 'Neuigkeit wurde veröffentlicht.', 'success')
|
|
.then(function () { location.reload(); });
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 18: Neuigkeit bearbeiten
|
|
// =====================================================================
|
|
|
|
// Öffnet einen Dialog zum Bearbeiten einer bestehenden Neuigkeit.
|
|
function editNews(newsId, currentTitle, currentContent, currentAuthor) {
|
|
Swal.fire({
|
|
title: 'Neuigkeit bearbeiten',
|
|
html:
|
|
'<div style="text-align:left;">' +
|
|
'<div style="margin-bottom:12px;">' +
|
|
'<label style="display:block;font-weight:600;font-size:1.15rem;margin-bottom:4px;">Titel</label>' +
|
|
'<input id="swal-news-title" class="swal2-input" style="margin:0;width:100%;" value="' + escapeHtml(currentTitle) + '">' +
|
|
'</div>' +
|
|
'<div style="margin-bottom:12px;">' +
|
|
'<label style="display:block;font-weight:600;font-size:1.15rem;margin-bottom:4px;">Inhalt</label>' +
|
|
'<textarea id="swal-news-content" class="swal2-textarea" style="margin:0;width:100%;">' + escapeHtml(currentContent) + '</textarea>' +
|
|
'</div>' +
|
|
'<div>' +
|
|
'<label style="display:block;font-weight:600;font-size:1.15rem;margin-bottom:4px;">Autor</label>' +
|
|
'<input id="swal-news-author" class="swal2-input" style="margin:0;width:100%;" value="' + escapeHtml(currentAuthor) + '">' +
|
|
'</div>' +
|
|
'</div>',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Speichern',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: ADMIN_CONFIG.primaryColor,
|
|
preConfirm: function () {
|
|
return {
|
|
title: document.getElementById('swal-news-title').value.trim(),
|
|
content: document.getElementById('swal-news-content').value.trim(),
|
|
author_name: document.getElementById('swal-news-author').value.trim() || 'Stadtverwaltung'
|
|
};
|
|
}
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'update_news',
|
|
news_id: newsId,
|
|
title: result.value.title,
|
|
content: result.value.content,
|
|
author_name: result.value.author_name
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
Swal.fire('Gespeichert!', 'Neuigkeit wurde aktualisiert.', 'success')
|
|
.then(function () { location.reload(); });
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// =====================================================================
|
|
// Block 19: Neuigkeit löschen
|
|
// =====================================================================
|
|
|
|
// Zeigt eine Lösch-Bestätigung und entfernt die Neuigkeit dauerhaft.
|
|
// Hinweis: Im Original-Code war dieser Block fälschlicherweise mit
|
|
// "// Create News Article" kommentiert — hier korrigiert.
|
|
function deleteNews(newsId) {
|
|
Swal.fire({
|
|
title: 'Neuigkeit löschen?',
|
|
text: 'Diese Aktion kann nicht rückgängig gemacht werden.',
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Löschen',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: '#c62828'
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'delete_news',
|
|
news_id: newsId
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
Swal.fire('Gelöscht!', 'Neuigkeit wurde gelöscht.', 'success')
|
|
.then(function () { location.reload(); });
|
|
});
|
|
});
|
|
} |