Feature: Moderation — Forum-Meldungen + POI-Korrekturen, SW by-v589
This commit is contained in:
parent
020153484a
commit
e2cd32a550
3 changed files with 127 additions and 5 deletions
|
|
@ -3,7 +3,7 @@
|
||||||
Router, State-Management, Navigation, Initialisierung.
|
Router, State-Management, Navigation, Initialisierung.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const APP_VER = '588'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
const APP_VER = '589'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||||
const APP_VERSION = '1.2.1'; // ← semantische Version, wird bei make release gesetzt
|
const APP_VERSION = '1.2.1'; // ← semantische Version, wird bei make release gesetzt
|
||||||
const IS_STAGING = location.hostname === 'staging.banyaro.app';
|
const IS_STAGING = location.hostname === 'staging.banyaro.app';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1474,14 +1474,19 @@ window.Page_admin = (() => {
|
||||||
async function _loadModeration(el) {
|
async function _loadModeration(el) {
|
||||||
el.innerHTML = `<div style="padding:var(--space-4);text-align:center;color:var(--c-text-muted)">Lade…</div>`;
|
el.innerHTML = `<div style="padding:var(--space-4);text-align:center;color:var(--c-text-muted)">Lade…</div>`;
|
||||||
|
|
||||||
const [zuchter, fotos] = await Promise.all([
|
const [zuchter, fotos, reports, poiEdits] = await Promise.all([
|
||||||
API.get('/wiki/zuchter/pending').catch(() => []),
|
API.get('/wiki/zuchter/pending').catch(() => []),
|
||||||
API.get('/wiki/foto-submissions').catch(() => []),
|
API.get('/wiki/foto-submissions').catch(() => []),
|
||||||
|
API.get('/moderation/reports').catch(() => []),
|
||||||
|
API.get('/moderation/poi-edits').catch(() => []),
|
||||||
]);
|
]);
|
||||||
|
const poiPending = poiEdits.filter(e => e.status === 'pending');
|
||||||
|
|
||||||
const modItems = [
|
const modItems = [
|
||||||
{ label: 'Züchter-Einreichungen', count: zuchter.length, icon: 'certificate' },
|
{ label: 'Züchter-Einreichungen', count: zuchter.length, icon: 'certificate' },
|
||||||
{ label: 'Foto-Einreichungen', count: fotos.length, icon: 'image' },
|
{ label: 'Foto-Einreichungen', count: fotos.length, icon: 'image' },
|
||||||
|
{ label: 'Forum-Meldungen', count: reports.length, icon: 'warning' },
|
||||||
|
{ label: 'POI-Korrekturen', count: poiPending.length, icon: 'map-pin' },
|
||||||
].filter(i => i.count > 0);
|
].filter(i => i.count > 0);
|
||||||
|
|
||||||
let html = `
|
let html = `
|
||||||
|
|
@ -1572,6 +1577,88 @@ window.Page_admin = (() => {
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Forum-Meldungen ---
|
||||||
|
html += `<h3 style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||||
|
color:var(--c-text-muted);text-transform:uppercase;letter-spacing:.06em;
|
||||||
|
margin:var(--space-6) 0 var(--space-3)">
|
||||||
|
Forum-Meldungen
|
||||||
|
<span style="background:${reports.length ? 'var(--c-danger)' : 'var(--c-primary)'};color:#fff;
|
||||||
|
border-radius:999px;padding:1px 8px;font-size:var(--text-xs);margin-left:6px">
|
||||||
|
${reports.length}
|
||||||
|
</span>
|
||||||
|
</h3>`;
|
||||||
|
if (!reports.length) {
|
||||||
|
html += `<p style="font-size:var(--text-sm);color:var(--c-text-muted);margin-bottom:var(--space-6)">Keine offenen Meldungen.</p>`;
|
||||||
|
} else {
|
||||||
|
html += `<div style="display:flex;flex-direction:column;gap:var(--space-3);margin-bottom:var(--space-6)">
|
||||||
|
${reports.map(r => `
|
||||||
|
<div class="card" style="padding:var(--space-4);border-left:3px solid var(--c-danger)">
|
||||||
|
<div style="display:flex;align-items:flex-start;gap:var(--space-3)">
|
||||||
|
<div style="flex:1;min-width:0">
|
||||||
|
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-bottom:var(--space-1)">
|
||||||
|
${_esc(r.target_type)} #${r.target_id} · Gemeldet von <strong>${_esc(r.melder_name || '?')}</strong>
|
||||||
|
</div>
|
||||||
|
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);margin-bottom:var(--space-1)">
|
||||||
|
Grund: ${_esc(r.grund)}
|
||||||
|
</div>
|
||||||
|
${r.content_preview ? `
|
||||||
|
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);
|
||||||
|
padding:var(--space-2) var(--space-3);background:var(--c-surface-2);
|
||||||
|
border-radius:var(--radius-sm)">${_esc(r.content_preview)}</div>` : ''}
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm btn-primary adm-mod-resolve" data-rid="${r.id}" title="Als erledigt markieren">
|
||||||
|
${UI.icon('check')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>`).join('')}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- POI-Korrekturen ---
|
||||||
|
html += `<h3 style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||||
|
color:var(--c-text-muted);text-transform:uppercase;letter-spacing:.06em;
|
||||||
|
margin:var(--space-2) 0 var(--space-3)">
|
||||||
|
POI-Korrekturen
|
||||||
|
<span style="background:${poiPending.length ? 'var(--c-warning,#e65100)' : 'var(--c-primary)'};color:#fff;
|
||||||
|
border-radius:999px;padding:1px 8px;font-size:var(--text-xs);margin-left:6px">
|
||||||
|
${poiPending.length}
|
||||||
|
</span>
|
||||||
|
</h3>`;
|
||||||
|
if (!poiPending.length) {
|
||||||
|
html += `<p style="font-size:var(--text-sm);color:var(--c-text-muted)">Keine ausstehenden POI-Korrekturen.</p>`;
|
||||||
|
} else {
|
||||||
|
html += `<div class="card adm-table-card"><div class="adm-table-scroll">
|
||||||
|
<table class="adm-table">
|
||||||
|
<thead><tr style="background:var(--c-surface-2);text-align:left">
|
||||||
|
<th class="adm-th">Ort</th>
|
||||||
|
<th class="adm-th">Feld</th>
|
||||||
|
<th class="adm-th">Alt</th>
|
||||||
|
<th class="adm-th">Neu</th>
|
||||||
|
<th class="adm-th">Von</th>
|
||||||
|
<th class="adm-th"></th>
|
||||||
|
</tr></thead>
|
||||||
|
<tbody>
|
||||||
|
${poiPending.map((e, i) => `
|
||||||
|
<tr style="${i%2===1?'background:var(--c-surface-2)':''}">
|
||||||
|
<td class="adm-td" style="font-weight:var(--weight-semibold)">${_esc(e.poi_name || `OSM #${e.osm_id}`)}</td>
|
||||||
|
<td class="adm-td"><code style="font-size:var(--text-xs)">${_esc(e.field)}</code></td>
|
||||||
|
<td class="adm-td" style="color:var(--c-text-muted);font-size:var(--text-xs)">${_esc(e.old_value || '—')}</td>
|
||||||
|
<td class="adm-td" style="font-size:var(--text-xs)">${_esc(e.new_value || '—')}</td>
|
||||||
|
<td class="adm-td" style="color:var(--c-text-muted)">${_esc(e.einreicher_name || '?')}</td>
|
||||||
|
<td class="adm-td" style="text-align:right;white-space:nowrap">
|
||||||
|
<button class="btn btn-sm btn-primary adm-poi-approve" data-id="${e.id}" style="margin-right:4px">
|
||||||
|
${UI.icon('check')}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-ghost adm-poi-reject" data-id="${e.id}" style="color:var(--c-danger)">
|
||||||
|
${UI.icon('x')}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>`).join('')}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
el.innerHTML = html;
|
el.innerHTML = html;
|
||||||
|
|
||||||
// Züchter freigeben
|
// Züchter freigeben
|
||||||
|
|
@ -1610,6 +1697,41 @@ window.Page_admin = (() => {
|
||||||
await _loadModeration(el);
|
await _loadModeration(el);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Forum-Meldung erledigen
|
||||||
|
el.querySelectorAll('.adm-mod-resolve').forEach(btn => {
|
||||||
|
btn.addEventListener('click', async () => {
|
||||||
|
btn.disabled = true;
|
||||||
|
try {
|
||||||
|
await API.patch(`/moderation/reports/${btn.dataset.rid}`, {});
|
||||||
|
await _loadModeration(el);
|
||||||
|
} catch (e) { UI.toast.error(e.message); btn.disabled = false; }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// POI-Korrektur freigeben
|
||||||
|
el.querySelectorAll('.adm-poi-approve').forEach(btn => {
|
||||||
|
btn.addEventListener('click', async () => {
|
||||||
|
btn.disabled = true;
|
||||||
|
try {
|
||||||
|
await API.patch(`/moderation/poi-edits/${btn.dataset.id}`, { action: 'approve' });
|
||||||
|
UI.toast.success('Korrektur übernommen.');
|
||||||
|
await _loadModeration(el);
|
||||||
|
} catch (e) { UI.toast.error(e.message); btn.disabled = false; }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// POI-Korrektur ablehnen
|
||||||
|
el.querySelectorAll('.adm-poi-reject').forEach(btn => {
|
||||||
|
btn.addEventListener('click', async () => {
|
||||||
|
btn.disabled = true;
|
||||||
|
try {
|
||||||
|
await API.patch(`/moderation/poi-edits/${btn.dataset.id}`, { action: 'reject' });
|
||||||
|
UI.toast.success('Korrektur abgelehnt.');
|
||||||
|
await _loadModeration(el);
|
||||||
|
} catch (e) { UI.toast.error(e.message); btn.disabled = false; }
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Offline-Cache + Push Notifications + Tile-Cache
|
Offline-Cache + Push Notifications + Tile-Cache
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const CACHE_VERSION = 'by-v588';
|
const CACHE_VERSION = 'by-v589';
|
||||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||||
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache
|
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue