Fix: Admin-Seite Mobile-kompatibel (Tabellen scrollbar, Filter wrap, Touch-Targets)
This commit is contained in:
parent
92620c2c52
commit
bfdf6ebfae
4 changed files with 208 additions and 81 deletions
|
|
@ -4657,3 +4657,136 @@ textarea.form-control {
|
||||||
}
|
}
|
||||||
.photo-editor-empty { font-size: 5rem; color: var(--c-text-secondary); }
|
.photo-editor-empty { font-size: 5rem; color: var(--c-text-secondary); }
|
||||||
.photo-editor-controls { width: 100%; }
|
.photo-editor-controls { width: 100%; }
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------
|
||||||
|
Admin-Seite — Mobile-Responsive Styles
|
||||||
|
------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Tab-Leiste mit Abstand nach unten */
|
||||||
|
.adm-tabs {
|
||||||
|
margin-bottom: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filter-Zeile (Suche + Select / Suche + Checkbox) */
|
||||||
|
.adm-filter-row {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-2);
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.adm-filter-input {
|
||||||
|
flex: 1 1 160px;
|
||||||
|
min-width: 0;
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
border: 1.5px solid var(--c-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--c-surface);
|
||||||
|
color: var(--c-text);
|
||||||
|
}
|
||||||
|
.adm-filter-select {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
border: 1.5px solid var(--c-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--c-surface);
|
||||||
|
color: var(--c-text);
|
||||||
|
max-width: 140px;
|
||||||
|
}
|
||||||
|
.adm-filter-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
color: var(--c-text-secondary);
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forum-Unternavigation scrollbar auf Mobile */
|
||||||
|
.adm-subnav {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-2);
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
overflow-x: auto;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
scrollbar-width: none;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
.adm-subnav::-webkit-scrollbar { display: none; }
|
||||||
|
.adm-subnav .btn { flex-shrink: 0; }
|
||||||
|
|
||||||
|
/* Tabellen: scrollbarer Container + Card ohne overflow:hidden */
|
||||||
|
.adm-table-card {
|
||||||
|
overflow: visible; /* Card-Schatten bleibt, overflow wird im Scroll-Container geregelt */
|
||||||
|
}
|
||||||
|
.adm-table-scroll {
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
border-radius: var(--radius-lg); /* für abgerundete Ecken bei overflow */
|
||||||
|
}
|
||||||
|
.adm-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
min-width: 480px; /* verhindert Quetschen unter diesem Wert */
|
||||||
|
}
|
||||||
|
.adm-th {
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
font-weight: var(--weight-semibold);
|
||||||
|
color: var(--c-text-secondary);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.adm-td {
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Job-ID unter dem Job-Namen: kürzen wenn zu lang */
|
||||||
|
.adm-job-id {
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
color: var(--c-text-muted);
|
||||||
|
font-weight: normal;
|
||||||
|
max-width: 180px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trigger-Spalte in Jobs-Tabelle */
|
||||||
|
.adm-td-trigger {
|
||||||
|
color: var(--c-text-muted);
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
max-width: 160px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Monospace-Badge (Audit-Log Aktion) */
|
||||||
|
.adm-badge-mono {
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: var(--c-surface-2);
|
||||||
|
color: var(--c-text-secondary);
|
||||||
|
font-family: monospace;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon-only Buttons in Tabellen: 44px Touch-Target */
|
||||||
|
.adm-icon-btn {
|
||||||
|
min-width: 44px;
|
||||||
|
min-height: 44px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auf sehr kleinen Screens: Select volle Breite */
|
||||||
|
@media (max-width: 360px) {
|
||||||
|
.adm-filter-select {
|
||||||
|
max-width: 100%;
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Router, State-Management, Navigation, Initialisierung.
|
Router, State-Management, Navigation, Initialisierung.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const APP_VER = '124'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
const APP_VER = '126'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||||
|
|
||||||
const App = (() => {
|
const App = (() => {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ window.Page_admin = (() => {
|
||||||
<div style="max-width:720px;margin:0 auto;padding:var(--space-4)">
|
<div style="max-width:720px;margin:0 auto;padding:var(--space-4)">
|
||||||
|
|
||||||
<!-- Tabs -->
|
<!-- Tabs -->
|
||||||
<div class="by-tabs" style="margin-bottom:var(--space-5)" id="adm-tabs">
|
<div class="by-tabs adm-tabs" id="adm-tabs">
|
||||||
${TABS.map(t => `
|
${TABS.map(t => `
|
||||||
<button class="by-tab${t.id === _tab ? ' active' : ''}" data-tab="${t.id}">
|
<button class="by-tab${t.id === _tab ? ' active' : ''}" data-tab="${t.id}">
|
||||||
${UI.icon(t.icon)} ${t.label}
|
${UI.icon(t.icon)} ${t.label}
|
||||||
|
|
@ -144,14 +144,10 @@ window.Page_admin = (() => {
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
async function _renderUsers(el) {
|
async function _renderUsers(el) {
|
||||||
el.innerHTML = `
|
el.innerHTML = `
|
||||||
<div style="display:flex;gap:var(--space-2);margin-bottom:var(--space-4)">
|
<div class="adm-filter-row">
|
||||||
<input id="adm-user-q" type="search" placeholder="Name oder E-Mail…"
|
<input id="adm-user-q" type="search" placeholder="Name oder E-Mail…"
|
||||||
style="flex:1;padding:var(--space-2) var(--space-3);border:1.5px solid var(--c-border);
|
class="adm-filter-input">
|
||||||
border-radius:var(--radius-md);font-size:var(--text-sm);font-family:inherit;
|
<select id="adm-user-rolle" class="adm-filter-select">
|
||||||
background:var(--c-surface);color:var(--c-text)">
|
|
||||||
<select id="adm-user-rolle" style="padding:var(--space-2) var(--space-3);
|
|
||||||
border:1.5px solid var(--c-border);border-radius:var(--radius-md);
|
|
||||||
font-size:var(--text-sm);font-family:inherit;background:var(--c-surface);color:var(--c-text)">
|
|
||||||
<option value="">Alle Rollen</option>
|
<option value="">Alle Rollen</option>
|
||||||
<option value="user">user</option>
|
<option value="user">user</option>
|
||||||
<option value="moderator">moderator</option>
|
<option value="moderator">moderator</option>
|
||||||
|
|
@ -339,7 +335,7 @@ window.Page_admin = (() => {
|
||||||
async function _renderForum(el) {
|
async function _renderForum(el) {
|
||||||
el.innerHTML = `
|
el.innerHTML = `
|
||||||
<!-- Unternavigation -->
|
<!-- Unternavigation -->
|
||||||
<div style="display:flex;gap:var(--space-2);margin-bottom:var(--space-4)">
|
<div class="adm-subnav">
|
||||||
<button class="btn btn-primary btn-sm adm-forum-nav" data-view="reports" id="adm-fn-reports">
|
<button class="btn btn-primary btn-sm adm-forum-nav" data-view="reports" id="adm-fn-reports">
|
||||||
Offene Meldungen
|
Offene Meldungen
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -429,13 +425,10 @@ window.Page_admin = (() => {
|
||||||
} else {
|
} else {
|
||||||
// Threads
|
// Threads
|
||||||
el.innerHTML = `
|
el.innerHTML = `
|
||||||
<div style="display:flex;gap:var(--space-2);margin-bottom:var(--space-3)">
|
<div class="adm-filter-row">
|
||||||
<input id="adm-thread-q" type="search" placeholder="Threads durchsuchen…"
|
<input id="adm-thread-q" type="search" placeholder="Threads durchsuchen…"
|
||||||
style="flex:1;padding:var(--space-2) var(--space-3);border:1.5px solid var(--c-border);
|
class="adm-filter-input">
|
||||||
border-radius:var(--radius-md);font-size:var(--text-sm);font-family:inherit;
|
<label class="adm-filter-label">
|
||||||
background:var(--c-surface);color:var(--c-text)">
|
|
||||||
<label style="display:flex;align-items:center;gap:var(--space-2);
|
|
||||||
font-size:var(--text-sm);color:var(--c-text-secondary);white-space:nowrap">
|
|
||||||
<input type="checkbox" id="adm-show-deleted"> Gelöschte
|
<input type="checkbox" id="adm-show-deleted"> Gelöschte
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -623,31 +616,32 @@ window.Page_admin = (() => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
el.innerHTML = `
|
el.innerHTML = `
|
||||||
<div class="card" style="overflow:hidden">
|
<div class="card adm-table-card">
|
||||||
<table style="width:100%;border-collapse:collapse;font-size:var(--text-sm)">
|
<div class="adm-table-scroll">
|
||||||
|
<table class="adm-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr style="background:var(--c-surface-2);text-align:left">
|
<tr style="background:var(--c-surface-2);text-align:left">
|
||||||
<th style="padding:var(--space-3) var(--space-4);font-weight:var(--weight-semibold);color:var(--c-text-secondary)">Job</th>
|
<th class="adm-th">Job</th>
|
||||||
<th style="padding:var(--space-3) var(--space-4);font-weight:var(--weight-semibold);color:var(--c-text-secondary)">Nächster Lauf</th>
|
<th class="adm-th">Nächster Lauf</th>
|
||||||
<th style="padding:var(--space-3) var(--space-4);font-weight:var(--weight-semibold);color:var(--c-text-secondary)">Trigger</th>
|
<th class="adm-th">Trigger</th>
|
||||||
<th style="padding:var(--space-3) var(--space-4);font-weight:var(--weight-semibold);color:var(--c-text-secondary)"></th>
|
<th class="adm-th"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
${jobs.map((j, i) => `
|
${jobs.map((j, i) => `
|
||||||
<tr style="${i % 2 === 1 ? 'background:var(--c-surface-2)' : ''}">
|
<tr style="${i % 2 === 1 ? 'background:var(--c-surface-2)' : ''}">
|
||||||
<td style="padding:var(--space-3) var(--space-4);font-weight:var(--weight-semibold);color:var(--c-text)">
|
<td class="adm-td" style="font-weight:var(--weight-semibold);color:var(--c-text)">
|
||||||
${_esc(j.name)}
|
${_esc(j.name)}
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);font-weight:normal">${_esc(j.id)}</div>
|
<div class="adm-job-id">${_esc(j.id)}</div>
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:var(--space-3) var(--space-4);color:var(--c-text-secondary)">
|
<td class="adm-td" style="color:var(--c-text-secondary);white-space:nowrap">
|
||||||
${j.next_run_time ? _formatDateTime(j.next_run_time) : '<span style="color:var(--c-text-muted)">—</span>'}
|
${j.next_run_time ? _formatDateTime(j.next_run_time) : '<span style="color:var(--c-text-muted)">—</span>'}
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:var(--space-3) var(--space-4);color:var(--c-text-muted);font-size:var(--text-xs);max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
|
<td class="adm-td adm-td-trigger">
|
||||||
${_esc(j.trigger)}
|
${_esc(j.trigger)}
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:var(--space-3) var(--space-4);text-align:right">
|
<td class="adm-td" style="text-align:right">
|
||||||
<button class="btn btn-sm btn-ghost adm-job-trigger" data-id="${_esc(j.id)}" data-name="${_esc(j.name)}"
|
<button class="btn btn-sm btn-ghost adm-job-trigger adm-icon-btn" data-id="${_esc(j.id)}" data-name="${_esc(j.name)}"
|
||||||
title="Jetzt ausführen" style="color:var(--c-primary)">
|
title="Jetzt ausführen" style="color:var(--c-primary)">
|
||||||
${UI.icon('play')}
|
${UI.icon('play')}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -657,6 +651,7 @@ window.Page_admin = (() => {
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
el.querySelectorAll('.adm-job-trigger').forEach(btn => {
|
el.querySelectorAll('.adm-job-trigger').forEach(btn => {
|
||||||
btn.addEventListener('click', async () => {
|
btn.addEventListener('click', async () => {
|
||||||
|
|
@ -704,33 +699,31 @@ window.Page_admin = (() => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
el.innerHTML = `
|
el.innerHTML = `
|
||||||
<div class="card" style="overflow:hidden">
|
<div class="card adm-table-card">
|
||||||
<table style="width:100%;border-collapse:collapse;font-size:var(--text-sm)">
|
<div class="adm-table-scroll">
|
||||||
|
<table class="adm-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr style="background:var(--c-surface-2);text-align:left">
|
<tr style="background:var(--c-surface-2);text-align:left">
|
||||||
<th style="padding:var(--space-3) var(--space-4);font-weight:var(--weight-semibold);color:var(--c-text-secondary)">Wann</th>
|
<th class="adm-th">Wann</th>
|
||||||
<th style="padding:var(--space-3) var(--space-4);font-weight:var(--weight-semibold);color:var(--c-text-secondary)">Admin</th>
|
<th class="adm-th">Admin</th>
|
||||||
<th style="padding:var(--space-3) var(--space-4);font-weight:var(--weight-semibold);color:var(--c-text-secondary)">Aktion</th>
|
<th class="adm-th">Aktion</th>
|
||||||
<th style="padding:var(--space-3) var(--space-4);font-weight:var(--weight-semibold);color:var(--c-text-secondary)">Ziel</th>
|
<th class="adm-th">Ziel</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
${rows.map((r, i) => `
|
${rows.map((r, i) => `
|
||||||
<tr style="${i % 2 === 1 ? 'background:var(--c-surface-2)' : ''}">
|
<tr style="${i % 2 === 1 ? 'background:var(--c-surface-2)' : ''}">
|
||||||
<td style="padding:var(--space-2) var(--space-4);color:var(--c-text-muted);white-space:nowrap;font-size:var(--text-xs)">
|
<td class="adm-td" style="color:var(--c-text-muted);white-space:nowrap;font-size:var(--text-xs)">
|
||||||
${_formatDateTime(r.created_at)}
|
${_formatDateTime(r.created_at)}
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:var(--space-2) var(--space-4);color:var(--c-text)">
|
<td class="adm-td" style="color:var(--c-text);white-space:nowrap">
|
||||||
${_esc(r.admin_name || '—')}
|
${_esc(r.admin_name || '—')}
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:var(--space-2) var(--space-4)">
|
<td class="adm-td">
|
||||||
<span style="font-size:var(--text-xs);padding:2px 7px;border-radius:3px;
|
<span class="adm-badge-mono">${_esc(r.action)}</span>
|
||||||
background:var(--c-surface-2);color:var(--c-text-secondary);font-family:monospace">
|
${r.detail ? `<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${_esc(r.detail)}</div>` : ''}
|
||||||
${_esc(r.action)}
|
|
||||||
</span>
|
|
||||||
${r.detail ? `<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${_esc(r.detail)}</div>` : ''}
|
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:var(--space-2) var(--space-4);color:var(--c-text-secondary);font-size:var(--text-xs)">
|
<td class="adm-td" style="color:var(--c-text-secondary);font-size:var(--text-xs);white-space:nowrap">
|
||||||
${_esc(r.target || '—')}
|
${_esc(r.target || '—')}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -738,6 +731,7 @@ window.Page_admin = (() => {
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Offline-Cache + Push Notifications + Tile-Cache
|
Offline-Cache + Push Notifications + Tile-Cache
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const CACHE_VERSION = 'by-v152';
|
const CACHE_VERSION = 'by-v153';
|
||||||
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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue