/* ============================================================
BAN YARO — Moderations-Panel
Nur für Moderatoren und Admins.
============================================================ */
window.Page_moderation = (() => {
let _container = null;
let _appState = null;
let _tab = 'uebersicht';
const TABS = [
{ id: 'uebersicht', label: 'Übersicht', icon: 'house-line' },
{ id: 'fotos', label: 'Fotos', icon: 'image' },
{ id: 'user', label: 'User', icon: 'users' },
{ id: 'forum', label: 'Forum', icon: 'chat-circle-dots' },
];
// ------------------------------------------------------------------
async function init(container, appState) {
_container = container;
_appState = appState;
const u = appState.user;
const isMod = u?.rolle === 'admin' || u?.rolle === 'moderator' || u?.is_moderator;
if (!isMod) {
container.innerHTML = _emptyState('shield', 'Kein Zugriff',
'Dieser Bereich ist nur für Moderatoren und Admins.');
return;
}
_render();
}
function refresh() { _renderTab(); }
function onDogChange() {}
// ------------------------------------------------------------------
// SHELL
// ------------------------------------------------------------------
function _render() {
_container.innerHTML = `
${TABS.map(t => `
${UI.icon(t.icon)} ${t.label}
`).join('')}
`;
_container.querySelector('#mod-tabs')
?.style.setProperty('--adm-tab-cols', Math.ceil(TABS.length / 2));
_container.querySelectorAll('#mod-tabs .by-tab').forEach(btn => {
btn.addEventListener('click', () => {
_tab = btn.dataset.tab;
_container.querySelectorAll('#mod-tabs .by-tab').forEach(b =>
b.classList.toggle('active', b.dataset.tab === _tab)
);
_renderTab();
});
});
_renderTab();
}
async function _renderTab() {
const el = _container.querySelector('#mod-content');
if (!el) return;
el.innerHTML = `Lade…
`;
try {
switch (_tab) {
case 'uebersicht': await _renderStats(el); break;
case 'fotos': await _renderFotos(el); break;
case 'user': await _renderUsers(el); break;
case 'forum': await _renderForum(el); break;
}
} catch (e) {
el.innerHTML = _emptyState('warning', 'Fehler', e.message || 'Unbekannter Fehler.');
}
}
// ------------------------------------------------------------------
// TAB: ÜBERSICHT
// ------------------------------------------------------------------
async function _renderStats(el) {
const s = await API.get('/moderation/stats');
el.innerHTML = `
${_statCard('warning',
'Offene Meldungen',
s.open_reports,
s.open_reports > 0 ? 'var(--c-danger)' : 'var(--c-text-muted)')}
${_statCard('image',
'Fotos ausstehend',
s.pending_fotos,
s.pending_fotos > 0 ? 'var(--c-warning)' : 'var(--c-text-muted)')}
${_statCard('skull',
'Gesperrte User',
s.banned_users,
s.banned_users > 0 ? '#f59e0b' : 'var(--c-text-muted)')}
${_statCard('storefront',
'Züchter ausstehend',
s.pending_zuchter,
s.pending_zuchter > 0 ? 'var(--c-warning)' : 'var(--c-text-muted)')}
${UI.icon('info')}
Das Moderations-Panel zeigt dir alle ausstehenden Aufgaben auf einen Blick.
Verwende die Tabs oben für Details zu Fotos, Usern und Forum-Meldungen.
`;
}
function _statCard(icon, label, value, color) {
return `
`;
}
// ------------------------------------------------------------------
// TAB: FOTOS
// ------------------------------------------------------------------
async function _renderFotos(el) {
el.innerHTML = `
${UI.icon('arrows-clockwise')} Aktualisieren
Lade…
`;
el.querySelector('#mod-fotos-refresh').addEventListener('click', () =>
_loadFotos(el.querySelector('#mod-fotos-list'))
);
await _loadFotos(el.querySelector('#mod-fotos-list'));
}
async function _loadFotos(el) {
el.innerHTML = `Lade…
`;
const fotos = await API.get('/moderation/fotos');
if (!fotos.length) {
el.innerHTML = _emptyState('check-circle', 'Keine ausstehenden Fotos',
'Alle Foto-Einreichungen wurden bearbeitet.');
return;
}
el.innerHTML = `
${fotos.map(f => `
${_esc(f.rasse_name || f.rasse_slug)}
von ${_esc(f.user_name)}
${f.aktuell_foto ? `
aktuelles Foto
` : ''}
Freigeben
Ablehnen
`).join('')}
`;
el.querySelectorAll('.mod-foto-approve').forEach(btn => {
btn.addEventListener('click', async () => {
btn.disabled = true;
try {
await API.patch(`/moderation/fotos/${btn.dataset.id}`, { action: 'approve' });
UI.toast.success('Foto freigegeben.');
await _loadFotos(el);
} catch (e) { UI.toast.error(e.message); btn.disabled = false; }
});
});
el.querySelectorAll('.mod-foto-reject').forEach(btn => {
btn.addEventListener('click', async () => {
btn.disabled = true;
try {
await API.patch(`/moderation/fotos/${btn.dataset.id}`, {
action: 'reject',
reject_reason: 'Nicht geeignet.'
});
UI.toast.success('Foto abgelehnt.');
await _loadFotos(el);
} catch (e) { UI.toast.error(e.message); btn.disabled = false; }
});
});
}
// ------------------------------------------------------------------
// TAB: USER
// ------------------------------------------------------------------
async function _renderUsers(el) {
el.innerHTML = `
Nur gesperrte
Lade…
`;
const load = async () => {
const q = el.querySelector('#mod-user-q').value;
const banned = el.querySelector('#mod-only-banned').checked ? 1 : 0;
const data = await API.get(
`/moderation/users?q=${encodeURIComponent(q)}&banned=${banned}`
);
_renderUserList(el.querySelector('#mod-user-list'), data.users, data.total, el);
};
let timer;
el.querySelector('#mod-user-q').addEventListener('input', () => {
clearTimeout(timer);
timer = setTimeout(load, 350);
});
el.querySelector('#mod-only-banned').addEventListener('change', load);
await load();
}
function _renderUserList(el, users, total, parentEl) {
if (!users.length) {
el.innerHTML = _emptyState('users', 'Keine Nutzer gefunden', '');
return;
}
el.innerHTML = `
${total} Nutzer gefunden
${users.map(u => `
${_esc(u.name[0].toUpperCase())}
${_esc(u.name)}
${u.is_banned ? `GESPERRT ` : ''}
${_esc(u.email)} ·
${_esc(u.rolle)}
${u.is_banned
? `
${UI.icon('lock-open')}
`
: `
${UI.icon('lock')}
`
}
`).join('')}
`;
el.querySelectorAll('.mod-ban').forEach(btn => {
btn.addEventListener('click', () => _banUser(btn.dataset.uid, btn.dataset.name, true, parentEl));
});
el.querySelectorAll('.mod-unban').forEach(btn => {
btn.addEventListener('click', () => _banUser(btn.dataset.uid, btn.dataset.name, false, parentEl));
});
}
async function _banUser(uid, name, ban, parentEl) {
if (ban) {
const reason = window.prompt(`${name} sperren — Grund (optional):`);
if (reason === null) return;
try {
await API.patch(`/moderation/users/${uid}`, {
is_banned: 1,
ban_reason: reason || 'Kein Grund angegeben.'
});
UI.toast.success(`${name} gesperrt.`);
_renderTab();
} catch (e) { UI.toast.error(e.message); }
} else {
try {
await API.patch(`/moderation/users/${uid}`, {
is_banned: 0,
ban_reason: null
});
UI.toast.success(`Sperre für ${name} aufgehoben.`);
_renderTab();
} catch (e) { UI.toast.error(e.message); }
}
}
// ------------------------------------------------------------------
// TAB: FORUM
// ------------------------------------------------------------------
async function _renderForum(el) {
el.innerHTML = `
${UI.icon('arrows-clockwise')} Aktualisieren
Lade…
`;
el.querySelector('#mod-forum-refresh').addEventListener('click', () =>
_loadReports(el.querySelector('#mod-forum-list'))
);
await _loadReports(el.querySelector('#mod-forum-list'));
}
async function _loadReports(el) {
el.innerHTML = `Lade…
`;
const reports = await API.get('/moderation/reports');
if (!reports.length) {
el.innerHTML = _emptyState('check-circle', 'Keine offenen Meldungen', 'Alles sauber.');
return;
}
el.innerHTML = `
${reports.map(r => `
${_esc(r.target_type)} #${r.target_id} ·
Gemeldet von ${_esc(r.melder_name)}
Grund: ${_esc(r.grund)}
${r.content_preview ? `
${_esc(r.content_preview)}
` : ''}
${UI.icon('check')}
`).join('')}
`;
el.querySelectorAll('.mod-resolve-btn').forEach(btn => {
btn.addEventListener('click', async () => {
btn.disabled = true;
try {
await API.patch(`/moderation/reports/${btn.dataset.rid}`, {});
UI.toast.success('Meldung als erledigt markiert.');
await _loadReports(el);
} catch (e) { UI.toast.error(e.message); btn.disabled = false; }
});
});
}
// ------------------------------------------------------------------
// HELPERS
// ------------------------------------------------------------------
function _emptyState(icon, title, text) {
return `
${title}
${text ? `
${text}
` : ''}
`;
}
function _esc(s) {
if (!s) return '';
return String(s)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"');
}
// ------------------------------------------------------------------
return { init, refresh, onDogChange };
})();