Feature: Timer-Schalter im Admin-Menü (v1.1.2)

Timer kann chapter-weit deaktiviert werden – Display-Ring, Countdown und Zeitbadges werden ausgeblendet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Heubaum
2026-04-21 20:35:58 +02:00
parent 3c37c4ad61
commit 69824b610c
2 changed files with 26 additions and 5 deletions

View File

@@ -1219,9 +1219,11 @@
<div class="status-dot dot-idle" id="sync-dot" title="Display-Verbindung"></div>
<button class="btn btn-secondary btn-sm" onclick="resetMeeting()">↺ Neu starten</button>
<button class="btn btn-secondary btn-sm" onclick="openPdfImport()">📄 PDF importieren</button>
<div id="ctrl-timer-controls" style="display:flex;gap:4px;">
<button class="btn btn-secondary btn-sm" onclick="editDefaultTimer()" title="Standard-Redezeit anpassen" style="font-variant-numeric:tabular-nums;">⏱ <span id="ctrl-timer-default">1:00</span></button>
<button class="btn btn-secondary btn-sm" id="ctrl-timer-toggle" onclick="toggleTimer()" style="font-variant-numeric:tabular-nums;min-width:80px;">▶ Start</button>
<button class="btn btn-secondary btn-sm" id="ctrl-timer-reset" onclick="resetTimer()" title="Timer zurücksetzen">↺ <span id="ctrl-timer-display" style="min-width:36px;display:inline-block;"></span></button>
</div>
<button class="btn btn-secondary btn-sm" onclick="openMemberSync()">🔄 Mitglieder aktualisieren</button>
<button class="btn btn-secondary btn-sm" onclick="openImportExport()">⬇ Import / Export</button>
<button class="btn btn-secondary btn-sm" onclick="window.open(location.href.split('#')[0] + '#display', '_blank', 'noopener')">🖥 Anzeige öffnen</button>
@@ -1711,7 +1713,7 @@
// ============================================================
// CONSTANTS & CHAPTER CONFIG
// ============================================================
const APP_VERSION = '1.1.0';
const APP_VERSION = '1.1.2';
const GITEA_RAW = 'http://72.60.134.73:3000/api/v1/repos/cheubaum/BNI-Vorstellungsrunde/raw';
const GITEA_API = 'http://72.60.134.73:3000/api/v1/repos/cheubaum/BNI-Vorstellungsrunde';
function giteaUrl(path) { return `${GITEA_RAW}/${path}?ref=main`; }
@@ -1758,6 +1760,7 @@ function defaultState() {
mode: 'members', // 'members' | 'absent-summary' | 'visitors'
defaultTimer: 60, // seconds (standard speaking time)
memberTimers: {}, // { memberId: customSeconds }
showTimer: true, // false = timer completely hidden
};
}
@@ -3947,12 +3950,17 @@ function timerCls(rem, total) {
}
function updateTimerDisplays() {
const timerOn = state.showTimer !== false;
const s = _t.remaining;
const tot = _t.total;
const cls = tot > 0 ? timerCls(s, tot) : '';
const txt = tot > 0 ? fmtSecs(s) : '';
const CIRC = 729; // 2 * PI * 116
// Control timer buttons visibility
const tc = document.getElementById('ctrl-timer-controls');
if (tc) tc.style.display = timerOn ? 'flex' : 'none';
// SVG ring
const ring = document.getElementById('timer-ring-progress');
const ringEl = document.getElementById('timer-ring-svg');
@@ -3960,7 +3968,7 @@ function updateTimerDisplays() {
const offset = tot > 0 ? CIRC * (1 - s / tot) : 0;
ring.style.strokeDashoffset = offset;
ring.className = 'timer-ring-progress' + (cls ? ' ' + cls : '');
ringEl.style.display = tot > 0 ? 'block' : 'none';
ringEl.style.display = (timerOn && tot > 0) ? 'block' : 'none';
}
// Display timer text
@@ -3968,7 +3976,7 @@ function updateTimerDisplays() {
if (dt) {
dt.textContent = txt;
dt.className = cls;
dt.style.display = tot > 0 ? 'block' : 'none';
dt.style.display = (timerOn && tot > 0) ? 'block' : 'none';
}
// Control timer display (live countdown)
@@ -4000,6 +4008,7 @@ function stopTimer() {
}
function toggleTimer() {
if (state.showTimer === false) return;
if (_t.running || _t.pending) {
stopTimer();
} else {
@@ -4016,6 +4025,7 @@ function resetTimer() {
}
function scheduleTimer(seconds) {
if (state.showTimer === false) return;
stopTimer();
_t.total = seconds; _t.remaining = seconds;
updateTimerDisplays();
@@ -4037,6 +4047,7 @@ function getPresenterSeconds(presenter) {
}
function renderTimerBadge(memberId) {
if (state.showTimer === false) return '';
const def = state.defaultTimer || 60;
const custom = state.memberTimers && state.memberTimers[memberId];
const secs = custom > 0 ? custom : def;
@@ -4459,6 +4470,15 @@ function renderAdminPanel() {
<div style="font-size:0.78rem;color:var(--muted);margin-bottom:16px;">Letzter Download: ${lastSync}</div>
<a href="admin.html" target="_blank" class="btn btn-primary btn-sm" style="width:100%;display:block;text-align:center;text-decoration:none;margin-bottom:16px;">⚙ Admin-Panel öffnen</a>
` : `<div style="color:var(--muted);font-size:0.88rem;margin-bottom:20px;">Kein Chapter aktiviert.</div>`}
<div style="border-top:1px solid var(--border);padding-top:14px;margin-bottom:14px;">
<div style="font-size:0.78rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:10px;">Einstellungen</div>
<label style="display:flex;align-items:center;gap:10px;cursor:pointer;font-size:0.88rem;">
<input type="checkbox" id="admin-show-timer" ${state.showTimer !== false ? 'checked' : ''}
onchange="state.showTimer=this.checked;saveState();updateTimerDisplays();renderMemberQueue();renderVisitorQueue();renderVisitorsList();"
style="width:16px;height:16px;cursor:pointer;">
Zeitanzeige aktiv
</label>
</div>
<div style="border-top:1px solid var(--border);padding-top:14px;">
<button class="btn btn-ghost btn-sm" style="width:100%;" onclick="confirmSwitchChapter()">↔ Chapter wechseln</button>
</div>
@@ -4478,6 +4498,7 @@ function confirmSwitchChapter() {
initChannel();
if (activeChapter) applyChapterUI(activeChapter.config);
renderCurrentView();
updateTimerDisplays();
checkLicenseAndUpdates();
// Keyboard shortcut hint

View File

@@ -1,4 +1,4 @@
{
"version": "1.1.1",
"notes": "Chapter Lumora und Callisto ergänzt"
"version": "1.1.2",
"notes": "Timer-Schalter im Admin-Menü, DigiSeed-Logo Fix"
}